1 | import * as path from 'path'
|
2 | import * as fs from 'fs-extra'
|
3 | import * as postcss from 'postcss'
|
4 | import * as pxtransform from 'postcss-pxtransform'
|
5 | import transformCSS from 'taro-css-to-react-native'
|
6 | import {
|
7 | FILE_PROCESSOR_MAP,
|
8 | npm as npmProcess,
|
9 | processTypeEnum,
|
10 | printLog,
|
11 | chalk
|
12 | } from '@tarojs/helper'
|
13 |
|
14 | import { StyleSheetValidation } from './StyleSheet/index'
|
15 |
|
16 | import * as stylelintConfig from '../config/rn-stylelint.json'
|
17 |
|
18 | const DEVICE_RATIO = 'deviceRatio'
|
19 |
|
20 | function getWrapedCSS (css) {
|
21 | return `
|
22 | import { StyleSheet, Dimensions } from 'react-native'
|
23 |
|
24 | // 一般app 只有竖屏模式,所以可以只获取一次 width
|
25 | const deviceWidthDp = Dimensions.get('window').width
|
26 | const uiWidthPx = 375
|
27 |
|
28 | function scalePx2dp (uiElementPx) {
|
29 | return uiElementPx * deviceWidthDp / uiWidthPx
|
30 | }
|
31 |
|
32 | export default StyleSheet.create(${css})
|
33 | `
|
34 | }
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 | function loadStyle ({ filePath, pluginsConfig }, appPath) {
|
44 | const fileExt = path.extname(filePath)
|
45 | const pluginName = FILE_PROCESSOR_MAP[fileExt]
|
46 | if (pluginName) {
|
47 | return npmProcess
|
48 | .callPlugin(pluginName, null, filePath, pluginsConfig[pluginName] || {}, appPath)
|
49 | .then(item => {
|
50 | return {
|
51 | css: item.css.toString(),
|
52 | filePath
|
53 | }
|
54 | })
|
55 | .catch(e => {
|
56 | printLog(processTypeEnum.ERROR, '样式预处理', filePath)
|
57 | console.log(e.stack)
|
58 | })
|
59 | }
|
60 | return new Promise((resolve, reject) => {
|
61 | fs.readFile(filePath, 'utf-8', (err, content) => {
|
62 | if (err) {
|
63 | return reject(err)
|
64 | }
|
65 | resolve({
|
66 | css: content,
|
67 | filePath
|
68 | })
|
69 | })
|
70 | })
|
71 | }
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 | function postCSS ({ css, filePath, projectConfig }) {
|
81 | const pxTransformConfig = {
|
82 | designWidth: projectConfig.designWidth || 750
|
83 | }
|
84 | if (projectConfig.hasOwnProperty(DEVICE_RATIO)) {
|
85 | pxTransformConfig[DEVICE_RATIO] = projectConfig.deviceRatio
|
86 | }
|
87 | return postcss([
|
88 | require('stylelint')(stylelintConfig),
|
89 | require('postcss-reporter')({ clearReportedMessages: true }),
|
90 | pxtransform({
|
91 | platform: 'rn',
|
92 | ...pxTransformConfig
|
93 | })
|
94 | ])
|
95 | .process(css, { from: filePath })
|
96 | .then(result => {
|
97 | return {
|
98 | css: result.css,
|
99 | filePath
|
100 | }
|
101 | })
|
102 | .catch(e => {
|
103 | printLog(processTypeEnum.ERROR, '样式转换', filePath)
|
104 | console.log(e.stack)
|
105 | })
|
106 | }
|
107 |
|
108 | function getStyleObject ({ css, filePath }) {
|
109 | let styleObject = {}
|
110 | try {
|
111 | styleObject = transformCSS(css)
|
112 | } catch (err) {
|
113 | printLog(processTypeEnum.WARNING, 'css-to-react-native 报错', filePath)
|
114 | console.log(chalk.red(err.stack))
|
115 | }
|
116 | return styleObject
|
117 | }
|
118 |
|
119 | function validateStyle ({ styleObject, filePath }) {
|
120 | for (const name in styleObject) {
|
121 | try {
|
122 | StyleSheetValidation.validateStyle(name, styleObject)
|
123 | } catch (err) {
|
124 |
|
125 | if (/Invalid prop `.*` of type `string` supplied to `.*`, expected `number`[^]*/g.test(err.message)) return
|
126 | printLog(processTypeEnum.WARNING, '样式不支持', filePath)
|
127 | console.log(chalk.red(err.message))
|
128 | }
|
129 | }
|
130 | }
|
131 |
|
132 | function writeStyleFile ({ css, tempFilePath }) {
|
133 | const fileContent = getWrapedCSS(css.replace(/"(scalePx2dp\(.*?\))"/g, '$1'))
|
134 | fs.ensureDirSync(path.dirname(tempFilePath))
|
135 | fs.writeFileSync(tempFilePath, fileContent)
|
136 | printLog(processTypeEnum.GENERATE, '生成样式文件', tempFilePath)
|
137 | }
|
138 |
|
139 | export { loadStyle, postCSS, getStyleObject, validateStyle, writeStyleFile }
|