@mpxjs/webpack-plugin 2.9.59 → 2.9.62

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 (106) hide show
  1. package/lib/platform/style/wx/index.js +314 -254
  2. package/lib/platform/template/wx/component-config/checkbox-group.js +8 -0
  3. package/lib/platform/template/wx/component-config/checkbox.js +8 -0
  4. package/lib/platform/template/wx/component-config/cover-image.js +15 -0
  5. package/lib/platform/template/wx/component-config/cover-view.js +9 -0
  6. package/lib/platform/template/wx/component-config/form.js +13 -1
  7. package/lib/platform/template/wx/component-config/icon.js +8 -0
  8. package/lib/platform/template/wx/component-config/index.js +5 -1
  9. package/lib/platform/template/wx/component-config/label.js +15 -0
  10. package/lib/platform/template/wx/component-config/movable-area.js +18 -1
  11. package/lib/platform/template/wx/component-config/movable-view.js +18 -1
  12. package/lib/platform/template/wx/component-config/navigator.js +8 -0
  13. package/lib/platform/template/wx/component-config/picker-view-column.js +8 -0
  14. package/lib/platform/template/wx/component-config/picker-view.js +18 -2
  15. package/lib/platform/template/wx/component-config/picker.js +14 -1
  16. package/lib/platform/template/wx/component-config/radio-group.js +8 -0
  17. package/lib/platform/template/wx/component-config/radio.js +8 -0
  18. package/lib/platform/template/wx/component-config/root-portal.js +15 -0
  19. package/lib/platform/template/wx/component-config/switch.js +8 -0
  20. package/lib/platform/template/wx/component-config/unsupported.js +1 -3
  21. package/lib/react/processScript.js +2 -0
  22. package/lib/runtime/components/react/context.ts +38 -0
  23. package/lib/runtime/components/react/dist/context.js +7 -0
  24. package/lib/runtime/components/react/dist/getInnerListeners.js +22 -11
  25. package/lib/runtime/components/react/dist/mpx-button.jsx +67 -45
  26. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +81 -0
  27. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +152 -0
  28. package/lib/runtime/components/react/dist/mpx-form.jsx +59 -0
  29. package/lib/runtime/components/react/dist/mpx-icon.jsx +51 -0
  30. package/lib/runtime/components/react/dist/mpx-image/index.jsx +17 -22
  31. package/lib/runtime/components/react/dist/mpx-image/svg.jsx +0 -1
  32. package/lib/runtime/components/react/dist/mpx-input.jsx +38 -16
  33. package/lib/runtime/components/react/dist/mpx-label.jsx +63 -0
  34. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +46 -0
  35. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +346 -0
  36. package/lib/runtime/components/react/dist/mpx-navigator.jsx +35 -0
  37. package/lib/runtime/components/react/dist/mpx-picker/date.jsx +69 -0
  38. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +138 -0
  39. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +139 -0
  40. package/lib/runtime/components/react/dist/mpx-picker/region.jsx +90 -0
  41. package/lib/runtime/components/react/dist/mpx-picker/regionData.js +6099 -0
  42. package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +76 -0
  43. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +244 -0
  44. package/lib/runtime/components/react/dist/mpx-picker/type.js +1 -0
  45. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +15 -0
  46. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +68 -0
  47. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +79 -0
  48. package/lib/runtime/components/react/dist/mpx-radio.jsx +169 -0
  49. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +11 -0
  50. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +66 -50
  51. package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +206 -147
  52. package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +9 -7
  53. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +3 -3
  54. package/lib/runtime/components/react/dist/mpx-switch.jsx +76 -0
  55. package/lib/runtime/components/react/dist/mpx-text.jsx +7 -19
  56. package/lib/runtime/components/react/dist/mpx-textarea.jsx +1 -1
  57. package/lib/runtime/components/react/dist/mpx-view.jsx +326 -96
  58. package/lib/runtime/components/react/dist/mpx-web-view.jsx +9 -15
  59. package/lib/runtime/components/react/dist/types/common.js +1 -0
  60. package/lib/runtime/components/react/dist/useNodesRef.js +3 -8
  61. package/lib/runtime/components/react/dist/utils.js +82 -14
  62. package/lib/runtime/components/react/getInnerListeners.ts +25 -13
  63. package/lib/runtime/components/react/mpx-button.tsx +87 -67
  64. package/lib/runtime/components/react/mpx-checkbox-group.tsx +147 -0
  65. package/lib/runtime/components/react/mpx-checkbox.tsx +245 -0
  66. package/lib/runtime/components/react/mpx-form.tsx +89 -0
  67. package/lib/runtime/components/react/mpx-icon.tsx +103 -0
  68. package/lib/runtime/components/react/mpx-image/index.tsx +20 -32
  69. package/lib/runtime/components/react/mpx-image/svg.tsx +2 -2
  70. package/lib/runtime/components/react/mpx-input.tsx +54 -26
  71. package/lib/runtime/components/react/mpx-label.tsx +115 -0
  72. package/lib/runtime/components/react/mpx-movable-area.tsx +67 -0
  73. package/lib/runtime/components/react/mpx-movable-view.tsx +425 -0
  74. package/lib/runtime/components/react/mpx-navigator.tsx +67 -0
  75. package/lib/runtime/components/react/mpx-picker/date.tsx +83 -0
  76. package/lib/runtime/components/react/mpx-picker/index.tsx +155 -0
  77. package/lib/runtime/components/react/mpx-picker/multiSelector.tsx +153 -0
  78. package/lib/runtime/components/react/mpx-picker/region.tsx +104 -0
  79. package/lib/runtime/components/react/mpx-picker/regionData.ts +6101 -0
  80. package/lib/runtime/components/react/mpx-picker/selector.tsx +92 -0
  81. package/lib/runtime/components/react/mpx-picker/time.tsx +274 -0
  82. package/lib/runtime/components/react/mpx-picker/type.ts +107 -0
  83. package/lib/runtime/components/react/mpx-picker-view-column.tsx +28 -0
  84. package/lib/runtime/components/react/mpx-picker-view.tsx +104 -0
  85. package/lib/runtime/components/react/mpx-radio-group.tsx +147 -0
  86. package/lib/runtime/components/react/mpx-radio.tsx +246 -0
  87. package/lib/runtime/components/react/mpx-root-portal.tsx +25 -0
  88. package/lib/runtime/components/react/mpx-scroll-view.tsx +82 -58
  89. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +203 -156
  90. package/lib/runtime/components/react/mpx-swiper/index.tsx +12 -13
  91. package/lib/runtime/components/react/mpx-swiper/type.ts +11 -4
  92. package/lib/runtime/components/react/mpx-swiper-item.tsx +5 -3
  93. package/lib/runtime/components/react/mpx-switch.tsx +127 -0
  94. package/lib/runtime/components/react/mpx-text.tsx +52 -68
  95. package/lib/runtime/components/react/mpx-textarea.tsx +2 -2
  96. package/lib/runtime/components/react/mpx-view.tsx +373 -140
  97. package/lib/runtime/components/react/mpx-web-view.tsx +24 -28
  98. package/lib/runtime/components/react/types/common.ts +12 -0
  99. package/lib/runtime/components/react/types/getInnerListeners.ts +2 -1
  100. package/lib/runtime/components/react/types/global.d.ts +4 -0
  101. package/lib/runtime/components/react/useNodesRef.ts +3 -8
  102. package/lib/runtime/components/react/utils.ts +93 -15
  103. package/lib/runtime/optionProcessor.js +19 -17
  104. package/lib/template-compiler/compiler.js +56 -41
  105. package/lib/template-compiler/gen-node-react.js +7 -7
  106. package/package.json +6 -3
