From ccaa6c8cd25bdaea4cdc32d46684b585594c5563 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 21 Aug 2024 11:47:50 +0900 Subject: [PATCH 1/4] fix(css): fix sass `file:` reference --- packages/vite/src/node/plugins/css.ts | 24 +++++++++++++++++-- playground/css/__tests__/css.spec.ts | 1 + playground/css/file-absolute.scss | 3 +++ playground/css/index.html | 3 +++ playground/css/sass.scss | 1 + .../css/vite.config-sass-modern-compiler.js | 19 ++------------- playground/css/vite.config-sass-modern.js | 15 ++++++++++++ playground/css/vite.config.js | 8 +++++++ 8 files changed, 55 insertions(+), 19 deletions(-) create mode 100644 playground/css/file-absolute.scss diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 848fb84fbf1cd1..e34bd36cdece59 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -2098,8 +2098,16 @@ const makeScssWorker = ( importer: string, filename: string, ) => { - importer = cleanScssBugUrl(importer) - const resolved = await resolvers.sass(url, importer) + let resolved: string | undefined + if (url.startsWith('file:')) { + const fileUrl = new URL(url) + if (fs.existsSync(fileUrl)) { + resolved = fileURLToPath(fileUrl) + } + } else { + importer = cleanScssBugUrl(importer) + resolved = await resolvers.sass(url, importer) + } if (resolved) { try { const data = await rebaseUrls( @@ -2203,6 +2211,12 @@ const makeModernScssWorker = ( url: string, importer: string, ): Promise => { + if (url.startsWith('file:')) { + const fileUrl = new URL(url) + if (fs.existsSync(fileUrl)) { + return fileURLToPath(fileUrl) + } + } importer = cleanScssBugUrl(importer) const resolved = await resolvers.sass(url, importer) return resolved ?? null @@ -2320,6 +2334,12 @@ const makeModernCompilerScssWorker = ( const internalImporter: Sass.Importer<'async'> = { async canonicalize(url, context) { + if (url.startsWith('file:')) { + const fileUrl = new URL(url) + if (fs.existsSync(fileUrl)) { + return fileUrl + } + } const importer = context.containingUrl ? fileURLToPath(context.containingUrl) : options.filename diff --git a/playground/css/__tests__/css.spec.ts b/playground/css/__tests__/css.spec.ts index cb7af939bbd152..14894bf11e4228 100644 --- a/playground/css/__tests__/css.spec.ts +++ b/playground/css/__tests__/css.spec.ts @@ -93,6 +93,7 @@ test('sass', async () => { isBuild ? /ok-[-\w]+\.png/ : `${viteTestUrl}/ok.png`, ) expect(await getColor(partialImport)).toBe('orchid') + expect(await getColor(await page.$('.sass-file-absolute'))).toBe('orange') editFile('sass.scss', (code) => code.replace('color: $injectedColor', 'color: red'), diff --git a/playground/css/file-absolute.scss b/playground/css/file-absolute.scss new file mode 100644 index 00000000000000..508930e3678f6e --- /dev/null +++ b/playground/css/file-absolute.scss @@ -0,0 +1,3 @@ +.sass-file-absolute { + color: orange; +} diff --git a/playground/css/index.html b/playground/css/index.html index a0e92b205e79f6..396ffc02fd3d27 100644 --- a/playground/css/index.html +++ b/playground/css/index.html @@ -41,6 +41,9 @@

CSS

@import dependency w/ no scss entrypoint: this should be lavender

+

+ @import "file:///xxx/absolute-path.scss" should be orange +

Less: This should be blue

diff --git a/playground/css/sass.scss b/playground/css/sass.scss index f2f9e685630597..44beb140e3fe3a 100644 --- a/playground/css/sass.scss +++ b/playground/css/sass.scss @@ -6,6 +6,7 @@ @import 'virtual-dep'; // virtual file added through importer @import '=/pkg-dep'; // package w/out sass field @import '=/weapp.wxss'; // wxss file +@import 'virtual-file-absolute'; .sass { /* injected via vite.config.js */ diff --git a/playground/css/vite.config-sass-modern-compiler.js b/playground/css/vite.config-sass-modern-compiler.js index 9759d58506f597..009da79192ec34 100644 --- a/playground/css/vite.config-sass-modern-compiler.js +++ b/playground/css/vite.config-sass-modern-compiler.js @@ -1,5 +1,6 @@ import { defineConfig } from 'vite' import baseConfig from './vite.config.js' +import configSassModern from './vite.config-sass-modern.js' export default defineConfig({ ...baseConfig, @@ -8,23 +9,7 @@ export default defineConfig({ preprocessorOptions: { ...baseConfig.css.preprocessorOptions, scss: { - api: 'modern-compiler', - additionalData: `$injectedColor: orange;`, - importers: [ - { - canonicalize(url) { - return url === 'virtual-dep' - ? new URL('custom-importer:virtual-dep') - : null - }, - load() { - return { - contents: ``, - syntax: 'scss', - } - }, - }, - ], + ...configSassModern.css.preprocessorOptions.scss, }, }, }, diff --git a/playground/css/vite.config-sass-modern.js b/playground/css/vite.config-sass-modern.js index 9f7acb3a098179..5b95ef0763cdd8 100644 --- a/playground/css/vite.config-sass-modern.js +++ b/playground/css/vite.config-sass-modern.js @@ -1,5 +1,7 @@ import { defineConfig } from 'vite' import baseConfig from './vite.config.js' +import { pathToFileURL } from 'node:url' +import path from 'node:path' export default defineConfig({ ...baseConfig, @@ -24,6 +26,19 @@ export default defineConfig({ } }, }, + { + canonicalize(url) { + return url === 'virtual-file-absolute' + ? new URL('custom-importer:virtual-file-absolute') + : null + }, + load() { + return { + contents: `@import "${pathToFileURL(path.join(import.meta.dirname, 'file-absolute.scss')).href}"`, + syntax: 'scss', + } + }, + }, ], }, }, diff --git a/playground/css/vite.config.js b/playground/css/vite.config.js index 5ac9d448a2734a..e2b7411f0a2367 100644 --- a/playground/css/vite.config.js +++ b/playground/css/vite.config.js @@ -1,4 +1,5 @@ import path from 'node:path' +import { pathToFileURL } from 'node:url' import stylus from 'stylus' import { defineConfig } from 'vite' @@ -65,6 +66,13 @@ export default defineConfig({ function (url) { return url === 'virtual-dep' ? { contents: '' } : null }, + function (url) { + return url === 'virtual-file-absolute' + ? { + contents: `@import "${pathToFileURL(path.join(import.meta.dirname, 'file-absolute.scss')).href}"`, + } + : null + }, function (url) { return url.endsWith('.wxss') ? { contents: '' } : null }, From 57bcf5c40f4a2c706c961b3ae1055be8b409b357 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 21 Aug 2024 11:53:53 +0900 Subject: [PATCH 2/4] refactor: handle `file:` in resolver --- packages/vite/src/node/plugins/css.ts | 44 +++++++++++---------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index e34bd36cdece59..e5ed214c8cd0fd 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -1085,17 +1085,27 @@ function createCSSResolvers(config: ResolvedConfig): CSSAtImportResolvers { }, get sass() { - return ( - sassResolve || - (sassResolve = config.createResolver({ + if (!sassResolve) { + const resolver = config.createResolver({ extensions: ['.scss', '.sass', '.css'], mainFields: ['sass', 'style'], conditions: ['sass', 'style'], tryIndex: true, tryPrefix: '_', preferRelative: true, - })) - ) + }) + sassResolve = async (...args) => { + const id = args[0] + if (id.startsWith('file:')) { + const fileUrl = new URL(id) + if (fs.existsSync(fileUrl)) { + return fileURLToPath(fileUrl) + } + } + return resolver(...args) + } + } + return sassResolve }, get less() { @@ -2098,16 +2108,8 @@ const makeScssWorker = ( importer: string, filename: string, ) => { - let resolved: string | undefined - if (url.startsWith('file:')) { - const fileUrl = new URL(url) - if (fs.existsSync(fileUrl)) { - resolved = fileURLToPath(fileUrl) - } - } else { - importer = cleanScssBugUrl(importer) - resolved = await resolvers.sass(url, importer) - } + importer = cleanScssBugUrl(importer) + const resolved = await resolvers.sass(url, importer) if (resolved) { try { const data = await rebaseUrls( @@ -2211,12 +2213,6 @@ const makeModernScssWorker = ( url: string, importer: string, ): Promise => { - if (url.startsWith('file:')) { - const fileUrl = new URL(url) - if (fs.existsSync(fileUrl)) { - return fileURLToPath(fileUrl) - } - } importer = cleanScssBugUrl(importer) const resolved = await resolvers.sass(url, importer) return resolved ?? null @@ -2334,12 +2330,6 @@ const makeModernCompilerScssWorker = ( const internalImporter: Sass.Importer<'async'> = { async canonicalize(url, context) { - if (url.startsWith('file:')) { - const fileUrl = new URL(url) - if (fs.existsSync(fileUrl)) { - return fileUrl - } - } const importer = context.containingUrl ? fileURLToPath(context.containingUrl) : options.filename From e4babedc97e469d9caddc76c98796e48e01a5549 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 21 Aug 2024 11:59:47 +0900 Subject: [PATCH 3/4] chore: lint --- playground/css/vite.config-sass-modern.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playground/css/vite.config-sass-modern.js b/playground/css/vite.config-sass-modern.js index 5b95ef0763cdd8..f83c2efb366f24 100644 --- a/playground/css/vite.config-sass-modern.js +++ b/playground/css/vite.config-sass-modern.js @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import baseConfig from './vite.config.js' import { pathToFileURL } from 'node:url' import path from 'node:path' +import { defineConfig } from 'vite' +import baseConfig from './vite.config.js' export default defineConfig({ ...baseConfig, From 67d0bb43923eb1a95da70b2ecac8fd738ad7767a Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 21 Aug 2024 19:14:57 +0900 Subject: [PATCH 4/4] fix: check `file://` not `file:` Co-authored-by: patak <583075+patak-dev@users.noreply.github.com> --- packages/vite/src/node/plugins/css.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index e5ed214c8cd0fd..e447a486fc39b7 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -1096,7 +1096,7 @@ function createCSSResolvers(config: ResolvedConfig): CSSAtImportResolvers { }) sassResolve = async (...args) => { const id = args[0] - if (id.startsWith('file:')) { + if (id.startsWith('file://')) { const fileUrl = new URL(id) if (fs.existsSync(fileUrl)) { return fileURLToPath(fileUrl)