Repository navigation
ng serve serves broken JS (HTTP 500 / "Failed to fetch dynamically imported module") when a chunk contains a //# sourceMappingURL= line inside a template literal #33544
Description
Command
serve
Is this a regression?
- Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
No response
Description
When serving with sourcemaps enabled, createAngularMemoryPlugin's load hook removes sourcemap URL comments from the served chunk before handing it to Vite:
code: mapContents ? code.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, '') : code,I believe this is done to strip the chunk's own trailing comment, but the g and m flags make the regex match EVERY line in the file that starts with //# sourceMappingURL= , including lines that are content inside a template literal, not real comments.
The consequence is: any library that embeds bundled code as a string (the classic worker-loader inline-worker pattern, where a webpack-built worker bundle is embedded as a string and instantiated via new Blob(...)). The embedded bundle text ends with its own //# sourceMappingURL=xxx.worker.js.map line. The emitted chunk then looks like:
createInlineWorker(`/******/ (() => { // webpackBootstrap
...thousands of lines of worker code...
//# sourceMappingURL=abc123.worker.js.map`, null);Note the template literal's closing backtick is on the same line as the embedded comment. The replace deletes that entire line — closing backtick and , null); included — producing an unterminated template literal. Vite's import-analysis then fails to parse the chunk and the dev server responds 500 with:
Failed to parse source for import analysis because the content contains invalid JS syntax.
In the browser, if the chunk is lazy, this surfaces as:
TypeError: Failed to fetch dynamically imported module: https://.../my-component.entry-XXXXXXXX.js
Important to note is that ng build is unaffected because the plugin only runs in the dev server.
Minimal Reproduction
ng new repro --defaults && cd repro- Add to
src/main.ts(this simulates what a bundled inline-worker dependency looks like after esbuild re-emits it — a string with mixed quotes and\nescapes is printed as a multiline template literal):
export const workerSource =
'console.log("double") + console.log(\'single\');\n' +
'console.log("double") + console.log(\'single\');\n' +
'//# sourceMappingURL=inline-worker.js.map';
console.log(workerSource.length);ng serve(development configuration,sourceMapenabled — the default)- Open the app. The served
main.jscontains an unterminated template literal and fails to parse; Vite returns 500 withFailed to parse source for import analysis.
You can observe the mechanism standalone: esbuild emits the string above as
var workerSource = `console.log("double") + console.log('single');
console.log("double") + console.log('single');
//# sourceMappingURL=inline-worker.js.map`;and applying code.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, '') to it removes the last line including the closing backtick — invalid JS.
Exception or Error
[vite] Internal server error: Failed to parse source for import analysis because the content contains invalid JS syntax. If you are using JSX, make sure to name the file with the .jsx or .tsx extension.
Plugin: vite:import-analysis
File: .angular/vite-root/<project>/chunk-XXXXXXXX.js
Browser (when the broken chunk is a dependency of a lazy chunk):
TypeError: Failed to fetch dynamically imported module: https://localhost:4200/.../component.entry-XXXXXXXX.js
Your Environment
- Angular CLI / `@angular/build`: 22.0.0 (regex verified still present in 22.0.5 and on `main`)
- Vite: 7.3.2
- Node: 24.x
- OS: Linux
Anything else relevant?
Suggested fix — only strip the chunk's own trailing comment by anchoring the match to the end of the file instead of using g/m:
code: mapContents ? code.replace(/(^|\n)\/\/# sourceMappingURL=[^\r\n]*\s*$/, '$1') : code,As a workaround we've been running this as a patch-package patch and it fixes the issue. Mid-file occurrences (which are always string content, since esbuild already consumes real sourcemap comments during bundling) are preserved, and Vite still appends its own inline map when serving.
The same greedy pattern also appears in javascript-transformer-worker.ts (data.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, '')) and may deserve the same anchoring, although we have not observed a failure through that path.
Activity
angular-automatic-lock-bot commentedon Aug 13, 2026
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.