From 69b499f998560146aaa38e8cd0ae28975540396b Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Thu, 26 Jun 2025 15:04:32 +0900 Subject: [PATCH 1/2] fix(css): skip resolving resolved paths in sass --- packages/vite/src/node/plugins/css.ts | 47 ++++++++++++-------- playground/css/__tests__/sass-tests.ts | 4 ++ playground/css/index.html | 3 ++ playground/css/nested/replacement-alias.scss | 3 ++ playground/css/sass.scss | 1 + playground/css/vite.config.js | 21 ++++++--- 6 files changed, 53 insertions(+), 26 deletions(-) create mode 100644 playground/css/nested/replacement-alias.scss diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 1051e7527b42d4..4db92302a18a6e 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -2433,23 +2433,31 @@ const makeScssWorker = ( return unquotedUrl.startsWith('#{') } - const internalImporter: Sass.Importer<'async'> = { + const createInternalImporter = ( + isForRelative: boolean, + ): Sass.Importer<'async'> => ({ async canonicalize(url, context) { - const importer = context.containingUrl - ? fileURLToPath(context.containingUrl) - : options.filename - const resolved = await resolvers.sass( - environment, - url, - cleanScssBugUrl(importer), - ) - if ( - resolved && - (resolved.endsWith('.css') || - resolved.endsWith('.scss') || - resolved.endsWith('.sass')) - ) { - return pathToFileURL(resolved) + if (isForRelative) { + // sass passes resolved paths for importer passed to `importer` option + const resolved = new URL(url, context.containingUrl ?? undefined) + if (fs.existsSync(resolved)) return resolved + } else { + const importer = context.containingUrl + ? fileURLToPath(context.containingUrl) + : options.filename + const resolved = await resolvers.sass( + environment, + url, + cleanScssBugUrl(importer), + ) + if ( + resolved && + (resolved.endsWith('.css') || + resolved.endsWith('.scss') || + resolved.endsWith('.sass')) + ) { + return pathToFileURL(resolved) + } } return null }, @@ -2472,12 +2480,13 @@ const makeScssWorker = ( result.contents ?? (await fsp.readFile(result.file, 'utf-8')) return { contents, syntax, sourceMapUrl: canonicalUrl } }, - } + }) + sassOptions.importers = [ ...(sassOptions.importers ?? []), - internalImporter, + createInternalImporter(false), ] - sassOptions.importer ??= internalImporter + sassOptions.importer ??= createInternalImporter(true) const result = await compiler.compileStringAsync(data, sassOptions) return { diff --git a/playground/css/__tests__/sass-tests.ts b/playground/css/__tests__/sass-tests.ts index d262de638aeecd..f0034d09d70604 100644 --- a/playground/css/__tests__/sass-tests.ts +++ b/playground/css/__tests__/sass-tests.ts @@ -7,6 +7,9 @@ export const sassTest = () => { const atImport = await page.$('.sass-at-import') const atImportAlias = await page.$('.sass-at-import-alias') const atImportRelative = await page.$('.sass-at-import-relative') + const atImportReplacementAlias = await page.$( + '.sass-at-import-replacement-alias', + ) const urlStartsWithVariable = await page.$('.sass-url-starts-with-variable') const urlStartsWithVariableInterpolation1 = await page.$( '.sass-url-starts-with-interpolation1', @@ -35,6 +38,7 @@ export const sassTest = () => { expect(await getBg(atImportRelative)).toMatch( isBuild ? /base64/ : '/nested/icon.png', ) + expect(await getColor(atImportReplacementAlias)).toBe('olive') expect(await getBg(urlStartsWithVariable)).toMatch( isBuild ? /ok-[-\w]+\.png/ : `${viteTestUrl}/ok.png`, ) diff --git a/playground/css/index.html b/playground/css/index.html index d52e88ab5860a1..6b44800f683a6f 100644 --- a/playground/css/index.html +++ b/playground/css/index.html @@ -36,6 +36,9 @@
@import from SASS relative: This should be olive and have bg image
++ @import with replement alias from SASS: This should be olive +
@import from SASS _partial: This should be orchid
url starts with variable
diff --git a/playground/css/nested/replacement-alias.scss b/playground/css/nested/replacement-alias.scss
new file mode 100644
index 00000000000000..41d521140fc37a
--- /dev/null
+++ b/playground/css/nested/replacement-alias.scss
@@ -0,0 +1,3 @@
+.sass-at-import-replacement-alias {
+ color: olive;
+}
diff --git a/playground/css/sass.scss b/playground/css/sass.scss
index fe1138c0214f14..cdb3b51a0ab466 100644
--- a/playground/css/sass.scss
+++ b/playground/css/sass.scss
@@ -9,6 +9,7 @@
@use '=/weapp.wxss'; // wxss file
@use 'virtual-file-absolute';
@use '=/scss-dir/main.scss'; // "./dir" reference from vite custom importer
+@use '=replace/nested/replacement-alias.scss';
.sass {
/* injected via vite.config.js */
diff --git a/playground/css/vite.config.js b/playground/css/vite.config.js
index 8adb5163e365b2..71ab69b3c35d67 100644
--- a/playground/css/vite.config.js
+++ b/playground/css/vite.config.js
@@ -62,13 +62,20 @@ export default defineConfig({
},
},
resolve: {
- alias: {
- '=': __dirname,
- spacefolder: __dirname + '/folder with space',
- '#alias': __dirname + '/aliased/foo.css',
- '#alias?inline': __dirname + '/aliased/foo.css?inline',
- '#alias-module': __dirname + '/aliased/bar.module.css',
- },
+ alias: [
+ { find: '=', replacement: __dirname },
+ { find: /=replace\/(.*)/, replacement: `${__dirname}/$1` },
+ { find: 'spacefolder', replacement: __dirname + '/folder with space' },
+ { find: '#alias', replacement: __dirname + '/aliased/foo.css' },
+ {
+ find: '#alias?inline',
+ replacement: __dirname + '/aliased/foo.css?inline',
+ },
+ {
+ find: '#alias-module',
+ replacement: __dirname + '/aliased/bar.module.css',
+ },
+ ],
},
css: {
modules: {
From 925e9b890fade07cfd54506fd6bc0f5f5c16f907 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=BF=A0?= CSS
@import from SASS relative: This should be olive and have bg image
- @import with replement alias from SASS: This should be olive + @import with replacement alias from SASS: This should be olive
@import from SASS _partial: This should be orchid
url starts with variable