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..bccd32b76f95e6 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 replacement 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: {