typescript - debug-vuejs-från-vs-code: obunden brytpunkt vid felsökning av vueJS-app i Chrome
Versioner :
Visual Studio Code: Version: 1.59.1 (Universal)
vueJS: 3.0.0
Chrome: Version 94.0.4606.61 (Official Build) (x86_64)
Jag använder JavaScript-debuggern som är inbyggd i VS Studio Code. Min applikationsstruktur (inom IDE:n) är som följer:
Root (Maven föräldraprojekt)
- backend (Maven-barnprojekt i Java)
- frontend (Maven-barnprojekt i vueJS)
Det vill säga, jag har en Java-backend som serverar ett vueJS-frontend, och alltihop är paketerat tillsammans i ett Tomcat-webbarkiv (dvs. war-fil). Detta fungerar faktiskt bra, och jag kan felsöka Java-koden med Tomcat-tillägget i VS Studio Code.
Problemet är att felsöka vueJS-logiken. Observera att min vueJS-app använder TypeScript-pluginet. debug-vuejs-from-vs-code startar fint (under rätt felsökningskonfiguration, visas nedan), och jag kan sätta en Browser Breakpoint som faktiskt utlöser ett stopp i IDE. Så den grundläggande kommunikationen mellan VS Studio Code och Chrome fungerar bra. Min misstanke är därför att konfigurationen - alltså antingen launch.json, tsconfig.json eller någon annan IDE-inställning - inte är helt korrekt. Detaljer följer.
vue.config.json:
module.exports = {
// Change build paths to make them Maven compatible; see https://cli.vuejs.org/config/.
outputDir: 'target/dist',
assetsDir: 'static',
publicPath: '/myapp',
configureWebpack: {
devtool: "source-map"
}
}
Här har jag aktiverat källkodskartläggning för webpack-minimerade filer i produktion (dvs klient-side-skript som körs i Chrome). Observera att min app är rotad på /myapp i Chrome.
launch.json:
{
"version": "0.2.0",
"configurations": [
{
"type": "pwa-chrome",
"name": "vuejs: chrome",
"request": "launch",
"url": "http://localhost:8080/myapp",
"breakOnLoad": true,
"webRoot": "${workspaceFolder}/frontend",
"outFiles": ["${workspaceFolder}/frontend/target/dist/static/js/*.js"],
"vueComponentPaths": [
"${workspaceFolder}/frontend/src/**/*.vue"
],
"sourceMaps": true,
"sourceMapPathOverrides": {
"webpack:///myapp/static/js/*": "${webRoot}/src/*",
"webpack:///./~/*": "${webRoot}/node_modules/*",
"webpack:////*": "/*",
"webpack://?:*/*": "${webRoot}/src/*",
"webpack:///([a-z]):/(.+)": "$1:/$2",
"webpack:///src/*": "${webRoot}/src/*",
}
}
]
}
Här motsvarar $ {workspaceFolder} bara roten av mitt källkodsförråd. sourceMapPathOverrides är för närvarande ett kaos - en kombination av standardmappningar och mina egna (därför fruktlösa) försök att mappa JavaScript-resursvägarna på Chrome-sidan till källkodsförrådsvägarna som refereras i IDE:n.
ts.config.json:
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
// "inlineSourceMap": true,
// "inlineSources": true,
"sourceMap": true,
"baseUrl": ".",
"resolveJsonModule": true,
"types": [
"webpack-env",
"jest"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}
En programmerare föreslog att eliminera sourceMap boolean och istället ange inlineSourceMap och inlineSources. Jag har kommenterat ut dessa inställningar, eftersom jag inte kunde avgöra om de hjälper eller inte.
VueJS-byggandet genererar utdata till target/dist, med den här typen av katalog-/fillayout:
target/dist
target/dist/favicon.ico
target/dist/index.html
target/dist/static
target/dist/static/css
target/dist/static/css/chunk-vendors.0f1ada3b.css
target/dist/static/css/app.b6011a27.css
target/dist/static/js
target/dist/static/js/app.74994c71.js.map
target/dist/static/js/chunk-vendors.377aa5d2.js
target/dist/static/js/chunk-vendors.377aa5d2.js.map
target/dist/static/js/app.74994c71.js
target/dist/static/img
target/dist/static/img/logo.82b9c7a5.png
Sammanfattningsvis tror jag att den inbyggda JavaScript-debuggern fungerar bra, men att det finns en konfigurationsbugg som resulterar i Obundna brytpunkter i vueJS-appen i Visual Studio Code.
Ser du problemet?
Svar #1
VS Code-dokumentationen för launch.json-attributet sourceMapPathOverrides är knapp. Särskilt kunde jag inte hitta några syntaktiska regler för överskrivningarna av mappningen. Dock, enligt tidigare kommentar, leder besök på VS Code-länkar i panelen som tillhandahålls av Debug: Diagnose Breakpoint Problems till användbara förklaringar och tips. Jag kunde alltså identifiera de felaktiga mappningarna och har för tillfället löst dem på följande sätt:
"sourceMapPathOverrides": {
"webpack:///./node_modules": "${webRoot}/node_modules",
"webpack:///./src/*": "${webRoot}/src/*"
}
Min hela launch.json visar nu:
{
"version": "0.2.0",
"configurations": [
{
"type": "pwa-chrome",
"name": "vuejs: chrome",
"request": "launch",
"url": "http://localhost:8080/myapp",
"breakOnLoad": true,
"webRoot": "${workspaceFolder}/frontend",
"pathMapping": {
"/_karma_webpack_": "${workspaceFolder}/frontend"
},
"outFiles": ["${workspaceFolder}/frontend/target/dist/**/*.js"],
"vueComponentPaths": [
"${workspaceFolder}/frontend/src/**/*.vue"
],
"sourceMaps": true,
"sourceMapPathOverrides": {
"webpack:///./node_modules": "${webRoot}/node_modules",
"webpack:///./src/*": "${webRoot}/src/*"
}
}
]
}
Med dessa mappningar kunde jag åtminstone få JS-debuggern att "bryta vid laddning". Men ett problem relaterat till att koppla källrad till webbläsarsidan, minifierad JS-logik, kvarstod. För att lösa det problemet inkluderade jag logik som beskrivs här i vue.config.js:
const { SourceMapConsumer, SourceMapGenerator } = require('source-map');
const sourceMaps = {};
module.exports = {
// Change build paths to make them Maven compatible; see https://cli.vuejs.org/config/.
outputDir: 'target/dist',
assetsDir: 'static',
publicPath: '/myapp',
configureWebpack() {
return {
devtool: 'source-map',
plugins: [{
apply(compiler) {
compiler.hooks.thisCompilation.tap('Initializing Compilation', (compilation) => {
compilation.hooks.succeedModule.tap('Module Built', (module) => {
const { resource } = module;
if (!resource) return;
if (/node_modules/.test(resource)) return;
if (!/\.vue/.test(resource)) return;
if (!/type=template/.test(resource)) return;
if (!module['_source'] || !module['_source']['_sourceMap']) return;
const pathWithoutQuery = module.resource.replace(/\?.*$/, '');
sourceMaps[pathWithoutQuery] = module['_source']['_sourceMap'];
});
compilation.hooks.finishModules.tapPromise('All Modules Built', async (modules) => {
for (const module of modules) {
const { resource } = module;
if (!resource) continue;
if (/node_modules/.test(resource)) continue;
if (!/\.vue/.test(resource)) continue;
if (!/type=script/.test(resource)) continue;
if (!/lang=ts/.test(resource)) continue;
if (!module['_source'] || !module['_source']['_sourceMap']) continue;
const pathWithoutQuery = module.resource.replace(/\?.*$/, '');
const templateSourceMap = sourceMaps[pathWithoutQuery];
if (!templateSourceMap) continue;
const scriptSourceMap = module['_source']['_sourceMap'];
scriptSourceMap.sourcesContent = [...templateSourceMap.sourcesContent];
scriptSourceMap.sources = [...templateSourceMap.sources];
const lines = (templateSourceMap.sourcesContent[0] || '').match(/.+/g);
let indexOfScriptTag = 0;
for (const line of lines) {
++indexOfScriptTag;
if (/<script/.test(line)) break;
}
const shiftedSourceMap = await SourceMapConsumer.with(scriptSourceMap, null, async (consumer) => {
const generator = new SourceMapGenerator();
await consumer.eachMapping((mapping) => {
const {
generatedColumn,
generatedLine,
originalColumn,
originalLine
} = mapping;
let name = mapping.name;
let source = templateSourceMap.sources[0] || null;
if (originalLine === null || originalColumn === null) {
name = null;
source = null;
}
else {
original = {
column: originalColumn,
line: originalLine + indexOfScriptTag,
};
}
generator.addMapping({
generated: {
column: generatedColumn,
line: generatedLine,
},
original,
source,
name
});
});
return generator.toJSON();
});
scriptSourceMap.mappings = shiftedSourceMap.mappings;
}
});
});
}
}]
};
}
}
Med denna uppsättning kunde jag starta pwa-chrome-felsökaren från VS Code och steg för steg gå igenom vueJS-komponentlogik i IDE:n.
Ändå har jag beslutat att denna särskilda kombination av teknologier -- dvs (i) vscode-js-debugger, (ii) vueJS 3.x, och (iii) TypeScript-plugin 4.1.x för vueJS 3.x -- helt enkelt inte är den bäst stödda miljön för att felsöka klient-sidig JavaScript genom en IDE.
