From c02fac41d31608ef58054f28ee3d8e099c4c6ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Fri, 7 Jul 2023 18:07:40 +0900 Subject: [PATCH] fix(css): use single postcss instance (#13738) --- packages/vite/src/node/plugins/css.ts | 146 +++++++++++++++----------- 1 file changed, 85 insertions(+), 61 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 8d6bb21129580f..32e6bde985c027 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -858,6 +858,72 @@ function getCssResolversKeys( return Object.keys(resolvers) as unknown as Array } +async function compileCSSPreprocessors( + id: string, + lang: PreprocessLang, + code: string, + config: ResolvedConfig, +): Promise<{ code: string; map?: ExistingRawSourceMap; deps?: Set }> { + const { preprocessorOptions, devSourcemap } = config.css ?? {} + const atImportResolvers = getAtImportResolvers(config) + + const preProcessor = preProcessors[lang] + let opts = (preprocessorOptions && preprocessorOptions[lang]) || {} + // support @import from node dependencies by default + switch (lang) { + case PreprocessLang.scss: + case PreprocessLang.sass: + opts = { + includePaths: ['node_modules'], + alias: config.resolve.alias, + ...opts, + } + break + case PreprocessLang.less: + case PreprocessLang.styl: + case PreprocessLang.stylus: + opts = { + paths: ['node_modules'], + alias: config.resolve.alias, + ...opts, + } + } + // important: set this for relative import resolving + opts.filename = cleanUrl(id) + opts.enableSourcemap = devSourcemap ?? false + + const preprocessResult = await preProcessor( + code, + config.root, + opts, + atImportResolvers, + ) + if (preprocessResult.error) { + throw preprocessResult.error + } + + let deps: Set | undefined + if (preprocessResult.deps) { + const normalizedFilename = normalizePath(opts.filename) + // sometimes sass registers the file itself as a dep + deps = new Set( + [...preprocessResult.deps].filter( + (dep) => normalizePath(dep) !== normalizedFilename, + ), + ) + } + + return { + code: preprocessResult.code, + map: combineSourcemapsIfExists( + opts.filename, + preprocessResult.map, + preprocessResult.additionalMap, + ), + deps, + } +} + const configToAtImportResolvers = new WeakMap< ResolvedConfig, CSSAtImportResolvers @@ -887,11 +953,7 @@ async function compileCSS( return compileLightningCSS(id, code, config, urlReplacer) } - const { - modules: modulesOptions, - preprocessorOptions, - devSourcemap, - } = config.css || {} + const { modules: modulesOptions, devSourcemap } = config.css || {} const isModule = modulesOptions !== false && cssModuleRE.test(id) // although at serve time it can work without processing, we do need to // crawl them in order to register watch dependencies. @@ -911,68 +973,25 @@ async function compileCSS( return { code, map: null } } - let preprocessorMap: ExistingRawSourceMap | undefined let modules: Record | undefined const deps = new Set() - const atImportResolvers = getAtImportResolvers(config) - // 2. pre-processors: sass etc. + let preprocessorMap: ExistingRawSourceMap | undefined if (isPreProcessor(lang)) { - const preProcessor = preProcessors[lang] - let opts = (preprocessorOptions && preprocessorOptions[lang]) || {} - // support @import from node dependencies by default - switch (lang) { - case PreprocessLang.scss: - case PreprocessLang.sass: - opts = { - includePaths: ['node_modules'], - alias: config.resolve.alias, - ...opts, - } - break - case PreprocessLang.less: - case PreprocessLang.styl: - case PreprocessLang.stylus: - opts = { - paths: ['node_modules'], - alias: config.resolve.alias, - ...opts, - } - } - // important: set this for relative import resolving - opts.filename = cleanUrl(id) - opts.enableSourcemap = devSourcemap ?? false - - const preprocessResult = await preProcessor( + const preprocessorResult = await compileCSSPreprocessors( + id, + lang, code, - config.root, - opts, - atImportResolvers, + config, ) - - if (preprocessResult.error) { - throw preprocessResult.error - } - - code = preprocessResult.code - preprocessorMap = combineSourcemapsIfExists( - opts.filename, - preprocessResult.map, - preprocessResult.additionalMap, - ) - - if (preprocessResult.deps) { - preprocessResult.deps.forEach((dep) => { - // sometimes sass registers the file itself as a dep - if (normalizePath(dep) !== normalizePath(opts.filename)) { - deps.add(dep) - } - }) - } + code = preprocessorResult.code + preprocessorMap = preprocessorResult.map + preprocessorResult.deps?.forEach((dep) => deps.add(dep)) } // 3. postcss + const atImportResolvers = getAtImportResolvers(config) const postcssOptions = (postcssConfig && postcssConfig.options) || {} const postcssPlugins = @@ -1010,10 +1029,15 @@ async function compileCSS( return id }, async load(id) { - const code = fs.readFileSync(id, 'utf-8') - const result = await compileCSS(id, code, config) - result.deps?.forEach((dep) => deps.add(dep)) - return result.code + const code = await fs.promises.readFile(id, 'utf-8') + const lang = id.match(CSS_LANGS_RE)?.[1] as CssLang | undefined + if (isPreProcessor(lang)) { + const result = await compileCSSPreprocessors(id, lang, code, config) + result.deps?.forEach((dep) => deps.add(dep)) + // TODO: support source map + return result.code + } + return code }, nameLayer(index) { return `vite--anon-layer-${getHash(id)}-${index}`