diff --git a/packages/compiler-sfc/package.json b/packages/compiler-sfc/package.json index c052df5c4b4..58227d33bdd 100644 --- a/packages/compiler-sfc/package.json +++ b/packages/compiler-sfc/package.json @@ -46,8 +46,8 @@ "lru-cache": "^5.1.1", "magic-string": "^0.25.7", "merge-source-map": "^1.1.0", - "postcss": "^7.0.32", - "postcss-modules": "^3.2.2", + "postcss": "^8.1.10", + "postcss-modules": "^4.0.0", "postcss-selector-parser": "^6.0.4", "source-map": "^0.6.1" }, diff --git a/packages/compiler-sfc/src/compileStyle.ts b/packages/compiler-sfc/src/compileStyle.ts index f9b7cbe417d..7da2713c4a0 100644 --- a/packages/compiler-sfc/src/compileStyle.ts +++ b/packages/compiler-sfc/src/compileStyle.ts @@ -1,9 +1,9 @@ import postcss, { ProcessOptions, - LazyResult, Result, - ResultMap, - ResultMessage + SourceMap, + Message, + LazyResult } from 'postcss' import trimPlugin from './stylePluginTrim' import scopedPlugin from './stylePluginScoped' @@ -35,28 +35,33 @@ export interface SFCStyleCompileOptions { map?: RawSourceMap } +/** + * Aligns with postcss-modules + * https://github.com/css-modules/postcss-modules + */ +export interface CSSModulesOptions { + scopeBehaviour?: 'global' | 'local' + generateScopedName?: + | string + | ((name: string, filename: string, css: string) => string) + hashPrefix?: string + localsConvention?: 'camelCase' | 'camelCaseOnly' | 'dashes' | 'dashesOnly' + exportGlobals?: boolean + globalModulePaths?: string[] +} + export interface SFCAsyncStyleCompileOptions extends SFCStyleCompileOptions { isAsync?: boolean // css modules support, note this requires async so that we can get the // resulting json modules?: boolean - // maps to postcss-modules options - // https://github.com/css-modules/postcss-modules - modulesOptions?: { - scopeBehaviour?: 'global' | 'local' - globalModulePaths?: string[] - generateScopedName?: - | string - | ((name: string, filename: string, css: string) => string) - hashPrefix?: string - localsConvention?: 'camelCase' | 'camelCaseOnly' | 'dashes' | 'dashesOnly' - } + modulesOptions?: CSSModulesOptions } export interface SFCStyleCompileResults { code: string map: RawSourceMap | undefined - rawResult: LazyResult | Result | undefined + rawResult: Result | LazyResult | undefined errors: Error[] modules?: Record dependencies: Set @@ -149,7 +154,7 @@ export function doCompileStyle( let result: LazyResult | undefined let code: string | undefined - let outMap: ResultMap | undefined + let outMap: SourceMap | undefined // stylus output include plain css. so need remove the repeat item const dependencies = new Set( preProcessedSource ? preProcessedSource.dependencies : [] @@ -162,7 +167,7 @@ export function doCompileStyle( errors.push(...preProcessedSource.errors) } - const recordPlainCssDependencies = (messages: ResultMessage[]) => { + const recordPlainCssDependencies = (messages: Message[]) => { messages.forEach(msg => { if (msg.type === 'dependency') { // postcss output path is absolute position path @@ -226,7 +231,7 @@ function preprocess( return preprocessor( options.source, - options.map, + options.inMap || options.map, { filename: options.filename, ...options.preprocessOptions diff --git a/packages/compiler-sfc/src/cssVars.ts b/packages/compiler-sfc/src/cssVars.ts index 7489d1b511d..4fb721f963d 100644 --- a/packages/compiler-sfc/src/cssVars.ts +++ b/packages/compiler-sfc/src/cssVars.ts @@ -8,7 +8,7 @@ import { BindingMetadata } from '@vue/compiler-dom' import { SFCDescriptor } from './parse' -import postcss, { Root } from 'postcss' +import { PluginCreator } from 'postcss' import hash from 'hash-sum' export const CSS_VARS_HELPER = `useCssVars` @@ -49,20 +49,21 @@ export interface CssVarsPluginOptions { isProd: boolean } -export const cssVarsPlugin = postcss.plugin( - 'vue-scoped', - opts => (root: Root) => { - const { id, isProd } = opts! - root.walkDecls(decl => { +export const cssVarsPlugin: PluginCreator = opts => { + const { id, isProd } = opts! + return { + postcssPlugin: 'vue-sfc-vars', + Declaration(decl) { // rewrite CSS variables if (cssVarRE.test(decl.value)) { decl.value = decl.value.replace(cssVarRE, (_, $1, $2, $3) => { return `var(--${genVarName(id, $1 || $2 || $3, isProd)})` }) } - }) + } } -) +} +cssVarsPlugin.postcss = true export function genCssVarsCode( vars: string[], diff --git a/packages/compiler-sfc/src/stylePluginScoped.ts b/packages/compiler-sfc/src/stylePluginScoped.ts index 44265d5ba31..87c8795cd79 100644 --- a/packages/compiler-sfc/src/stylePluginScoped.ts +++ b/packages/compiler-sfc/src/stylePluginScoped.ts @@ -1,181 +1,202 @@ -import postcss, { Root } from 'postcss' -import selectorParser, { Node, Selector } from 'postcss-selector-parser' +import { PluginCreator, Rule } from 'postcss' +import selectorParser from 'postcss-selector-parser' import { warn } from './warn' const animationNameRE = /^(-\w+-)?animation-name$/ const animationRE = /^(-\w+-)?animation$/ -export default postcss.plugin('vue-scoped', (id: any) => (root: Root) => { +const scopedPlugin: PluginCreator = (id = '') => { const keyframes = Object.create(null) const shortId = id.replace(/^data-v-/, '') - root.each(function rewriteSelectors(node) { - if (node.type !== 'rule') { - // handle media queries - if (node.type === 'atrule') { - if (node.name === 'media' || node.name === 'supports') { - node.each(rewriteSelectors) - } else if (/-?keyframes$/.test(node.name)) { - // register keyframes - keyframes[node.params] = node.params = node.params + '-' + shortId - } + return { + postcssPlugin: 'vue-sfc-scoped', + Rule(rule) { + processRule(id, rule) + }, + AtRule(node) { + if ( + /-?keyframes$/.test(node.name) && + !node.params.endsWith(`-${shortId}`) + ) { + // register keyframes + keyframes[node.params] = node.params = node.params + '-' + shortId } - return - } - - node.selector = selectorParser(selectors => { - function rewriteSelector(selector: Selector, slotted?: boolean) { - let node: Node | null = null - let shouldInject = true - // find the last child node to insert attribute selector - selector.each(n => { - // DEPRECATED ">>>" and "/deep/" combinator - if ( - n.type === 'combinator' && - (n.value === '>>>' || n.value === '/deep/') - ) { - n.value = ' ' - n.spaces.before = n.spaces.after = '' - warn( - `the >>> and /deep/ combinators have been deprecated. ` + - `Use :deep() instead.` - ) - return false + }, + OnceExit(root) { + if (Object.keys(keyframes).length) { + // If keyframes are found in this