@mpxjs/webpack-plugin 2.9.17 → 2.9.19-react.0

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.
Files changed (66) hide show
  1. package/lib/config.js +59 -97
  2. package/lib/dependencies/ResolveDependency.js +2 -2
  3. package/lib/helpers.js +5 -1
  4. package/lib/index.js +27 -19
  5. package/lib/loader.js +56 -118
  6. package/lib/native-loader.js +43 -20
  7. package/lib/platform/index.js +3 -0
  8. package/lib/platform/style/wx/index.js +413 -0
  9. package/lib/platform/template/wx/component-config/button.js +36 -0
  10. package/lib/platform/template/wx/component-config/cover-view.js +1 -1
  11. package/lib/platform/template/wx/component-config/image.js +15 -0
  12. package/lib/platform/template/wx/component-config/input.js +36 -0
  13. package/lib/platform/template/wx/component-config/scroll-view.js +27 -1
  14. package/lib/platform/template/wx/component-config/swiper-item.js +13 -1
  15. package/lib/platform/template/wx/component-config/swiper.js +25 -1
  16. package/lib/platform/template/wx/component-config/text.js +17 -1
  17. package/lib/platform/template/wx/component-config/textarea.js +39 -0
  18. package/lib/platform/template/wx/component-config/unsupported.js +18 -0
  19. package/lib/platform/template/wx/component-config/view.js +15 -1
  20. package/lib/platform/template/wx/index.js +89 -7
  21. package/lib/react/index.js +92 -0
  22. package/lib/react/processJSON.js +362 -0
  23. package/lib/react/processScript.js +40 -0
  24. package/lib/react/processStyles.js +63 -0
  25. package/lib/react/processTemplate.js +151 -0
  26. package/lib/react/script-helper.js +79 -0
  27. package/lib/react/style-helper.js +91 -0
  28. package/lib/runtime/components/react/event.config.ts +32 -0
  29. package/lib/runtime/components/react/getInnerListeners.ts +289 -0
  30. package/lib/runtime/components/react/getInnerListeners.type.ts +68 -0
  31. package/lib/runtime/components/react/mpx-button.tsx +402 -0
  32. package/lib/runtime/components/react/mpx-image/index.tsx +351 -0
  33. package/lib/runtime/components/react/mpx-image/svg.tsx +21 -0
  34. package/lib/runtime/components/react/mpx-input.tsx +389 -0
  35. package/lib/runtime/components/react/mpx-scroll-view.tsx +412 -0
  36. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +407 -0
  37. package/lib/runtime/components/react/mpx-swiper/index.tsx +68 -0
  38. package/lib/runtime/components/react/mpx-swiper/type.ts +69 -0
  39. package/lib/runtime/components/react/mpx-swiper-item.tsx +42 -0
  40. package/lib/runtime/components/react/mpx-text.tsx +106 -0
  41. package/lib/runtime/components/react/mpx-textarea.tsx +46 -0
  42. package/lib/runtime/components/react/mpx-view.tsx +397 -0
  43. package/lib/runtime/components/react/utils.ts +92 -0
  44. package/lib/runtime/components/web/event.js +100 -0
  45. package/lib/runtime/components/web/getInnerListeners.js +0 -78
  46. package/lib/runtime/components/web/mpx-button.vue +1 -1
  47. package/lib/runtime/components/web/mpx-navigator.vue +1 -1
  48. package/lib/runtime/components/web/mpx-scroll-view.vue +113 -37
  49. package/lib/runtime/oc.wxs +16 -0
  50. package/lib/runtime/optionProcessor.js +1 -1
  51. package/lib/runtime/stringify.wxs +3 -7
  52. package/lib/runtime/useNodesRef.ts +39 -0
  53. package/lib/style-compiler/index.js +2 -1
  54. package/lib/template-compiler/compiler.js +544 -121
  55. package/lib/template-compiler/gen-node-react.js +95 -0
  56. package/lib/template-compiler/index.js +19 -31
  57. package/lib/utils/env.js +17 -0
  58. package/lib/utils/make-map.js +1 -1
  59. package/lib/utils/shallow-stringify.js +17 -0
  60. package/lib/utils/ts-loader-watch-run-loader-filter.js +4 -1
  61. package/lib/web/index.js +122 -0
  62. package/lib/web/processMainScript.js +6 -4
  63. package/lib/web/processScript.js +9 -5
  64. package/lib/web/processTemplate.js +14 -14
  65. package/lib/web/script-helper.js +11 -19
  66. package/package.json +7 -3
