feat(compiler-sfc): export dependencies for css and css preprocessors (#1278)
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
/**
|
||||
* @jest-environment node
|
||||
*/
|
||||
|
||||
import {
|
||||
compileStyle,
|
||||
compileStyleAsync,
|
||||
SFCStyleCompileOptions
|
||||
} from '../src/compileStyle'
|
||||
import { mockWarn } from '@vue/shared'
|
||||
import path from 'path'
|
||||
|
||||
describe('SFC scoped CSS', () => {
|
||||
mockWarn()
|
||||
@@ -318,3 +323,20 @@ describe('SFC CSS modules', () => {
|
||||
expect(result.modules!.bazQux).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('SFC style preprocessors', () => {
|
||||
test('scss @import', () => {
|
||||
const res = compileStyle({
|
||||
source: `
|
||||
@import "./import.scss";
|
||||
`,
|
||||
filename: path.resolve(__dirname, './fixture/test.scss'),
|
||||
id: '',
|
||||
preprocessLang: 'scss'
|
||||
})
|
||||
|
||||
expect([...res.dependencies]).toStrictEqual([
|
||||
path.join(__dirname, './fixture/import.scss')
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
3
packages/compiler-sfc/__tests__/fixture/import.scss
vendored
Normal file
3
packages/compiler-sfc/__tests__/fixture/import.scss
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
div {
|
||||
color: red;
|
||||
}
|
||||
0
packages/compiler-sfc/__tests__/fixture/test.scss
Normal file
0
packages/compiler-sfc/__tests__/fixture/test.scss
Normal file
@@ -54,6 +54,7 @@
|
||||
"devDependencies": {
|
||||
"@types/consolidate": "^0.14.0",
|
||||
"@types/lru-cache": "^5.1.0",
|
||||
"pug": "^2.0.4"
|
||||
"pug": "^2.0.4",
|
||||
"sass": "^1.26.9"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import postcss, { ProcessOptions, LazyResult, Result, ResultMap } from 'postcss'
|
||||
import postcss, {
|
||||
ProcessOptions,
|
||||
LazyResult,
|
||||
Result,
|
||||
ResultMap,
|
||||
ResultMessage
|
||||
} from 'postcss'
|
||||
import trimPlugin from './stylePluginTrim'
|
||||
import scopedPlugin from './stylePluginScoped'
|
||||
import {
|
||||
@@ -48,6 +54,7 @@ export interface SFCStyleCompileResults {
|
||||
rawResult: LazyResult | Result | undefined
|
||||
errors: Error[]
|
||||
modules?: Record<string, string>
|
||||
dependencies: Set<string>
|
||||
}
|
||||
|
||||
export function compileStyle(
|
||||
@@ -132,12 +139,28 @@ export function doCompileStyle(
|
||||
let result: LazyResult | undefined
|
||||
let code: string | undefined
|
||||
let outMap: ResultMap | 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: ResultMessage[]) => {
|
||||
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)
|
||||
|
||||
@@ -149,16 +172,19 @@ export function doCompileStyle(
|
||||
map: result.map && (result.map.toJSON() as any),
|
||||
errors,
|
||||
modules: cssModules,
|
||||
rawResult: result
|
||||
rawResult: result,
|
||||
dependencies: recordPlainCssDependencies(result.messages)
|
||||
}))
|
||||
.catch(error => ({
|
||||
code: '',
|
||||
map: undefined,
|
||||
errors: [...errors, error],
|
||||
rawResult: undefined
|
||||
rawResult: undefined,
|
||||
dependencies
|
||||
}))
|
||||
}
|
||||
|
||||
recordPlainCssDependencies(result.messages)
|
||||
// force synchronous transform (we know we only have sync plugins)
|
||||
code = result.css
|
||||
outMap = result.map
|
||||
@@ -170,7 +196,8 @@ export function doCompileStyle(
|
||||
code: code || ``,
|
||||
map: outMap && (outMap.toJSON() as any),
|
||||
errors,
|
||||
rawResult: result
|
||||
rawResult: result,
|
||||
dependencies
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import merge from 'merge-source-map'
|
||||
import path from 'path'
|
||||
|
||||
export interface StylePreprocessor {
|
||||
render(
|
||||
@@ -13,6 +14,7 @@ export interface StylePreprocessorResults {
|
||||
code: string
|
||||
map?: object
|
||||
errors: Error[]
|
||||
dependencies: string[]
|
||||
}
|
||||
|
||||
// .scss/.sass processor
|
||||
@@ -29,18 +31,20 @@ const scss: StylePreprocessor = {
|
||||
|
||||
try {
|
||||
const result = nodeSass.renderSync(finalOptions)
|
||||
|
||||
// sass output path is position path
|
||||
const dependencies = result.stats.includedFiles
|
||||
if (map) {
|
||||
return {
|
||||
code: result.css.toString(),
|
||||
map: merge(map, JSON.parse(result.map.toString())),
|
||||
errors: []
|
||||
errors: [],
|
||||
dependencies
|
||||
}
|
||||
}
|
||||
|
||||
return { code: result.css.toString(), errors: [] }
|
||||
return { code: result.css.toString(), errors: [], dependencies }
|
||||
} catch (e) {
|
||||
return { code: '', errors: [e] }
|
||||
return { code: '', errors: [e], dependencies: [] }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,17 +79,26 @@ const less: StylePreprocessor = {
|
||||
}
|
||||
)
|
||||
|
||||
if (error) return { code: '', errors: [error] }
|
||||
|
||||
if (error) return { code: '', errors: [error], dependencies: [] }
|
||||
// less output path is relative path
|
||||
const dependencies = getAbsolutePaths(
|
||||
result.imports,
|
||||
path.dirname(options.fileName)
|
||||
)
|
||||
if (map) {
|
||||
return {
|
||||
code: result.css.toString(),
|
||||
map: merge(map, result.map),
|
||||
errors: []
|
||||
errors: [],
|
||||
dependencies: dependencies
|
||||
}
|
||||
}
|
||||
|
||||
return { code: result.css.toString(), errors: [] }
|
||||
return {
|
||||
code: result.css.toString(),
|
||||
errors: [],
|
||||
dependencies: dependencies
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,17 +112,23 @@ const styl: StylePreprocessor = {
|
||||
if (map) ref.set('sourcemap', { inline: false, comment: false })
|
||||
|
||||
const result = ref.render()
|
||||
// stylus output path is relative path
|
||||
const dependencies = getAbsolutePaths(
|
||||
ref.deps(),
|
||||
path.dirname(options.fileName)
|
||||
)
|
||||
if (map) {
|
||||
return {
|
||||
code: result,
|
||||
map: merge(map, ref.sourcemap),
|
||||
errors: []
|
||||
errors: [],
|
||||
dependencies
|
||||
}
|
||||
}
|
||||
|
||||
return { code: result, errors: [] }
|
||||
return { code: result, errors: [], dependencies }
|
||||
} catch (e) {
|
||||
return { code: '', errors: [e] }
|
||||
return { code: '', errors: [e], dependencies: [] }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,3 +142,7 @@ export const processors: Record<PreprocessLang, StylePreprocessor> = {
|
||||
styl,
|
||||
stylus: styl
|
||||
}
|
||||
|
||||
function getAbsolutePaths(relativePaths: string[], dirname: string): string[] {
|
||||
return relativePaths.map(relativePath => path.join(dirname, relativePath))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user