Skip to content

Commit 47a064b

Browse files
committed
feat(@angular-devkit/build-angular): emit external sourcemaps for component styles
This commits, changes the behaviour in the esbuild based builders by emitting component sourcemaps in external files instead of inlining them. Closes #24049 and closes #26676
1 parent ceffafe commit 47a064b

File tree

6 files changed

+87
-52
lines changed

6 files changed

+87
-52
lines changed

packages/angular_devkit/build_angular/src/builders/application/tests/options/sourcemap_spec.ts

+36
Original file line numberDiff line numberDiff line change
@@ -136,5 +136,41 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
136136

137137
harness.expectFile('dist/browser/main.js.map').content.toContain('"x_google_ignoreList"');
138138
});
139+
140+
it('should generate component sourcemaps when sourcemaps when true', async () => {
141+
await harness.writeFile('src/app/app.component.css', `* { color: red}`);
142+
143+
harness.useTarget('build', {
144+
...BASE_OPTIONS,
145+
sourceMap: true,
146+
});
147+
148+
const { result } = await harness.executeOnce();
149+
150+
expect(result?.success).toBeTrue();
151+
152+
harness
153+
.expectFile('dist/browser/main.js')
154+
.content.toContain('sourceMappingURL=app.component.css.map');
155+
harness.expectFile('dist/browser/app.component.css.map').toExist();
156+
});
157+
158+
it('should not generate component sourcemaps when sourcemaps when false', async () => {
159+
await harness.writeFile('src/app/app.component.css', `* { color: red}`);
160+
161+
harness.useTarget('build', {
162+
...BASE_OPTIONS,
163+
sourceMap: false,
164+
});
165+
166+
const { result } = await harness.executeOnce();
167+
168+
expect(result?.success).toBeTrue();
169+
170+
harness
171+
.expectFile('dist/browser/main.js')
172+
.content.not.toContain('sourceMappingURL=app.component.css.map');
173+
harness.expectFile('dist/browser/app.component.css.map').toNotExist();
174+
});
139175
});
140176
});

packages/angular_devkit/build_angular/src/tools/esbuild/angular/compiler-plugin.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,15 @@ export function createCompilerPlugin(
151151
);
152152
}
153153

154-
const { contents, resourceFiles, referencedFiles, errors, warnings } = stylesheetResult;
154+
const { contents, outputFiles, metafile, referencedFiles, errors, warnings } =
155+
stylesheetResult;
155156
if (errors) {
156157
(result.errors ??= []).push(...errors);
157158
}
158159
(result.warnings ??= []).push(...warnings);
159160
additionalResults.set(stylesheetFile ?? containingFile, {
160-
outputFiles: resourceFiles,
161-
metafile: stylesheetResult.metafile,
161+
outputFiles,
162+
metafile,
162163
});
163164

164165
if (referencedFiles) {

packages/angular_devkit/build_angular/src/tools/esbuild/angular/component-stylesheets.ts

+43-45
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class ComponentStylesheetBundler {
5757
});
5858
});
5959

60-
return extractResult(await bundlerContext.bundle(), bundlerContext.watchFiles);
60+
return this.extractResult(await bundlerContext.bundle(), bundlerContext.watchFiles);
6161
}
6262

6363
async bundleInline(data: string, filename: string, language: string) {
@@ -103,7 +103,7 @@ export class ComponentStylesheetBundler {
103103
});
104104

105105
// Extract the result of the bundling from the output files
106-
return extractResult(await bundlerContext.bundle(), bundlerContext.watchFiles);
106+
return this.extractResult(await bundlerContext.bundle(), bundlerContext.watchFiles);
107107
}
108108

109109
invalidate(files: Iterable<string>) {
@@ -128,52 +128,50 @@ export class ComponentStylesheetBundler {
128128

129129
await Promise.allSettled(contexts.map((context) => context.dispose()));
130130
}
131-
}
132131