@@ -6,8 +6,12 @@ const createHelpers = require('./helpers')
6
6
  const getJSONContent = require('./utils/get-json-content')
7
7
  const async = require('async')
8
8
  const { matchCondition } = require('./utils/match-condition')
9
- const { JSON_JS_EXT } = require('./utils/const')
9
+ const { JSON_JS_EXT, MPX_APP_MODULE_ID } = require('./utils/const')
10
10
  const getRulesRunner = require('./platform')
11
+ const getEntryName = require('./utils/get-entry-name')
12
+ const AppEntryDependency = require('./dependencies/AppEntryDependency')
13
+ const RecordResourceMapDependency = require('./dependencies/RecordResourceMapDependency')
14
+
11
15
  // todo native-loader考虑与mpx-loader或加强复用,原生组件约等于4个区块都为src的.mpx文件
12
16
  module.exports = function (content) {
13
17
  this.cacheable()
@@ -21,8 +25,8 @@ module.exports = function (content) {
21
25
  const loaderContext = this
22
26
  const isProduction = this.minimize || process.env.NODE_ENV === 'production'
23
27
  const filePath = this.resourcePath
24
- const moduleId = 'm' + mpx.pathHash(filePath)
25
28
  const { resourcePath, queryObj } = parseRequest(this.resource)
29
+ const packageRoot = queryObj.packageRoot || mpx.currentPackageRoot
26
30
  const mode = mpx.mode
27
31
  const globalSrcMode = mpx.srcMode
28
32
  const localSrcMode = queryObj.mode
@@ -31,7 +35,6 @@ module.exports = function (content) {
31
35
  const componentsMap = mpx.componentsMap[packageName]
32
36
  const parsed = path.parse(resourcePath)
33
37
  const resourceName = path.join(parsed.dir, parsed.name)
34
- const isApp = !(pagesMap[resourcePath] || componentsMap[resourcePath])
35
38
  const srcMode = localSrcMode || globalSrcMode
36
39
  const typeExtMap = config[srcMode].typeExtMap
37
40
  const typeResourceMap = {}
@@ -143,6 +146,7 @@ module.exports = function (content) {
143
146
  useJSONJS
144
147
  }, null, this, callback)
145
148
  }, (content, callback) => {
149
+ let componentPlaceholder = []
146
150
  let json
147
151
  try {
148
152
  json = JSON5.parse(content)
@@ -158,7 +162,35 @@ module.exports = function (content) {
158
162
  warn: emitWarning,
159
163
  error: emitError
160
164
  }
161
- if (!isApp) {
165
+
166
+ let ctorType = pagesMap[resourcePath]
167
+ ? 'page'
168
+ : componentsMap[resourcePath]
169
+ ? 'component'
170
+ : 'app'
171
+
172
+ // 支持资源query传入isPage或isComponent支持页面/组件单独编译
173
+ if (ctorType === 'app' && (queryObj.isComponent || queryObj.isPage)) {
174
+ const entryName = getEntryName(this) || mpx.getOutputPath(resourcePath, queryObj.isComponent ? 'component' : 'page')
175
+ ctorType = queryObj.isComponent ? 'component' : 'page'
176
+ this._module.addPresentationalDependency(new RecordResourceMapDependency(resourcePath, ctorType, entryName, packageRoot))
177
+ }
178
+
179
+ // 处理构造器类型
180
+ const ctor = ctorType === 'page'
181
+ ? (mpx.forceUsePageCtor || mode === 'ali') ? 'Page' : 'Component'
182
+ : ctorType === 'component'
183
+ ? 'Component'
184
+ : 'App'
185
+
186
+ if (ctorType === 'app') {
187
+ const appName = getEntryName(this)
188
+ if (appName) this._module.addPresentationalDependency(new AppEntryDependency(resourcePath, appName))
189
+ }
190
+
191
+ const moduleId = ctorType === 'app' ? MPX_APP_MODULE_ID : 'm' + mpx.pathHash(filePath)
192
+
193
+ if (ctorType !== 'app') {
162
194
  rulesRunnerOptions.mainKey = pagesMap[resourcePath] ? 'page' : 'component'
163
195
  }
164
196
  const rulesRunner = getRulesRunner(rulesRunnerOptions)
@@ -166,6 +198,9 @@ module.exports = function (content) {
166
198
  if (json.usingComponents) {
167
199
  usingComponents = usingComponents.concat(Object.keys(json.usingComponents))
168
200
  }
201
+ if (json.componentPlaceholder) {
202
+ componentPlaceholder = componentPlaceholder.concat(Object.values(json.componentPlaceholder))
203
+ }
169
204
  const {
170
205
  getRequire
171
206
  } = createHelpers(loaderContext)
@@ -180,13 +215,15 @@ module.exports = function (content) {
180
215
 
181
216
  switch (type) {
182
217
  case 'template':
183
- if (isApp) return ''
218
+ if (ctorType === 'app') return ''
184
219
  Object.assign(extraOptions, {
185
220
  hasScoped,
186
221
  hasComment,
187
222
  isNative,
223
+ ctorType,
188
224
  moduleId,
189
- usingComponents
225
+ usingComponents,
226
+ componentPlaceholder
190
227
  })
191
228
  break
192
229
  case 'styles':
@@ -209,20 +246,6 @@ module.exports = function (content) {
209
246
  output += `global.currentResource = ${JSON.stringify(filePath)}\n`
210
247
  }
211
248
 
212
- // 注入构造函数
213
- let ctor = 'App'
214
- let ctorType = 'app'
215
- if (pagesMap[resourcePath]) {
216
- ctorType = 'page'
217
- if (mpx.forceUsePageCtor || mode === 'ali') {
218
- ctor = 'Page'
219
- } else {
220
- ctor = 'Component'
221
- }
222
- } else if (componentsMap[resourcePath]) {
223
- ctor = 'Component'
224
- ctorType = 'component'
225
- }
226
249
  output += `global.currentCtor = ${ctor}\n`
227
250
  output += `global.currentCtorType = ${JSON.stringify(ctor.replace(/^./, (match) => {
228
251
  return match.toLowerCase()
@@ -16,6 +16,9 @@ module.exports = function getRulesRunner ({
16
16
  template: {
17
17
  wx: require('./template/wx')
18
18
  },
19
+ style: {
20
+ wx: require('./style/wx')
21
+ },
19
22
  json: {
20
23
  wx: require('./json/wx')
21
24
  }
@@ -0,0 +1,413 @@
1
+ const { hump2dash } = require('../../../utils/hump-dash')
2
+
3
+ module.exports = function getSpec ({ warn, error }) {
4
+ // React Native 双端都不支持的 CSS property
5
+ const unsupportedPropExp = /^(box-sizing|white-space|text-overflow|animation|transition)$/
6
+ const unsupportedPropMode = {
7
+ // React Native ios 不支持的 CSS property
8
+ ios: /^(vertical-align)$/,
9
+ // React Native android 不支持的 CSS property
10
+ android: /^(text-decoration-style|text-decoration-color|shadow-offset|shadow-opacity|shadow-radius)$/
11
+ }
12
+ const unsupportedPropError = ({ prop, mode }) => {
13
+ error(`Property [${prop}] is not supported in React Native ${mode} environment!`)
14
+ }
15
+
16
+ // React 属性支持的枚举值
17
+ const SUPPORTED_PROP_VAL_ARR = {
18
+ overflow: ['visible', 'hidden', 'scroll'],
19
+ 'border-style': ['solid', 'dotted', 'dashed'],
20
+ display: ['flex', 'none'],
21
+ 'pointer-events': ['auto', 'none'],
22
+ 'vertical-align': ['auto', 'top', 'bottom', 'center'],
23
+ position: ['relative', 'absolute'],
24
+ 'font-variant': ['small-caps', 'oldstyle-nums', 'lining-nums', 'tabular-nums', 'proportional-nums'],
25
+ 'text-align': ['left', 'right', 'center', 'justify'],
26
+ 'font-style': ['normal', 'italic'],
27
+ 'font-weight': ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
28
+ 'text-decoration-line': ['none', 'underline', 'line-through', 'underline line-through'],
29
+ 'text-transform': ['none', 'uppercase', 'lowercase', 'capitalize'],
30
+ 'user-select': ['auto', 'text', 'none', 'contain', 'all'],
31
+ 'align-content': ['flex-start', 'flex-end', 'center', 'stretch', 'space-between', 'space-around'],
32
+ 'align-items': ['flex-start', 'flex-end', 'center', 'stretch', 'baseline'],
33
+ 'align-self': ['auto', 'flex-start', 'flex-end', 'center', 'stretch', 'baseline'],
34
+ 'justify-content': ['flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'space-evenly', 'none'],
35
+ 'background-size': ['contain', 'cover', 'auto'],
36
+ 'background-repeat': ['no-repeat']
37
+ }
38
+ const propValExp = new RegExp('^(' + Object.keys(SUPPORTED_PROP_VAL_ARR).join('|') + ')$')
39
+ const isIllegalValue = ({ prop, value }) => SUPPORTED_PROP_VAL_ARR[prop]?.length > 0 && !SUPPORTED_PROP_VAL_ARR[prop].includes(value)
40
+ const unsupportedValueError = ({ prop, value }) => {
41
+ error(`Property [${prop}] only support value [${SUPPORTED_PROP_VAL_ARR[prop]?.join(',')}] in React Native environment, the value [${value}] does not support!`)
42
+ }
43
+
44
+ // 过滤的不合法的属性
45
+ const delRule = ({ prop, value }, { mode }) => {
46
+ if (unsupportedPropExp.test(prop) || unsupportedPropMode[mode].test(prop)) {
47
+ unsupportedPropError({ prop, mode })
48
+ return false
49
+ }
50
+ if (isIllegalValue({ prop, value })) {
51
+ unsupportedValueError({ prop, value })
52
+ return false
53
+ }
54
+ }
55
+
56
+ // color & number 值校验
57
+ const ValueType = {
58
+ number: 'number',
59
+ color: 'color',
60
+ default: 'default' // 不校验
61
+ }
62
+ // number 类型支持的单位
63
+ const numberRegExp = /^\s*(\d+(\.\d+)?)(rpx|px|%)?\s*$/
64
+ // RN 不支持的颜色格式
65
+ const colorRegExp = /^\s*(lab|lch|oklab|oklch|color-mix|color|hwb|lch|light-dark).*$/
66
+
67
+ const verifyValues = ({ prop, value, valueType }) => {
68
+ // 校验 value 枚举 是否支持
69
+ switch (valueType) {
70
+ case ValueType.color: {
71
+ const isNumber = numberRegExp.test(value)
72
+ const isUnsupporttedColor = colorRegExp.test(value)
73
+ isNumber && warn(`React Native property [${prop}]'s valueType is ${valueType}, we does not set type number`)
74
+ isUnsupporttedColor && warn('React Native color does not support type [lab,lch,oklab,oklch,color-mix,color,hwb,lch,light-dark]')
75
+ return !isNumber && !isUnsupporttedColor
76
+ }
77
+ case ValueType.number: {
78
+ const isNumber = numberRegExp.test(value)
79
+ !isNumber && warn(`React Native property [${prop}] unit only supports [rpx,px,%]`)
80
+ return isNumber
81
+ }
82
+ default:
83
+ return true
84
+ }
85
+ }
86
+ // 统一校验 value type 值类型
87
+ const checkCommonValue = (valueType) => ({ prop, value }) => {
88
+ verifyValues({ prop, value, valueType })
89
+ }
90
+
91
+ // 简写转换规则
92
+ const AbbreviationMap = {
93
+ 'text-shadow': { // 仅支持 offset-x | offset-y | blur-radius | color 排序
94
+ 'textShadowOffset.width': ValueType.number,
95
+ 'textShadowOffset.height': ValueType.number,
96
+ textShadowRadius: ValueType.number,
97
+ textShadowColor: ValueType.color
98
+ },
99
+ border: { // 仅支持 width | style | color 这种排序
100
+ borderWidth: ValueType.number,
101
+ borderStyle: ValueType.default,
102
+ borderColor: ValueType.color
103
+ },
104
+ 'box-shadow': { // 仅支持 offset-x | offset-y | blur-radius | color 排序
105
+ 'shadowOffset.width': ValueType.number,
106
+ 'shadowOffset.height': ValueType.number,
107
+ shadowRadius: ValueType.number,
108
+ shadowColor: ValueType.color
109
+ },
110
+ 'text-decoration': { // 仅支持 text-decoration-line text-decoration-style text-decoration-color 这种格式
111
+ textDecorationLine: ValueType.default,
112
+ textDecorationStyle: ValueType.default,
113
+ textDecorationColor: ValueType.color
114
+ },
115
+ flex: { // /* Three values: flex-grow | flex-shrink | flex-basis */
116
+ flexGrow: ValueType.number,
117
+ flexShrink: ValueType.number,
118
+ flexBasis: ValueType.number
119
+ },
120
+ 'flex-flow': { // 仅支持 flex-flow: <'flex-direction'> or flex-flow: <'flex-direction'> and <'flex-wrap'>
121
+ flexDirection: ValueType.default,
122
+ flexWrap: ValueType.default
123
+ },
124
+ 'border-radius': {
125
+ borderTopLeftRadius: ValueType.number,
126
+ borderTopRightRadius: ValueType.number,
127
+ borderBottomRightRadius: ValueType.number,
128
+ borderBottomLeftRadius: ValueType.number
129
+ }
130
+ }
131
+ const formatAbbreviation = ({ value, keyMap }) => {
132
+ const values = value.trim().split(/\s(?![^()]*\))/)
133
+ const cssMap = []
134
+ const props = Object.getOwnPropertyNames(keyMap)
135
+ let idx = 0
136
+ // 按值的个数循环赋值
137
+ while (idx < values.length && idx < props.length) {
138
+ const prop = props[idx]
139
+ const valueType = keyMap[prop]
140
+ const dashProp = hump2dash(prop)
141
+ // 校验 value 类型
142
+ verifyValues({ prop, value: values[idx], valueType })
143
+ const value = values[idx]
144
+ if (isIllegalValue({ prop: dashProp, value })) {
145
+ // 过滤不支持 value
146
+ unsupportedValueError({ prop: dashProp, value })
147
+ } else if (prop.includes('.')) {
148
+ // 多个属性值的prop
149
+ const [main, sub] = prop.split('.')
150
+ const cssData = cssMap.find(item => item.prop === main)
151
+ if (cssData) { // 设置过
152
+ cssData.value[sub] = value
153
+ } else { // 第一次设置
154
+ cssMap.push({
155
+ prop: main,
156
+ value: {
157
+ [sub]: value
158
+ }
159
+ })
160
+ }
161
+ } else {
162
+ // 单个值的属性
163
+ cssMap.push({
164
+ prop,
165
+ value
166
+ })
167
+ }
168
+ idx += 1
169
+ }
170
+ return cssMap
171
+ }
172
+ const getAbbreviation = ({ prop, value }) => {
173
+ const keyMap = AbbreviationMap[prop]
174
+ return formatAbbreviation({ prop, value, keyMap })
175
+ }
176
+ // 简写过滤安卓不支持的类型
177
+ const getAbbreviationAndroid = ({ prop, value }, { mode }) => {
178
+ const cssMap = getAbbreviation({ prop, value })
179
+ // android 不支持的 shadowOffset shadowOpacity shadowRadius textDecorationStyle 和 textDecorationStyle
180
+ return cssMap.filter(({ prop }) => { // 不支持的 prop 提示 & 过滤不支持的 prop
181
+ const dashProp = hump2dash(prop)
182
+ if (unsupportedPropMode.android.test(dashProp)) {
183
+ unsupportedPropError({ prop: dashProp, mode })
184
+ return false
185
+ }
186
+ return true
187
+ })
188
+ }
189
+
190
+ const formatMargins = ({ prop, value }) => {
191
+ const values = value.trim().split(/\s(?![^()]*\))/)
192
+ // validate
193
+ for (let i = 0; i < values.length; i++) {
194
+ verifyValues({ prop, value: values[i], valueType: ValueType.number })
195
+ }
196
+ // format
197
+ let suffix = []
198
+ switch (values.length) {
199
+ // case 1:
200
+ case 2:
201
+ suffix = ['Vertical', 'Horizontal']
202
+ break
203
+ case 3:
204
+ suffix = ['Top', 'Horizontal', 'Bottom']
205
+ break
206
+ case 4:
207
+ suffix = ['Top', 'Right', 'Bottom', 'Left']
208
+ break
209
+ }
210
+ return values.map((value, index) => {
211
+ return {
212
+ prop: `${prop}${suffix[index] || ''}`,
213
+ value: value
214
+ }
215
+ })
216
+ }
217
+
218
+ const formatLineHeight = ({ prop, value }) => {
219
+ if (!verifyValues({ prop, value, valueType: ValueType.number })) return false
220
+
221
+ return {
222
+ prop,
223
+ value: /\d+(\.\d+)?$/.test(value) ? `${Math.round(value * 100)}%` : value
224
+ }
225
+ }
226
+
227
+ const getFontVariant = ({ prop, value }) => {
228
+ if (/^(font-variant-caps|font-variant-numeric|font-variant-east-asian|font-variant-alternates|font-variant-ligatures)$/.test(prop)) {
229
+ error(`Property [${prop}] is not supported in React Native environment, please replace [font-variant]!`)
230
+ }
231
+ prop = 'font-variant'
232
+ // 校验枚举值
233
+ if (isIllegalValue({ prop, value })) {
234
+ unsupportedValueError({ prop, value })
235
+ return false
236
+ }
237
+ return {
238
+ prop,
239
+ value
240
+ }
241
+ }
242
+
243
+ // background 相关属性的处理,仅支持以下属性,不支持其他背景相关的属性:/^((?!(-color)).)*background((?!(-color)).)*$/ 包含background且不包含background-color
244
+ const checkBackgroundImage = ({ prop, value }, { mode }) => {
245
+ const bgPropMap = {
246
+ image: 'background-image',
247
+ color: 'background-color',
248
+ size: 'background-size',
249
+ repeat: 'background-repeat',
250
+ // position: 'background-position',
251
+ all: 'background'
252
+ }
253
+ const urlExp = /url\(["']?(.*?)["']?\)/
254
+ switch (prop) {
255
+ case bgPropMap.color: {
256
+ // background-color 背景色校验一下颜色值
257
+ verifyValues({ prop, value, valueType: ValueType.color })
258
+ return { prop, value }
259
+ }
260
+ case bgPropMap.image: {
261
+ // background-image 仅支持背景图
262
+ const imgUrl = value.match(urlExp)?.[0]
263
+ if (/.*linear-gradient*./.test(value)) {
264
+ error(`<linear-gradient()> is not supported in React Native ${mode} environment!`)
265
+ }
266
+ if (imgUrl) {
267
+ return { prop, value: imgUrl }
268
+ } else {
269
+ error(`[${prop}] only support value <url()>`)
270
+ return false
271
+ }
272
+ }
273
+ case bgPropMap.size: {
274
+ // background-size
275
+ // 不支持逗号分隔的多个值:设置多重背景!!!
276
+ // 支持一个值:这个值指定图片的宽度,图片的高度隐式的为 auto
277
+ // 支持两个值:第一个值指定图片的宽度,第二个值指定图片的高度
278
+ if (value.includes(',')) { // commas are not allowed in values
279
+ error(`background size value[${value}] does not support commas in React Native ${mode} environment!`)
280
+ return false
281
+ }
282
+ const values = []
283
+ value.trim().split(/\s(?![^()]*\))/).forEach(item => {
284
+ if (numberRegExp.test(item) || !isIllegalValue({ prop, value: item })) {
285
+ // 支持 number 值 / container cover auto 枚举
286
+ values.push(item)
287
+ } else {
288
+ error(`background size value[${value}] does not support in React Native ${mode} environment!`)
289
+ }
290
+ })
291
+ // value 无有效值时返回false
292
+ return values.length === 0 ? false : { prop, value: values }
293
+ }
294
+ case bgPropMap.repeat: {
295
+ // background-repeat 仅支持 no-repeat
296
+ if (isIllegalValue({ prop, value })) {
297
+ unsupportedValueError({ prop, value })
298
+ return false
299
+ }
300
+ return { prop, value }
301
+ }
302
+ case bgPropMap.all: {
303
+ // background: 仅支持 background-image & background-color & background-size
304
+ const bgMap = []
305
+ const values = value.trim().split(/\s(?![^()]*\))/)
306
+ values.forEach(item => {
307
+ const url = item.match(urlExp)?.[0]
308
+ if (/.*linear-gradient*./.test(item)) {
309
+ error(`<linear-gradient()> is not supported in React Native ${mode} environment!`)
310
+ } else if (url) {
311
+ bgMap.push({ prop: bgPropMap.image, value: url })
312
+ } else if (/^(#[0-9a-f]{3}$|#[0-9a-f]{6}$|rgb|rgba)/i.test(item)) {
313
+ bgMap.push({ prop: bgPropMap.color, value: item })
314
+ } else if (SUPPORTED_PROP_VAL_ARR[bgPropMap.size].includes(item)) {
315
+ bgMap.push({ prop: bgPropMap.size, value: item })
316
+ } else if (SUPPORTED_PROP_VAL_ARR[bgPropMap.repeat].includes(item)) {
317
+ bgMap.push({ prop: bgPropMap.repeat, value: item })
318
+ }
319
+ })
320
+ return bgMap.length ? bgMap : false
321
+ }
322
+ }
323
+ unsupportedPropError({ prop, mode })
324
+ return false
325
+ }
326
+
327
+ const getBorderRadius = ({ prop, value }) => {
328
+ const values = value.trim().split(/\s(?![^()]*\))/)
329
+ if (values.length === 1) {
330
+ verifyValues({ prop, value, valueType: ValueType.number })
331
+ return { prop, value }
332
+ } else {
333
+ return getAbbreviation({ prop, value })
334
+ }
335
+ }
336
+
337
+ const spec = {
338
+ supportedModes: ['ios', 'android'],
339
+ rules: [
340
+ { // 背景相关属性的处理
341
+ test: /.*background*./,
342
+ ios: checkBackgroundImage,
343
+ android: checkBackgroundImage
344
+ },
345
+ { // RN 不支持的 CSS property
346
+ test: unsupportedPropExp,
347
+ ios: delRule,
348
+ android: delRule
349
+ },
350
+ { // React Native android 不支持的 CSS property
351
+ test: unsupportedPropMode.android,
352
+ android: delRule
353
+ },
354
+ { // React Native ios 不支持的 CSS property
355
+ test: unsupportedPropMode.ios,
356
+ ios: delRule
357
+ },
358
+ { // RN 支持的 CSS property value
359
+ test: propValExp,
360
+ ios: delRule,
361
+ android: delRule
362
+ },
363
+ {
364
+ test: 'box-shadow',
365
+ ios: getAbbreviation,
366
+ android: getAbbreviationAndroid
367
+ },
368
+ {
369
+ test: 'text-decoration',
370
+ ios: getAbbreviation,
371
+ android: getAbbreviationAndroid
372
+ },
373
+ {
374
+ test: /.*font-variant.*/,
375
+ ios: getFontVariant,
376
+ android: getFontVariant
377
+ },
378
+ {
379
+ test: 'border-radius',
380
+ ios: getBorderRadius,
381
+ android: getBorderRadius
382
+ },
383
+ { // margin padding 内外边距的处理
384
+ test: /.*(margin|padding).*/,
385
+ ios: formatMargins,
386
+ android: formatMargins
387
+ },
388
+ // 通用的简写格式匹配
389
+ {
390
+ test: new RegExp('^(' + Object.keys(AbbreviationMap).join('|') + ')$'),
391
+ ios: getAbbreviation,
392
+ android: getAbbreviation
393
+ },
394
+ // 值类型校验放到最后
395
+ { // color 颜色值校验
396
+ test: /.*color.*/i,
397
+ ios: checkCommonValue(ValueType.color),
398
+ android: checkCommonValue(ValueType.color)
399
+ },
400
+ { // color 颜色值校验
401
+ test: 'line-height',
402
+ ios: formatLineHeight,
403
+ android: formatLineHeight
404
+ },
405
+ { // number 值校验
406
+ test: /.*width|height|left|right|top|bottom|radius|margin|padding|spacing|offset|size.*/i,
407
+ ios: checkCommonValue(ValueType.number),
408
+ android: checkCommonValue(ValueType.number)
409
+ }
410
+ ]
411
+ }
412
+ return spec
413
+ }
@@ -30,6 +30,12 @@ module.exports = function ({ print }) {
30
30
  const webEventLog = print({ platform: 'web', tag: TAG_NAME, isError: false, type: 'event' })
31
31
  const qaPropLog = print({ platform: 'qa', tag: TAG_NAME, isError: false })
32
32
  const wxPropValueLog = print({ platform: 'wx', tag: TAG_NAME, isError: false, type: 'value' })
33
+ const iosValueLogError = print({ platform: 'ios', tag: TAG_NAME, isError: true, type: 'value' })
34
+ const iosPropLog = print({ platform: 'ios', tag: TAG_NAME, isError: false })
35
+ const iosEventLog = print({ platform: 'ios', tag: TAG_NAME, isError: false, type: 'event' })
36
+ const androidValueLogError = print({ platform: 'android', tag: TAG_NAME, isError: true, type: 'value' })
37
+ const androidPropLog = print({ platform: 'android', tag: TAG_NAME, isError: false })
38
+ const androidEventLog = print({ platform: 'android', tag: TAG_NAME, isError: false, type: 'event' })
33
39
 
34
40
  return {
35
41
  test: TAG_NAME,
@@ -37,6 +43,14 @@ module.exports = function ({ print }) {
37
43
  el.isBuiltIn = true
38
44
  return 'mpx-button'
39
45
  },
46
+ ios (tag, { el }) {
47
+ el.isBuiltIn = true
48
+ return 'mpx-button'
49
+ },
50
+ android (tag, { el }) {
51
+ el.isBuiltIn = true
52
+ return 'mpx-button'
53
+ },
40
54
  props: [
41
55
  {
42
56
  test: 'open-type',
@@ -107,6 +121,18 @@ module.exports = function ({ print }) {
107
121
  ttValueLogError({ name, value })
108
122
  }
109
123
  }
124
+ },
125
+ ios ({ name, value }) {
126
+ const supported = ['share']
127
+ if (!supported.includes(value)) {
128
+ iosValueLogError({ name, value })
129
+ }
130
+ },
131
+ android ({ name, value }) {
132
+ const supported = ['share']
133
+ if (!supported.includes(value)) {
134
+ androidValueLogError({ name, value })
135
+ }
110
136
  }
111
137
  },
112
138
  {
@@ -143,6 +169,11 @@ module.exports = function ({ print }) {
143
169
  {
144
170
  test: /^(open-type|lang|session-from|send-message-title|send-message-path|send-message-img|app-parameter|show-message-card|bindgetuserinfo|bindcontact|bindgetphonenumber|binderror|bindopensetting|bindlaunchapp)$/,
145
171
  qa: qaPropLog
172
+ },
173
+ {
174
+ test: /^(lang|from-type|hover-class|send-message-title|send-message-path|send-message-img|app-parameter|show-message-card|phone-number-no-quota-toast|bindgetuserinfo|bindcontact|createliveactivity|bindgetphonenumber|bindgetrealtimephonenumber|binderror|bindopensetting|bindlaunchapp|bindchooseavatar|bindagreeprivacyauthorization)$/,
175
+ ios: iosPropLog,
176
+ android: androidPropLog
146
177
  }
147
178
  ],
148
179
  event: [
@@ -175,6 +206,11 @@ module.exports = function ({ print }) {
175
206
  {
176
207
  test: /^(getuserinfo|contact|error|launchapp|opensetting|getphonenumber)$/,
177
208
  web: webEventLog
209
+ },
210
+ {
211
+ test: /^(getuserinfo|contact|getphonenumber|bindgetrealtimephonenumber|error|opensetting|launchapp|chooseavatar|agreeprivacyauthorization)$/,
212
+ ios: iosEventLog,
213
+ android: androidEventLog
178
214
  }
179
215
  ]
180
216
  }
@@ -7,7 +7,7 @@ module.exports = function ({ print }) {
7
7
  return {
8
8
  test: TAG_NAME,
9
9
  web (tag, { el }) {
10
- if (el.hasEvent) {
10
+ if (el.hasModel) {
11
11
  el.isBuiltIn = true
12
12
  }
13
13
  if (el.isBuiltIn) {
@@ -6,6 +6,8 @@ module.exports = function ({ print }) {
6
6
  const jdPropLog = print({ platform: 'jd', tag: TAG_NAME, isError: false })
7
7
  const ttPropLog = print({ platform: 'bytedance', tag: TAG_NAME, isError: false })
8
8
  const qaPropLog = print({ platform: 'qa', tag: TAG_NAME, isError: false })
9
+ const iosPropLog = print({ platform: 'ios', tag: TAG_NAME, isError: false })
10
+ const androidPropLog = print({ platform: 'android', tag: TAG_NAME, isError: false })
9
11
 
10
12
  return {
11
13
  test: TAG_NAME,
@@ -13,6 +15,14 @@ module.exports = function ({ print }) {
13
15
  el.isBuiltIn = true
14
16
  return 'mpx-image'
15
17
  },
18
+ ios (tag, { el }) {
19
+ el.isBuiltIn = true
20
+ return 'mpx-image'
21
+ },
22
+ android (tag, { el }) {
23
+ el.isBuiltIn = true
24
+ return 'mpx-image'
25
+ },
16
26
  props: [
17
27
  {
18
28
  test: /^show-menu-by-longpress$/,
@@ -34,6 +44,11 @@ module.exports = function ({ print }) {
34
44
  {
35
45
  test: /^(show-menu-by-longpress|webp)$/,
36
46
  qa: qaPropLog
47
+ },
48
+ {
49
+ test: /^(show-menu-by-longpress|fade-in)$/,
50
+ ios: iosPropLog,
51
+ android: androidPropLog
37
52
  }
38
53
  ]
39
54
  }