UNPKG

ciallorize

Version:

Ciallorize your bundled js into ascii art.

121 lines (107 loc) 4.05 kB
const parser = require('@babel/parser'); const IGNORED_PROPERTIES = new Set([ 'loc', 'start', 'end', 'comments', 'extra', 'tokens', 'leadingComments', 'trailingComments', 'innerComments' ]); /** * 比较两段代码的 AST 是否相同,失败时抛出差异 * @param {string} code1 原始代码 * @param {string} code2 重组后的代码 * @throws {Error} 包含具体差异信息 */ function compareASTWithDiff(code1, code2) { const ast1 = normalizeAST(parseAST(code1)); const ast2 = normalizeAST(parseAST(code2)); const path = []; // 记录当前比较路径(如 ["Program", "body", "0"]) try { deepCompareAST(ast1, ast2, path); return true; } catch (error) { // console.error(`AST 比较失败:\n${error.message}`); return false; } } function parseAST(code) { return parser.parse(code, { sourceType: 'script', plugins: [], // 根据需要使用 JSX/Flow 等插件 }); } function normalizeAST(ast) { return JSON.parse( JSON.stringify(ast, (key, value) => { if (IGNORED_PROPERTIES.has(key)) return undefined; // 删除无关属性 return value; }) ); } /** * 递归比较 AST 节点,失败时抛出差异 * @param {ASTNode} node1 * @param {ASTNode} node2 * @param {string[]} path */ function deepCompareAST(node1, node2, path) { if (node1 === node2) return; if (!node1 || !node2) { throw new Error(`节点缺失: ${path.join('.')}\n 节点1: ${JSON.stringify(node1)}\n 节点2: ${JSON.stringify(node2)}`); } // 检查节点类型 if (node1.type !== node2.type) { throw new Error(`类型不同: ${path.join('.')}\n 节点1类型: ${node1.type}\n 节点2类型: ${node2.type}`); } // 特殊节点处理 switch (node1.type) { case 'Identifier': if (node1.name !== node2.name) { throw new Error(`标识符名称不同: ${path.join('.')}\n 节点1: ${node1.name}\n 节点2: ${node2.name}`); } return; case 'Literal': if (node1.value !== node2.value) { throw new Error(`字面量值不同: ${path.join('.')}\n 节点1: ${node1.value}\n 节点2: ${node2.value}`); } return; case 'RegExpLiteral': if (node1.pattern !== node2.pattern || node1.flags !== node2.flags) { throw new Error(`正则表达式不同: ${path.join('.')}\n 节点1: /${node1.pattern}/${node1.flags}\n 节点2: /${node2.pattern}/${node2.flags}`); } return; } // 比较子节点(忽略位置和额外属性) const keys1 = Object.keys(node1).filter(k => !['loc', 'start', 'end', 'extra', 'tokens', 'comments'].includes(k)); const keys2 = Object.keys(node2); // 检查属性数量 if (keys1.length !== keys2.length) { const missingKeys = keys1.filter(k => !keys2.includes(k)); const extraKeys = keys2.filter(k => !keys1.includes(k)); throw new Error(`属性数量不同: ${path.join('.')}\n 缺失属性: ${missingKeys.join(', ')}\n 多余属性: ${extraKeys.join(', ')}`); } // 递归比较每个属性 for (const key of keys1) { const val1 = node1[key]; const val2 = node2[key]; path.push(key); // 处理数组(如 body、params) if (Array.isArray(val1)) { if (!Array.isArray(val2) || val1.length !== val2.length) { throw new Error(`数组长度不同: ${path.join('.')}\n 节点1长度: ${val1.length}\n 节点2长度: ${val2.length}`); } for (let i = 0; i < val1.length; i++) { path.push(`${i}`); deepCompareAST(val1[i], val2[i], path); path.pop(); } } // 递归对象 else if (typeof val1 === 'object' && val1 !== null) { deepCompareAST(val1, val2, path); } // 基本类型直接比较 else if (val1 !== val2) { throw new Error(`属性值不同: ${path.join('.')}\n 节点1.${key}: ${val1}\n 节点2.${key}: ${val2}`); } path.pop(); } } module.exports = compareASTWithDiff;