Skip to content

Commit d10fece

Browse files
committed
fix(@angular/build): properly rebase Sass url() values with leading interpolations
Previously, the Sass url() rebasing logic was skipping values that contained leading Sass interpolations. This was because an interpolation starts with the `#` character which in CSS indicates a fragment identifier and not a path. However, Sass special cases this scenario by checking if the next character is the `{` character and considers it an interpolation if present. The Angular rebasing logic will now match this behavior. (cherry picked from commit 0b8a520)
1 parent dff4dea commit d10fece

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

packages/angular/build/src/builders/application/tests/behavior/stylesheet-url-resolution_spec.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,58 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
269269
harness.expectFile('dist/browser/media/logo.svg').toExist();
270270
});
271271

272+
it('should rebase a URL with an leading interpolation referencing a local resource', async () => {
273+
await harness.writeFiles({
274+
'src/styles.scss': `@use 'theme/a';`,
275+
'src/theme/a.scss': `
276+
@import './b';
277+
.a {
278+
background-image: url(#{$my-var}logo.svg)
279+
}
280+
`,
281+
'src/theme/b.scss': `$my-var: "./images/";`,
282+
'src/theme/images/logo.svg': `<svg></svg>`,
283+
});
284+
285+
harness.useTarget('build', {
286+
...BASE_OPTIONS,
287+
outputHashing: OutputHashing.None,
288+
styles: ['src/styles.scss'],
289+
});
290+
291+
const { result } = await harness.executeOnce();
292+
expect(result?.success).toBeTrue();
293+
294+
harness.expectFile('dist/browser/styles.css').content.toContain(`url("./media/logo.svg")`);
295+
harness.expectFile('dist/browser/media/logo.svg').toExist();
296+
});
297+
298+
it('should rebase a URL with an non-leading interpolation referencing a local resource', async () => {
299+
await harness.writeFiles({
300+
'src/styles.scss': `@use 'theme/a';`,
301+
'src/theme/a.scss': `
302+
@import './b';
303+
.a {
304+
background-image: url(./#{$my-var}logo.svg)
305+
}
306+
`,
307+
'src/theme/b.scss': `$my-var: "./images/";`,
308+
'src/theme/images/logo.svg': `<svg></svg>`,
309+
});
310+
311+
harness.useTarget('build', {
312+
...BASE_OPTIONS,
313+
outputHashing: OutputHashing.None,
314+
styles: ['src/styles.scss'],
315+
});
316+
317+
const { result } = await harness.executeOnce();
318+
expect(result?.success).toBeTrue();
319+
320+
harness.expectFile('dist/browser/styles.css').content.toContain(`url("./media/logo.svg")`);
321+
harness.expectFile('dist/browser/media/logo.svg').toExist();
322+
});
323+
272324
it('should not process a URL that has been marked as external', async () => {
273325
await harness.writeFiles({
274326
'src/styles.scss': `@use 'theme/a';`,

packages/angular/build/src/tools/sass/rebasing-importer.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,12 @@ abstract class UrlRebasingImporter implements Importer<'sync'> {
8484
}
8585

8686
// Skip if root-relative, absolute or protocol relative url
87-
if (/^((?:\w+:)?\/\/|data:|chrome:|#|\/)/.test(value)) {
87+
if (/^((?:\w+:)?\/\/|data:|chrome:|\/)/.test(value)) {
88+
continue;
89+
}
90+
91+
// Skip if a fragment identifier but not a Sass interpolation
92+
if (value[0] === '#' && value[1] !== '{') {
8893
continue;
8994
}
9095

0 commit comments

Comments
 (0)