@@ -2,23 +2,45 @@ const { hump2dash } = require('../../../utils/hump-dash')
2
2
 
3
3
  module.exports = function getSpec ({ warn, error }) {
4
4
  // React Native 双端都不支持的 CSS property
5
- const unsupportedPropExp = /^(box-sizing|white-space|text-overflow|animation|transition)$/
5
+ const unsupportedPropExp = /^(white-space|text-overflow|animation|transition|font-variant-caps|font-variant-numeric|font-variant-east-asian|font-variant-alternates|font-variant-ligatures|background-position|caret-color)$/
6
6
  const unsupportedPropMode = {
7
7
  // React Native ios 不支持的 CSS property
8
8
  ios: /^(vertical-align)$/,
9
9
  // React Native android 不支持的 CSS property
10
10
  android: /^(text-decoration-style|text-decoration-color|shadow-offset|shadow-opacity|shadow-radius)$/
11
11
  }
12
+ // 不支持的属性提示
12
13
  const unsupportedPropError = ({ prop, mode }) => {
13
14
  error(`Property [${prop}] is not supported in React Native ${mode} environment!`)
14
15
  }
15
-
16
+ // prop 校验
17
+ const verifyProps = ({ prop, value }, { mode }, isError = true) => {
18
+ prop = prop.trim()
19
+ const tips = isError ? error : warn
20
+ if (unsupportedPropExp.test(prop) || unsupportedPropMode[mode].test(prop)) {
21
+ tips(`Property [${prop}] is not supported in React Native ${mode} environment!`)
22
+ return false
23
+ }
24
+ return true
25
+ }
26
+ // 值类型
27
+ const ValueType = {
28
+ number: 'number',
29
+ color: 'color',
30
+ enum: 'enum'
31
+ }
16
32
  // React 属性支持的枚举值
17
33
  const SUPPORTED_PROP_VAL_ARR = {
34
+ 'box-sizing': ['border-box'],
35
+ 'backface-visibility': ['visible', 'hidden'],
18
36
  overflow: ['visible', 'hidden', 'scroll'],
19
37
  'border-style': ['solid', 'dotted', 'dashed'],
38
+ 'object-fit': ['cover', 'contain', 'fill', 'scale-down'],
39
+ direction: ['inherit', 'ltr', 'rtl'],
20
40
  display: ['flex', 'none'],
21
- 'pointer-events': ['auto', 'none'],
41
+ 'flex-direction': ['row', 'row-reverse', 'column', 'column-reverse'],
42
+ 'flex-wrap': ['wrap', 'nowrap', 'wrap-reverse'],
43
+ 'pointer-events': ['auto', 'box-none', 'box-only', 'none'],
22
44
  'vertical-align': ['auto', 'top', 'bottom', 'center'],
23
45
  position: ['relative', 'absolute'],
24
46
  'font-variant': ['small-caps', 'oldstyle-nums', 'lining-nums', 'tabular-nums', 'proportional-nums'],
@@ -26,148 +48,140 @@ module.exports = function getSpec ({ warn, error }) {
26
48
  'font-style': ['normal', 'italic'],
27
49
  'font-weight': ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
28
50
  'text-decoration-line': ['none', 'underline', 'line-through', 'underline line-through'],
51
+ 'text-decoration-style': ['solid', 'double', 'dotted', 'dashed'],
29
52
  'text-transform': ['none', 'uppercase', 'lowercase', 'capitalize'],
30
53
  'user-select': ['auto', 'text', 'none', 'contain', 'all'],
31
- 'align-content': ['flex-start', 'flex-end', 'center', 'stretch', 'space-between', 'space-around'],
54
+ 'align-content': ['flex-start', 'flex-end', 'center', 'stretch', 'space-between', 'space-around', 'space-evenly'],
32
55
  'align-items': ['flex-start', 'flex-end', 'center', 'stretch', 'baseline'],
33
56
  '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']
57
+ 'justify-content': ['flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'space-evenly'],
58
+ 'background-size': ['contain', 'cover', 'auto', ValueType.number],
59
+ 'background-position': ['left', 'right', 'top', 'bottom', 'center', ValueType.number],
60
+ 'background-repeat': ['no-repeat'],
61
+ width: ['auto', ValueType.number],
62
+ height: ['auto', ValueType.number],
63
+ 'flex-basis': ['auto', ValueType.number],
64
+ margin: ['auto', ValueType.number],
65
+ 'margin-top': ['auto', ValueType.number],
66
+ 'margin-left': ['auto', ValueType.number],
67
+ 'margin-bottom': ['auto', ValueType.number],
68
+ 'margin-right': ['auto', ValueType.number],
69
+ 'margin-horizontal': ['auto', ValueType.number],
70
+ 'margin-vertical': ['auto', ValueType.number]
37
71
  }
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
72
+ // 获取值类型
73
+ const getValueType = (prop) => {
74
+ const propValueTypeRules = [
75
+ // 重要!!优先判断是不是枚举类型
76
+ [ValueType.enum, new RegExp('^(' + Object.keys(SUPPORTED_PROP_VAL_ARR).join('|') + ')$')],
77
+ [ValueType.number, /^((opacity|flex-grow|flex-shrink|gap|left|right|top|bottom)|(.+-(width|height|left|right|top|bottom|radius|spacing|size|gap|index|offset|opacity)))$/],
78
+ [ValueType.color, /^(color|(.+-color))$/]
79
+ ]
80
+ for (const rule of propValueTypeRules) {
81
+ if (rule[1].test(prop)) return rule[0]
53
82
  }
54
83
  }
55
-
56
- // color & number 值校验
57
- const ValueType = {
58
- number: 'number',
59
- color: 'color',
60
- default: 'default' // 不校验
61
- }
62
- // number 类型支持的单位(包含auto)
63
- const numberRegExp = /^\s*((-?\d+(\.\d+)?)(rpx|px|%)?)|(auto)\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(`Property [${prop}] receives a valid color as value, not a number.`)
74
- isUnsupporttedColor && warn('React Native\'s supported color format does not contain [lab,lch,oklab,oklch,color-mix,color,hwb,lch,light-dark].')
75
- return !isNumber && !isUnsupporttedColor
76
- }
84
+ // 属性值校验
85
+ const verifyValues = ({ prop, value }, isError = true) => {
86
+ prop = prop.trim()
87
+ value = value.trim()
88
+ const type = getValueType(prop)
89
+ const namedColor = ['transparent', 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 'beige', 'bisque', 'black', 'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkgrey', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', 'green', 'greenyellow', 'grey', 'honeydew', 'hotpink', 'indianred', 'indigo', 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgreen', 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategrey', 'lightsteelblue', 'lightyellow', 'lime', 'limegreen', 'linen', 'magenta', 'maroon', 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive', 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'rebeccapurple', 'red', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue', 'slategray', 'snow', 'springgreen', 'steelblue', 'tan', 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen']
90
+ const valueExp = {
91
+ number: /^(-?\d+(\.\d+)?)(rpx|px|%)?$/,
92
+ color: new RegExp(('^(' + namedColor.join('|') + ')$') + '|(^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$)|^(rgb|rgba|hsl|hsla|hwb)\\(.+\\)$')
93
+ }
94
+ const tips = isError ? error : warn
95
+ switch (type) {
77
96
  case ValueType.number: {
78
- const isNumber = numberRegExp.test(value)
79
- !isNumber && warn(`React Native property [${prop}] unit only supports [rpx,px,%]`)
80
- return isNumber
97
+ if (!valueExp.number.test(value)) {
98
+ tips(`The value type of [${prop}] only supports [Number] in React Native environment, eg 10rpx, 10px, 10%, 10, please check again`)
99
+ return false
100
+ }
101
+ return true
102
+ }
103
+ case ValueType.color: {
104
+ if (!valueExp.color.test(value)) {
105
+ tips(`The value type of [${prop}] only supports [Color] in React Native environment, eg #000, rgba(0,0,0,0), please check again`)
106
+ return false
107
+ }
108
+ return true
81
109
  }
82
- default:
110
+ case ValueType.enum: {
111
+ const isIn = SUPPORTED_PROP_VAL_ARR[prop].includes(value)
112
+ const isType = Object.keys(valueExp).some(item => valueExp[item].test(value) && SUPPORTED_PROP_VAL_ARR[prop].includes(ValueType[item]))
113
+ if (!isIn && !isType) {
114
+ tips(`Property [${prop}] only support value [${SUPPORTED_PROP_VAL_ARR[prop]?.join(',')}] in React Native environment, the value [${value}] does not support!`)
115
+ return false
116
+ }
83
117
  return true
118
+ }
84
119
  }
120
+ return true
85
121
  }
86
- // 统一校验 value type 值类型
87
- const checkCommonValue = (valueType) => ({ prop, value }) => {
88
- verifyValues({ prop, value, valueType })
122
+ // prop & value 校验:过滤的不合法的属性和属性值
123
+ const verification = ({ prop, value }, { mode }) => {
124
+ return verifyProps({ prop, value }, { mode }) && verifyValues({ prop, value }) && ({ prop, value })
89
125
  }
90
126
 
91
127
  // 简写转换规则
92
128
  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
- 'border-left': { // 仅支持 width | style | color 这种排序
105
- borderLeftWidth: ValueType.number,
106
- borderLeftStyle: ValueType.default,
107
- borderLeftColor: ValueType.color
108
- },
109
- 'border-right': { // 仅支持 width | style | color 这种排序
110
- borderRightWidth: ValueType.number,
111
- borderRightStyle: ValueType.default,
112
- borderRightColor: ValueType.color
113
- },
114
- 'border-top': { // 仅支持 width | style | color 这种排序
115
- borderTopWidth: ValueType.number,
116
- borderTopStyle: ValueType.default,
117
- borderTopColor: ValueType.color
118
- },
119
- 'border-bottom': { // 仅支持 width | style | color 这种排序
120
- borderBottomWidth: ValueType.number,
121
- borderBottomStyle: ValueType.default,
122
- borderBottomColor: ValueType.color
123
- },
124
- 'box-shadow': { // 仅支持 offset-x | offset-y | blur-radius | color 排序
125
- 'shadowOffset.width': ValueType.number,
126
- 'shadowOffset.height': ValueType.number,
127
- shadowRadius: ValueType.number,
128
- shadowColor: ValueType.color
129
- },
130
- 'text-decoration': { // 仅支持 text-decoration-line text-decoration-style text-decoration-color 这种格式
131
- textDecorationLine: ValueType.default,
132
- textDecorationStyle: ValueType.default,
133
- textDecorationColor: ValueType.color
134
- },
135
- flex: { // /* Three values: flex-grow | flex-shrink | flex-basis */
136
- flexGrow: ValueType.number,
137
- flexShrink: ValueType.number,
138
- flexBasis: ValueType.number
139
- },
140
- 'flex-flow': { // 仅支持 flex-flow: <'flex-direction'> or flex-flow: <'flex-direction'> and <'flex-wrap'>
141
- flexDirection: ValueType.default,
142
- flexWrap: ValueType.default
143
- },
144
- 'border-radius': {
145
- borderTopLeftRadius: ValueType.number,
146
- borderTopRightRadius: ValueType.number,
147
- borderBottomRightRadius: ValueType.number,
148
- borderBottomLeftRadius: ValueType.number
149
- }
129
+ // 仅支持 offset-x | offset-y | blur-radius | color 排序
130
+ 'text-shadow': ['textShadowOffset.width', 'textShadowOffset.height', 'textShadowRadius', 'textShadowColor'],
131
+ // 仅支持 width | style | color 这种排序
132
+ border: ['borderWidth', 'borderStyle', 'borderColor'],
133
+ // 仅支持 width | style | color 这种排序
134
+ 'border-left': ['borderLeftWidth', 'borderLeftStyle', 'borderLeftColor'],
135
+ // 仅支持 width | style | color 这种排序
136
+ 'border-right': ['borderRightWidth', 'borderRightStyle', 'borderRightColor'],
137
+ // 仅支持 width | style | color 这种排序
138
+ 'border-top': ['borderTopWidth', 'borderTopStyle', 'borderTopColor'],
139
+ // 仅支持 width | style | color 这种排序
140
+ 'border-bottom': ['borderBottomWidth', 'borderBottomStyle', 'borderBottomColor'],
141
+ // 仅支持 offset-x | offset-y | blur-radius | color 排序
142
+ 'box-shadow': ['shadowOffset.width', 'shadowOffset.height', 'shadowRadius', 'shadowColor'],
143
+ // 仅支持 text-decoration-line text-decoration-style text-decoration-color 这种格式
144
+ 'text-decoration': ['textDecorationLine', 'textDecorationStyle', 'textDecorationColor'],
145
+ // flex-grow | flex-shrink | flex-basis
146
+ flex: ['flexGrow', 'flexShrink', 'flexBasis'],
147
+ // flex-flow: <'flex-direction'> or flex-flow: <'flex-direction'> and <'flex-wrap'>
148
+ 'flex-flow': ['flexDirection', 'flexWrap'],
149
+ 'border-radius': ['borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius']
150
150
  }
151
- const formatAbbreviation = ({ value, keyMap }) => {
151
+ const formatAbbreviation = ({ prop, value }, { mode }) => {
152
+ const original = `${prop}:${value}`
153
+ const props = AbbreviationMap[prop]
152
154
  const values = value.trim().split(/\s(?![^()]*\))/)
153
155
  const cssMap = []
154
- const props = Object.getOwnPropertyNames(keyMap)
155
156
  let idx = 0
156
157
  let propsIdx = 0
157
- // 按值的个数循环赋值
158
- while (idx < values.length || propsIdx < props.length) {
158
+ const diff = values.length - props.length
159
+ while (idx < values.length) {
159
160
  const prop = props[propsIdx]
160
- const valueType = keyMap[prop]
161
- const dashProp = hump2dash(prop)
161
+ if (!prop) {
162
+ error(`the value of [${original}] has not enough props to assign in React Native environment, please check again`)
163
+ break
164
+ }
162
165
  const value = values[idx]
163
- if (isIllegalValue({ prop: dashProp, value })) {
164
- // 过滤 rn prop 不支持 value
165
- unsupportedValueError({ prop: dashProp, value })
166
- idx += 1
167
- propsIdx += 1
168
- } else if (!verifyValues({ prop, value, valueType })) {
169
- // 校验 value 类型,类型不符则匹配下一个value
170
- idx += 1
166
+ const newProp = hump2dash(prop.replace(/\..+/, ''))
167
+ if (!verifyProps({ prop: newProp, value }, { mode }, diff === 0)) {
168
+ // ios or android 不支持的 prop,跳过 prop
169
+ if (diff === 0) {
170
+ propsIdx++
171
+ idx++
172
+ } else {
173
+ propsIdx++
174
+ }
175
+ } else if (!verifyValues({ prop: newProp, value }, diff === 0)) {
176
+ // 值不合法 跳过 value
177
+ if (diff === 0) {
178
+ propsIdx++
179
+ idx++
180
+ } else if (diff < 0) {
181
+ propsIdx++
182
+ } else {
183
+ idx++
184
+ }
171
185
  } else if (prop.includes('.')) {
172
186
  // 多个属性值的prop
173
187
  const [main, sub] = prop.split('.')
@@ -196,30 +210,10 @@ module.exports = function getSpec ({ warn, error }) {
196
210
  }
197
211
  return cssMap
198
212
  }
199
- const getAbbreviation = ({ prop, value }) => {
200
- const keyMap = AbbreviationMap[prop]
201
- return formatAbbreviation({ prop, value, keyMap })
202
- }
203
- // 简写过滤安卓不支持的类型
204
- const getAbbreviationAndroid = ({ prop, value }, { mode }) => {
205
- const cssMap = getAbbreviation({ prop, value })
206
- // android 不支持的 shadowOffset shadowOpacity shadowRadius textDecorationStyle 和 textDecorationStyle
207
- return cssMap.filter(({ prop }) => { // 不支持的 prop 提示 & 过滤不支持的 prop
208
- const dashProp = hump2dash(prop)
209
- if (unsupportedPropMode.android.test(dashProp)) {
210
- unsupportedPropError({ prop: dashProp, mode })
211
- return false
212
- }
213
- return true
214
- })
215
- }
216
213
 
214
+ // margin padding
217
215
  const formatMargins = ({ prop, value }) => {
218
216
  const values = value.trim().split(/\s(?![^()]*\))/)
219
- // validate
220
- for (let i = 0; i < values.length; i++) {
221
- verifyValues({ prop, value: values[i], valueType: ValueType.number })
222
- }
223
217
  // format
224
218
  let suffix = []
225
219
  switch (values.length) {
@@ -235,55 +229,38 @@ module.exports = function getSpec ({ warn, error }) {
235
229
  break
236
230
  }
237
231
  return values.map((value, index) => {
232
+ const newProp = `${prop}${suffix[index] || ''}`
233
+ // validate
234
+ verifyValues({ prop: hump2dash(newProp), value }, false)
238
235
  return {
239
- prop: `${prop}${suffix[index] || ''}`,
236
+ prop: newProp,
240
237
  value: value
241
238
  }
242
239
  })
243
240
  }
244
241
 
242
+ // line-height
245
243
  const formatLineHeight = ({ prop, value }) => {
246
- if (!verifyValues({ prop, value, valueType: ValueType.number })) return false
247
-
248
- return {
244
+ return verifyValues({ prop, value }) && ({
249
245
  prop,
250
246
  value: /^\s*-?\d+(\.\d+)?\s*$/.test(value) ? `${Math.round(value * 100)}%` : value
251
- }
252
- }
253
-
254
- const getFontVariant = ({ prop, value }) => {
255
- if (/^(font-variant-caps|font-variant-numeric|font-variant-east-asian|font-variant-alternates|font-variant-ligatures)$/.test(prop)) {
256
- error(`Property [${prop}] is not supported in React Native environment, please replace [font-variant]!`)
257
- }
258
- prop = 'font-variant'
259
- // 校验枚举值
260
- if (isIllegalValue({ prop, value })) {
261
- unsupportedValueError({ prop, value })
262
- return false
263
- }
264
- return {
265
- prop,
266
- value
267
- }
247
+ })
268
248
  }
269
249
 
270
- // background 相关属性的处理,仅支持以下属性,不支持其他背景相关的属性:/^((?!(-color)).)*background((?!(-color)).)*$/ 包含background且不包含background-color
250
+ // background 相关属性的转换 Todo
251
+ // 仅支持以下属性,不支持其他背景相关的属性
252
+ // /^((?!(-color)).)*background((?!(-color)).)*$/ 包含background且不包含background-color
271
253
  const checkBackgroundImage = ({ prop, value }, { mode }) => {
272
254
  const bgPropMap = {
273
255
  image: 'background-image',
274
256
  color: 'background-color',
275
257
  size: 'background-size',
276
258
  repeat: 'background-repeat',
277
- // position: 'background-position',
259
+ position: 'background-position',
278
260
  all: 'background'
279
261
  }
280
262
  const urlExp = /url\(["']?(.*?)["']?\)/
281
263
  switch (prop) {
282
- case bgPropMap.color: {
283
- // background-color 背景色校验一下颜色值
284
- verifyValues({ prop, value, valueType: ValueType.color })
285
- return { prop, value }
286
- }
287
264
  case bgPropMap.image: {
288
265
  // background-image 仅支持背景图
289
266
  const imgUrl = value.match(urlExp)?.[0]
@@ -308,23 +285,26 @@ module.exports = function getSpec ({ warn, error }) {
308
285
  }
309
286
  const values = []
310
287
  value.trim().split(/\s(?![^()]*\))/).forEach(item => {
311
- if (numberRegExp.test(item) || !isIllegalValue({ prop, value: item })) {
288
+ if (verifyValues({ prop, value: item })) {
312
289
  // 支持 number 值 / container cover auto 枚举
313
290
  values.push(item)
314
- } else {
315
- error(`background size value[${value}] does not support in React Native ${mode} environment!`)
316
291
  }
317
292
  })
318
293
  // value 无有效值时返回false
319
294
  return values.length === 0 ? false : { prop, value: values }
320
295
  }
321
- case bgPropMap.repeat: {
322
- // background-repeat 仅支持 no-repeat
323
- if (isIllegalValue({ prop, value })) {
324
- unsupportedValueError({ prop, value })
325
- return false
326
- }
327
- return { prop, value }
296
+ case bgPropMap.position: {
297
+ const values = []
298
+ value.trim().split(/\s(?![^()]*\))/).forEach(item => {
299
+ if (verifyValues({ prop, value: item })) {
300
+ // 支持 number 值 / 枚举, center与50%等价
301
+ values.push(item === 'center' ? '50%' : item)
302
+ } else {
303
+ error(`background position value[${value}] does not support in React Native ${mode} environment!`)
304
+ }
305
+ })
306
+
307
+ return { prop, value: values }
328
308
  }
329
309
  case bgPropMap.all: {
330
310
  // background: 仅支持 background-image & background-color & background-repeat
@@ -336,14 +316,11 @@ module.exports = function getSpec ({ warn, error }) {
336
316
  error(`<linear-gradient()> is not supported in React Native ${mode} environment!`)
337
317
  } else if (url) {
338
318
  bgMap.push({ prop: bgPropMap.image, value: url })
339
- } else if (/^(#[0-9a-f]{3}$|#[0-9a-f]{6}$|rgb|rgba)/i.test(item)) {
319
+ } else if (verifyValues({ prop: bgPropMap.color, value: item }, false)) {
340
320
  bgMap.push({ prop: bgPropMap.color, value: item })
341
- } else if (SUPPORTED_PROP_VAL_ARR[bgPropMap.repeat].includes(item)) {
321
+ } else if (verifyValues({ prop: bgPropMap.repeat, value: item }, false)) {
342
322
  bgMap.push({ prop: bgPropMap.repeat, value: item })
343
323
  }
344
- // else if (SUPPORTED_PROP_VAL_ARR[bgPropMap.size].includes(item)) {
345
- // bgMap.push({ prop: bgPropMap.size, value: item })
346
- // }
347
324
  })
348
325
  return bgMap.length ? bgMap : false
349
326
  }
@@ -352,23 +329,30 @@ module.exports = function getSpec ({ warn, error }) {
352
329
  return false
353
330
  }
354
331
 
355
- const getBorderRadius = ({ prop, value }) => {
332
+ // border-radius 缩写转换
333
+ const getBorderRadius = ({ prop, value }, { mode }) => {
356
334
  const values = value.trim().split(/\s(?![^()]*\))/)
357
335
  if (values.length === 1) {
358
- verifyValues({ prop, value, valueType: ValueType.number })
336
+ verifyValues({ prop, value }, false)
359
337
  return { prop, value }
360
338
  } else {
361
- return getAbbreviation({ prop, value })
339
+ if (values.length === 2) {
340
+ values.push(...values)
341
+ } else if (values.length === 3) {
342
+ values.push(values[1])
343
+ }
344
+ return formatAbbreviation({ prop, value: values.join(' ') }, { mode })
362
345
  }
363
346
  }
364
347
 
348
+ // transform 转换
365
349
  const formatTransform = ({ prop, value }, { mode }) => {
366
350
  if (Array.isArray(value)) return { prop, value }
367
351
  const values = value.trim().split(/\s(?![^()]*\))/)
368
352
  const transform = []
369
353
  values.forEach(item => {
370
354
  const match = item.match(/([/\w]+)\(([^)]+)\)/)
371
- if (match.length >= 3) {
355
+ if (match && match.length >= 3) {
372
356
  let key = match[1]
373
357
  const val = match[2]
374
358
  switch (key) {
@@ -382,6 +366,7 @@ module.exports = function getSpec ({ warn, error }) {
382
366
  case 'rotate':
383
367
  case 'skewX':
384
368
  case 'skewY':
369
+ case 'perspective':
385
370
  // 单个值处理
386
371
  transform.push({ [key]: val })
387
372
  break
@@ -395,19 +380,19 @@ module.exports = function getSpec ({ warn, error }) {
395
380
  case 'rotate3d': // x y z angle
396
381
  case 'translate3d': // x y 支持 z不支持
397
382
  case 'scale3d': // x y 支持 z不支持
398
- {
399
- // 2 个以上的值处理
400
- key = key.replace('3d', '')
401
- const vals = val.split(',').splice(0, key === 'rotate' ? 4 : 3)
402
- const xyz = ['X', 'Y', 'Z']
403
- transform.push(...vals.map((v, index) => {
404
- if (key !== 'rotate' && index > 1) {
405
- unsupportedPropError({ prop: `${key}Z`, mode })
406
- }
407
- return { [`${key}${xyz[index] || ''}`]: v.trim() }
408
- }))
409
- break
410
- }
383
+ {
384
+ // 2 个以上的值处理
385
+ key = key.replace('3d', '')
386
+ const vals = val.split(',').splice(0, key === 'rotate' ? 4 : 3)
387
+ const xyz = ['X', 'Y', 'Z']
388
+ transform.push(...vals.map((v, index) => {
389
+ if (key !== 'rotate' && index > 1) {
390
+ unsupportedPropError({ prop: `${key}Z`, mode })
391
+ }
392
+ return { [`${key}${xyz[index] || ''}`]: v.trim() }
393
+ }))
394
+ break
395
+ }
411
396
  case 'translateZ':
412
397
  case 'scaleZ':
413
398
  default:
@@ -425,47 +410,113 @@ module.exports = function getSpec ({ warn, error }) {
425
410
  }
426
411
  }
427
412
 
428
- const spec = {
413
+ const isNumber = (value) => {
414
+ return !isNaN(+value)
415
+ }
416
+
417
+ const getIntegersFlex = ({ prop, value }) => {
418
+ if (isNumber(value) && value >= 0) {
419
+ return { prop, value }
420
+ } else {
421
+ error(`The value of ${prop} accepts any floating point value >= 0.`)
422
+ return false
423
+ }
424
+ }
425
+
426
+ const formatFlex = ({ prop, value }, { mode }) => {
427
+ let values = value.trim().split(/\s(?![^()]*\))/)
428
+ if (values.length > 3) {
429
+ error('The value of prop [flex] supports up to three values')
430
+ values = values.splice(0, 3)
431
+ }
432
+ const cssMap = []
433
+ const lastOne = values[values.length - 1]
434
+ const isAuto = lastOne === 'auto'
435
+ // 枚举值 none initial
436
+ if (values.includes('initial') || values.includes('none')) {
437
+ // css flex: initial ===> flex: 0 1 ===> rn flex 0 1
438
+ // css flex: none ===> css flex: 0 0 ===> rn flex 0 0
439
+ if (values.length === 1) {
440
+ // 添加 basis 和 shrink
441
+ // value=initial 则 flexShrink=1,其他场景都是0
442
+ cssMap.push(...[{ prop: 'flexGrow', value: 0 }, { prop: 'flexShrink', value: +(values[0] === 'initial') }])
443
+ } else {
444
+ error('When setting the value of flex to none or initial, only one value is supported.')
445
+ }
446
+ return cssMap
447
+ }
448
+ // 最后一个值是flexBasis 的有效值(auto或者有单位百分比、px等)
449
+ // flex 0 1 auto flex auto flex 1 auto flex 1 30px flex 1 10% flex 1 1 auto
450
+ if (!isNumber(lastOne)) {
451
+ // 添加 grow 和 shrink
452
+ // 在设置 flex basis 有效值的场景下,如果没有设置 grow 和 shrink,则默认为1
453
+ // 单值 flex: 1 1 <flex-basis>
454
+ // 双值 flex: <flex-grow> 1 <flex-basis>
455
+ // 三值 flex: <flex-grow> <flex-shrink> <flex-basis>
456
+ for (let i = 0; i < 2; i++) {
457
+ const item = getIntegersFlex({ prop: AbbreviationMap[prop][i], value: isNumber(values[i]) ? values[i] : 1 })
458
+ item && cssMap.push(item)
459
+ }
460
+ if (!isAuto) {
461
+ // 有单位(百分比、px等) 的 value 赋值 flexBasis,auto 不处理
462
+ cssMap.push({
463
+ prop: 'flexBasis',
464
+ value: lastOne
465
+ })
466
+ }
467
+ return cssMap
468
+ }
469
+ // 纯数值:value 按flex-grow flex-shrink flex-basis 顺序赋值
470
+ // 兜底 shrink & basis
471
+ if (values.length === 1) {
472
+ values.push(...[1, 0])
473
+ } else if (values.length === 2) {
474
+ values.push(0)
475
+ }
476
+ // 循环赋值
477
+ for (let i = 0; i < values.length; i++) {
478
+ const item = getIntegersFlex({ prop: AbbreviationMap[prop][i], value: values[i] })
479
+ item && cssMap.push(item)
480
+ }
481
+ return cssMap
482
+ }
483
+
484
+ const formatFontFamily = ({ prop, value }) => {
485
+ // 去掉引号 取逗号分隔后的第一个
486
+ const newVal = value.replace(/"|'/g, '').trim()
487
+ const values = newVal.split(',').filter(i => i)
488
+ if (!newVal || !values.length) {
489
+ error(`The value of prop [${prop}: ${value}] is invaild, please check again`)
490
+ return false
491
+ } else if (values.length > 1) {
492
+ warn(`The value of prop [${prop}] only supports one, and the first one is used by default`)
493
+ }
494
+ return { prop, value: values[0].trim() }
495
+ }
496
+
497
+ const formatBoxShadow = ({ prop, value }, { mode }) => {
498
+ value = value.trim()
499
+ if (value === 'none') {
500
+ return false
501
+ }
502
+ const cssMap = formatAbbreviation({ prop, value }, { mode })
503
+ if (mode === 'android') return cssMap
504
+ // ios 阴影需要额外设置 shadowOpacity=1
505
+ cssMap.push({
506
+ prop: 'shadowOpacity',
507
+ value: 1
508
+ })
509
+ return cssMap
510
+ }
511
+
512
+ return {
429
513
  supportedModes: ['ios', 'android'],
430
514
  rules: [
431
515
  { // 背景相关属性的处理
432
- test: /^(background|background-image|background-color|background-size|background-repeat|background-position)$/,
516
+ test: /^(background|background-image|background-size|background-position)$/,
433
517
  ios: checkBackgroundImage,
434
518
  android: checkBackgroundImage
435
519
  },
436
- { // RN 不支持的 CSS property
437
- test: unsupportedPropExp,
438
- ios: delRule,
439
- android: delRule
440
- },
441
- { // React Native android 不支持的 CSS property
442
- test: unsupportedPropMode.android,
443
- android: delRule
444
- },
445
- { // React Native ios 不支持的 CSS property
446
- test: unsupportedPropMode.ios,
447
- ios: delRule
448
- },
449
- { // RN 支持的 CSS property value
450
- test: propValExp,
451
- ios: delRule,
452
- android: delRule
453
- },
454
- {
455
- test: 'box-shadow',
456
- ios: getAbbreviation,
457
- android: getAbbreviationAndroid
458
- },
459
- {
460
- test: 'text-decoration',
461
- ios: getAbbreviation,
462
- android: getAbbreviationAndroid
463
- },
464
- {
465
- test: /^(font-variant|font-variant-caps|font-variant-numeric|font-variant-east-asian|font-variant-alternates|font-variant-ligatures)$/,
466
- ios: getFontVariant,
467
- android: getFontVariant
468
- },
469
520
  {
470
521
  test: 'border-radius',
471
522
  ios: getBorderRadius,
@@ -476,12 +527,6 @@ module.exports = function getSpec ({ warn, error }) {
476
527
  ios: formatMargins,
477
528
  android: formatMargins
478
529
  },
479
- // 通用的简写格式匹配
480
- {
481
- test: new RegExp('^(' + Object.keys(AbbreviationMap).join('|') + ')$'),
482
- ios: getAbbreviation,
483
- android: getAbbreviation
484
- },
485
530
  { // line-height 换算
486
531
  test: 'line-height',
487
532
  ios: formatLineHeight,
@@ -492,18 +537,33 @@ module.exports = function getSpec ({ warn, error }) {
492
537
  ios: formatTransform,
493
538
  android: formatTransform
494
539
  },
495
- // 值类型校验放到最后
496
- { // color 颜色值校验 color xx-color 等
497
- test: /^(color|(.+-color))$/,
498
- ios: checkCommonValue(ValueType.color),
499
- android: checkCommonValue(ValueType.color)
540
+ {
541
+ test: 'flex',
542
+ ios: formatFlex,
543
+ android: formatFlex
544
+ },
545
+ {
546
+ test: 'font-family',
547
+ ios: formatFontFamily,
548
+ android: formatFontFamily
500
549
  },
501
- { // number 值校验 // width height xx-left xx-top 等
502
- test: /^((width|height)|(.+-(left|right|top|bottom|radius|spacing|size)))$/,
503
- ios: checkCommonValue(ValueType.number),
504
- android: checkCommonValue(ValueType.number)
550
+ {
551
+ test: 'box-shadow',
552
+ ios: formatBoxShadow,
553
+ android: formatBoxShadow
554
+ },
555
+ // 通用的简写格式匹配
556
+ {
557
+ test: new RegExp('^(' + Object.keys(AbbreviationMap).join('|') + ')$'),
558
+ ios: formatAbbreviation,
559
+ android: formatAbbreviation
560
+ },
561
+ // 属性&属性值校验
562
+ {
563
+ test: () => true,
564
+ ios: verification,
565
+ android: verification
505
566
  }
506
567
  ]
507
568
  }
508
- return spec
509
569
  }