import { hyphenate, capitalize } from '@vue/shared'; function createDecl(prop, value, important, raws, source) { const decl = { type: 'decl', prop, value: value.toString(), raws, source, }; if (important) { decl.important = true; } return decl; } const backgroundColor = 'backgroundColor'; const backgroundImage = 'backgroundImage'; const handleTransformBackground = (decl) => { const { value, important, raws, source } = decl; if (/^#?\S+$/.test(value) || /^rgba?(.+)$/.test(value)) { return [ createDecl(backgroundImage, 'none', important, raws, source), createDecl(backgroundColor, value, important, raws, source), ]; } else if (/^linear-gradient(.+)$/.test(value)) { return [ createDecl(backgroundImage, value, important, raws, source), createDecl(backgroundColor, 'transparent', important, raws, source), ]; } else if (value == '') { return [ createDecl(backgroundImage, 'none', important, raws, source), createDecl(backgroundColor, 'transparent', important, raws, source), ]; } return [decl]; }; const handleTransformBackgroundNvue = (decl) => { const { value, important, raws, source } = decl; if (/^#?\S+$/.test(value) || /^rgba?(.+)$/.test(value)) { return [createDecl(backgroundColor, value, important, raws, source)]; } else if (/^linear-gradient(.+)$/.test(value)) { return [createDecl(backgroundImage, value, important, raws, source)]; } else if (value == '') { return [decl]; } return [decl]; }; function createTransformBackground(options) { return (decl) => { // nvue 平台维持原有逻辑不变 const isUvuePlatform = options.type === 'uvue'; if (isUvuePlatform) { return handleTransformBackground(decl); } else { return handleTransformBackgroundNvue(decl); } }; } function borderTop() { return 'borderTop'; } function borderRight() { return 'borderRight'; } function borderBottom() { return 'borderBottom'; } function borderLeft() { return 'borderLeft'; } const transformBorderColor = (decl) => { const { prop, value, important, raws, source } = decl; const _property_split = hyphenate(prop).split('-'); let property = _property_split[_property_split.length - 1]; { property = capitalize(property); } const splitResult = value.replace(/\s*,\s*/g, ',').split(/\s+/); // 1pt switch (splitResult.length) { case 1: if (_property_split.length === 3) { // border-top-width return [decl]; } // border-width splitResult.push(splitResult[0], splitResult[0], splitResult[0]); break; case 2: splitResult.push(splitResult[0], splitResult[1]); break; case 3: splitResult.push(splitResult[1]); break; } return [ createDecl(borderTop() + property, splitResult[0], important, raws, source), createDecl(borderRight() + property, splitResult[1], important, raws, source), createDecl(borderBottom() + property, splitResult[2], important, raws, source), createDecl(borderLeft() + property, splitResult[3], important, raws, source), ]; }; const transformBorderColorNvue = (decl) => { const { prop, value, important, raws, source } = decl; let property = hyphenate(prop).split('-')[1]; { property = capitalize(property); } const splitResult = value.replace(/\s*,\s*/g, ',').split(/\s+/); switch (splitResult.length) { case 1: return [decl]; case 2: splitResult.push(splitResult[0], splitResult[1]); break; case 3: splitResult.push(splitResult[1]); break; } return [ createDecl(borderTop + property, splitResult[0], important, raws, source), createDecl(borderRight + property, splitResult[1], important, raws, source), createDecl(borderBottom + property, splitResult[2], important, raws, source), createDecl(borderLeft + property, splitResult[3], important, raws, source), ]; }; const transformBorderStyle = transformBorderColor; const transformBorderStyleNvue = transformBorderColorNvue; const transformBorderWidth = transformBorderColor; const transformBorderWidthNvue = transformBorderColorNvue; function createTransformBorder(options) { return (decl) => { const borderWidth = () => { return 'Width'; }; const borderStyle = () => { return 'Style'; }; const borderColor = () => { return 'Color'; }; const { prop, value, important, raws, source } = decl; const splitResult = value.replace(/\s*,\s*/g, ',').split(/\s+/); const result = [ /^[\d\.]+\S*|^(thin|medium|thick)$/, /^(solid|dashed|dotted|none)$/, /\S+/, ].map((item) => { const index = splitResult.findIndex((str) => item.test(str)); return index < 0 ? null : splitResult.splice(index, 1)[0]; }); const isUvuePlatform = options.type == 'uvue'; if (isUvuePlatform) { if (splitResult.length > 0 && value != '') { return [decl]; } } else { // nvue 维持不变 if (splitResult.length > 0) { return [decl]; } } const defaultWidth = (str) => { if (str != null) { return str.trim(); } if (options.type == 'uvue') { return 'medium'; } return '0'; }; const defaultStyle = (str) => { if (str != null) { return str.trim(); } if (options.type == 'uvue') { return 'none'; } return 'solid'; }; const defaultColor = (str) => { if (str != null) { return str.trim(); } return '#000000'; }; if (!isUvuePlatform) { // nvue 维持不变 return [ createDecl(prop + borderWidth(), defaultWidth(result[0]), important, raws, source), createDecl(prop + borderStyle(), defaultStyle(result[1]), important, raws, source), createDecl(prop + borderColor(), defaultColor(result[2]), important, raws, source), ]; } return [ ...transformBorderWidth(createDecl(prop + borderWidth(), defaultWidth(result[0]), important, raws, source)), ...transformBorderStyle(createDecl(prop + borderStyle(), defaultStyle(result[1]), important, raws, source)), ...transformBorderColor(createDecl(prop + borderColor(), defaultColor(result[2]), important, raws, source)), ]; }; } function createTransformBorderNvue(options) { return (decl) => { const borderWidth = 'Width'; const borderStyle = 'Style'; const borderColor = 'Color'; const { prop, value, important, raws, source } = decl; const splitResult = value.replace(/\s*,\s*/g, ',').split(/\s+/); const result = [ /^[\d\.]+\S*|^(thin|medium|thick)$/, /^(solid|dashed|dotted|none)$/, /\S+/, ].map((item) => { const index = splitResult.findIndex((str) => item.test(str)); return index < 0 ? null : splitResult.splice(index, 1)[0]; }); if (splitResult.length) { return [decl]; } return [ createDecl(prop + borderWidth, (result[0] || '0').trim(), important, raws, source), createDecl(prop + borderStyle, (result[1] || 'solid').trim(), important, raws, source), createDecl(prop + borderColor, (result[2] || '#000000').trim(), important, raws, source), ]; }; } const borderTopLeftRadius = 'borderTopLeftRadius'; const borderTopRightRadius = 'borderTopRightRadius'; const borderBottomRightRadius = 'borderBottomRightRadius'; const borderBottomLeftRadius = 'borderBottomLeftRadius'; const transformBorderRadius = (decl) => { const { value, important, raws, source } = decl; const splitResult = value.split(/\s+/); if (value.includes('/')) { return [decl]; } switch (splitResult.length) { case 1: splitResult.push(splitResult[0], splitResult[0], splitResult[0]); break; case 2: splitResult.push(splitResult[0], splitResult[1]); break; case 3: splitResult.push(splitResult[1]); break; } return [ createDecl(borderTopLeftRadius, splitResult[0], important, raws, source), createDecl(borderTopRightRadius, splitResult[1], important, raws, source), createDecl(borderBottomRightRadius, splitResult[2], important, raws, source), createDecl(borderBottomLeftRadius, splitResult[3], important, raws, source), ]; }; const transformBorderRadiusNvue = (decl) => { const { value, important, raws, source } = decl; const splitResult = value.split(/\s+/); if (value.includes('/')) { return [decl]; } // const isUvuePlatform = options.type == 'uvue' switch (splitResult.length) { case 1: return [decl]; case 2: splitResult.push(splitResult[0], splitResult[1]); break; case 3: splitResult.push(splitResult[1]); break; } return [ createDecl(borderTopLeftRadius, splitResult[0], important, raws, source), createDecl(borderTopRightRadius, splitResult[1], important, raws, source), createDecl(borderBottomRightRadius, splitResult[2], important, raws, source), createDecl(borderBottomLeftRadius, splitResult[3], important, raws, source), ]; }; const flexDirection = 'flexDirection'; const flexWrap = 'flexWrap'; const transformFlexFlow = (decl) => { const { value, important, raws, source } = decl; const splitResult = value.split(/\s+/); const result = [ /^(column|column-reverse|row|row-reverse)$/, /^(nowrap|wrap|wrap-reverse)$/, ].map((item) => { const index = splitResult.findIndex((str) => item.test(str)); return index < 0 ? null : splitResult.splice(index, 1)[0]; }); if (splitResult.length) { return [decl]; } return [ createDecl(flexDirection, result[0] || 'column', important, raws, source), createDecl(flexWrap, result[1] || 'nowrap', important, raws, source), ]; }; const top = 'Top'; const right = 'Right'; const bottom = 'Bottom'; const left = 'Left'; const createTransformBox = (type) => { return (decl) => { const { value, important, raws, source } = decl; const splitResult = value.split(/\s+/); switch (splitResult.length) { case 1: splitResult.push(splitResult[0], splitResult[0], splitResult[0]); break; case 2: splitResult.push(splitResult[0], splitResult[1]); break; case 3: splitResult.push(splitResult[1]); break; } return [ createDecl(type + top, splitResult[0], important, raws, source), createDecl(type + right, splitResult[1], important, raws, source), createDecl(type + bottom, splitResult[2], important, raws, source), createDecl(type + left, splitResult[3], important, raws, source), ]; }; }; const transformMargin = createTransformBox('margin'); const transformPadding = createTransformBox('padding'); const transitionProperty = 'transitionProperty'; const transitionDuration = 'transitionDuration'; const transitionTimingFunction = 'transitionTimingFunction'; const transitionDelay = 'transitionDelay'; const transformTransition = (decl) => { const { value, important, raws, source } = decl; const result = []; let match; // 针对 cubic-bezier 特殊处理 // eg: cubic-bezier(0.42, 0, 1.0, 3) // (0.2,-2,0.8,2) if (decl.value.includes('cubic-bezier')) { const CHUNK_REGEXP = /^(\S*)?\s*(\d*\.?\d+(?:ms|s)?)?\s*((\S*)|cubic-bezier\(.*\))?\s*(\d*\.?\d+(?:ms|s)?)?$/; match = value.match(CHUNK_REGEXP); } else { const CHUNK_REGEXP = /^(\S*)?\s*(\d*\.?\d+(?:ms|s)?)?\s*(\S*)?\s*(\d*\.?\d+(?:ms|s)?)?$/; match = value.match(CHUNK_REGEXP); } if (!match) { return result; } match[1] && result.push(createDecl(transitionProperty, match[1], important, raws, source)); match[2] && result.push(createDecl(transitionDuration, match[2], important, raws, source)); match[3] && result.push(createDecl(transitionTimingFunction, match[3], important, raws, source)); match[4] && result.push(createDecl(transitionDelay, match[4], important, raws, source)); return result; }; function getDeclTransforms(options) { const transformBorder = options.type == 'uvue' ? createTransformBorder(options) : createTransformBorderNvue(); const styleMap = { transition: transformTransition, border: transformBorder, background: createTransformBackground(options), borderTop: transformBorder, borderRight: transformBorder, borderBottom: transformBorder, borderLeft: transformBorder, borderStyle: options.type == 'uvue' ? transformBorderStyle : transformBorderStyleNvue, borderWidth: options.type == 'uvue' ? transformBorderWidth : transformBorderWidthNvue, borderColor: options.type == 'uvue' ? transformBorderColor : transformBorderColorNvue, borderRadius: options.type == 'uvue' ? transformBorderRadius : transformBorderRadiusNvue, // uvue已经支持这些简写属性,不需要展开 // margin,padding继续展开,确保样式的优先级 margin: transformMargin, padding: transformPadding, /* eslint-disable no-restricted-syntax */ ...(options.type !== 'uvue' ? { flexFlow: transformFlexFlow, } : {}), }; let result = {}; { result = styleMap; } return result; } let DeclTransforms; const expanded = Symbol('expanded'); function expand(options) { const plugin = { postcssPlugin: `${options.type || 'nvue'}:expand`, Declaration(decl) { if (decl[expanded]) { return; } if (!DeclTransforms) { DeclTransforms = getDeclTransforms(options); } const transform = DeclTransforms[decl.prop]; if (transform) { const res = transform(decl); const isSame = res.length === 1 && res[0] === decl; if (!isSame) { decl.replaceWith(res); } } decl[expanded] = true; }, }; return plugin; } export { expand };