Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
65b0c41
formatTextDecoration
wenwenhua May 28, 2026
fe3113b
Merge branch 'style-enhancement' into style-enhancement-text-decoration
wenwenhua May 28, 2026
246e3fc
add text-decoration
wenwenhua May 28, 2026
41d0b52
add formatTextDecoration
wenwenhua May 28, 2026
d8f3ca6
Merge branch 'fix-style-260528' into style-enhancement-text-decoration
wenwenhua May 28, 2026
5d01095
update transform
wenwenhua May 28, 2026
fd208c8
删除多余的 __formatValue 调用
wenwenhua May 28, 2026
def144a
del supportedLineSingles
wenwenhua May 28, 2026
4c759dc
del supportedLineSingles
wenwenhua May 28, 2026
f70e0ad
del supportedLineValues
wenwenhua May 28, 2026
af70966
del supportedLineValues
wenwenhua May 28, 2026
9247d02
Merge branch 'fix-style-260528' into style-enhancement-text-decoration
wenwenhua May 28, 2026
66a0414
Merge branch 'style-enhancement' into style-enhancement-text-decoration
wenwenhua May 29, 2026
7f28772
优化 formatTextDecoration & bgposition 调换顺序
wenwenhua Jun 5, 2026
7c1b09b
Merge branch 'fix-style-260528' into style-enhancement-text-decoration
wenwenhua Jun 5, 2026
456da2b
Merge branch 'style-enhancement' into style-enhancement-text-decoration
wenwenhua Jun 5, 2026
ff734d4
add bgposition & text-decoration
wenwenhua Jun 5, 2026
0c93e9b
Merge branch 'master' into fix-style-260528
wenwenhua Jun 5, 2026
168bb3d
Merge branch 'fix-style-260528' into style-enhancement-text-decoration
wenwenhua Jun 5, 2026
650f11f
fix background cover contain
wenwenhua Jun 5, 2026
d9c5618
Merge branch 'fix-style-260528' into style-enhancement-text-decoration
wenwenhua Jun 5, 2026
b338863
fix cr
wenwenhua Jun 5, 2026
e6270ac
Merge branch 'fix-style-260528' into style-enhancement-text-decoration
wenwenhua Jun 5, 2026
00d263c
fix boxShadow 不生效问题,保留 px 单位,仅处理 rpx 转 px
wenwenhua Jun 8, 2026
7804e9e
Merge branch 'fix-style-260528' into style-enhancement-text-decoration
wenwenhua Jun 8, 2026
64aa7a9
add 简写属性支持单个css var
wenwenhua Jun 8, 2026
c241651
Merge branch 'style-enhancement' into style-enhancement-text-decoration
wenwenhua Jun 10, 2026
e6520e4
cr 更新
wenwenhua Jun 10, 2026
48895ee
cr 更新
wenwenhua Jun 10, 2026
14a8df3
cr 更新
wenwenhua Jun 10, 2026
29413ad
cr 更新
wenwenhua Jun 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 71 additions & 12 deletions packages/webpack-plugin/lib/platform/style/wx/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,8 @@ module.exports = function getSpec ({ warn, error }) {
const props = AbbreviationMap[prop]
const values = Array.isArray(value) ? value : parseValues(value)
const cssMap = []
// 复合属性不支持单个css var(css var可以接收单个值可以是复合值,复合值运行时不处理,这里前置提示一下)
if (values.length === 1 && cssVariableExp.test(value)) {
error(`Property ${prop} in ${selector} is abbreviated property and does not support a single CSS var`)
return cssMap
return { prop, value }
}
let idx = 0
let propsIdx = 0
Expand Down Expand Up @@ -364,15 +362,20 @@ module.exports = function getSpec ({ warn, error }) {
return values.length === 0 ? false : { prop: bgPropMap.size, value: values }
}
const formatBackgroundPosition = (value) => {
const yAxisKeywords = ['top', 'bottom']
const values = []
parseValues(value).forEach(item => {
if (verifyValues({ prop: bgPropMap.position, value: item, selector })) {
// 支持 number 值 / 枚举, center与50%等价
values.push(item === 'center' ? '50%' : item)
} else {
error(`Value of [${bgPropMap.size}] in ${selector} does not support commas, received [${value}], please check again!`)
error(`Value of [${bgPropMap.position}] in ${selector} does not support value [${item}]`)
}
})
// CSS 允许 y x 顺序的关键字(如 top left),但输出需要 [x, y] 顺序
if (values.length === 2 && yAxisKeywords.includes(values[0])) {
;[values[0], values[1]] = [values[1], values[0]]
}
return { prop: bgPropMap.position, value: values }
}
switch (prop) {
Expand All @@ -395,8 +398,7 @@ module.exports = function getSpec ({ warn, error }) {
case bgPropMap.all: {
// background: 支持 image/color/repeat 与 position/size
if (cssVariableExp.test(value)) {
error(`Property [${bgPropMap.all}] in ${selector} is abbreviated property and does not support CSS var`)
return false
return { prop, value }
}
// background: none
if (value === 'none') {
Expand Down Expand Up @@ -499,9 +501,6 @@ module.exports = function getSpec ({ warn, error }) {
key = key === 'rotate' ? 'rotateZ' : key
transform.push({ [key]: val })
break
case 'matrix':
transform.push({ [key]: parseValues(val, ',').map(val => +val) })
break
case 'translate':
case 'scale':
case 'skew':
Expand All @@ -524,12 +523,47 @@ module.exports = function getSpec ({ warn, error }) {
}))
break
}
case 'rotate3d': {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rotate3d好像支持单值0 看mdn上

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

你也确认一下支持别的单值或者多值么

const parts = parseValues(val, ',')
if (parts.length === 4) {
const x = +parts[0].trim()
const y = +parts[1].trim()
const z = +parts[2].trim()
const angle = parts[3].trim()
if (x && !y && !z) transform.push({ rotateX: angle })
else if (!x && y && !z) transform.push({ rotateY: angle })
else if (!x && !y && z) transform.push({ rotateZ: angle })
else unsupportedPropError({ prop, value, selector }, { mode })
} else {
error(`Value of [transform] in ${selector} does not support rotate3d with ${parts.length} values, only 4 values are supported`)
}
break
}
case 'matrix':
{
const matrixValues = parseValues(val, ',').map(val => +val)
if (matrixValues.length === 6) {
const [a, b, c, d, tx, ty] = matrixValues
transform.push({ matrix: [a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1] })
} else {
error(`Value of [transform] in ${selector} does not support matrix with ${matrixValues.length} values, only 6 values are supported in ${mode} environment!`)
}
break
}
case 'matrix3d':
{
const matrixValues = parseValues(val, ',').map(val => +val)
if (matrixValues.length === 16) {
transform.push({ matrix: matrixValues })
} else {
error(`Value of [transform] in ${selector} does not support matrix3d with ${matrixValues.length} values, only 16 values are supported in ${mode} environment!`)
}
break
}
// 不支持的属性处理
case 'translateZ':
case 'scaleZ':
case 'rotate3d': // x y z angle
case 'matrix3d':
default:
// 不支持的属性处理
unsupportedPropError({ prop, value, selector }, { mode })
break
}
Expand Down Expand Up @@ -637,6 +671,25 @@ module.exports = function getSpec ({ warn, error }) {
// })
// return cssMap
// }

const formatTextDecoration = ({ prop, value, selector }, { mode }) => {
const values = Array.isArray(value) ? value : parseValues(value)
if (values.length === 1 && cssVariableExp.test(value)) {
return { prop, value }
}
const lineValues = []
const otherValues = []
for (const v of values) {
if (SUPPORTED_PROP_VAL_ARR['text-decoration-line'].includes(v)) {
lineValues.push(v)
} else {
otherValues.push(v)
}
}
const processedValues = lineValues.length > 0 ? [lineValues.join(' '), ...otherValues] : otherValues
return formatAbbreviation({ prop, value: processedValues, selector }, { mode })
}

const formatBorder = ({ prop, value, selector }, { mode }) => {
value = value.trim()
if (value === 'none') {
Expand Down Expand Up @@ -694,6 +747,12 @@ module.exports = function getSpec ({ warn, error }) {
// android: formatBoxShadow,
// harmony: formatBoxShadow
// },
{
test: 'text-decoration',
ios: formatTextDecoration,
android: formatTextDecoration,
harmony: formatTextDecoration
},
{
test: 'border',
ios: formatBorder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,12 @@ function backgroundSize (imageProps: ImageProps, preImageInfo: PreImageInfo, ima
// 枚举值
if (typeof width === 'string' && ['cover', 'contain'].includes(width)) {
if (layoutInfo && imageSize) {
const layoutRatio = layoutWidth / imageSizeWidth
const eleRatio = imageSizeWidth / imageSizeHeight
// 容器宽高比 大于 图片的宽高比,依据宽度作为基准,否则以高度为基准
if ((layoutRatio <= eleRatio && (width as string) === 'contain') || (layoutRatio >= eleRatio && (width as string) === 'cover')) {
const containerRatio = layoutWidth / layoutHeight
const imageRatio = imageSizeWidth / imageSizeHeight
// 容器宽高比 小于等于 图片宽高比:contain 按宽缩放,cover 按高缩放
if ((containerRatio <= imageRatio && (width as string) === 'contain') || (containerRatio >= imageRatio && (width as string) === 'cover')) {
dimensions = calculateSize(layoutWidth as number, imageSizeHeight / imageSizeWidth, true) as Size
} else if ((layoutRatio > eleRatio && (width as string) === 'contain') || (layoutRatio < eleRatio && (width as string) === 'cover')) {
} else if ((containerRatio > imageRatio && (width as string) === 'contain') || (containerRatio < imageRatio && (width as string) === 'cover')) {
dimensions = calculateSize(layoutHeight as number, imageSizeWidth / imageSizeHeight) as Size
}
}
Expand Down
93 changes: 79 additions & 14 deletions packages/webpack-plugin/lib/runtime/components/react/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -519,25 +519,41 @@ function parseTransform (transformStr: string) {
let key = match[1]
const val = match[2]
switch (key) {
case 'translateX':
case 'translateY':
case 'scaleX':
case 'scaleY':
case 'rotateX':
case 'rotateY':
case 'rotateZ':
case 'rotate':
case 'skewX':
case 'skewY':
case 'perspective':
// rotate 处理成 rotateZ
key = key === 'rotate' ? 'rotateZ' : key
// 单个值处理
transform.push({ [key]: val })
break
case 'translateX':
case 'translateY':
case 'scaleX':
case 'scaleY':
case 'perspective':
transform.push({ [key]: global.__formatValue(val) })
break
case 'matrix':
transform.push({ [key]: parseValues(val, ',').map(val => +val) })
case 'matrix': {
const matrixValues = parseValues(val, ',').map(v => +v.trim())
if (matrixValues.length === 6) {
const [a, b, c, d, tx, ty] = matrixValues
transform.push({ matrix: [a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1] })
} else {
error(`Transform matrix only supports 6 values in React Native, got ${matrixValues.length}`)
}
break
}
case 'matrix3d': {
const matrixValues = parseValues(val, ',').map(v => +v.trim())
if (matrixValues.length === 16) {
transform.push({ matrix: matrixValues })
} else {
error(`Transform matrix only supports 16 values in React Native, got ${matrixValues.length}`)
}
break
}
case 'translate':
case 'scale':
case 'skew':
Expand All @@ -557,6 +573,24 @@ function parseTransform (transformStr: string) {
}))
break
}
case 'rotate3d': {
const parts = parseValues(val, ',')
if (parts.length === 4) {
const x = +parts[0].trim()
const y = +parts[1].trim()
const z = +parts[2].trim()
const angle = parts[3].trim()
if (x && !y && !z) transform.push({ rotateX: angle })
else if (!x && y && !z) transform.push({ rotateY: angle })
else if (!x && !y && z) transform.push({ rotateZ: angle })
} else {
error(`Transform rotate3d only supports 4 values, got ${parts.length}`)
}
break
}
case 'translateZ':
case 'scaleZ':
break
}
}
})
Expand All @@ -572,7 +606,14 @@ function transformTransform (style: Record<string, any>) {
function transformBoxShadow (styleObj: Record<string, any>) {
if (!styleObj.boxShadow) return
styleObj.boxShadow = parseValues(styleObj.boxShadow).reduce((res, i, idx) => {
return `${res}${idx === 0 ? '' : ' '}${global.__formatValue(i)}`
let formatted: string | number
// 需要保留 px 关键字,这里仅处理 rpx 转 px
if (/\d+rpx$/.test(i)) {
formatted = global.__formatValue(i) + 'px'
} else {
formatted = i
}
return `${res}${idx === 0 ? '' : ' '}${formatted}`
}, '')
}

Expand Down Expand Up @@ -657,6 +698,20 @@ function transformFlex (styleObj: Record<string, any>) {
}
}

function expandTextDecoration (values: string[]): string[] {
const supportedLineValues = new Set(['none', 'underline', 'line-through'])
const lineValues: string[] = []
const otherValues: string[] = []
for (const v of values) {
if (supportedLineValues.has(v)) {
lineValues.push(v)
} else {
otherValues.push(v)
}
}
return lineValues.length > 0 ? [lineValues.join(' '), ...otherValues] : otherValues
}
// Todo 目前仅支持指定属性的简写值,且不同于编译时的 class 处理,暂不支持缺省值,后续优化
function transformShorthand (styleObj: Record<string, any>, shorthandKeys: string[]) {
if (shorthandKeys.length === 0) return
for (const key of shorthandKeys) {
Expand All @@ -672,7 +727,12 @@ function transformShorthand (styleObj: Record<string, any>, shorthandKeys: strin
const props = runtimeAbbreviationMap[key]
if (!props) continue
if (hasOwn(runtimeCompositeStyleMap, key) && values.length === 1) continue
const expandedValues = hasOwn(runtimeCompositeStyleMap, key) ? expandCompositeValues(values) : values
let expandedValues = values
if (hasOwn(runtimeCompositeStyleMap, key)) {
expandedValues = expandCompositeValues(values)
} else if (key === 'textDecoration') {
expandedValues = expandTextDecoration(values)
}
const pairs = expandAbbreviation(expandedValues, props)
delete styleObj[key]
for (const [prop, val] of pairs) {
Expand All @@ -691,14 +751,19 @@ function transformFontFamily (styleObj: Record<string, any>) {
const values = parseValues(stripped, ',')
styleObj.fontFamily = values[0].trim()
}

function transOrderXY (values: string[]) {
if (values.length === 2 && ['top', 'bottom'].includes(values[0])) {
[values[0], values[1]] = [values[1], values[0]]
}
return values
}
function transformBackground (styleObj: Record<string, any>) {
if (typeof styleObj.backgroundSize === 'string') {
styleObj.backgroundSize = parseValues(styleObj.backgroundSize)
}
if (typeof styleObj.backgroundPosition === 'string') {
const parts = parseValues(styleObj.backgroundPosition)
styleObj.backgroundPosition = parts.map(v => v === 'center' ? '50%' : v)
styleObj.backgroundPosition = transOrderXY(parts.map(v => v === 'center' ? '50%' : v))
}
const value = styleObj.background
if (typeof value !== 'string') return
Expand Down Expand Up @@ -739,7 +804,7 @@ function transformBackground (styleObj: Record<string, any>) {
}
}
}
if (positionValues.length) styleObj.backgroundPosition = positionValues
if (positionValues.length) styleObj.backgroundPosition = transOrderXY(positionValues)
if (sizeValues.length) styleObj.backgroundSize = sizeValues
}

Expand Down
20 changes: 20 additions & 0 deletions packages/webpack-plugin/test/platform/wx/style/style-rn.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,26 @@ describe('React Native style validation for CSS variables', () => {
expect(config.warn).not.toHaveBeenCalled()
expect(config.error).not.toHaveBeenCalled()
})

describe('background-position [x, y] ordering', () => {
const positionCases = [
{ input: 'top left', expected: ['"left"', '"top"'], desc: 'top left → [left, top]' },
{ input: 'bottom right', expected: ['"right"', '"bottom"'], desc: 'bottom right → [right, bottom]' },
{ input: 'left top', expected: ['"left"', '"top"'], desc: 'left top → [left, top] (already correct)' },
{ input: 'top center', expected: ['"50%"', '"top"'], desc: 'top center → [50%, top]' },
{ input: '50% 30%', expected: ['"50%"', '"30%"'], desc: '50% 30% → [50%, 30%] (no change)' }
]

positionCases.forEach(({ input, expected, desc }) => {
test(desc, () => {
const css = `.bg { background-position: ${input}; }`
const config = createConfig()
const result = getClassMap({ content: css, filename: 'test.css', ...config })
expect(result.bg).toEqual({ backgroundPosition: expected })
expect(config.error).not.toHaveBeenCalled()
})
})
})
})

describe('Direct normal values (without CSS variables)', () => {
Expand Down
Loading