1608 lines
48 KiB
JavaScript
1608 lines
48 KiB
JavaScript
'use strict';
|
||
|
||
var parseCSSFont = require('parse-css-font');
|
||
var postcss = require('postcss');
|
||
|
||
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
||
|
||
var parseCSSFont__default = /*#__PURE__*/_interopDefault(parseCSSFont);
|
||
var postcss__default = /*#__PURE__*/_interopDefault(postcss);
|
||
|
||
const COMBINATORS_RE = /^((?:(?:\.[A-Za-z0-9_\-]+)+[\+\~\> ])*)((?:\.[A-Za-z0-9_\-\:]+)+)$/;
|
||
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 NUM_REGEXP = /^[-]?\d*\.?\d+$/;
|
||
const LENGTH_REGEXP = /^[-+]?\d*\.?\d+(\S*)$/;
|
||
const SUPPORTED_VALUES_REGEXP = /supported values are: ([^)]+)/;
|
||
const SUPPORT_CSS_UNIT = ['px', 'pt', 'wx', 'upx', 'rpx'];
|
||
const isNumber = (val) => typeof val === 'number';
|
||
const cacheStringFunction$1 = (fn) => {
|
||
const cache = Object.create(null);
|
||
return ((str) => {
|
||
const hit = cache[str];
|
||
return hit || (cache[str] = fn(str));
|
||
});
|
||
};
|
||
const hyphenateRE$1 = /([A-Z])/g;
|
||
const hyphenateStyleProperty = cacheStringFunction$1((str) => str
|
||
.replace(hyphenateRE$1, (_, m) => {
|
||
if (typeof m === 'string') {
|
||
return '-' + m.toLowerCase();
|
||
}
|
||
return m;
|
||
})
|
||
.toLowerCase());
|
||
function autofixedReason(v, result) {
|
||
return 'NOTE: property value `' + v + '` is autofixed to `' + result + '`';
|
||
}
|
||
function validReason(k, v) {
|
||
return ('ERROR: property value `' +
|
||
v +
|
||
'` is not valid for `' +
|
||
hyphenateStyleProperty(k) +
|
||
'`');
|
||
}
|
||
function defaultValueReason(k, v) {
|
||
return ('NOTE: property value `' +
|
||
v +
|
||
'` is the DEFAULT value for `' +
|
||
hyphenateStyleProperty(k) +
|
||
'` (could be removed)');
|
||
}
|
||
function supportedEnumReason(k, v, items) {
|
||
return ('ERROR: property value `' +
|
||
v +
|
||
'` is not supported for `' +
|
||
hyphenateStyleProperty(k) +
|
||
'` (supported values are: `' +
|
||
items.join('`|`') +
|
||
'`)');
|
||
}
|
||
function supportedValueWithTipsReason(k, v, tips) {
|
||
return ('ERROR: property value `' +
|
||
v +
|
||
'` is not supported for `' +
|
||
hyphenateStyleProperty(k) +
|
||
'` ' +
|
||
tips);
|
||
}
|
||
function supportedUnitWithAutofixedReason(unit, v, result) {
|
||
return ('NOTE: unit `' +
|
||
unit +
|
||
'` is not supported and property value `' +
|
||
v +
|
||
'` is autofixed to `' +
|
||
result +
|
||
'`');
|
||
}
|
||
function compatibilityReason(k) {
|
||
return ('NOTE: the ' +
|
||
hyphenateStyleProperty(k) +
|
||
' property may have compatibility problem on native');
|
||
}
|
||
function supportedPropertyReason(k) {
|
||
return ('WARNING: `' +
|
||
hyphenateStyleProperty(k) +
|
||
'` is not a standard property name (may not be supported)');
|
||
}
|
||
function getSupportedPlatforms(uniPlatform) {
|
||
const supportedPlatforms = [];
|
||
if (uniPlatform?.app?.android?.unixVer !== 'x') {
|
||
supportedPlatforms.push('app-android');
|
||
}
|
||
if (uniPlatform?.app.ios?.unixVer !== 'x') {
|
||
supportedPlatforms.push('app-ios');
|
||
}
|
||
return supportedPlatforms;
|
||
}
|
||
function normalizeReasons(reasons, k, v) {
|
||
let enums = [];
|
||
for (let i = 0; i < reasons.length; i++) {
|
||
const reason = reasons[i];
|
||
if (SUPPORTED_VALUES_REGEXP.test(reason)) {
|
||
const match = reason.match(SUPPORTED_VALUES_REGEXP);
|
||
if (match) {
|
||
const values = match[1]
|
||
.split('|')
|
||
.map((item) => item.replace(/`/g, ''))
|
||
.filter(Boolean);
|
||
enums.push(...values);
|
||
reasons.splice(i, 1);
|
||
i--;
|
||
}
|
||
}
|
||
}
|
||
if (enums.length > 0) {
|
||
enums = [...new Set(enums)];
|
||
reasons.push(supportedEnumReason(k, v, enums));
|
||
}
|
||
return reasons;
|
||
}
|
||
|
||
const backgroundColor = 'background-color' ;
|
||
const backgroundImage = 'background-image' ;
|
||
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),
|
||
];
|
||
};
|
||
|
||
/**
|
||
* Make a map and return a function for checking if a key
|
||
* is in that map.
|
||
* IMPORTANT: all calls of this function must be prefixed with
|
||
* \/\*#\_\_PURE\_\_\*\/
|
||
* So that rollup can tree-shake them if necessary.
|
||
*/
|
||
|
||
(process.env.NODE_ENV !== 'production')
|
||
? Object.freeze({})
|
||
: {};
|
||
(process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];
|
||
const extend = Object.assign;
|
||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||
const hasOwn = (val, key) => hasOwnProperty.call(val, key);
|
||
const isFunction = (val) => typeof val === 'function';
|
||
const isString = (val) => typeof val === 'string';
|
||
const cacheStringFunction = (fn) => {
|
||
const cache = Object.create(null);
|
||
return ((str) => {
|
||
const hit = cache[str];
|
||
return hit || (cache[str] = fn(str));
|
||
});
|
||
};
|
||
const camelizeRE = /-(\w)/g;
|
||
/**
|
||
* @private
|
||
*/
|
||
const camelize = cacheStringFunction((str) => {
|
||
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));
|
||
});
|
||
const hyphenateRE = /\B([A-Z])/g;
|
||
/**
|
||
* @private
|
||
*/
|
||
const hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, '-$1').toLowerCase());
|
||
|
||
const borderTop = 'border-top-' ;
|
||
const borderRight = 'border-right-' ;
|
||
const borderBottom = 'border-bottom-' ;
|
||
const borderLeft = 'border-left-' ;
|
||
const transformBorderColor = (decl) => {
|
||
const { prop, value, important, raws, source } = decl;
|
||
let property = hyphenate(prop).split('-')[1];
|
||
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 = 'border-top-left-radius'
|
||
;
|
||
const borderTopRightRadius = 'border-top-right-radius'
|
||
;
|
||
const borderBottomRightRadius = 'border-bottom-right-radius'
|
||
;
|
||
const borderBottomLeftRadius = 'border-bottom-left-radius'
|
||
;
|
||
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 = 'flex-direction' ;
|
||
const flexWrap = 'flex-wrap' ;
|
||
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 transformFont = (decl) => {
|
||
const { value, important, raws, source } = decl;
|
||
const result = [];
|
||
const font = parseCSSFont__default.default(value);
|
||
if (font.system) {
|
||
return result;
|
||
}
|
||
const { style, weight, size, lineHeight, family } = font;
|
||
if (style) {
|
||
result.push(createDecl('font-style', style, important, raws, source));
|
||
}
|
||
if (weight) {
|
||
result.push(createDecl('font-weight', weight, important, raws, source));
|
||
}
|
||
if (size) {
|
||
result.push(createDecl('font-size', size, important, raws, source));
|
||
}
|
||
if (lineHeight) {
|
||
result.push(createDecl('line-height', lineHeight, important, raws, source));
|
||
}
|
||
if (family) {
|
||
result.push(createDecl('font-family', serialize(family), important, raws, source));
|
||
}
|
||
return result;
|
||
};
|
||
function serialize(family) {
|
||
return family.map((f) => (f.includes(' ') ? `"${f}"` : f)).join(', ');
|
||
}
|
||
|
||
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 = 'transition-property'
|
||
;
|
||
const transitionDuration = 'transition-duration'
|
||
;
|
||
const transitionTimingFunction = 'transition-timing-function'
|
||
;
|
||
const transitionDelay = 'transition-delay' ;
|
||
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 = {};
|
||
{
|
||
styleMap.font = transformFont;
|
||
for (const property in styleMap) {
|
||
result[hyphenateStyleProperty(property)] = styleMap[property];
|
||
}
|
||
}
|
||
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;
|
||
}
|
||
|
||
const normalizeColor = (v) => {
|
||
v = (v || '').toString();
|
||
if (v.match(/^#[0-9a-fA-F]{6}$/)) {
|
||
return { value: v };
|
||
}
|
||
if (v.match(/^#[0-9a-fA-F]{3}$/)) {
|
||
return {
|
||
value: '#' + v[1] + v[1] + v[2] + v[2] + v[3] + v[3],
|
||
reason: function reason(k, v, result) {
|
||
return autofixedReason(v, result);
|
||
},
|
||
};
|
||
}
|
||
if (EXTENDED_COLOR_KEYWORDS[v]) {
|
||
return {
|
||
value: EXTENDED_COLOR_KEYWORDS[v],
|
||
reason: function reason(k, v, result) {
|
||
return autofixedReason(v, result);
|
||
},
|
||
};
|
||
}
|
||
let arrColor, r, g, b, a;
|
||
const RGB_REGEXP = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/gi;
|
||
const RGBA_REGEXP = /^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d*\.?\d+)\s*\)$/gi;
|
||
if ((arrColor = RGB_REGEXP.exec(v))) {
|
||
r = parseInt(arrColor[1]);
|
||
g = parseInt(arrColor[2]);
|
||
b = parseInt(arrColor[3]);
|
||
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
|
||
return { value: 'rgb(' + [r, g, b].join(',') + ')' };
|
||
}
|
||
}
|
||
if ((arrColor = RGBA_REGEXP.exec(v))) {
|
||
r = parseInt(arrColor[1]);
|
||
g = parseInt(arrColor[2]);
|
||
b = parseInt(arrColor[3]);
|
||
a = parseFloat(arrColor[4]);
|
||
if (r >= 0 &&
|
||
r <= 255 &&
|
||
g >= 0 &&
|
||
g <= 255 &&
|
||
b >= 0 &&
|
||
b <= 255 &&
|
||
a >= 0 &&
|
||
a <= 1) {
|
||
return { value: 'rgba(' + [r, g, b, a].join(',') + ')' };
|
||
}
|
||
}
|
||
if (v === 'transparent') {
|
||
return { value: 'rgba(0,0,0,0)' };
|
||
}
|
||
return {
|
||
value: null,
|
||
reason(k, v, result) {
|
||
return validReason(k, v);
|
||
},
|
||
};
|
||
};
|
||
// http://www.w3.org/TR/css3-color/#svg-color
|
||
const EXTENDED_COLOR_KEYWORDS = {
|
||
aliceblue: '#F0F8FF',
|
||
antiquewhite: '#FAEBD7',
|
||
aqua: '#00FFFF',
|
||
aquamarine: '#7FFFD4',
|
||
azure: '#F0FFFF',
|
||
beige: '#F5F5DC',
|
||
bisque: '#FFE4C4',
|
||
black: '#000000',
|
||
blanchedalmond: '#FFEBCD',
|
||
blue: '#0000FF',
|
||
blueviolet: '#8A2BE2',
|
||
brown: '#A52A2A',
|
||
burlywood: '#DEB887',
|
||
cadetblue: '#5F9EA0',
|
||
chartreuse: '#7FFF00',
|
||
chocolate: '#D2691E',
|
||
coral: '#FF7F50',
|
||
cornflowerblue: '#6495ED',
|
||
cornsilk: '#FFF8DC',
|
||
crimson: '#DC143C',
|
||
cyan: '#00FFFF',
|
||
darkblue: '#00008B',
|
||
darkcyan: '#008B8B',
|
||
darkgoldenrod: '#B8860B',
|
||
darkgray: '#A9A9A9',
|
||
darkgreen: '#006400',
|
||
darkgrey: '#A9A9A9',
|
||
darkkhaki: '#BDB76B',
|
||
darkmagenta: '#8B008B',
|
||
darkolivegreen: '#556B2F',
|
||
darkorange: '#FF8C00',
|
||
darkorchid: '#9932CC',
|
||
darkred: '#8B0000',
|
||
darksalmon: '#E9967A',
|
||
darkseagreen: '#8FBC8F',
|
||
darkslateblue: '#483D8B',
|
||
darkslategray: '#2F4F4F',
|
||
darkslategrey: '#2F4F4F',
|
||
darkturquoise: '#00CED1',
|
||
darkviolet: '#9400D3',
|
||
deeppink: '#FF1493',
|
||
deepskyblue: '#00BFFF',
|
||
dimgray: '#696969',
|
||
dimgrey: '#696969',
|
||
dodgerblue: '#1E90FF',
|
||
firebrick: '#B22222',
|
||
floralwhite: '#FFFAF0',
|
||
forestgreen: '#228B22',
|
||
fuchsia: '#FF00FF',
|
||
gainsboro: '#DCDCDC',
|
||
ghostwhite: '#F8F8FF',
|
||
gold: '#FFD700',
|
||
goldenrod: '#DAA520',
|
||
gray: '#808080',
|
||
green: '#008000',
|
||
greenyellow: '#ADFF2F',
|
||
grey: '#808080',
|
||
honeydew: '#F0FFF0',
|
||
hotpink: '#FF69B4',
|
||
indianred: '#CD5C5C',
|
||
indigo: '#4B0082',
|
||
ivory: '#FFFFF0',
|
||
khaki: '#F0E68C',
|
||
lavender: '#E6E6FA',
|
||
lavenderblush: '#FFF0F5',
|
||
lawngreen: '#7CFC00',
|
||
lemonchiffon: '#FFFACD',
|
||
lightblue: '#ADD8E6',
|
||
lightcoral: '#F08080',
|
||
lightcyan: '#E0FFFF',
|
||
lightgoldenrodyellow: '#FAFAD2',
|
||
lightgray: '#D3D3D3',
|
||
lightgreen: '#90EE90',
|
||
lightgrey: '#D3D3D3',
|
||
lightpink: '#FFB6C1',
|
||
lightsalmon: '#FFA07A',
|
||
lightseagreen: '#20B2AA',
|
||
lightskyblue: '#87CEFA',
|
||
lightslategray: '#778899',
|
||
lightslategrey: '#778899',
|
||
lightsteelblue: '#B0C4DE',
|
||
lightyellow: '#FFFFE0',
|
||
lime: '#00FF00',
|
||
limegreen: '#32CD32',
|
||
linen: '#FAF0E6',
|
||
magenta: '#FF00FF',
|
||
maroon: '#800000',
|
||
mediumaquamarine: '#66CDAA',
|
||
mediumblue: '#0000CD',
|
||
mediumorchid: '#BA55D3',
|
||
mediumpurple: '#9370DB',
|
||
mediumseagreen: '#3CB371',
|
||
mediumslateblue: '#7B68EE',
|
||
mediumspringgreen: '#00FA9A',
|
||
mediumturquoise: '#48D1CC',
|
||
mediumvioletred: '#C71585',
|
||
midnightblue: '#191970',
|
||
mintcream: '#F5FFFA',
|
||
mistyrose: '#FFE4E1',
|
||
moccasin: '#FFE4B5',
|
||
navajowhite: '#FFDEAD',
|
||
navy: '#000080',
|
||
oldlace: '#FDF5E6',
|
||
olive: '#808000',
|
||
olivedrab: '#6B8E23',
|
||
orange: '#FFA500',
|
||
orangered: '#FF4500',
|
||
orchid: '#DA70D6',
|
||
palegoldenrod: '#EEE8AA',
|
||
palegreen: '#98FB98',
|
||
paleturquoise: '#AFEEEE',
|
||
palevioletred: '#DB7093',
|
||
papayawhip: '#FFEFD5',
|
||
peachpuff: '#FFDAB9',
|
||
peru: '#CD853F',
|
||
pink: '#FFC0CB',
|
||
plum: '#DDA0DD',
|
||
powderblue: '#B0E0E6',
|
||
purple: '#800080',
|
||
red: '#FF0000',
|
||
rosybrown: '#BC8F8F',
|
||
royalblue: '#4169E1',
|
||
saddlebrown: '#8B4513',
|
||
salmon: '#FA8072',
|
||
sandybrown: '#F4A460',
|
||
seagreen: '#2E8B57',
|
||
seashell: '#FFF5EE',
|
||
sienna: '#A0522D',
|
||
silver: '#C0C0C0',
|
||
skyblue: '#87CEEB',
|
||
slateblue: '#6A5ACD',
|
||
slategray: '#708090',
|
||
slategrey: '#708090',
|
||
snow: '#FFFAFA',
|
||
springgreen: '#00FF7F',
|
||
steelblue: '#4682B4',
|
||
tan: '#D2B48C',
|
||
teal: '#008080',
|
||
thistle: '#D8BFD8',
|
||
tomato: '#FF6347',
|
||
turquoise: '#40E0D0',
|
||
violet: '#EE82EE',
|
||
wheat: '#F5DEB3',
|
||
white: '#FFFFFF',
|
||
whitesmoke: '#F5F5F5',
|
||
yellow: '#FFFF00',
|
||
yellowgreen: '#9ACD32',
|
||
};
|
||
|
||
function createEnumNormalize(items) {
|
||
return (v) => {
|
||
const index = items.indexOf(v);
|
||
if (index > 0) {
|
||
return { value: v };
|
||
}
|
||
if (index === 0) {
|
||
return {
|
||
value: v,
|
||
reason: function reason(k, v, result) {
|
||
return defaultValueReason(k, v);
|
||
},
|
||
};
|
||
}
|
||
return {
|
||
value: null,
|
||
reason: function reason(k, v, result) {
|
||
return supportedEnumReason(k, v, items);
|
||
},
|
||
};
|
||
};
|
||
}
|
||
function createEnumNormalizeWithPlatform(items) {
|
||
return (v, { platform }) => {
|
||
const property = items.find((item) => item.name === v);
|
||
const supportedEnum = items
|
||
.filter((item) => {
|
||
const supportedPlatforms = getSupportedPlatforms(item.uniPlatform);
|
||
return supportedPlatforms.includes(platform);
|
||
})
|
||
.map((item) => item.name);
|
||
if (property) {
|
||
const supportedPlatforms = getSupportedPlatforms(property.uniPlatform);
|
||
// TODO 未跨平台支持的属性特殊提示
|
||
if (!supportedPlatforms.includes(platform)) {
|
||
return {
|
||
value: null,
|
||
reason: function reason(k, v, result) {
|
||
return supportedEnumReason(k, v, supportedEnum);
|
||
},
|
||
};
|
||
}
|
||
return { value: v };
|
||
}
|
||
return {
|
||
value: null,
|
||
reason: function reason(k, v, result) {
|
||
return supportedEnumReason(k, v, supportedEnum);
|
||
},
|
||
};
|
||
};
|
||
}
|
||
|
||
const normalizeFlexWrap = (v) => {
|
||
const values = ['nowrap', 'wrap', 'wrap-reverse'];
|
||
const index = values.indexOf(v);
|
||
if (index > 0) {
|
||
return {
|
||
value: v,
|
||
reason(k, v, result) {
|
||
return compatibilityReason(k);
|
||
},
|
||
};
|
||
}
|
||
if (index === 0) {
|
||
return {
|
||
value: v,
|
||
reason: function reason(k, v, result) {
|
||
return defaultValueReason(k, v);
|
||
},
|
||
};
|
||
}
|
||
return {
|
||
value: null,
|
||
reason(k, v, result) {
|
||
return supportedEnumReason(k, v, values);
|
||
},
|
||
};
|
||
};
|
||
|
||
const normalizeInteger = (v) => {
|
||
v = (v || '').toString();
|
||
if (v.match(/^[-+]?\d+$/)) {
|
||
return { value: parseInt(v, 10) };
|
||
}
|
||
return {
|
||
value: null,
|
||
reason: function reason(k, v, result) {
|
||
return supportedEnumReason(k, v, ['integer']);
|
||
},
|
||
};
|
||
};
|
||
|
||
function createNormalizeLength({ removePx, property, } = {}) {
|
||
return (v, options) => {
|
||
v = (v || '').toString();
|
||
const match = v.match(LENGTH_REGEXP);
|
||
if (match) {
|
||
var unit = match[1];
|
||
const uvue = options.type === 'uvue';
|
||
if (uvue) {
|
||
if (!unit || (unit === 'px' && removePx)) {
|
||
return { value: parseFloat(v) };
|
||
}
|
||
else if (unit === 'px' ||
|
||
unit === 'rpx' ||
|
||
// 只有line-height支持em单位
|
||
(unit === 'em' && property === 'line-height')) {
|
||
return { value: v };
|
||
}
|
||
}
|
||
else {
|
||
// nvue
|
||
if (!unit || unit === 'px') {
|
||
return { value: parseFloat(v) };
|
||
}
|
||
if (SUPPORT_CSS_UNIT.includes(unit)) {
|
||
return { value: v };
|
||
}
|
||
else {
|
||
return {
|
||
value: parseFloat(v),
|
||
reason(k, v, result) {
|
||
return supportedUnitWithAutofixedReason(unit, v, result);
|
||
},
|
||
};
|
||
}
|
||
}
|
||
}
|
||
return {
|
||
value: null,
|
||
reason(k, v, result) {
|
||
return supportedEnumReason(k, v, ['number', 'pixel']);
|
||
},
|
||
};
|
||
};
|
||
}
|
||
const normalizeLength = createNormalizeLength({
|
||
removePx: true,
|
||
});
|
||
const normalizeLengthWithOptions = createNormalizeLength;
|
||
const normalizePercent = (v) => {
|
||
v = (v || '').toString();
|
||
const match = v.match(LENGTH_REGEXP);
|
||
if (match) {
|
||
var unit = match[1];
|
||
if (unit === '%') {
|
||
return { value: v };
|
||
}
|
||
}
|
||
return {
|
||
value: null,
|
||
reason(k, v, result) {
|
||
return supportedEnumReason(k, v, ['percent']);
|
||
},
|
||
};
|
||
};
|
||
|
||
const normalizeNumber = (v) => {
|
||
v = (v || '').toString();
|
||
var match = v.match(LENGTH_REGEXP);
|
||
if (match && !match[1]) {
|
||
return { value: parseFloat(v) };
|
||
}
|
||
return {
|
||
value: null,
|
||
reason: function reason(k, v, result) {
|
||
return supportedEnumReason(k, v, ['number']);
|
||
},
|
||
};
|
||
};
|
||
|
||
const normalizeString = (v) => {
|
||
v = (v || '').toString().replace(/["']/g, '');
|
||
return {
|
||
value: v,
|
||
};
|
||
};
|
||
|
||
const normalizeShorthandLength = (v, options) => {
|
||
v = (v || '').toString();
|
||
let value = [];
|
||
let reason = [];
|
||
const results = v.split(/\s+/).map((v) => normalizeLength(v, options));
|
||
for (let i = 0; i < results.length; ++i) {
|
||
const res = results[i];
|
||
if (res.value === null) {
|
||
return res;
|
||
}
|
||
value.push(res.value);
|
||
reason.push(res.reason);
|
||
}
|
||
return {
|
||
value: value.join(' '),
|
||
reason: function (k, v, result) {
|
||
return reason
|
||
.map(function (res) {
|
||
if (isFunction(res)) {
|
||
return res(k, v, result);
|
||
}
|
||
})
|
||
.join('\n');
|
||
},
|
||
};
|
||
};
|
||
|
||
const normalizeTransform = (v) => {
|
||
return { value: v };
|
||
};
|
||
|
||
const normalizeInterval = (v) => {
|
||
v = (v || 0).toString();
|
||
let match, num;
|
||
if ((match = v.match(/^\d*\.?\d+(ms|s)?$/))) {
|
||
num = parseFloat(match[0]);
|
||
if (!match[1]) {
|
||
return { value: parseInt(num + '') };
|
||
}
|
||
if (match[1] === 's') {
|
||
num *= 1000;
|
||
}
|
||
return {
|
||
value: parseInt(num + ''),
|
||
reason(k, v, result) {
|
||
return autofixedReason(v, result);
|
||
},
|
||
};
|
||
}
|
||
return {
|
||
value: null,
|
||
reason(k, v, result) {
|
||
return supportedEnumReason(k, v, ['number of seconds', 'milliseconds']);
|
||
},
|
||
};
|
||
};
|
||
|
||
const normalizeTimingFunction = (v) => {
|
||
v = (v || '').toString();
|
||
if (v.match(/^linear|ease|ease-in|ease-out|ease-in-out$/)) {
|
||
return { value: v };
|
||
}
|
||
let match;
|
||
if ((match = v.match(/^cubic-bezier\(\s*(.*)\s*,\s*(.*)\s*,\s*(.*)\s*,\s*(.*)\s*\)$/))) {
|
||
if (match[1].match(NUM_REGEXP) &&
|
||
match[2].match(NUM_REGEXP) &&
|
||
match[3].match(NUM_REGEXP) &&
|
||
match[4].match(NUM_REGEXP)) {
|
||
const ret = [
|
||
parseFloat(match[1]),
|
||
parseFloat(match[2]),
|
||
parseFloat(match[3]),
|
||
parseFloat(match[4]),
|
||
].join(',');
|
||
return { value: 'cubic-bezier(' + ret + ')' };
|
||
}
|
||
}
|
||
return {
|
||
value: null,
|
||
reason(k, v, result) {
|
||
return supportedEnumReason(k, v, [
|
||
'linear',
|
||
'ease',
|
||
'ease-in',
|
||
'ease-out',
|
||
'ease-in-out',
|
||
'cubic-bezier(n,n,n,n)',
|
||
]);
|
||
},
|
||
};
|
||
};
|
||
|
||
function createCombinedNormalize(normalizes) {
|
||
return (v, options) => {
|
||
const reasons = [];
|
||
for (let i = 0; i < normalizes.length; i++) {
|
||
const result = normalizes[i](v, options);
|
||
if (result.value !== null) {
|
||
return result;
|
||
}
|
||
if (result.reason) {
|
||
reasons.push(result.reason);
|
||
}
|
||
}
|
||
return {
|
||
value: null,
|
||
reason(k, v, result) {
|
||
return normalizeReasons(reasons.map((reason) => reason(k, v, result)), k, v).join('\n');
|
||
},
|
||
};
|
||
};
|
||
}
|
||
|
||
const normalizeGradient = (v) => {
|
||
v = (v || '').toString();
|
||
if (/^linear-gradient(.+)$/s.test(v)) {
|
||
return { value: v };
|
||
}
|
||
return {
|
||
// 枚举里会做reason提示
|
||
value: null,
|
||
};
|
||
};
|
||
const normalizeUrl = (v) => {
|
||
v = (v || '').toString();
|
||
if (/^url(.+)$/s.test(v)) {
|
||
return { value: v };
|
||
}
|
||
return {
|
||
value: null,
|
||
};
|
||
};
|
||
|
||
function normalizePlatform(normalize, uniPlatform) {
|
||
return (v, options, declInfo) => {
|
||
const currentPlatform = options.platform;
|
||
const supportedPlatforms = getSupportedPlatforms(uniPlatform);
|
||
// TODO 未跨平台支持的属性特殊提示
|
||
if (!supportedPlatforms.includes(currentPlatform)) {
|
||
return {
|
||
value: v,
|
||
reason(k, v, result) {
|
||
return supportedPropertyReason(k);
|
||
},
|
||
};
|
||
}
|
||
return normalize(v, options, declInfo);
|
||
};
|
||
}
|
||
|
||
function normalizeShorthandProperty(normalize) {
|
||
return (v, options) => {
|
||
v = (v || '').toString();
|
||
const value = [];
|
||
const reasons = [];
|
||
const results = v.split(/\s+/).map((v) => normalize(v, options));
|
||
for (let i = 0; i < results.length; ++i) {
|
||
const res = results[i];
|
||
if (res.value === null) {
|
||
return res;
|
||
}
|
||
if (res.reason) {
|
||
reasons.push(res.reason);
|
||
}
|
||
value.push(res.value);
|
||
}
|
||
return {
|
||
value: value.length === 1 ? value[0] : value.join(' '),
|
||
reason: function (k, v, result) {
|
||
return reasons.map((reason) => reason(k, v, result)).join('\n');
|
||
},
|
||
};
|
||
};
|
||
}
|
||
|
||
function normalizeFontFace(normalize) {
|
||
return (v, options, declInfo) => {
|
||
if (declInfo?.atRule === 'font-face') {
|
||
return {
|
||
value: null,
|
||
reason(k, v, result) {
|
||
const items = ['font-family', 'src'];
|
||
const name = '@' + declInfo.atRule;
|
||
return ('ERROR: property `' +
|
||
hyphenateStyleProperty(k) +
|
||
'` is not supported for `' +
|
||
name +
|
||
'` (supported properties are: `' +
|
||
items.join('`|`') +
|
||
'`)');
|
||
},
|
||
};
|
||
}
|
||
return normalize(v, options, declInfo);
|
||
};
|
||
}
|
||
// 只有@font-face下的src属性才支持
|
||
const normalizeSrc = (v, options, declInfo) => {
|
||
if (declInfo?.atRule === 'font-face') {
|
||
return { value: v };
|
||
}
|
||
return {
|
||
value: null,
|
||
reason(k, v, result) {
|
||
return supportedPropertyReason(k);
|
||
},
|
||
};
|
||
};
|
||
|
||
const normalizeFlexFlow = (v) => {
|
||
v = (v || '').toString();
|
||
const values = v.split(/\s+/);
|
||
// flex-flow 需要定义每一个属性值
|
||
if (values.length === 1) {
|
||
return {
|
||
value: v,
|
||
reason(k, v, result) {
|
||
return supportedValueWithTipsReason(k, v, '(both property values must be explicitly defined)');
|
||
},
|
||
};
|
||
}
|
||
return {
|
||
value: v,
|
||
};
|
||
};
|
||
|
||
// 从 property.ts 中移动到 map 里,避免循环依赖
|
||
const normalizeProperty = (v, options) => {
|
||
v = (v || '').toString();
|
||
v = v
|
||
.split(/\s*,\s*/)
|
||
.map(camelize)
|
||
.join(',');
|
||
if (v.split(/\s*,\s*/).every((p) => !!getNormalizeMap(options)[p])) {
|
||
return { value: v };
|
||
}
|
||
return {
|
||
value: null,
|
||
reason: function reason(k, v, result) {
|
||
return supportedEnumReason(k, v, ['css property']);
|
||
},
|
||
};
|
||
};
|
||
const normalizeDefault = (v) => {
|
||
return { value: v };
|
||
};
|
||
const NVUE_PROP_NAME_GROUPS = {
|
||
boxModel: {
|
||
display: createEnumNormalize(['flex']),
|
||
width: normalizeLength,
|
||
height: normalizeLength,
|
||
overflow: createEnumNormalize(['hidden']),
|
||
padding: normalizeShorthandLength,
|
||
paddingLeft: normalizeLength,
|
||
paddingRight: normalizeLength,
|
||
paddingTop: normalizeLength,
|
||
paddingBottom: normalizeLength,
|
||
margin: normalizeShorthandLength,
|
||
marginLeft: normalizeLength,
|
||
marginRight: normalizeLength,
|
||
marginTop: normalizeLength,
|
||
marginBottom: normalizeLength,
|
||
borderWidth: normalizeLength,
|
||
borderLeftWidth: normalizeLength,
|
||
borderTopWidth: normalizeLength,
|
||
borderRightWidth: normalizeLength,
|
||
borderBottomWidth: normalizeLength,
|
||
borderColor: normalizeColor,
|
||
borderLeftColor: normalizeColor,
|
||
borderTopColor: normalizeColor,
|
||
borderRightColor: normalizeColor,
|
||
borderBottomColor: normalizeColor,
|
||
borderStyle: createEnumNormalize(['dotted', 'dashed', 'solid']),
|
||
borderTopStyle: createEnumNormalize(['dotted', 'dashed', 'solid']),
|
||
borderRightStyle: createEnumNormalize(['dotted', 'dashed', 'solid']),
|
||
borderBottomStyle: createEnumNormalize(['dotted', 'dashed', 'solid']),
|
||
borderLeftStyle: createEnumNormalize(['dotted', 'dashed', 'solid']),
|
||
borderRadius: normalizeLength,
|
||
borderBottomLeftRadius: normalizeLength,
|
||
borderBottomRightRadius: normalizeLength,
|
||
borderTopLeftRadius: normalizeLength,
|
||
borderTopRightRadius: normalizeLength,
|
||
},
|
||
flexbox: {
|
||
flex: normalizeNumber,
|
||
flexWrap: normalizeFlexWrap,
|
||
flexDirection: createEnumNormalize([
|
||
'column',
|
||
'row',
|
||
'column-reverse',
|
||
'row-reverse',
|
||
]),
|
||
justifyContent: createEnumNormalize([
|
||
'flex-start',
|
||
'flex-end',
|
||
'center',
|
||
'space-between',
|
||
'space-around',
|
||
]),
|
||
alignItems: createEnumNormalize([
|
||
'stretch',
|
||
'flex-start',
|
||
'flex-end',
|
||
'center',
|
||
]),
|
||
},
|
||
position: {
|
||
position: createEnumNormalize(['relative', 'absolute', 'sticky', 'fixed']),
|
||
top: normalizeLength,
|
||
bottom: normalizeLength,
|
||
left: normalizeLength,
|
||
right: normalizeLength,
|
||
zIndex: normalizeInteger,
|
||
},
|
||
common: {
|
||
opacity: normalizeNumber,
|
||
boxShadow: normalizeDefault,
|
||
backgroundColor: normalizeColor,
|
||
backgroundImage: normalizeDefault,
|
||
},
|
||
text: {
|
||
lines: normalizeInteger,
|
||
color: normalizeColor,
|
||
fontSize: normalizeLength,
|
||
fontStyle: createEnumNormalize(['normal', 'italic']),
|
||
fontFamily: normalizeDefault,
|
||
fontWeight: createEnumNormalize([
|
||
'normal',
|
||
'bold',
|
||
'100',
|
||
'200',
|
||
'300',
|
||
'400',
|
||
'500',
|
||
'600',
|
||
'700',
|
||
'800',
|
||
'900',
|
||
]),
|
||
textDecoration: createEnumNormalize(['none', 'underline', 'line-through']),
|
||
textAlign: createEnumNormalize(['left', 'center', 'right']),
|
||
textOverflow: createEnumNormalize(['clip', 'ellipsis', 'unset', 'fade']),
|
||
lineHeight: normalizeLength,
|
||
},
|
||
transition: {
|
||
transitionProperty: normalizeProperty,
|
||
transitionDuration: normalizeInterval,
|
||
transitionDelay: normalizeInterval,
|
||
transitionTimingFunction: normalizeTimingFunction,
|
||
},
|
||
transform: {
|
||
transform: normalizeTransform,
|
||
transformOrigin: normalizeTransform, // fixed by xxxxxx
|
||
},
|
||
customized: {
|
||
itemSize: normalizeLength,
|
||
itemColor: normalizeColor,
|
||
itemSelectedColor: normalizeColor,
|
||
textColor: normalizeColor,
|
||
timeColor: normalizeColor,
|
||
textHighlightColor: normalizeColor,
|
||
},
|
||
};
|
||
const uvueNormalizeMap = {
|
||
transform: normalizeTransform,
|
||
fontFamily: normalizeString,
|
||
textDecoration: normalizeDefault,
|
||
boxShadow: normalizeDefault,
|
||
transitionProperty: normalizeProperty,
|
||
transitionTimingFunction: normalizeTimingFunction,
|
||
};
|
||
const restrictionMap = {
|
||
["length" /* Restriction.LENGTH */]: normalizeLength,
|
||
["percentage" /* Restriction.PERCENTAGE */]: normalizePercent,
|
||
["number" /* Restriction.NUMBER */]: normalizeNumber,
|
||
["number(0-1)" /* Restriction.NUMBER_0_1 */]: normalizeNumber,
|
||
["integer" /* Restriction.INTEGER */]: normalizeInteger,
|
||
["color" /* Restriction.COLOR */]: normalizeColor,
|
||
["time" /* Restriction.TIME */]: normalizeInterval,
|
||
["property" /* Restriction.PROPERTY */]: normalizeProperty,
|
||
["timing-function" /* Restriction.TIMING_FUNCTION */]: normalizeTimingFunction,
|
||
["gradient" /* Restriction.GRADIENT */]: normalizeGradient,
|
||
["url" /* Restriction.URL */]: normalizeUrl,
|
||
};
|
||
// @font-face下不支持的属性
|
||
const invalidFontFaceProperties = ['fontWeight', 'fontStyle', 'fontVariant'];
|
||
function getUVueNormalizeMap() {
|
||
const result = {
|
||
src: normalizeSrc,
|
||
};
|
||
let cssJson;
|
||
try {
|
||
// eslint-disable-next-line no-restricted-globals
|
||
cssJson = require('../lib/css.json');
|
||
}
|
||
catch (e) {
|
||
// 单元测试环境,源码目录
|
||
// eslint-disable-next-line no-restricted-globals
|
||
cssJson = require('../../lib/css.json');
|
||
}
|
||
const { properties } = cssJson;
|
||
for (let i = 0; i < properties.length; i++) {
|
||
const property = properties[i];
|
||
const prop = camelize(property.name);
|
||
let normalize;
|
||
if (uvueNormalizeMap[prop]) {
|
||
normalize = uvueNormalizeMap[prop];
|
||
}
|
||
else {
|
||
const normalizes = getNormalizes(property);
|
||
if (normalizes.length > 1) {
|
||
normalize = createCombinedNormalize(normalizes);
|
||
}
|
||
else if (normalizes.length === 1) {
|
||
normalize = normalizes[0];
|
||
}
|
||
else {
|
||
normalize = normalizeDefault;
|
||
}
|
||
// 简写属性
|
||
if (property.shorthand) {
|
||
normalize = normalizeShorthandProperty(normalize);
|
||
}
|
||
// 处理@font-face下不支持的属性
|
||
if (invalidFontFaceProperties.includes(prop)) {
|
||
normalize = normalizeFontFace(normalize);
|
||
}
|
||
// 校验flexFlow属性值的个数,先临时写死,后续考虑根据css.json动态判断
|
||
if (prop === 'flexFlow') {
|
||
normalize = createCombinedNormalize([normalizeFlexFlow, normalize]);
|
||
}
|
||
}
|
||
result[prop] = normalizePlatform(normalize, property.uniPlatform);
|
||
}
|
||
return result;
|
||
}
|
||
function getNormalizes(property) {
|
||
const normalizes = [];
|
||
const { restrictions } = property;
|
||
restrictions.forEach((restriction) => {
|
||
let normalize = restrictionMap[restriction];
|
||
if (normalize) {
|
||
if (restriction === "length" /* Restriction.LENGTH */) {
|
||
// 如果同时有number和length,例如line-height: 1.5, line-height: 16px,则不能移除px
|
||
normalize = normalizeLengthWithOptions({
|
||
removePx: !restrictions.includes("number" /* Restriction.NUMBER */),
|
||
property: property.name,
|
||
});
|
||
}
|
||
normalizes.push(normalize);
|
||
}
|
||
});
|
||
// enum
|
||
if (property?.values?.length) {
|
||
normalizes.push(createEnumNormalizeWithPlatform(property.values));
|
||
}
|
||
return normalizes;
|
||
}
|
||
let normalizeMap;
|
||
function getNormalizeMap(options) {
|
||
if (normalizeMap) {
|
||
return normalizeMap;
|
||
}
|
||
const uvue = options.type === 'uvue';
|
||
if (uvue) {
|
||
normalizeMap = getUVueNormalizeMap();
|
||
}
|
||
else {
|
||
normalizeMap = Object.keys(NVUE_PROP_NAME_GROUPS).reduce((res, name) => {
|
||
const group = NVUE_PROP_NAME_GROUPS[name];
|
||
Object.keys(group).forEach((prop) => {
|
||
res[prop] = group[prop];
|
||
});
|
||
return res;
|
||
}, {});
|
||
}
|
||
return normalizeMap;
|
||
}
|
||
|
||
const normalized = Symbol('normalized');
|
||
function normalize(opts = {}) {
|
||
if (!hasOwn(opts, 'logLevel')) {
|
||
opts.logLevel = 'WARNING';
|
||
}
|
||
const plugin = {
|
||
postcssPlugin: `${opts.type || 'nvue'}:normalize`,
|
||
Declaration: createDeclarationProcessor(opts),
|
||
};
|
||
{
|
||
plugin.Rule = createRuleProcessor(opts);
|
||
}
|
||
return plugin;
|
||
}
|
||
function createRuleProcessor(opts = {}) {
|
||
return (rule, helper) => {
|
||
if (rule[normalized]) {
|
||
return;
|
||
}
|
||
rule.selector = rule.selectors
|
||
.map((selector) => {
|
||
selector = selector
|
||
.replace(/\s*([\+\~\>])\s*/g, '$1')
|
||
.replace(/\s+/, ' ');
|
||
if (COMBINATORS_RE.test(selector)) {
|
||
return selector;
|
||
}
|
||
let type = opts.type || 'nvue';
|
||
rule.warn(helper.result, 'ERROR: Selector `' +
|
||
selector +
|
||
'` is not supported. ' +
|
||
type +
|
||
' only support classname selector');
|
||
return '';
|
||
})
|
||
.filter(Boolean)
|
||
.join(', ');
|
||
if (!rule.selector) {
|
||
rule.remove();
|
||
}
|
||
rule[normalized] = true;
|
||
};
|
||
}
|
||
function createDeclarationProcessor(options) {
|
||
return (decl, helper) => {
|
||
if (decl[normalized]) {
|
||
return;
|
||
}
|
||
decl.prop = camelize(decl.prop);
|
||
const { value, log } = normalizeDecl(decl, options);
|
||
if (isString(value) || isNumber(value)) {
|
||
decl.value = value;
|
||
}
|
||
if (log && log.reason && helper) {
|
||
const { reason } = log;
|
||
let needLog = false;
|
||
if (options.logLevel === 'NOTE') {
|
||
needLog = true;
|
||
}
|
||
else if (options.logLevel === 'ERROR') {
|
||
if (reason.startsWith('ERROR:')) {
|
||
needLog = true;
|
||
}
|
||
}
|
||
else {
|
||
if (!reason.startsWith('NOTE:')) {
|
||
needLog = true;
|
||
}
|
||
}
|
||
needLog && decl.warn(helper.result, reason);
|
||
}
|
||
if (value === null) {
|
||
decl.remove();
|
||
}
|
||
decl[normalized] = true;
|
||
};
|
||
}
|
||
function normalizeDecl(decl, options) {
|
||
let { prop: name, value } = decl;
|
||
let result, log;
|
||
const normalize = getNormalizeMap(options)[name];
|
||
if (isFunction(normalize)) {
|
||
if (!isFunction(value)) {
|
||
result = normalize(value, options, {
|
||
atRule: decl.parent?.type === 'atrule' ? decl.parent.name : '',
|
||
});
|
||
}
|
||
else {
|
||
result = { value: value };
|
||
}
|
||
if (result.reason) {
|
||
log = { reason: result.reason(name, value, result.value) };
|
||
}
|
||
}
|
||
else {
|
||
// ensure number type, no `px`
|
||
if (isString(value)) {
|
||
const match = value.match(LENGTH_REGEXP);
|
||
if (match && (!match[1] || SUPPORT_CSS_UNIT.indexOf(match[1]) === -1)) {
|
||
value = parseFloat(value);
|
||
}
|
||
}
|
||
result = { value: value };
|
||
log = {
|
||
reason: 'WARNING: `' +
|
||
hyphenateStyleProperty(name) +
|
||
'` is not a standard property name (may not be supported)',
|
||
};
|
||
}
|
||
return {
|
||
value: result.value,
|
||
log,
|
||
};
|
||
}
|
||
|
||
function objectifier(node) {
|
||
if (!node) {
|
||
return {};
|
||
}
|
||
const context = {
|
||
'FONT-FACE': [],
|
||
TRANSITION: {},
|
||
};
|
||
const result = transform(node, context);
|
||
if (context['FONT-FACE'].length) {
|
||
result['@FONT-FACE'] = context['FONT-FACE'];
|
||
}
|
||
if (Object.keys(context.TRANSITION).length) {
|
||
result['@TRANSITION'] = context.TRANSITION;
|
||
}
|
||
return result;
|
||
}
|
||
function transform(node, context) {
|
||
const result = {};
|
||
node.each((child) => {
|
||
if (child.type === 'atrule') {
|
||
const body = transform(child, context);
|
||
const fontFamily = body.fontFamily;
|
||
if (fontFamily && '"\''.indexOf(fontFamily[0]) > -1) {
|
||
body.fontFamily = fontFamily.slice(1, fontFamily.length - 1);
|
||
}
|
||
context['FONT-FACE'].push(body);
|
||
}
|
||
else if (child.type === 'rule') {
|
||
const body = transform(child, context);
|
||
child.selectors.forEach((selector) => {
|
||
transformSelector(selector, body, result, context);
|
||
});
|
||
}
|
||
else if (child.type === 'decl') {
|
||
if (child.important) {
|
||
result['!' + child.prop] = child.value;
|
||
// !important的值域优先级高,故删除非!important的值域
|
||
delete result[child.prop];
|
||
}
|
||
else {
|
||
if (!hasOwn(result, '!' + child.prop)) {
|
||
result[child.prop] = child.value;
|
||
}
|
||
}
|
||
}
|
||
});
|
||
return result;
|
||
}
|
||
function transformSelector(selector, body, result, context) {
|
||
const res = selector.match(COMBINATORS_RE);
|
||
if (!res) {
|
||
return;
|
||
}
|
||
let parentSelector = res[1];
|
||
let curSelector = res[2].substring(1);
|
||
// .a.b => a.b
|
||
const dotIndex = curSelector.indexOf('.');
|
||
if (dotIndex > -1) {
|
||
parentSelector += curSelector.substring(dotIndex);
|
||
curSelector = curSelector.substring(0, dotIndex);
|
||
}
|
||
const pseudoIndex = curSelector.indexOf(':');
|
||
if (pseudoIndex > -1) {
|
||
const pseudoClass = curSelector.slice(pseudoIndex);
|
||
curSelector = curSelector.slice(0, pseudoIndex);
|
||
Object.keys(body).forEach(function (name) {
|
||
body[name + pseudoClass] = body[name];
|
||
delete body[name];
|
||
});
|
||
}
|
||
transition(curSelector, body, context);
|
||
if (!Object.keys(body).length) {
|
||
return;
|
||
}
|
||
result = (result[curSelector] || (result[curSelector] = {}));
|
||
if (result[parentSelector]) {
|
||
// clone
|
||
result[parentSelector] = processImportant(extend({}, result[parentSelector], body));
|
||
}
|
||
else {
|
||
result[parentSelector] = body;
|
||
}
|
||
}
|
||
/**
|
||
* 处理 important 属性,如果某个属性是 important,需要将非 important 的该属性移除掉
|
||
* @param body
|
||
*/
|
||
function processImportant(body) {
|
||
Object.keys(body).forEach((name) => {
|
||
if (name.startsWith('!')) {
|
||
delete body[name.substring(1)];
|
||
}
|
||
});
|
||
return body;
|
||
}
|
||
function transition(className, body, { TRANSITION }) {
|
||
Object.keys(body).forEach((prop) => {
|
||
if (prop.indexOf('transition') === 0 && prop !== 'transition') {
|
||
const realProp = prop.replace('transition', '');
|
||
TRANSITION[className] = TRANSITION[className] || {};
|
||
TRANSITION[className][realProp[0].toLowerCase() + realProp.slice(1)] =
|
||
body[prop];
|
||
}
|
||
});
|
||
}
|
||
|
||
async function parse(input, options = {}) {
|
||
const { root, messages } = await postcss__default.default([
|
||
expand(options),
|
||
normalize(options),
|
||
])
|
||
.process(input, {
|
||
from: options.filename || 'foo.css',
|
||
})
|
||
.catch((err) => {
|
||
return {
|
||
root: null,
|
||
messages: [
|
||
{
|
||
type: 'error',
|
||
text: err.message,
|
||
},
|
||
],
|
||
};
|
||
});
|
||
if (options.noCode === true) {
|
||
return { code: '', messages };
|
||
}
|
||
const obj = root ? objectifier(root) : {};
|
||
if (options.map || options.mapOf) {
|
||
return {
|
||
code: mapToInitStringChunk(objToMap(obj), options.ts, true, options.mapOf, options.chunk),
|
||
messages,
|
||
};
|
||
}
|
||
return { code: JSON.stringify(obj), messages };
|
||
}
|
||
function mapToInitStringChunk(map, ts = false, isRoot = false, isMapOf = false, chunk = 0) {
|
||
if (!chunk) {
|
||
return mapToInitString(map, ts, isRoot, isMapOf);
|
||
}
|
||
const chunks = [];
|
||
let chunkMap = new Map();
|
||
let chunkCount = 0;
|
||
for (const [key, value] of map) {
|
||
if (chunkCount === chunk) {
|
||
chunks.push(mapToInitString(chunkMap, ts, isRoot, isMapOf));
|
||
chunkMap = new Map();
|
||
chunkCount = 0;
|
||
}
|
||
chunkMap.set(key, value);
|
||
chunkCount++;
|
||
}
|
||
if (chunkCount) {
|
||
chunks.push(mapToInitString(chunkMap, ts, isRoot, isMapOf));
|
||
}
|
||
return `[${chunks.join(',')}]`;
|
||
}
|
||
function mapToInitString(map, ts = false, isRoot = false, isMapOf = false) {
|
||
const entries = [];
|
||
for (let [key, value] of map) {
|
||
if (value instanceof Map) {
|
||
entries.push(`["${key}", ${mapToInitString(value, ts, false, isMapOf)}]`);
|
||
}
|
||
else {
|
||
entries.push(`["${key}", ${JSON.stringify(value)}]`);
|
||
}
|
||
}
|
||
if (isMapOf) {
|
||
return `utsMapOf([${entries.join(', ')}])`;
|
||
}
|
||
return `new Map${ts
|
||
? isRoot
|
||
? '<string, Map<string, Map<string, any>>>'
|
||
: '<string, any>'
|
||
: ''}([${entries.join(', ')}])`;
|
||
}
|
||
function objToMap(obj) {
|
||
const map = new Map();
|
||
for (const key in obj) {
|
||
const value = obj[key];
|
||
if (typeof value === 'object') {
|
||
map.set(key, objToMap(value));
|
||
}
|
||
else {
|
||
map.set(key, value);
|
||
}
|
||
}
|
||
return map;
|
||
}
|
||
|
||
exports.expand = expand;
|
||
exports.normalize = normalize;
|
||
exports.objectifier = objectifier;
|
||
exports.parse = parse;
|