{"id":13671025,"url":"https://github.com/shuerguo999/gogoAST","last_synced_at":"2025-04-27T13:33:29.889Z","repository":{"id":57279796,"uuid":"244684486","full_name":"shuerguo999/gogoAST","owner":"shuerguo999","description":"The simplest tool to parse/transform/generate code on ast","archived":false,"fork":false,"pushed_at":"2021-01-24T17:51:40.000Z","size":484,"stargazers_count":30,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-20T03:34:38.690Z","etag":null,"topics":["acorn","ast","ast-types","astquery","asttool","babel","babel-generator","babel-parser","babel-traverse","babel-types","babylon","easyast","esprima","htmlparser","jscodeshift","mustache","recast","simpleast"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shuerguo999.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-03-03T16:21:16.000Z","updated_at":"2025-03-22T03:06:39.000Z","dependencies_parsed_at":"2022-09-19T15:12:44.202Z","dependency_job_id":null,"html_url":"https://github.com/shuerguo999/gogoAST","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shuerguo999%2FgogoAST","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shuerguo999%2FgogoAST/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shuerguo999%2FgogoAST/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shuerguo999%2FgogoAST/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shuerguo999","download_url":"https://codeload.github.com/shuerguo999/gogoAST/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251145847,"owners_count":21543108,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["acorn","ast","ast-types","astquery","asttool","babel","babel-generator","babel-parser","babel-traverse","babel-types","babylon","easyast","esprima","htmlparser","jscodeshift","mustache","recast","simpleast"],"created_at":"2024-08-02T09:00:56.192Z","updated_at":"2025-04-27T13:33:24.861Z","avatar_url":"https://github.com/shuerguo999.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# GOGOAST\n\n![](https://img.alicdn.com/tfs/TB17V2NvHj1gK0jSZFuXXcrHpXa-256-256.png)\n\n全网最简单易上手，可读性最强的AST处理工具！\n\n# Install\n- node包安装\n```\n    npm install gogoast\n```\n- 浏览器执行\n    \u003e commonjs引入gogoast.js\n\n# 为什么你需要用gogoAST？\n- 大幅减少代码量——如果你需要使用AST对代码进行升级、改造、分析，快用gogoAST帮你摆脱繁琐冗余的的代码，专注于你的核心逻辑。不需要traverse，像剥洋葱一样一层一层的对比、操作、构造ast节点。\n- 降低理解成本——甚至不需要理解什么是CallExpression、Identifier、ImportDeclaration这些概念，就可以畅快运用AST。\n- 基于recast，转换后的代码基本与源代码的格式差异最小。\n- 凡是需要借助babel、recast、jscodeshift、esprima...完成的需求，gogoast都能帮你更快更简单的完成。\n\n# 快速开始\n- 创建一个AST对象\n``` javascript\nconst code = `some code`\nconst GG = require('gogoast');\nconst AST = GG.createAstObj(code);\n```\n\n- 查找代码片段\n用一段包含通配符（$_$）的'代码选择器'来查找相应的代码片段，如：\n\n    - 查找代码中所有的变量\n    \n        ``` javascript\n        const { matchWildCardList } = AST.getAstsBySelector(`$_$`);\n        ```\n    - 查找代码中所有的字符串\n    \n        ``` javascript\n        const { matchWildCardList } = AST.getAstsBySelector(`'$_$'`);\n        ```\n    - 查找某些函数的调用\n        ``` javascript\n        const { nodePathList, matchWildCardList } = AST.getAstsBySelector([\n            '$_$.setTip($_$, $_$)',\n            'tip.show($_$)'\n        ]);\n        ```\n    - 选择器示例：\n        ``` javascript\n        var $_$ = $_$\n        function $_$ () { $_$ }\n        View.extend($_$)\n        $_$ ? $_$ : $_$\n        $_$ \u0026\u0026 $_$\n        ```\n        \n返回结果 |nodePathList | matchWildCardList|\n-- |-- | :--: \n描述|代码选择器匹配到的代码片段 | 代码选择器中通配符匹配到的代码片段\n结构|nodePath[]|{ stucture: node, value: simpleNode }[]\n解释|nodePath对象包含匹配到的ast结构及其上下文| structure中的node是ast结构，value是简化后的node，便于取值\n\n\n\n- 替换一段代码\n1. 在完整的AST片段中，通过param1查找对应的代码片段\n2. 通过param2生成一段代码，填入param1中通配符对应的代码\n3. 将param2生成的代码替换param1匹配的代码\n```javascript\n    AST.replaceSelBySel(param1, param2);\n    AST.replaceSelBySel('const $_$ = require($_$)', 'import $_$ from $_$');\n```\n\n\n# gogoAST与主流AST工具之对比\n\n目前，自定义babel插件和jscodeshift是有代表性的AST工具，通过下面两个例子可以看出gogoAST的代码可读性和简洁的优势。\n\n- 示例1: 与自定义babel插件对比\n\n    对于下面这段js代码\n    \n    ```javascript\n    import a from 'a';\n    console.log('get A')\n    var b = console.log()\n    console.log.bind()\n    var c = console.log\n    console.log = func\n    ```\n    \n    假如我们希望对不同的console.log做不同的处理，变成下面这样\n    \n    ```\n    import a from 'a';\n    var b = void 0;\n    console.log.bind()\n    var c = function(){};\n    console.log = func\n    ```\n\n    - 用自定义Babel插件实现的核心代码：\n\n    ```javascript\n    // 代码来源：https://zhuanlan.zhihu.com/p/32189701\n    module.exports = function({ types: t }) {\n    return {\n        name: \"transform-remove-console\",\n        visitor: {\n        CallExpression(path, state) {\n            const callee = path.get(\"callee\");\n\n            if (!callee.isMemberExpression()) return;\n\n            if (isIncludedConsole(callee, state.opts.exclude)) {\n            // console.log()\n            if (path.parentPath.isExpressionStatement()) {\n                path.remove();\n            } else {\n            //var a = console.log()\n                path.replaceWith(createVoid0());\n            }\n            } else if (isIncludedConsoleBind(callee, state.opts.exclude)) {\n            // console.log.bind()\n                path.replaceWith(createNoop());\n            }\n        },\n        MemberExpression: {\n            exit(path, state) {\n            if (\n                isIncludedConsole(path, state.opts.exclude) \u0026\u0026\n                !path.parentPath.isMemberExpression()\n            ) {\n            //console.log = func\n                if (\n                path.parentPath.isAssignmentExpression() \u0026\u0026\n                path.parentKey === \"left\"\n                ) {\n                path.parentPath.get(\"right\").replaceWith(createNoop());\n                } else {\n                //var a = console.log\n                path.replaceWith(createNoop());\n                }\n            }\n            }\n        }\n        }\n    };\n\n    ```\n\n    - 用gogoAST实现：\n\n    ```javascript\n    const AST = GG.createAstObj(code);\n    AST.replaceSelBySel(`var $_$ = console.log()`, `$_$ = void 0`);\n    AST.replaceSelBySel(`console.log()`, null);\n    AST.replaceSelBySel(`var $_$ = console.log`, `$_$ = function(){}`);\n    const result = AST.generate();\n    ```\n\n\n- 示例2: 与jscodeshift对比\n\n    对于下面这段代码：\n    \n    ```javascript\n    import car from 'car';\n\n    const suv = car.factory('white', 'Kia', 'Sorento', 2010, 50000, null, true);\n    const truck = car.factory('silver', 'Toyota', 'Tacoma', 2006, 100000, true, true);\n    ```\n    \n    假如我们希望将函数的多个入参封装为一个对象传入，变成下面这样\n\n    ```javascript\n    import car from 'car';\n    const suv = car.factory({\"color\":\"white\",\"make\":\"Kia\",\"model\":\"Sorento\",\"year\":2010,\"miles\":50000,\"bedliner\":null,\"alarm\":true});\n    const truck = car.factory({\"color\":\"silver\",\"make\":\"Toyota\",\"model\":\"Tacoma\",\"year\":2006,\"miles\":100000,\"bedliner\":true,\"alarm\":true});\n    ```\n\n    - 用jscodeshift实现的核心代码：\n\n    ``` javascript\n    // 代码来源：https://www.toptal.com/javascript/write-code-to-rewrite-your-code\n    export default (fileInfo, api) =\u003e {\n    const j = api.jscodeshift;\n    const root = j(fileInfo.source);\n\n    // find declaration for \"car\" import\n    const importDeclaration = root.find(j.ImportDeclaration, {\n        source: {\n        type: 'Literal',\n        value: 'car',\n        },\n    });\n\n    // get the local name for the imported module\n    const localName =\n        importDeclaration.find(j.Identifier)\n        .get(0)\n        .node.name;\n\n    // current order of arguments\n    const argKeys = [\n        'color',\n        'make',\n        'model',\n        'year',\n        'miles',\n        'bedliner',\n        'alarm',\n    ];\n\n    // find where `.factory` is being called\n    return root.find(j.CallExpression, {\n        callee: {\n            type: 'MemberExpression',\n            object: {\n            name: localName,\n            },\n            property: {\n            name: 'factory',\n            },\n        }\n        })\n        .replaceWith(nodePath =\u003e {\n        const { node } = nodePath;\n\n        // use a builder to create the ObjectExpression\n        const argumentsAsObject = j.objectExpression(\n\n            // map the arguments to an Array of Property Nodes\n            node.arguments.map((arg, i) =\u003e\n            j.property(\n                'init',\n                j.identifier(argKeys[i]),\n                j.literal(arg.value)\n            )\n            )\n        );\n\n        // replace the arguments with our new ObjectExpression\n        node.arguments = [argumentsAsObject];\n\n        return node;\n        })\n\n        // specify print options for recast\n        .toSource({ quote: 'single', trailingComma: true });\n    };\n    ```\n\n    - 用gogoAST实现：\n\n    ``` javascript\n    const AST = GG.createAstObj(code);\n    const argKeys = ['color', 'make', 'model', 'year', 'miles', 'bedliner', 'alarm' ];\n    const { nodePathList, matchWildCardList } = AST.getAstsBySelector(`const $_$ = car.factory($_$)`, 'nn', false);\n    nodePathList.forEach((path, i) =\u003e {\n        const extra = matchWildCardList[i];\n        const variableName = extra.shift().value;\n        const obj = {};\n        extra.forEach((ext, ei) =\u003e {\n            obj[argKeys[ei]] = ext.structure.value\n        });\n        const newCall = GG.buildAstByAstStr(`${variableName} = car.factory(${JSON.stringify(obj)})`)\n        AST.replaceAstByAst(path, newCall);\n    })\n    const result = AST.generate();\n    ```\n\n# API\n陆续补充中\n\n1. 创建一个实例：createAstObj\n\n    ```javascript\n    const GG = require('gogoast');\n    const AST = GG.createAstObj(p, options);    \n    // options非必传，格式同babel-parse,如 { allowImportExportEverywhere: true, plugins: ['jsx'] }\n    ```\n    \n2. 通过选择器查找AST节点：getAstsBySelector\n    - 选择器是一段包含通配符（$_$）的代码\n    - nodePathList：返回找到的ast节点路径，包含自己节点、父节点等信息\n    - matchWildCardList：返回通配符$_$代表的节点信息，其中structure是节点完整信息，value是简略信息\n\n    ```javascript\n    const { nodePathList, matchWildCardList } = AST.getAstsBySelector([\n        '$_$.setTip($_$, $_$)',\n        'tip.show($_$)'\n    ]);\n\n    ```\n\n3. 通过选择器替换另一个选择器查找到的AST节点：replaceSelBySel\n    - 就像'abcd'.replace('a', 'z')一样好用\n\n    ```javascript\n    AST.replaceSelBySel('const $_$ = require($_$)', 'import $_$ from $_$');\n    AST.replaceSelBySel('$.extend(true, $_$, $_$)', 'Object.assign($_$, $_$)');\n    AST.replaceSelBySel('$.each($_$, function($_$, $_$) { $_$ } )', '$_$.forEach($_$, $_$)');\n    ```\n\n4. 创建一个AST节点：buildAstByAstStr\n    - 可以通过字符串拼接AST节点的方式构造新的AST节点\n\n    ```javascript\n    const type = 'error';\n    const content = ASTNODE; // 从其他代码中提取出来或者自己构造的ast节点\n    GG.buildAstByAstStr(`\n        Alert.show({\n            type: '${type}',\n            content: '$_$content$_$'\n        })\n    `, {\n        content\n    })\n    ```\n\n5. GG模块其他基本方法\n    - 将AST节点转成字符串 generate(ast)\n    - 获取AST节点的所有父节点 getParentListByAst(path)\n    - 判断一个AST节点是否包含某子节点，子节点用选择器表示 hasChildrenSelector(path, childSelector)\n    - 用一个AST节点替换某个字符串 replaceStrByAst\n    - replaceAstByAst\n    - getPrevAst\n    - getNextAst\n    - insertAstListBefore\n    - insertAstListAfter\n    - removeAst\n    - ......\n    - AST实例上也有部分方法，用法相比GG模块调用少传第一个参数，实际传入的是该实例自身的ast结构\n\n6. 特殊类型AST节点的构造方法\n    - buildObjectProperty\n\n    ```javascript\n    // 为什么不直接用buildAstByAstStr？把一个对象粘贴进astexplore就知道了\n    AST.buildObjectProperty({\n        url: 'getList',\n        type: 'get'\n    })\n    ```\n\n    - appendJsxAttr\n    ```javascript\n    const locaid = '98s8dh3';\n    const params = [ 'a=${aa}', 'b=${{aaa:\"222\",xxx:{ssss:111}}}', 'c=${\"s\"}', 'd=${a+1}','e=${ ss?2:1}' ]\n\n    AST.appendJsxAttr({\n       'attr1: `{${'`'}gostr='$'{gostr};locaid=d${locaid};${params.join('\u0026')}}${'`'}}`, // 模板字符串\n        a: `{a+1}`, // 表达式\n        b: `'a'`, // 字符串\n        c: `{a}` // 变量\n    });\n\n    // 结果：\n    \u003cdiv attr1={`gostr=${gostr};locaid=d98s8dh3;a=${aa}\u0026b=${{aaa:\"222\",xxx{ssss:111}}}\u0026c=${\"s\"}\u0026d=${a+1}\u0026e=${ ss?2:1}}`}a={a+1} b='a' c={a}\u003e\n    \u003c/div\u003e\n    ```\n\n# 使用示例（陆续补充）\n1. 将一段代码最外层所有未export的变量定义都加上export\n``` javascript\nconst GG = require('gogoast');\n\nconst AST = GG.createAstObj(code);     // code是源代码字符串\n\nconst { nodePathList } = AST.getAstsBySelector(`const $_$ = $_$`, true, 'n');   // 匹配到最外层的变量定义\nnodePathList.forEach(p =\u003e {\n    if (p.parent.node.type == 'ExportNamedDeclaration') {    // declarator类型的节点肯定至少存在两级parent，不会报错\n        return;     // 已经export的不处理\n    }\n    GG.replaceAstByAst(p, { type: 'ExportNamedDeclaration', declaration: p.value })\n})\nconsole.log(AST.generate())     // 输出代码\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshuerguo999%2FgogoAST","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshuerguo999%2FgogoAST","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshuerguo999%2FgogoAST/lists"}