310 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			310 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| '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;
 |