| import valueParser from 'postcss-value-parser'; | |
| const transformRegex = /transform$/i; | |
| /** | |
| * @param {(number|string)[]} list | |
| * @param {valueParser.Node} node | |
| * @param {number} index | |
| * @return {(number|string)[]} | |
| */ | |
| function getValues(list, node, index) { | |
| if (index % 2 === 0) { | |
| /** @type {number|string} */ | |
| let value = Number.NaN; | |
| if ( | |
| node.type === 'function' && | |
| (node.value === 'var' || node.value === 'env') && | |
| node.nodes.length === 1 | |
| ) { | |
| value = valueParser.stringify(node.nodes); | |
| } else if (node.type === 'word') { | |
| value = Number.parseFloat(node.value); | |
| } | |
| return [...list, value]; | |
| } | |
| return list; | |
| } | |
| /** | |
| * @param {valueParser.FunctionNode} node | |
| * @param {(number|string)[]} values | |
| * @return {void} | |
| */ | |
| function matrix3d(node, values) { | |
| if (values.length !== 16) { | |
| return; | |
| } | |
| // matrix3d(a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1) => matrix(a, b, c, d, tx, ty) | |
| if ( | |
| values[15] && | |
| values[2] === 0 && | |
| values[3] === 0 && | |
| values[6] === 0 && | |
| values[7] === 0 && | |
| values[8] === 0 && | |
| values[9] === 0 && | |
| values[10] === 1 && | |
| values[11] === 0 && | |
| values[14] === 0 && | |
| values[15] === 1 | |
| ) { | |
| const { nodes } = node; | |
| node.value = 'matrix'; | |
| node.nodes = [ | |
| nodes[0], // a | |
| nodes[1], // , | |
| nodes[2], // b | |
| nodes[3], // , | |
| nodes[8], // c | |
| nodes[9], // , | |
| nodes[10], // d | |
| nodes[11], // , | |
| nodes[24], // tx | |
| nodes[25], // , | |
| nodes[26], // ty | |
| ]; | |
| } | |
| } | |
| const rotate3dMappings = new Map([ | |
| [[1, 0, 0].toString(), 'rotateX'], // rotate3d(1, 0, 0, a) => rotateX(a) | |
| [[0, 1, 0].toString(), 'rotateY'], // rotate3d(0, 1, 0, a) => rotateY(a) | |
| [[0, 0, 1].toString(), 'rotate'], // rotate3d(0, 0, 1, a) => rotate(a) | |
| ]); | |
| /** | |
| * @param {valueParser.FunctionNode} node | |
| * @param {(number|string)[]} values | |
| * @return {void} | |
| */ | |
| function rotate3d(node, values) { | |
| if (values.length !== 4) { | |
| return; | |
| } | |
| const { nodes } = node; | |
| const match = rotate3dMappings.get(values.slice(0, 3).toString()); | |
| if (match) { | |
| node.value = match; | |
| node.nodes = [nodes[6]]; | |
| } | |
| } | |
| /** | |
| * @param {valueParser.FunctionNode} node | |
| * @param {(number|string)[]} values | |
| * @return {void} | |
| */ | |
| function rotateZ(node, values) { | |
| if (values.length !== 1) { | |
| return; | |
| } | |
| // rotateZ(rz) => rotate(rz) | |
| node.value = 'rotate'; | |
| } | |
| /** | |
| * @param {valueParser.FunctionNode} node | |
| * @param {(number|string)[]} values | |
| * @return {void} | |
| */ | |
| function scale(node, values) { | |
| if (values.length !== 2) { | |
| return; | |
| } | |
| const { nodes } = node; | |
| const [first, second] = values; | |
| // scale(sx, sy) => scale(sx) | |
| if (first === second) { | |
| node.nodes = [nodes[0]]; | |
| return; | |
| } | |
| // scale(sx, 1) => scaleX(sx) | |
| if (second === 1) { | |
| node.value = 'scaleX'; | |
| node.nodes = [nodes[0]]; | |
| return; | |
| } | |
| // scale(1, sy) => scaleY(sy) | |
| if (first === 1) { | |
| node.value = 'scaleY'; | |
| node.nodes = [nodes[2]]; | |
| } | |
| } | |
| /** | |
| * @param {valueParser.FunctionNode} node | |
| * @param {(number|string)[]} values | |
| * @return {void} | |
| */ | |
| function scale3d(node, values) { | |
| if (values.length !== 3) { | |
| return; | |
| } | |
| const { nodes } = node; | |
| const [first, second, third] = values; | |
| // scale3d(sx, 1, 1) => scaleX(sx) | |
| if (second === 1 && third === 1) { | |
| node.value = 'scaleX'; | |
| node.nodes = [nodes[0]]; | |
| return; | |
| } | |
| // scale3d(1, sy, 1) => scaleY(sy) | |
| if (first === 1 && third === 1) { | |
| node.value = 'scaleY'; | |
| node.nodes = [nodes[2]]; | |
| return; | |
| } | |
| // scale3d(1, 1, sz) => scaleZ(sz) | |
| if (first === 1 && second === 1) { | |
| node.value = 'scaleZ'; | |
| node.nodes = [nodes[4]]; | |
| } | |
| } | |
| /** | |
| * @param {valueParser.FunctionNode} node | |
| * @param {(number|string)[]} values | |
| * @return {void} | |
| */ | |
| function translate(node, values) { | |
| if (values.length !== 2) { | |
| return; | |
| } | |
| const { nodes } = node; | |
| // translate(tx, 0) => translate(tx) | |
| if (values[1] === 0) { | |
| node.nodes = [nodes[0]]; | |
| return; | |
| } | |
| // translate(0, ty) => translateY(ty) | |
| if (values[0] === 0) { | |
| node.value = 'translateY'; | |
| node.nodes = [nodes[2]]; | |
| } | |
| } | |
| /** | |
| * @param {valueParser.FunctionNode} node | |
| * @param {(number|string)[]} values | |
| * @return {void} | |
| */ | |
| function translate3d(node, values) { | |
| if (values.length !== 3) { | |
| return; | |
| } | |
| const { nodes } = node; | |
| // translate3d(0, 0, tz) => translateZ(tz) | |
| if (values[0] === 0 && values[1] === 0) { | |
| node.value = 'translateZ'; | |
| node.nodes = [nodes[4]]; | |
| } | |
| } | |
| const reducers = new Map([ | |
| ['matrix3d', matrix3d], | |
| ['rotate3d', rotate3d], | |
| ['rotateZ', rotateZ], | |
| ['scale', scale], | |
| ['scale3d', scale3d], | |
| ['translate', translate], | |
| ['translate3d', translate3d], | |
| ]); | |
| /** | |
| * @param {string} name | |
| * @return {string} | |
| */ | |
| function normalizeReducerName(name) { | |
| const lowerCasedName = name.toLowerCase(); | |
| if (lowerCasedName === 'rotatez') { | |
| return 'rotateZ'; | |
| } | |
| return lowerCasedName; | |
| } | |
| /** | |
| * @param {valueParser.Node} node | |
| * @return {false} | |
| */ | |
| function reduce(node) { | |
| if (node.type === 'function') { | |
| const normalizedReducerName = normalizeReducerName(node.value); | |
| const reducer = reducers.get(normalizedReducerName); | |
| if (reducer !== undefined) { | |
| /** @type {(number|string)[]} */ | |
| let values = []; | |
| for (const [index, child] of node.nodes.entries()) { | |
| values = getValues(values, child, index); | |
| } | |
| reducer(node, values); | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * @return {import('postcss').Plugin} | |
| */ | |
| function pluginCreator() { | |
| return { | |
| postcssPlugin: 'postcss-reduce-transforms', | |
| prepare() { | |
| const cache = new Map(); | |
| return { | |
| /** | |
| * @param {import('postcss').Root} css | |
| */ | |
| OnceExit(css) { | |
| css.walkDecls(transformRegex, (decl) => { | |
| const value = decl.value; | |
| if (!value) { | |
| return; | |
| } | |
| if (cache.has(value)) { | |
| decl.value = cache.get(value); | |
| return; | |
| } | |
| const result = valueParser(value).walk(reduce).toString(); | |
| decl.value = result; | |
| cache.set(value, result); | |
| }); | |
| }, | |
| }; | |
| }, | |
| }; | |
| } | |
| /** @type {true} */ | |
| pluginCreator.postcss = true; | |
| const moduleExports = pluginCreator; | |
| export { moduleExports as default, moduleExports as 'module.exports' }; |