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.

Relaterade artiklar