133-
function extractResult(result: BundleContextResult, referencedFiles?: Set<string>) {
134-
let contents = '';
135-
let map;
136-
let outputPath;
137-
const resourceFiles: OutputFile[] = [];
138-
if (!result.errors) {
139-
for (const outputFile of result.outputFiles) {
140-
const filename = path.basename(outputFile.path);
141-
if (outputFile.type === BuildOutputFileType.Media) {
142-
// The output files could also contain resources (images/fonts/etc.) that were referenced
143-
resourceFiles.push(outputFile);
144-
} else if (filename.endsWith('.css')) {
145-
outputPath = outputFile.path;
146-
contents = outputFile.text;
147-
} else if (filename.endsWith('.css.map')) {
148-
map = outputFile.text;
149-
} else {
150-
throw new Error(
151-
`Unexpected non CSS/Media file "${filename}" outputted during component stylesheet processing.`,
152-
);
132+
private extractResult(result: BundleContextResult, referencedFiles?: Set<string>) {
133+
let contents = '';
134+
let metafile;
135+
const outputFiles: OutputFile[] = [];
136+
137+
if (!result.errors) {
138+
for (const outputFile of result.outputFiles) {
139+
const filename = path.basename(outputFile.path);
140+
141+
// Needed for Bazel as otherwise the files will not be written in the correct place.
142+
outputFile.path = path.join(this.options.workspaceRoot, outputFile.path);
143+
144+
if (outputFile.type === BuildOutputFileType.Media) {
145+
// The output files could also contain resources (images/fonts/etc.) that were referenced
146+
outputFiles.push(outputFile);
147+
} else if (filename.endsWith('.css')) {
148+
contents = outputFile.text;
149+
} else if (filename.endsWith('.css.map')) {
150+
outputFiles.push(outputFile);
151+
} else {
152+
throw new Error(
153+
`Unexpected non CSS/Media file "${filename}" outputted during component stylesheet processing.`,
154+
);
155+
}
153156
}
157+
158+
metafile = result.metafile;
159+
// Remove entryPoint fields from outputs to prevent the internal component styles from being
160+
// treated as initial files. Also mark the entry as a component resource for stat reporting.
161+
Object.values(metafile.outputs).forEach((output) => {
162+
delete output.entryPoint;
163+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
164+
(output as any)['ng-component'] = true;
165+
});
154166
}
155-
}
156167

157-
let metafile;
158-
if (!result.errors) {
159-
metafile = result.metafile;
160-
// Remove entryPoint fields from outputs to prevent the internal component styles from being
161-
// treated as initial files. Also mark the entry as a component resource for stat reporting.
162-
Object.values(metafile.outputs).forEach((output) => {
163-
delete output.entryPoint;
164-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
165-
(output as any)['ng-component'] = true;
166-
});
168+
return {
169+
errors: result.errors,
170+
warnings: result.warnings,
171+
contents,
172+
outputFiles,
173+
metafile,
174+
referencedFiles,
175+
};
167176
}
168-
169-
return {
170-
errors: result.errors,
171-
warnings: result.warnings,
172-
contents,
173-
map,
174-
path: outputPath,
175-
resourceFiles,
176-
metafile,
177-
referencedFiles,
178-
};
179177
}

packages/angular_devkit/build_angular/src/tools/esbuild/angular/jit-plugin-callbacks.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,10 @@ export function setupJitPluginCallbacks(
121121
);
122122
}
123123

124-
const { contents, resourceFiles, errors, warnings, metafile, referencedFiles } =
124+
const { contents, outputFiles, errors, warnings, metafile, referencedFiles } =
125125
stylesheetResult;
126126

127-
additionalResultFiles.set(entry.path, { outputFiles: resourceFiles, metafile });
127+
additionalResultFiles.set(entry.path, { outputFiles, metafile });
128128

129129
return {
130130
errors,

packages/angular_devkit/build_angular/src/tools/esbuild/compiler-plugin-options.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export function createCompilerPluginOptions(
6060
// Hidden component stylesheet sourcemaps are inaccessible which is effectively
6161
// the same as being disabled. Disabling has the advantage of avoiding the overhead
6262
// of sourcemap processing.
63-
!!sourcemapOptions.styles && (sourcemapOptions.hidden ? false : 'inline'),
63+
sourcemapOptions.styles && !sourcemapOptions.hidden ? 'linked' : false,
6464
outputNames,
6565
includePaths: stylePreprocessorOptions?.includePaths,
6666
externalDependencies,

packages/angular_devkit/build_angular/src/tools/esbuild/stylesheets/bundle-options.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export interface BundleStylesheetOptions {
2222
optimization: boolean;
2323
inlineFonts: boolean;
2424
preserveSymlinks?: boolean;
25-
sourcemap: boolean | 'external' | 'inline';
25+
sourcemap: boolean | 'external' | 'inline' | 'linked';
2626
outputNames: { bundles: string; media: string };
2727
includePaths?: string[];
2828
externalDependencies?: string[];

0 commit comments

Comments
 (0)