diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2023-10-14 15:05:55 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2023-10-14 15:05:55 +0800 |
| commit | c06f7ee0a40255214c477e13d9b956a7e07c2900 (patch) | |
| tree | f5982a568ef8fab865d0142e71c7494fea3e4c1b /node_modules/csso/lib/restructure/4-restructShorthand.js | |
| parent | 3fd9400d4dccc25aa29b5702216732e0b7542073 (diff) | |
postcss,(plugins): nanocss, postcss-preset-env
Diffstat (limited to 'node_modules/csso/lib/restructure/4-restructShorthand.js')
| -rw-r--r-- | node_modules/csso/lib/restructure/4-restructShorthand.js | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/node_modules/csso/lib/restructure/4-restructShorthand.js b/node_modules/csso/lib/restructure/4-restructShorthand.js new file mode 100644 index 00000000..41acb515 --- /dev/null +++ b/node_modules/csso/lib/restructure/4-restructShorthand.js @@ -0,0 +1,425 @@ +import { List, generate, walk } from 'css-tree'; + +const REPLACE = 1; +const REMOVE = 2; +const TOP = 0; +const RIGHT = 1; +const BOTTOM = 2; +const LEFT = 3; +const SIDES = ['top', 'right', 'bottom', 'left']; +const SIDE = { + 'margin-top': 'top', + 'margin-right': 'right', + 'margin-bottom': 'bottom', + 'margin-left': 'left', + + 'padding-top': 'top', + 'padding-right': 'right', + 'padding-bottom': 'bottom', + 'padding-left': 'left', + + 'border-top-color': 'top', + 'border-right-color': 'right', + 'border-bottom-color': 'bottom', + 'border-left-color': 'left', + 'border-top-width': 'top', + 'border-right-width': 'right', + 'border-bottom-width': 'bottom', + 'border-left-width': 'left', + 'border-top-style': 'top', + 'border-right-style': 'right', + 'border-bottom-style': 'bottom', + 'border-left-style': 'left' +}; +const MAIN_PROPERTY = { + 'margin': 'margin', + 'margin-top': 'margin', + 'margin-right': 'margin', + 'margin-bottom': 'margin', + 'margin-left': 'margin', + + 'padding': 'padding', + 'padding-top': 'padding', + 'padding-right': 'padding', + 'padding-bottom': 'padding', + 'padding-left': 'padding', + + 'border-color': 'border-color', + 'border-top-color': 'border-color', + 'border-right-color': 'border-color', + 'border-bottom-color': 'border-color', + 'border-left-color': 'border-color', + 'border-width': 'border-width', + 'border-top-width': 'border-width', + 'border-right-width': 'border-width', + 'border-bottom-width': 'border-width', + 'border-left-width': 'border-width', + 'border-style': 'border-style', + 'border-top-style': 'border-style', + 'border-right-style': 'border-style', + 'border-bottom-style': 'border-style', + 'border-left-style': 'border-style' +}; + +class TRBL { + constructor(name) { + this.name = name; + this.loc = null; + this.iehack = undefined; + this.sides = { + 'top': null, + 'right': null, + 'bottom': null, + 'left': null + }; + } + + getValueSequence(declaration, count) { + const values = []; + let iehack = ''; + const hasBadValues = declaration.value.type !== 'Value' || declaration.value.children.some(function(child) { + let special = false; + + switch (child.type) { + case 'Identifier': + switch (child.name) { + case '\\0': + case '\\9': + iehack = child.name; + return; + + case 'inherit': + case 'initial': + case 'unset': + case 'revert': + special = child.name; + break; + } + break; + + case 'Dimension': + switch (child.unit) { + // is not supported until IE11 + case 'rem': + + // v* units is too buggy across browsers and better + // don't merge values with those units + case 'vw': + case 'vh': + case 'vmin': + case 'vmax': + case 'vm': // IE9 supporting "vm" instead of "vmin". + special = child.unit; + break; + } + break; + + case 'Hash': // color + case 'Number': + case 'Percentage': + break; + + case 'Function': + if (child.name === 'var') { + return true; + } + + special = child.name; + break; + + default: + return true; // bad value + } + + values.push({ + node: child, + special, + important: declaration.important + }); + }); + + if (hasBadValues || values.length > count) { + return false; + } + + if (typeof this.iehack === 'string' && this.iehack !== iehack) { + return false; + } + + this.iehack = iehack; // move outside + + return values; + } + + canOverride(side, value) { + const currentValue = this.sides[side]; + + return !currentValue || (value.important && !currentValue.important); + } + + add(name, declaration) { + function attemptToAdd() { + const sides = this.sides; + const side = SIDE[name]; + + if (side) { + if (side in sides === false) { + return false; + } + + const values = this.getValueSequence(declaration, 1); + + if (!values || !values.length) { + return false; + } + + // can mix only if specials are equal + for (const key in sides) { + if (sides[key] !== null && sides[key].special !== values[0].special) { + return false; + } + } + + if (!this.canOverride(side, values[0])) { + return true; + } + + sides[side] = values[0]; + + return true; + } else if (name === this.name) { + const values = this.getValueSequence(declaration, 4); + + if (!values || !values.length) { + return false; + } + + switch (values.length) { + case 1: + values[RIGHT] = values[TOP]; + values[BOTTOM] = values[TOP]; + values[LEFT] = values[TOP]; + break; + + case 2: + values[BOTTOM] = values[TOP]; + values[LEFT] = values[RIGHT]; + break; + + case 3: + values[LEFT] = values[RIGHT]; + break; + } + + // can mix only if specials are equal + for (let i = 0; i < 4; i++) { + for (const key in sides) { + if (sides[key] !== null && sides[key].special !== values[i].special) { + return false; + } + } + } + + for (let i = 0; i < 4; i++) { + if (this.canOverride(SIDES[i], values[i])) { + sides[SIDES[i]] = values[i]; + } + } + + return true; + } + } + + if (!attemptToAdd.call(this)) { + return false; + } + + // TODO: use it when we can refer to several points in source + // if (this.loc) { + // this.loc = { + // primary: this.loc, + // merged: declaration.loc + // }; + // } else { + // this.loc = declaration.loc; + // } + if (!this.loc) { + this.loc = declaration.loc; + } + + return true; + } + + isOkToMinimize() { + const top = this.sides.top; + const right = this.sides.right; + const bottom = this.sides.bottom; + const left = this.sides.left; + + if (top && right && bottom && left) { + const important = + top.important + + right.important + + bottom.important + + left.important; + + return important === 0 || important === 4; + } + + return false; + } + + getValue() { + const result = new List(); + const sides = this.sides; + const values = [ + sides.top, + sides.right, + sides.bottom, + sides.left + ]; + const stringValues = [ + generate(sides.top.node), + generate(sides.right.node), + generate(sides.bottom.node), + generate(sides.left.node) + ]; + + if (stringValues[LEFT] === stringValues[RIGHT]) { + values.pop(); + if (stringValues[BOTTOM] === stringValues[TOP]) { + values.pop(); + if (stringValues[RIGHT] === stringValues[TOP]) { + values.pop(); + } + } + } + + for (let i = 0; i < values.length; i++) { + result.appendData(values[i].node); + } + + if (this.iehack) { + result.appendData({ + type: 'Identifier', + loc: null, + name: this.iehack + }); + } + + return { + type: 'Value', + loc: null, + children: result + }; + } + + getDeclaration() { + return { + type: 'Declaration', + loc: this.loc, + important: this.sides.top.important, + property: this.name, + value: this.getValue() + }; + } +} + +function processRule(rule, shorts, shortDeclarations, lastShortSelector) { + const declarations = rule.block.children; + const selector = rule.prelude.children.first.id; + + rule.block.children.forEachRight(function(declaration, item) { + const property = declaration.property; + + if (!MAIN_PROPERTY.hasOwnProperty(property)) { + return; + } + + const key = MAIN_PROPERTY[property]; + let shorthand; + let operation; + + if (!lastShortSelector || selector === lastShortSelector) { + if (key in shorts) { + operation = REMOVE; + shorthand = shorts[key]; + } + } + + if (!shorthand || !shorthand.add(property, declaration)) { + operation = REPLACE; + shorthand = new TRBL(key); + + // if can't parse value ignore it and break shorthand children + if (!shorthand.add(property, declaration)) { + lastShortSelector = null; + return; + } + } + + shorts[key] = shorthand; + shortDeclarations.push({ + operation, + block: declarations, + item, + shorthand + }); + + lastShortSelector = selector; + }); + + return lastShortSelector; +} + +function processShorthands(shortDeclarations, markDeclaration) { + shortDeclarations.forEach(function(item) { + const shorthand = item.shorthand; + + if (!shorthand.isOkToMinimize()) { + return; + } + + if (item.operation === REPLACE) { + item.item.data = markDeclaration(shorthand.getDeclaration()); + } else { + item.block.remove(item.item); + } + }); +} + +export default function restructBlock(ast, indexer) { + const stylesheetMap = {}; + const shortDeclarations = []; + + walk(ast, { + visit: 'Rule', + reverse: true, + enter(node) { + const stylesheet = this.block || this.stylesheet; + const ruleId = (node.pseudoSignature || '') + '|' + node.prelude.children.first.id; + let ruleMap; + let shorts; + + if (!stylesheetMap.hasOwnProperty(stylesheet.id)) { + ruleMap = { + lastShortSelector: null + }; + stylesheetMap[stylesheet.id] = ruleMap; + } else { + ruleMap = stylesheetMap[stylesheet.id]; + } + + if (ruleMap.hasOwnProperty(ruleId)) { + shorts = ruleMap[ruleId]; + } else { + shorts = {}; + ruleMap[ruleId] = shorts; + } + + ruleMap.lastShortSelector = processRule.call(this, node, shorts, shortDeclarations, ruleMap.lastShortSelector); + } + }); + + processShorthands(shortDeclarations, indexer.declaration); +}; |
