From 721d50bae9549811a0b065e296f071a3af7745c9 Mon Sep 17 00:00:00 2001
From: Charles Lyding <19598772+clydin@users.noreply.github.com>
Date: Wed, 29 May 2024 11:13:51 -0400
Subject: [PATCH] fix(@angular/build): avoid escaping rebased Sass URL values
Remove escaping of the raw existing values present within a URL
when rebasing Sass files. These values are present in input code
and should retain their respective errors and behavior as written.
The escaping of the prefixed path is retained to prevent parsing errors
for the internally injected values.
---
.../stylesheet-url-resolution_spec.ts | 30 ++++++++++++++++++
.../build/src/tools/sass/rebasing-importer.ts | 31 ++-----------------
2 files changed, 33 insertions(+), 28 deletions(-)
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) {