| 'use strict'; |
| const valueParser = require('postcss-value-parser'); |
| |
| /** |
| * @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 = 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 = 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]]; |
| |
| return; |
| } |
| } |
| |
| /** |
| * @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]]; |
| |
| return; |
| } |
| } |
| |
| /** |
| * @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]]; |
| |
| return; |
| } |
| } |
| |
| /** |
| * @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) { |
| reducer(node, node.nodes.reduce(getValues, [])); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @type {import('postcss').PluginCreator<void>} |
| * @return {import('postcss').Plugin} |
| */ |
| function pluginCreator() { |
| return { |
| postcssPlugin: 'postcss-reduce-transforms', |
| prepare() { |
| const cache = new Map(); |
| return { |
| OnceExit(css) { |
| css.walkDecls(/transform$/i, (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); |
| }); |
| }, |
| }; |
| }, |
| }; |
| } |
| |
| pluginCreator.postcss = true; |
| module.exports = pluginCreator; |