242 lines
8.1 KiB
JavaScript
242 lines
8.1 KiB
JavaScript
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 transformBackground = (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)];
|
||
}
|
||
return [decl];
|
||
};
|
||
|
||
const borderWidth = 'Width';
|
||
const borderStyle = 'Style';
|
||
const borderColor = 'Color';
|
||
const transformBorder = (decl) => {
|
||
const { prop, value, important, raws, source } = decl;
|
||
const splitResult = value.replace(/\s*,\s*/g, ',').split(/\s+/);
|
||
const result = [/^[\d\.]+\S*$/, /^(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 borderTop = 'borderTop';
|
||
const borderRight = 'borderRight';
|
||
const borderBottom = 'borderBottom';
|
||
const borderLeft = 'borderLeft';
|
||
const transformBorderColor = (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 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:
|
||
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 transformBorderStyle = transformBorderColor;
|
||
|
||
const transformBorderWidth = transformBorderColor;
|
||
|
||
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 CHUNK_REGEXP = /^(\S*)?\s*(\d*\.?\d+(?:ms|s)?)?\s*(\S*)?\s*(\d*\.?\d+(?:ms|s)?)?$/;
|
||
const { value, important, raws, source } = decl;
|
||
const result = [];
|
||
const 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 styleMap = {
|
||
transition: transformTransition,
|
||
border: transformBorder,
|
||
background: transformBackground,
|
||
borderTop: transformBorder,
|
||
borderRight: transformBorder,
|
||
borderBottom: transformBorder,
|
||
borderLeft: transformBorder,
|
||
borderStyle: transformBorderStyle,
|
||
borderWidth: transformBorderWidth,
|
||
borderColor: transformBorderColor,
|
||
borderRadius: transformBorderRadius,
|
||
// uvue已经支持这些简写属性,不需要展开
|
||
/* eslint-disable no-restricted-syntax */
|
||
...(options.type !== 'uvue'
|
||
? {
|
||
margin: transformMargin,
|
||
padding: transformPadding,
|
||
flexFlow: transformFlexFlow,
|
||
}
|
||
: {}),
|
||
};
|
||
let result = {};
|
||
{
|
||
result = styleMap;
|
||
}
|
||
return result;
|
||
}
|
||
let DeclTransforms;
|
||
const expanded = Symbol('expanded');
|
||
function expand(options) {
|
||
const plugin = {
|
||
postcssPlugin: '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 };
|