@tarojs/rn-style-transformer 4.0.1-alpha.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +8 -0
- package/LICENSE +8 -8
- package/README.md +21 -36
- package/__tests__/config.spec.js +203 -0
- package/__tests__/index.spec.js +252 -0
- package/__tests__/platform.spec.js +78 -0
- package/__tests__/styles/b.css +3 -0
- package/__tests__/styles/b.less +3 -0
- package/__tests__/styles/b.rn.css +3 -0
- package/__tests__/styles/b.scss +3 -0
- package/__tests__/styles/b.styl +3 -0
- package/__tests__/styles/c.css +3 -0
- package/__tests__/styles/c.less +5 -0
- package/__tests__/styles/c.rn.scss +5 -0
- package/__tests__/styles/c.scss +5 -0
- package/__tests__/styles/d.rn.scss +3 -0
- package/__tests__/styles/d.scss +3 -0
- package/__tests__/styles/mixins.scss +18 -0
- package/__tests__/styles/nest.rn.less +3 -0
- package/__tests__/styles/variable.scss +1 -0
- package/babel.config.js +12 -0
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/transforms/StyleSheet/ColorPropType.js.map +1 -1
- package/dist/transforms/StyleSheet/ImageStylePropTypes.js +1 -1
- package/dist/transforms/StyleSheet/ImageStylePropTypes.js.map +1 -1
- package/dist/transforms/StyleSheet/ShadowPropTypesIOS.js +1 -1
- package/dist/transforms/StyleSheet/ShadowPropTypesIOS.js.map +1 -1
- package/dist/transforms/StyleSheet/StyleSheetValidation.js +1 -1
- package/dist/transforms/StyleSheet/StyleSheetValidation.js.map +1 -1
- package/dist/transforms/StyleSheet/TextStylePropTypes.js +1 -1
- package/dist/transforms/StyleSheet/TextStylePropTypes.js.map +1 -1
- package/dist/transforms/StyleSheet/TransformPropTypes.js.map +1 -1
- package/dist/transforms/StyleSheet/ViewStylePropTypes.js +1 -1
- package/dist/transforms/StyleSheet/ViewStylePropTypes.js.map +1 -1
- package/dist/transforms/StyleSheet/deprecatedPropType.js.map +1 -1
- package/dist/transforms/StyleSheet/normalizeColor.js.map +1 -1
- package/dist/transforms/index.js +6 -6
- package/dist/transforms/index.js.map +1 -1
- package/dist/transforms/less.js +1 -1
- package/dist/transforms/less.js.map +1 -1
- package/dist/transforms/postcss.js +5 -17
- package/dist/transforms/postcss.js.map +1 -1
- package/dist/transforms/sass.js +2 -2
- package/dist/transforms/sass.js.map +1 -1
- package/dist/transforms/stylus.js +1 -1
- package/dist/transforms/stylus.js.map +1 -1
- package/dist/utils/index.js +2 -2
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/reporterSkip.js.map +1 -1
- package/jest.config.js +8 -0
- package/package.json +31 -34
- package/src/config/rn-stylelint.json +5 -0
- package/src/index.ts +52 -0
- package/src/transforms/StyleSheet/ColorPropType.ts +76 -0
- package/src/transforms/StyleSheet/ImageResizeMode.ts +50 -0
- package/src/transforms/StyleSheet/ImageStylePropTypes.ts +62 -0
- package/src/transforms/StyleSheet/LayoutPropTypes.ts +559 -0
- package/src/transforms/StyleSheet/ShadowPropTypesIOS.ts +48 -0
- package/src/transforms/StyleSheet/StyleSheetValidation.ts +81 -0
- package/src/transforms/StyleSheet/TextStylePropTypes.ts +123 -0
- package/src/transforms/StyleSheet/TransformPropTypes.ts +109 -0
- package/src/transforms/StyleSheet/ViewStylePropTypes.ts +61 -0
- package/src/transforms/StyleSheet/deprecatedPropType.ts +31 -0
- package/src/transforms/StyleSheet/index.ts +5 -0
- package/src/transforms/StyleSheet/normalizeColor.ts +369 -0
- package/src/transforms/index.ts +231 -0
- package/src/transforms/less.ts +50 -0
- package/src/transforms/postcss.ts +96 -0
- package/src/transforms/sass.ts +151 -0
- package/src/transforms/stylus.ts +124 -0
- package/src/types/index.ts +242 -0
- package/src/utils/index.ts +229 -0
- package/src/utils/lessImport.ts +50 -0
- package/src/utils/reporterSkip.ts +27 -0
- package/tsconfig.json +12 -0
- package/dist/index.d.ts +0 -8
- package/dist/transforms/StyleSheet/ColorPropType.d.ts +0 -10
- package/dist/transforms/StyleSheet/ImageResizeMode.d.ts +0 -6
- package/dist/transforms/StyleSheet/ImageStylePropTypes.d.ts +0 -128
- package/dist/transforms/StyleSheet/LayoutPropTypes.d.ts +0 -388
- package/dist/transforms/StyleSheet/ShadowPropTypesIOS.d.ts +0 -45
- package/dist/transforms/StyleSheet/StyleSheetValidation.d.ts +0 -15
- package/dist/transforms/StyleSheet/TextStylePropTypes.d.ts +0 -191
- package/dist/transforms/StyleSheet/TransformPropTypes.d.ts +0 -67
- package/dist/transforms/StyleSheet/ViewStylePropTypes.d.ts +0 -136
- package/dist/transforms/StyleSheet/deprecatedPropType.d.ts +0 -13
- package/dist/transforms/StyleSheet/index.d.ts +0 -2
- package/dist/transforms/StyleSheet/normalizeColor.d.ts +0 -10
- package/dist/transforms/index.d.ts +0 -56
- package/dist/transforms/less.d.ts +0 -2
- package/dist/transforms/postcss.d.ts +0 -13
- package/dist/transforms/sass.d.ts +0 -2
- package/dist/transforms/stylus.d.ts +0 -3
- package/dist/types/index.d.ts +0 -206
- package/dist/utils/index.d.ts +0 -21
- package/dist/utils/lessImport.d.ts +0 -5
- package/dist/utils/reporterSkip.d.ts +0 -7
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { printLog, processTypeEnum, recursiveMerge } from '@tarojs/helper'
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
import transformCSS from 'taro-css-to-react-native'
|
|
4
|
+
|
|
5
|
+
import { Config, PostcssConfig, RenderAdditionalResult, TransformOptions } from '../types'
|
|
6
|
+
import { normalizeSourceMap } from '../utils'
|
|
7
|
+
import lessTransform from './less'
|
|
8
|
+
import postcssTransform, { makePostcssPlugins } from './postcss'
|
|
9
|
+
import sassTransform from './sass'
|
|
10
|
+
import { StyleSheetValidation } from './StyleSheet'
|
|
11
|
+
import stylusTransform, { defaultOptions as stylusDefaultOptions } from './stylus'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export function getWrapedCSS (css) {
|
|
16
|
+
return `
|
|
17
|
+
import { StyleSheet } from 'react-native'
|
|
18
|
+
import { scalePx2dp, scaleVu2dp } from '@tarojs/runtime-rn'
|
|
19
|
+
|
|
20
|
+
// 用来标识 rn-runner transformer 是否读写缓存
|
|
21
|
+
function ignoreStyleFileCache() {}
|
|
22
|
+
|
|
23
|
+
export default StyleSheet.create(${css})
|
|
24
|
+
`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 校验样式
|
|
29
|
+
*/
|
|
30
|
+
function validateStyle ({ styleObject, filename }) {
|
|
31
|
+
for (const name in styleObject) {
|
|
32
|
+
try {
|
|
33
|
+
StyleSheetValidation.validateStyle(name, styleObject)
|
|
34
|
+
} catch (err) {
|
|
35
|
+
// 先忽略掉 scalePx2dp 的报错
|
|
36
|
+
if (/Invalid prop `.*` of type `string` supplied to `.*`, expected `number`[^]*/g.test(err.message)) return
|
|
37
|
+
printLog(processTypeEnum.WARNING, err.message, `entry file: ${filename}`)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
interface PostcssParam {
|
|
42
|
+
css: string
|
|
43
|
+
map: any
|
|
44
|
+
filename: string
|
|
45
|
+
additionalData: string
|
|
46
|
+
postcssConfig: PostcssConfig
|
|
47
|
+
transformOptions: TransformOptions
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const designWidth = 750
|
|
51
|
+
|
|
52
|
+
const deviceRatio = {
|
|
53
|
+
640: 2.34 / 2,
|
|
54
|
+
750: 1,
|
|
55
|
+
828: 1.81 / 2
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const enum ProcessTypes {
|
|
59
|
+
POSTCSS = 'postcss',
|
|
60
|
+
SASS = 'sass',
|
|
61
|
+
LESS = 'less',
|
|
62
|
+
STYLUS = 'stylus',
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const DEFAULT_RN_CONFIG = {
|
|
66
|
+
[ProcessTypes.POSTCSS]: {
|
|
67
|
+
options: {},
|
|
68
|
+
scalable: true
|
|
69
|
+
},
|
|
70
|
+
[ProcessTypes.SASS]: {
|
|
71
|
+
options: {}
|
|
72
|
+
},
|
|
73
|
+
[ProcessTypes.LESS]: {
|
|
74
|
+
options: {}
|
|
75
|
+
},
|
|
76
|
+
[ProcessTypes.STYLUS]: {
|
|
77
|
+
options: stylusDefaultOptions
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export default class StyleTransform {
|
|
82
|
+
config: Config
|
|
83
|
+
|
|
84
|
+
processConfigMap = new Map()
|
|
85
|
+
|
|
86
|
+
constructor (config: Config) {
|
|
87
|
+
this.init(config)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
init = (config: Config) => {
|
|
91
|
+
this.config = {
|
|
92
|
+
designWidth: config.designWidth || designWidth,
|
|
93
|
+
deviceRatio: config.deviceRatio || deviceRatio,
|
|
94
|
+
sass: config.sass || {},
|
|
95
|
+
alias: config.alias ?? {},
|
|
96
|
+
rn: recursiveMerge({}, DEFAULT_RN_CONFIG, config.rn)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
Reflect.ownKeys(this.config.rn).forEach((key: string) => {
|
|
100
|
+
if (
|
|
101
|
+
[
|
|
102
|
+
ProcessTypes.SASS,
|
|
103
|
+
ProcessTypes.LESS,
|
|
104
|
+
ProcessTypes.STYLUS,
|
|
105
|
+
ProcessTypes.POSTCSS
|
|
106
|
+
].includes(key.toLocaleLowerCase() as any)
|
|
107
|
+
) {
|
|
108
|
+
const processConfig = {
|
|
109
|
+
...this.config.rn[key],
|
|
110
|
+
alias: config.alias ?? {}
|
|
111
|
+
}
|
|
112
|
+
if (key.toLocaleLowerCase() === ProcessTypes.SASS) {
|
|
113
|
+
processConfig.sass = this.config.sass
|
|
114
|
+
}
|
|
115
|
+
this.processConfigMap.set(key, processConfig)
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @description 样式文件处理
|
|
122
|
+
* @param {string} src
|
|
123
|
+
* @param {string} filename
|
|
124
|
+
* @param {object} options
|
|
125
|
+
*/
|
|
126
|
+
async processStyle (src: string, filename: string, options: TransformOptions) {
|
|
127
|
+
let result: undefined | RenderAdditionalResult
|
|
128
|
+
let css = src
|
|
129
|
+
let map: undefined | string
|
|
130
|
+
let additionalData = ''
|
|
131
|
+
const ext = path.extname(filename)
|
|
132
|
+
if (/.less$/i.test(ext)) {
|
|
133
|
+
result = await lessTransform(src, filename, this.processConfigMap.get(ProcessTypes.LESS))
|
|
134
|
+
} else if (/.s(c|a)ss$/i.test(ext)) {
|
|
135
|
+
result = await sassTransform(src, filename, this.processConfigMap.get(ProcessTypes.SASS), options)
|
|
136
|
+
} else if (/.styl(us)?$/i.test(ext)) {
|
|
137
|
+
result = await stylusTransform(src, filename, this.processConfigMap.get(ProcessTypes.STYLUS))
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (result) {
|
|
141
|
+
css = Buffer.isBuffer(result.css) ? result.css.toString() : result.css
|
|
142
|
+
map = Buffer.isBuffer(result.map) ? result.map.toString() : result.map
|
|
143
|
+
additionalData = result.additionalData
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// postcss 插件,比如处理平台特有样式,单位转换
|
|
147
|
+
return await this.postCSS({
|
|
148
|
+
css,
|
|
149
|
+
map,
|
|
150
|
+
filename,
|
|
151
|
+
additionalData,
|
|
152
|
+
transformOptions: options,
|
|
153
|
+
postcssConfig: this.processConfigMap.get(ProcessTypes.POSTCSS)
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* postcss处理
|
|
159
|
+
* @param param0 PostcssParam
|
|
160
|
+
* @returns {Promise | any}
|
|
161
|
+
*/
|
|
162
|
+
postCSS ({
|
|
163
|
+
css,
|
|
164
|
+
map,
|
|
165
|
+
filename,
|
|
166
|
+
postcssConfig,
|
|
167
|
+
transformOptions,
|
|
168
|
+
additionalData
|
|
169
|
+
}: PostcssParam) {
|
|
170
|
+
const plugins = makePostcssPlugins({
|
|
171
|
+
filename,
|
|
172
|
+
postcssConfig,
|
|
173
|
+
transformOptions,
|
|
174
|
+
designWidth: this.config.designWidth,
|
|
175
|
+
deviceRatio: this.config.deviceRatio,
|
|
176
|
+
additionalData: additionalData
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
return postcssTransform(
|
|
180
|
+
css,
|
|
181
|
+
filename,
|
|
182
|
+
{
|
|
183
|
+
plugins,
|
|
184
|
+
options: {
|
|
185
|
+
...postcssConfig.options,
|
|
186
|
+
map: map && {
|
|
187
|
+
prev: normalizeSourceMap(map, filename),
|
|
188
|
+
inline: false,
|
|
189
|
+
annotation: false
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
).then(result => {
|
|
194
|
+
return {
|
|
195
|
+
...result,
|
|
196
|
+
filename
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* @description 处理样式入口
|
|
203
|
+
* @param {string} src
|
|
204
|
+
* @param {string} filename
|
|
205
|
+
* @param {object} transform
|
|
206
|
+
* @return {string} JSONString
|
|
207
|
+
*/
|
|
208
|
+
async transform (src: string, filename: string, options: TransformOptions) {
|
|
209
|
+
// printLog(processTypeEnum.START, '样式文件处理开始', filename)
|
|
210
|
+
const result = await this.processStyle(src, filename, options)
|
|
211
|
+
|
|
212
|
+
// 把 css 转换成对象 rn 的样式,接入 taro 的 css-to-react-native,比如有单位的处理
|
|
213
|
+
const styleObject = transformCSS(
|
|
214
|
+
result.css,
|
|
215
|
+
{
|
|
216
|
+
parseMediaQueries: true,
|
|
217
|
+
scalable: this.config.rn.postcss?.scalable
|
|
218
|
+
}
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
// stylelint,转换成对象,对对象进行校验
|
|
222
|
+
validateStyle({ styleObject, filename })
|
|
223
|
+
const css = JSON.stringify(styleObject, null, 2)
|
|
224
|
+
.replace(/"(scalePx2dp\(.*?\))"/g, '$1')
|
|
225
|
+
.replace(/"(scaleVu2dp\(.*?\))"/g, '$1')
|
|
226
|
+
.replace(/\\\\/g, '\\')
|
|
227
|
+
|
|
228
|
+
// 注入自适应方法 scalePx2dp
|
|
229
|
+
return getWrapedCSS(css)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import less from 'less'
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
|
|
4
|
+
import { LessConfig, RenderAdditionalResult, RenderResult } from '../types'
|
|
5
|
+
import { getAdditionalData, insertBefore } from '../utils'
|
|
6
|
+
import makeLessImport from '../utils/lessImport'
|
|
7
|
+
|
|
8
|
+
function renderToCSS (src, filename, options = {} as any) {
|
|
9
|
+
// default plugins
|
|
10
|
+
const plugins = [makeLessImport(options)]
|
|
11
|
+
// default paths set current filePath
|
|
12
|
+
const paths = [path.dirname(path.resolve(process.cwd(), filename))]
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
less
|
|
15
|
+
.render(src, {
|
|
16
|
+
...options,
|
|
17
|
+
filename,
|
|
18
|
+
plugins: plugins.concat(options.plugins || []),
|
|
19
|
+
paths: paths.concat(options.paths || [])
|
|
20
|
+
}, (err, result) => {
|
|
21
|
+
if (err) {
|
|
22
|
+
return reject(err.message)
|
|
23
|
+
}
|
|
24
|
+
resolve(result)
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default function transform (
|
|
30
|
+
src: string,
|
|
31
|
+
filename: string,
|
|
32
|
+
config: LessConfig
|
|
33
|
+
) {
|
|
34
|
+
const additionalData = getAdditionalData(src, config.additionalData)
|
|
35
|
+
const data = insertBefore(src, additionalData)
|
|
36
|
+
|
|
37
|
+
return renderToCSS(
|
|
38
|
+
data,
|
|
39
|
+
filename,
|
|
40
|
+
{
|
|
41
|
+
sourceMap: {
|
|
42
|
+
outputFilename: `${filename}.map`
|
|
43
|
+
},
|
|
44
|
+
alias: config.alias,
|
|
45
|
+
...config.options
|
|
46
|
+
}
|
|
47
|
+
).then((result: RenderResult) => {
|
|
48
|
+
return { ...result, additionalData } as RenderAdditionalResult
|
|
49
|
+
})
|
|
50
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { isNpmPkg, printLog, processTypeEnum, recursiveMerge } from '@tarojs/helper'
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
import postcss from 'postcss'
|
|
4
|
+
import postcssImport from 'postcss-import'
|
|
5
|
+
import pxtransform from 'postcss-pxtransform'
|
|
6
|
+
import { sync as resolveSync } from 'resolve'
|
|
7
|
+
|
|
8
|
+
import stylelintConfig from '../config/rn-stylelint.json'
|
|
9
|
+
import { resolveStyle } from '../utils'
|
|
10
|
+
import reporterSkip from '../utils/reporterSkip'
|
|
11
|
+
|
|
12
|
+
const defaultPxtransformOption: {
|
|
13
|
+
[key: string]: any
|
|
14
|
+
} = {
|
|
15
|
+
enable: true,
|
|
16
|
+
config: {
|
|
17
|
+
platform: 'rn'
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function makePostcssPlugins ({
|
|
22
|
+
filename,
|
|
23
|
+
designWidth,
|
|
24
|
+
deviceRatio,
|
|
25
|
+
postcssConfig,
|
|
26
|
+
transformOptions,
|
|
27
|
+
additionalData
|
|
28
|
+
}) {
|
|
29
|
+
const optionsWithDefaults = ['pxtransform', 'postcss-import', 'postcss-reporter', 'stylelint', 'cssModules']
|
|
30
|
+
|
|
31
|
+
if (designWidth) {
|
|
32
|
+
defaultPxtransformOption.config.designWidth = designWidth
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (deviceRatio) {
|
|
36
|
+
defaultPxtransformOption.config.deviceRatio = deviceRatio
|
|
37
|
+
}
|
|
38
|
+
const pxtransformOption = recursiveMerge({}, defaultPxtransformOption, postcssConfig.pxtransform)
|
|
39
|
+
|
|
40
|
+
const plugins = [
|
|
41
|
+
postcssImport({
|
|
42
|
+
resolve: function resolve (id: string, basedir: string, options: postcssImport.AtImportOptions) {
|
|
43
|
+
return resolveStyle(
|
|
44
|
+
id,
|
|
45
|
+
{
|
|
46
|
+
...options,
|
|
47
|
+
basedir,
|
|
48
|
+
defaultExt: '.css', // 省略后缀则默认 `.css`
|
|
49
|
+
alias: postcssConfig.alias,
|
|
50
|
+
platform: transformOptions.platform
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
if (pxtransformOption.enable) {
|
|
58
|
+
plugins.push(pxtransform(pxtransformOption.config))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const skipRows = additionalData ? additionalData.split('\n').length : 0
|
|
62
|
+
|
|
63
|
+
plugins.push(
|
|
64
|
+
require('stylelint')(stylelintConfig),
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
reporterSkip({ skipRows, filename }),
|
|
67
|
+
require('postcss-reporter')({ clearReportedMessages: true })
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
Object.entries(postcssConfig).forEach(([pluginName, pluginOption]) => {
|
|
71
|
+
if (optionsWithDefaults.indexOf(pluginName) > -1) return
|
|
72
|
+
if (!pluginOption || !(pluginOption as any).enable) return
|
|
73
|
+
|
|
74
|
+
if (!isNpmPkg(pluginName)) { // local plugin
|
|
75
|
+
pluginName = path.join(process.cwd(), pluginName)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const pluginPath = resolveSync(pluginName, { basedir: process.cwd() })
|
|
80
|
+
plugins.push(require(pluginPath)((pluginOption as any).config || {}))
|
|
81
|
+
} catch (e) {
|
|
82
|
+
const msg = e.code === 'MODULE_NOT_FOUND' ? `缺少postcss插件${pluginName}, 已忽略` : e
|
|
83
|
+
printLog(msg, processTypeEnum.WARNING)
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
return plugins
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default function transform (src: string, filename: string, { options, plugins }) {
|
|
91
|
+
return postcss(plugins)
|
|
92
|
+
.process(src, { from: filename, ...options })
|
|
93
|
+
.then(result => {
|
|
94
|
+
return result
|
|
95
|
+
})
|
|
96
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import * as fs from 'fs'
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
|
|
4
|
+
import { RenderAdditionalResult, RenderResult, SassConfig, SassGlobalConfig, TransformOptions } from '../types'
|
|
5
|
+
import { getAdditionalData, insertAfter, insertBefore, resolveStyle, sortStyle } from '../utils'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 用过用户手动安装了 node-sass,启用node-sass,默认使用 sass
|
|
9
|
+
*/
|
|
10
|
+
function getSassImplementation () {
|
|
11
|
+
let sassImplPkg = 'node-sass'
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
require.resolve('node-sass')
|
|
15
|
+
} catch (error) {
|
|
16
|
+
try {
|
|
17
|
+
require.resolve('sass')
|
|
18
|
+
sassImplPkg = 'sass'
|
|
19
|
+
} catch (ignoreError) {
|
|
20
|
+
sassImplPkg = 'sass'
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return require(sassImplPkg)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const sassImplementation = getSassImplementation()
|
|
28
|
+
|
|
29
|
+
function makeURL (resource: string, rootDir: string) {
|
|
30
|
+
const url = path.resolve(rootDir, resource)
|
|
31
|
+
if (!fs.existsSync(url)) {
|
|
32
|
+
throw new Error(`全局注入 scss 文件路径错误: ${url}`)
|
|
33
|
+
}
|
|
34
|
+
return url
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function makeImportStatement (filePath: string, resource: string, rootDir: string) {
|
|
38
|
+
const url = makeURL(resource, rootDir)
|
|
39
|
+
const relativePath = path.relative(filePath, url).replace(/\\/g, '/') // fix window path error
|
|
40
|
+
return `@import './${relativePath}'`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getGlobalResource (filename: string, config: SassGlobalConfig) {
|
|
44
|
+
let resource = ''
|
|
45
|
+
const projectDirectory = config.projectDirectory || process.cwd()
|
|
46
|
+
const filePath = path.dirname(path.resolve(projectDirectory, filename))
|
|
47
|
+
if (typeof config.resource === 'string') {
|
|
48
|
+
resource = makeImportStatement(filePath, config.resource, projectDirectory)
|
|
49
|
+
}
|
|
50
|
+
if (Array.isArray(config.resource)) {
|
|
51
|
+
const resources = config.resource?.map(source => makeImportStatement(filePath, source, projectDirectory)) || []
|
|
52
|
+
resource = resources.join(';\n')
|
|
53
|
+
}
|
|
54
|
+
// https://taro-docs.jd.com/taro/docs/config-detail/#sassdata, data 覆盖 resurce 配置
|
|
55
|
+
return insertAfter(resource, config?.data)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function combineResource (src: string, filename: string, config: SassConfig) {
|
|
59
|
+
// sass config
|
|
60
|
+
const globalResource = getGlobalResource(filename, config.sass || {})
|
|
61
|
+
|
|
62
|
+
// sass tranform config
|
|
63
|
+
const additionalData = getAdditionalData(src, config.additionalData)
|
|
64
|
+
|
|
65
|
+
return insertAfter(globalResource, additionalData)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function renderToCSS (src, filename, options, transformOptions: TransformOptions) {
|
|
69
|
+
const defaultOpts = {
|
|
70
|
+
importer: function (...params) { /* url, prev, done */
|
|
71
|
+
let [url, prev] = params
|
|
72
|
+
// url is the path in import as is, which LibSass encountered.
|
|
73
|
+
// prev is the previously resolved path.
|
|
74
|
+
// done is an optional callback, either consume it or return value synchronously.
|
|
75
|
+
// this.options contains this options hash, this.callback contains the node-style callback
|
|
76
|
+
let basedir = ''
|
|
77
|
+
let defaultExt = ''
|
|
78
|
+
if (process.platform === 'win32') {
|
|
79
|
+
prev = decodeURIComponent(prev)
|
|
80
|
+
}
|
|
81
|
+
if (path.isAbsolute(prev)) {
|
|
82
|
+
({ dir: basedir, ext: defaultExt } = path.parse(prev))
|
|
83
|
+
} else {
|
|
84
|
+
({ dir: basedir, ext: defaultExt } = path.parse(path.resolve(process.cwd(), filename)))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 外部 sass importer 配置
|
|
88
|
+
if (typeof options.importer === 'function') {
|
|
89
|
+
({ file: url } = options.importer(params))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const file = resolveStyle(
|
|
94
|
+
url,
|
|
95
|
+
{
|
|
96
|
+
basedir,
|
|
97
|
+
defaultExt,
|
|
98
|
+
alias: options.alias,
|
|
99
|
+
platform: transformOptions.platform
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
const contents = fs.readFileSync(file, 'utf8')
|
|
103
|
+
return { file, contents }
|
|
104
|
+
} catch (err) {
|
|
105
|
+
return err
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const opts = { ...options, ...defaultOpts, data: src }
|
|
111
|
+
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
sassImplementation.render(opts, (err, result) => {
|
|
114
|
+
if (err) {
|
|
115
|
+
reject(err)
|
|
116
|
+
} else {
|
|
117
|
+
resolve(result)
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default function transform (
|
|
124
|
+
src: string,
|
|
125
|
+
filename: string,
|
|
126
|
+
config: SassConfig,
|
|
127
|
+
transformOptions: TransformOptions
|
|
128
|
+
) {
|
|
129
|
+
const additionalData = combineResource(src, filename, config)
|
|
130
|
+
let data = insertBefore(src, additionalData)
|
|
131
|
+
data = sortStyle(data)
|
|
132
|
+
|
|
133
|
+
if (!data) {
|
|
134
|
+
data = `\n${data}` // fix empty file error. reference https://github.com/sass/node-sass/blob/91c40a0bf0a3923ab9f91b82dcd479c25486235a/lib/index.js#L430
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return renderToCSS(
|
|
138
|
+
data,
|
|
139
|
+
filename,
|
|
140
|
+
{
|
|
141
|
+
// file: filename, // fix[issues/11983]: with file option,dart-sass importer donot execute。
|
|
142
|
+
outFile: `${filename}.map`,
|
|
143
|
+
sourceMap: true, // If no outFile is set, sourceMap parameter is ignored.
|
|
144
|
+
alias: config.alias,
|
|
145
|
+
...config.options
|
|
146
|
+
},
|
|
147
|
+
transformOptions
|
|
148
|
+
).then((result: RenderResult) => {
|
|
149
|
+
return { ...result, additionalData } as RenderAdditionalResult
|
|
150
|
+
})
|
|
151
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import stylus from 'stylus'
|
|
2
|
+
|
|
3
|
+
import { RenderAdditionalResult, RenderResult, StylusConfig, StylusRenderOptions } from '../types'
|
|
4
|
+
import { getAdditionalData, insertBefore } from '../utils'
|
|
5
|
+
|
|
6
|
+
export const defaultOptions: StylusRenderOptions = {
|
|
7
|
+
use: [],
|
|
8
|
+
include: [],
|
|
9
|
+
import: [],
|
|
10
|
+
define: {},
|
|
11
|
+
includeCSS: false,
|
|
12
|
+
// resolveURL: { nocheck: false },
|
|
13
|
+
lineNumbers: false,
|
|
14
|
+
hoistAtrules: false,
|
|
15
|
+
disableCache: false
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function renderToCSS (src, filename, options = {} as StylusRenderOptions) {
|
|
19
|
+
const stylusOptions = { filename, ...options }
|
|
20
|
+
const styl = stylus(src, stylusOptions)
|
|
21
|
+
|
|
22
|
+
styl.set(
|
|
23
|
+
'sourcemap',
|
|
24
|
+
{
|
|
25
|
+
comment: true,
|
|
26
|
+
sourceRoot: '.', // stylusOptions.dest
|
|
27
|
+
basePath: '.'
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
// include regular CSS on @import
|
|
32
|
+
if (stylusOptions.includeCSS) {
|
|
33
|
+
styl.set('include css', true)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (stylusOptions.hoistAtrules) {
|
|
37
|
+
styl.set('hoist atrules', true)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (stylusOptions.lineNumbers) {
|
|
41
|
+
styl.set('linenos', true)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (stylusOptions.disableCache) {
|
|
45
|
+
styl.set('cache', false)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
typeof stylusOptions.use !== 'undefined' &&
|
|
50
|
+
stylusOptions.use.length > 0
|
|
51
|
+
) {
|
|
52
|
+
let { length } = stylusOptions.use
|
|
53
|
+
|
|
54
|
+
// eslint-disable-next-line no-plusplus
|
|
55
|
+
while (length--) {
|
|
56
|
+
const [item] = stylusOptions.use.splice(length, 1)
|
|
57
|
+
let useIn: any = item
|
|
58
|
+
if (typeof item === 'string') {
|
|
59
|
+
try {
|
|
60
|
+
const resolved = require.resolve(item)
|
|
61
|
+
|
|
62
|
+
// eslint-disable-next-line import/no-dynamic-require, global-require
|
|
63
|
+
useIn = require(resolved)(stylusOptions)
|
|
64
|
+
} catch (error) {
|
|
65
|
+
const msg = `Failed to load "${item}" Stylus plugin.Are you sure it's installed?\n${error}`
|
|
66
|
+
throw new Error(msg)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
styl.use(useIn)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (typeof stylusOptions.import !== 'undefined') {
|
|
74
|
+
for (const imported of stylusOptions.import) {
|
|
75
|
+
styl.import(imported)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (typeof stylusOptions.include !== 'undefined') {
|
|
80
|
+
for (const included of stylusOptions.include) {
|
|
81
|
+
styl.include(included)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// if (stylusOptions.resolveURL !== false) {
|
|
86
|
+
// styl.define('url', urlResolver(stylusOptions.resolveURL))
|
|
87
|
+
// }
|
|
88
|
+
|
|
89
|
+
if (typeof stylusOptions.define !== 'undefined') {
|
|
90
|
+
const definitions = Array.isArray(stylusOptions.define)
|
|
91
|
+
? stylusOptions.define
|
|
92
|
+
: Object.entries(stylusOptions.define)
|
|
93
|
+
|
|
94
|
+
for (const defined of definitions) {
|
|
95
|
+
styl.define.apply(null, defined)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
styl.render((err, css) => {
|
|
101
|
+
if (err) {
|
|
102
|
+
reject(err)
|
|
103
|
+
} else {
|
|
104
|
+
resolve({
|
|
105
|
+
css,
|
|
106
|
+
map: styl.sourcemap
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export default function transform (src: string, filename: string, config: StylusConfig) {
|
|
114
|
+
const additionalData = getAdditionalData(src, config.additionalData)
|
|
115
|
+
const data = insertBefore(src, additionalData)
|
|
116
|
+
|
|
117
|
+
return renderToCSS(
|
|
118
|
+
data,
|
|
119
|
+
filename,
|
|
120
|
+
config.options
|
|
121
|
+
).then((result: RenderResult) => {
|
|
122
|
+
return { ...result, additionalData } as RenderAdditionalResult
|
|
123
|
+
})
|
|
124
|
+
}
|