2019-11-07 10:58:15 +08:00
|
|
|
import merge from 'merge-source-map'
|
2023-04-09 16:50:00 +08:00
|
|
|
import type { RawSourceMap } from 'source-map-js'
|
2023-04-05 16:35:10 +08:00
|
|
|
import type { SFCStyleCompileOptions } from '../compileStyle'
|
2020-09-16 21:28:31 +08:00
|
|
|
import { isFunction } from '@vue/shared'
|
2019-11-07 10:58:15 +08:00
|
|
|
|
2020-07-17 01:38:46 +08:00
|
|
|
export type StylePreprocessor = (
|
|
|
|
source: string,
|
|
|
|
map: RawSourceMap | undefined,
|
|
|
|
options: {
|
|
|
|
[key: string]: any
|
2020-09-16 21:28:31 +08:00
|
|
|
additionalData?: string | ((source: string, filename: string) => string)
|
2020-07-17 01:38:46 +08:00
|
|
|
filename: string
|
|
|
|
},
|
|
|
|
customRequire: SFCStyleCompileOptions['preprocessCustomRequire'],
|
|
|
|
) => StylePreprocessorResults
|
2019-11-07 10:58:15 +08:00
|
|
|
|
|
|
|
export interface StylePreprocessorResults {
|
|
|
|
code: string
|
|
|
|
map?: object
|
2019-12-12 02:33:45 +08:00
|
|
|
errors: Error[]
|
2020-07-17 00:33:37 +08:00
|
|
|
dependencies: string[]
|
2019-11-07 10:58:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// .scss/.sass processor
|
2020-07-17 01:38:46 +08:00
|
|
|
const scss: StylePreprocessor = (source, map, options, load = require) => {
|
|
|
|
const nodeSass = load('sass')
|
|
|
|
const finalOptions = {
|
|
|
|
...options,
|
2020-09-16 21:28:31 +08:00
|
|
|
data: getSource(source, options.filename, options.additionalData),
|
2020-07-17 01:38:46 +08:00
|
|
|
file: options.filename,
|
|
|
|
outFile: options.filename,
|
|
|
|
sourceMap: !!map,
|
2019-11-07 10:58:15 +08:00
|
|
|
}
|
|
|
|
|
2020-07-17 01:38:46 +08:00
|
|
|
try {
|
|
|
|
const result = nodeSass.renderSync(finalOptions)
|
|
|
|
const dependencies = result.stats.includedFiles
|
2019-11-07 10:58:15 +08:00
|
|
|
if (map) {
|
|
|
|
return {
|
|
|
|
code: result.css.toString(),
|
2023-11-27 12:18:01 +08:00
|
|
|
map: merge(
|
|
|
|
map,
|
|
|
|
result.map.toJSON
|
|
|
|
? result.map.toJSON()
|
|
|
|
: JSON.parse(result.map.toString()),
|
|
|
|
),
|
2020-07-17 00:33:37 +08:00
|
|
|
errors: [],
|
2020-07-17 01:38:46 +08:00
|
|
|
dependencies,
|
2019-11-07 10:58:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-17 01:38:46 +08:00
|
|
|
return { code: result.css.toString(), errors: [], dependencies }
|
2021-09-02 21:53:57 +08:00
|
|
|
} catch (e: any) {
|
2020-07-17 01:38:46 +08:00
|
|
|
return { code: '', errors: [e], dependencies: [] }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const sass: StylePreprocessor = (source, map, options, load) =>
|
|
|
|
scss(
|
|
|
|
source,
|
|
|
|
map,
|
|
|
|
{
|
|
|
|
...options,
|
|
|
|
indentedSyntax: true,
|
|
|
|
},
|
|
|
|
load,
|
|
|
|
)
|
|
|
|
|
|
|
|
// .less
|
|
|
|
const less: StylePreprocessor = (source, map, options, load = require) => {
|
|
|
|
const nodeLess = load('less')
|
|
|
|
|
|
|
|
let result: any
|
|
|
|
let error: Error | null = null
|
|
|
|
nodeLess.render(
|
2020-09-16 21:28:31 +08:00
|
|
|
getSource(source, options.filename, options.additionalData),
|
2020-07-17 01:38:46 +08:00
|
|
|
{ ...options, syncImport: true },
|
|
|
|
(err: Error | null, output: any) => {
|
|
|
|
error = err
|
|
|
|
result = output
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
if (error) return { code: '', errors: [error], dependencies: [] }
|
2020-07-29 01:45:24 +08:00
|
|
|
const dependencies = result.imports
|
2020-07-17 01:38:46 +08:00
|
|
|
if (map) {
|
2020-07-17 00:33:37 +08:00
|
|
|
return {
|
|
|
|
code: result.css.toString(),
|
2020-07-17 01:38:46 +08:00
|
|
|
map: merge(map, result.map),
|
2020-07-17 00:33:37 +08:00
|
|
|
errors: [],
|
|
|
|
dependencies: dependencies,
|
|
|
|
}
|
2019-11-07 10:58:15 +08:00
|
|
|
}
|
2020-07-17 01:38:46 +08:00
|
|
|
|
|
|
|
return {
|
|
|
|
code: result.css.toString(),
|
|
|
|
errors: [],
|
|
|
|
dependencies: dependencies,
|
|
|
|
}
|
2019-11-07 10:58:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// .styl
|
2020-07-17 01:38:46 +08:00
|
|
|
const styl: StylePreprocessor = (source, map, options, load = require) => {
|
|
|
|
const nodeStylus = load('stylus')
|
|
|
|
try {
|
2023-07-10 17:52:56 +08:00
|
|
|
const ref = nodeStylus(source, options)
|
2020-07-17 01:38:46 +08:00
|
|
|
if (map) ref.set('sourcemap', { inline: false, comment: false })
|
|
|
|
|
|
|
|
const result = ref.render()
|
2020-07-29 01:45:24 +08:00
|
|
|
const dependencies = ref.deps()
|
2020-07-17 01:38:46 +08:00
|
|
|
if (map) {
|
|
|
|
return {
|
|
|
|
code: result,
|
|
|
|
map: merge(map, ref.sourcemap),
|
|
|
|
errors: [],
|
|
|
|
dependencies,
|
2019-11-07 10:58:15 +08:00
|
|
|
}
|
|
|
|
}
|
2020-07-17 01:38:46 +08:00
|
|
|
|
|
|
|
return { code: result, errors: [], dependencies }
|
2021-09-02 21:53:57 +08:00
|
|
|
} catch (e: any) {
|
2020-07-17 01:38:46 +08:00
|
|
|
return { code: '', errors: [e], dependencies: [] }
|
2019-11-07 10:58:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-16 21:28:31 +08:00
|
|
|
function getSource(
|
|
|
|
source: string,
|
|
|
|
filename: string,
|
|
|
|
additionalData?: string | ((source: string, filename: string) => string),
|
|
|
|
) {
|
|
|
|
if (!additionalData) return source
|
|
|
|
if (isFunction(additionalData)) {
|
|
|
|
return additionalData(source, filename)
|
|
|
|
}
|
|
|
|
return additionalData + source
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:19:39 +08:00
|
|
|
export type PreprocessLang = 'less' | 'sass' | 'scss' | 'styl' | 'stylus'
|
|
|
|
|
|
|
|
export const processors: Record<PreprocessLang, StylePreprocessor> = {
|
2019-11-07 10:58:15 +08:00
|
|
|
less,
|
|
|
|
sass,
|
|
|
|
scss,
|
|
|
|
styl,
|
|
|
|
stylus: styl,
|
|
|
|
}
|