UNPKG

@yugu/gogocode

Version:

The simplest tool to parse/transform/generate code on ast

313 lines (308 loc) 11.7 kB
const getSelector = require('./get-selector'); const { find } = require('./find'); const parse = require('./parse'); const generate = require('./serialize-node'); const core = { // 通过选择器获取,返回ast片段 getAstsBySelector( ast, selector, { strictSequence, deep, parseOptions, expando = 'g123o456g789o' } = {} ) { //strictSequence作用: // 有的时候数组不要求顺序,如{a:$_$}匹配{b:1, a:2} // 有的时候需要,如function($_$, $_$)匹配function(a, b) {} if (!Array.isArray(selector)) { selector = [selector]; } let nodePathList = []; let matchWildCardList = []; const selectorAst = selector.map((item) => { const sel = getSelector( item, this.parseOptions || parseOptions, expando ); if (!sel.nodeType) { throw new Error( '语句类型缺失,请在 https://github.com/thx/gogocode/issues 上提供您的代码样例' ); } return sel; }); // const posStrList = []; const reachedNodeList = []; selectorAst.forEach((item) => { const res = find.call( ast, item.nodeType, item.structure, strictSequence, deep, expando ); res.nodePathList.forEach((p, i) => { // let posStr = ''; // if (p.node.content.openStart) { // posStr = `${p.node.content.openStart.startPosition},${p.node.content.openEnd.endPosition}`; // } else if (p.node.content.value) { // posStr = `${p.node.content.value.startPosition},${p.node.content.value.endPosition}`; // } // if (posStrList.indexOf(posStr) == -1) { // // 去重 // nodePathList.push(p); // matchWildCardList.push(res.matchWildCardList[i]); // posStrList.push(posStr); // } if (reachedNodeList.indexOf(p.node.content) == -1) { nodePathList.push(p); matchWildCardList.push(res.matchWildCardList[i]); reachedNodeList.push(p.node.content) } }); }); return { nodePathList, matchWildCardList, pathList: nodePathList, extraDataList: matchWildCardList }; }, getParentListByAst(path) { const list = []; while (path && path.parentPath) { list.push(path.parentPath); path = path.parentPath; } return list; }, buildAstByAstStr( str, astPatialMap = {}, { isProgram = false, parseOptions } = {} ) { try { const ast = parse(str, parseOptions); const program = core.replaceStrByAst(ast, astPatialMap); if (program) { if (isProgram) { return program; } else { if (program.content.children && program.content.children[0]) { if (program.content.children.length > 1) { return program.content.children } else if (program.content.children.length == 1) { return program.content.children[0]; } else { return null } } } } else { return null; } } catch (e) { console.log('buildAstByAstStr failed:' + e); } }, replaceStrByAst(ast, astPatialMap = {}) { for (let key in astPatialMap) { const valueAst = astPatialMap[key]; const { nodePathList } = core.getAstsBySelector( ast, `'$_$${key}$_$'` ); if (nodePathList.length > 0) { nodePathList[0].replace(valueAst); } } return ast; }, replaceAstByAst(oldAst, newAst, _index) { if (oldAst.parent && oldAst.parent.node && oldAst.parent.node.content) { oldAst.parent.node.content.children.splice( _index, 1, newAst ); } else if (oldAst.node.nodeType == 'document') { oldAst.node.content.children = [newAst]; } }, replaceSelBySel( ast, selector, replacer, strictSequence, parseOptions, expando = 'g123o456g789o' ) { // 用于结构不一致的,整体替换 if (ast.node) { ast = ast.node; } const { nodePathList, matchWildCardList } = core.getAstsBySelector( ast, selector, { strictSequence, deep: 'nn', parseOptions: this.parseOptions || parseOptions, expando } ); const originReplacer = replacer; nodePathList.forEach((path, i) => { const extra = matchWildCardList[i]; replacer = originReplacer; if (typeof replacer == 'function') { replacer = replacer(extra, path); } if (Object.keys(extra).length > 0 && typeof replacer == 'string') { let newReplacer = replacer; for (let key in extra) { if (key.match(/\$\$\$/)) { let key$$$ = key.replace(/\$\$\$/, ''); key$$$ == '$' && (key$$$ = ''); let join = '\n'; let wildCardCode = extra[key] .map((item) => { let codeStr = ''; if (item.key) { if (item.value) { codeStr = `${item.key.content}="${item.value.content}"`; } else { codeStr = item.key.content; } } else if (item.nodeType == 'text') { codeStr = item.content.value.content; } else if (item.nodeType == 'tag') { codeStr = generate(item); } try { // 嵌套replace const childAst = core.buildAstByAstStr( generate(item), {}, { isProgram: true } ); core.replaceSelBySel( childAst, selector, replacer, strictSequence, parseOptions, expando ); codeStr = generate(childAst); } catch (e) { // } return codeStr; }) .join(join); // 不能都用,连接,还是需要找到$_$ newReplacer = newReplacer.replace( '$$$' + key$$$, wildCardCode ); } else { let wildCardCode = extra[key][0].value; key == '0' && (key = ''); newReplacer = newReplacer .replace(new RegExp(`\\$_\\$${key}`, 'g'), wildCardCode); // 通过选择器替换ast,返回完整ast } } core.replace(path, newReplacer) } else { core.replace(path, replacer) } }); }, replace(oldPath, newReplacer) { let parentContent, index, isDoc, newNodes; if (oldPath.node.nodeType == 'document') { isDoc = true; } else { parentContent = oldPath.parent.node.content.children || []; index = parentContent.indexOf(oldPath.node); } if (!newReplacer) { // 删除节点 if (index > -1) { if (isDoc) { oldPath.node.content.children = [] } else { parentContent.splice(index, 1); } } return; } else if (newReplacer.nodeType == 'document') { newNodes = newReplacer.content.children || [] } else if (typeof newReplacer == 'string') { // 通过字符串解析为node if (index > -1) { newNodes = core.buildAstByAstStr( newReplacer, {}, { isProgram: true } ).content.children; } } else if (newReplacer[0] && newReplacer[0].nodePath) { // AST实例 newNodes = [newReplacer.node] } else if (newReplacer.type) { // AST node newNodes = [newReplacer] } if (isDoc) { oldPath.node.content.children = newNodes } else { newNodes.forEach(newNode => { newNode.parentRef = oldPath.parent.node; parentContent.splice(index, 1, newNode); }) } }, removeAst(ast, selector, { strictSequence, parseOptions, expando } = {}) { if (!ast || typeof ast !== 'object') { throw new Error('remove failed! first argument mast be object'); } if ( !selector || (typeof selector !== 'object' && typeof selector !== 'string' && !Array.isArray(selector)) ) { throw new Error( 'remove failed! first argument mast be object、string or string array' ); } // const selectorAst = getSelector(selector, this.parseOptions); // console.log(selectorAst) const { nodePathList } = core.getAstsBySelector(ast, selector, { strictSequence, parseOptions, expando }); // const { nodePathList } = find.call(ast, selectorAst.nodeType, selectorAst.structure, true, 'nn'); // console.log(nodePathList) nodePathList.forEach((path) => { // 多条语句逗号分割的话,只删除一个;一条语句的话,删除父节点 const parentChildList = path.parent.node.content.children; const index = parentChildList.indexOf(path.node); parentChildList.splice(index, 1); }); }, remove(ast) { try { ast.node.parentRef.content.children.forEach((item, index) => { if (item == ast.node) { ast.node.parentRef.content.children.splice(index, 1); } }); } catch (e) { throw `remove failed! ${e}`; } } }; module.exports = core;