diff --git a/packages/angular/build/src/builders/application/tests/behavior/stylesheet-url-resolution_spec.ts b/packages/angular/build/src/builders/application/tests/behavior/stylesheet-url-resolution_spec.ts
index ebe3a8f2762f..a95017c0bd42 100644
--- a/packages/angular/build/src/builders/application/tests/behavior/stylesheet-url-resolution_spec.ts
+++ b/packages/angular/build/src/builders/application/tests/behavior/stylesheet-url-resolution_spec.ts
@@ -295,6 +295,36 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
harness.expectFile('dist/browser/media/logo.svg').toExist();
});
+ it('should rebase a URL with interpolation using concatenation referencing a local resource', async () => {
+ await harness.writeFiles({
+ 'src/styles.scss': `@use 'theme/a';`,
+ 'src/theme/a.scss': `
+ @import './b';
+ $extra-var: "2";
+ $postfix-var: "xyz";
+ .a {
+ background-image: url("#{$my-var}logo#{$extra-var+ "-" + $postfix-var}.svg")
+ }
+ `,
+ 'src/theme/b.scss': `$my-var: "./images/";`,
+ 'src/theme/images/logo2-xyz.svg': ``,
+ });
+
+ harness.useTarget('build', {
+ ...BASE_OPTIONS,
+ outputHashing: OutputHashing.None,
+ styles: ['src/styles.scss'],
+ });
+
+ const { result } = await harness.executeOnce();
+ expect(result?.success).toBeTrue();
+
+ harness
+ .expectFile('dist/browser/styles.css')
+ .content.toContain(`url("./media/logo2-xyz.svg")`);
+ harness.expectFile('dist/browser/media/logo2-xyz.svg').toExist();
+ });
+
it('should rebase a URL with an non-leading interpolation referencing a local resource', async () => {
await harness.writeFiles({
'src/styles.scss': `@use 'theme/a';`,
diff --git a/packages/angular/build/src/tools/sass/rebasing-importer.ts b/packages/angular/build/src/tools/sass/rebasing-importer.ts
index 533cefd0bdb8..68a69ae8dbb3 100644
--- a/packages/angular/build/src/tools/sass/rebasing-importer.ts
+++ b/packages/angular/build/src/tools/sass/rebasing-importer.ts
@@ -24,28 +24,6 @@ export interface DirectoryEntry {
directories: Set;
}
-/**
- * Ensures that a bare specifier URL path that is intended to be treated as
- * a relative path has a leading `./` or `../` prefix.
- *
- * @param url A bare specifier URL path that should be considered relative.
- * @returns
- */
-function ensureRelative(url: string): string {
- // Empty
- if (!url) {
- return url;
- }
-
- // Already relative
- if (url[0] === '.' && (url[1] === '/' || (url[1] === '.' && url[2] === '/'))) {
- return url;
- }
-
- // Needs prefix
- return './' + url;
-}
-
/**
* A Sass Importer base class that provides the load logic to rebase all `url()` functions
* within a stylesheet. The rebasing will ensure that the URLs in the output of the Sass compiler
@@ -100,18 +78,15 @@ abstract class UrlRebasingImporter implements Importer<'sync'> {
// Sass variable usage either starts with a `$` or contains a namespace and a `.$`
const valueNormalized = value[0] === '$' || /^\w+\.\$/.test(value) ? `#{${value}}` : value;
- const rebasedPath =
- relative(this.entryDirectory, stylesheetDirectory) + '||file:' + valueNormalized;
+ const rebasedPath = relative(this.entryDirectory, stylesheetDirectory);
// Normalize path separators and escape characters
// https://developer.mozilla.org/en-US/docs/Web/CSS/url#syntax
- const rebasedUrl = ensureRelative(
- rebasedPath.replace(/\\/g, '/').replace(/[()\s'"]/g, '\\$&'),
- );
+ const rebasedUrl = rebasedPath.replace(/\\/g, '/').replace(/[()\s'"]/g, '\\$&');
updatedContents ??= new MagicString(contents);
// Always quote the URL to avoid potential downstream parsing problems
- updatedContents.update(start, end, `"${rebasedUrl}"`);
+ updatedContents.update(start, end, `"${rebasedUrl}||file:${valueNormalized}"`);
}
if (updatedContents) {