Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(css): use single postcss instance #13738

Merged
merged 1 commit into from
Jul 7, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 85 additions & 61 deletions packages/vite/src/node/plugins/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,72 @@ function getCssResolversKeys(
return Object.keys(resolvers) as unknown as Array<keyof CSSAtImportResolvers>
}

async function compileCSSPreprocessors(
id: string,
lang: PreprocessLang,
code: string,
config: ResolvedConfig,
): Promise<{ code: string; map?: ExistingRawSourceMap; deps?: Set<string> }> {
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<string> | 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
Expand Down Expand Up @@ -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.
Expand All @@ -911,68 +973,25 @@ async function compileCSS(
return { code, map: null }
}

let preprocessorMap: ExistingRawSourceMap | undefined
let modules: Record<string, string> | undefined
const deps = new Set<string>()

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 =
Expand Down Expand Up @@ -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}`
Expand Down