| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- import postcss, {
- ProcessOptions,
- Result,
- SourceMap,
- Message,
- LazyResult
- } from 'postcss'
- import trimPlugin from './stylePluginTrim'
- import scopedPlugin from './stylePluginScoped'
- import {
- processors,
- StylePreprocessor,
- StylePreprocessorResults,
- PreprocessLang
- } from './stylePreprocessors'
- import { RawSourceMap } from 'source-map'
- import { cssVarsPlugin } from './cssVars'
- export interface SFCStyleCompileOptions {
- source: string
- filename: string
- id: string
- scoped?: boolean
- trim?: boolean
- isProd?: boolean
- inMap?: RawSourceMap
- preprocessLang?: PreprocessLang
- preprocessOptions?: any
- preprocessCustomRequire?: (id: string) => any
- postcssOptions?: any
- postcssPlugins?: any[]
- /**
- * @deprecated use `inMap` instead.
- */
- 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
- modulesOptions?: CSSModulesOptions
- }
- export interface SFCStyleCompileResults {
- code: string
- map: RawSourceMap | undefined
- rawResult: Result | LazyResult | undefined
- errors: Error[]
- modules?: Record<string, string>
- dependencies: Set<string>
- }
- export function compileStyle(
- options: SFCStyleCompileOptions
- ): SFCStyleCompileResults {
- return doCompileStyle({
- ...options,
- isAsync: false
- }) as SFCStyleCompileResults
- }
- export function compileStyleAsync(
- options: SFCAsyncStyleCompileOptions
- ): Promise<SFCStyleCompileResults> {
- return doCompileStyle({
- ...options,
- isAsync: true
- }) as Promise<SFCStyleCompileResults>
- }
- export function doCompileStyle(
- options: SFCAsyncStyleCompileOptions
- ): SFCStyleCompileResults | Promise<SFCStyleCompileResults> {
- const {
- filename,
- id,
- scoped = false,
- trim = true,
- isProd = false,
- modules = false,
- modulesOptions = {},
- preprocessLang,
- postcssOptions,
- postcssPlugins
- } = options
- const preprocessor = preprocessLang && processors[preprocessLang]
- const preProcessedSource = preprocessor && preprocess(options, preprocessor)
- const map = preProcessedSource
- ? preProcessedSource.map
- : options.inMap || options.map
- const source = preProcessedSource ? preProcessedSource.code : options.source
- const shortId = id.replace(/^data-v-/, '')
- const longId = `data-v-${shortId}`
- const plugins = (postcssPlugins || []).slice()
- plugins.unshift(cssVarsPlugin({ id: shortId, isProd }))
- if (trim) {
- plugins.push(trimPlugin())
- }
- if (scoped) {
- plugins.push(scopedPlugin(longId))
- }
- let cssModules: Record<string, string> | undefined
- if (modules) {
- if (__GLOBAL__ || __ESM_BROWSER__) {
- throw new Error(
- '[@vue/compiler-sfc] `modules` option is not supported in the browser build.'
- )
- }
- if (!options.isAsync) {
- throw new Error(
- '[@vue/compiler-sfc] `modules` option can only be used with compileStyleAsync().'
- )
- }
- plugins.push(
- require('postcss-modules')({
- ...modulesOptions,
- getJSON: (_cssFileName: string, json: Record<string, string>) => {
- cssModules = json
- }
- })
- )
- }
- const postCSSOptions: ProcessOptions = {
- ...postcssOptions,
- to: filename,
- from: filename
- }
- if (map) {
- postCSSOptions.map = {
- inline: false,
- annotation: false,
- prev: map
- }
- }
- let result: LazyResult | undefined
- let code: string | undefined
- let outMap: SourceMap | undefined
- // stylus output include plain css. so need remove the repeat item
- const dependencies = new Set(
- preProcessedSource ? preProcessedSource.dependencies : []
- )
- // sass has filename self when provided filename option
- dependencies.delete(filename)
- const errors: Error[] = []
- if (preProcessedSource && preProcessedSource.errors.length) {
- errors.push(...preProcessedSource.errors)
- }
- const recordPlainCssDependencies = (messages: Message[]) => {
- messages.forEach(msg => {
- if (msg.type === 'dependency') {
- // postcss output path is absolute position path
- dependencies.add(msg.file)
- }
- })
- return dependencies
- }
- try {
- result = postcss(plugins).process(source, postCSSOptions)
- // In async mode, return a promise.
- if (options.isAsync) {
- return result
- .then(result => ({
- code: result.css || '',
- map: result.map && result.map.toJSON(),
- errors,
- modules: cssModules,
- rawResult: result,
- dependencies: recordPlainCssDependencies(result.messages)
- }))
- .catch(error => ({
- code: '',
- map: undefined,
- errors: [...errors, error],
- rawResult: undefined,
- dependencies
- }))
- }
- recordPlainCssDependencies(result.messages)
- // force synchronous transform (we know we only have sync plugins)
- code = result.css
- outMap = result.map
- } catch (e: any) {
- errors.push(e)
- }
- return {
- code: code || ``,
- map: outMap && outMap.toJSON(),
- errors,
- rawResult: result,
- dependencies
- }
- }
- function preprocess(
- options: SFCStyleCompileOptions,
- preprocessor: StylePreprocessor
- ): StylePreprocessorResults {
- if ((__ESM_BROWSER__ || __GLOBAL__) && !options.preprocessCustomRequire) {
- throw new Error(
- `[@vue/compiler-sfc] Style preprocessing in the browser build must ` +
- `provide the \`preprocessCustomRequire\` option to return the in-browser ` +
- `version of the preprocessor.`
- )
- }
- return preprocessor(
- options.source,
- options.inMap || options.map,
- {
- filename: options.filename,
- ...options.preprocessOptions
- },
- options.preprocessCustomRequire
- )
- }
|