jscodeshift draft ================= [Demo 1: jscodeshift][1] [1]: https://astexplorer.net/#/gist/e3f97665181bba0c63cfe93a9e6ef9d2/3378c38de4f740071af4f9329136f5a422c3b914 "Link to the opening jscodeshift demo" Intro ----- It's basically search and replace at the syntax level instead of at the source text level like below. ```sh find * -name '\*.{js,jsx}' | \ xargs -n1 sed -i 's/regexp/replacement/' ``` Use Cases --------- - Reduce coding patterns in your codebase - Change codebase as domain term changes - Upgrade codebase as you learn more - Provide codemods as your API upgrades Search ------ Use [`` .find(astNodeType, filter = undefined) ``][2] to search. ### Find by astNodeType ### [Demo 2: Find by astNodeType][3] `` astNodeType `` can be any `` namedTypes ``. E.g. [j.ObjectExpression][4] See files in [the def directory][5] for more AST node type definitions. Note: AST node types always begin with an upper case letter. E.g. XMLDefaultDeclaration. ### Find by filter ### [Demo 3: Find by filter][6] `` filter `` is an object to filter the found result recursively. E.g. `` { value: { type:"Literal", raw: "0x62" } } `` [2]: https://github.com/facebook/jscodeshift/blob/2bed715/src/collections/Node.js#L27-L59 "Source code of the .find method" [3]: https://astexplorer.net/#/gist/aedeb5ee1568081622f7c28f6368836a/b30bfb5d905c5b024a7de215e6865dce913dbc70 "Link to the find by astNodeType demo" [4]: https://github.com/benjamn/ast-types/blob/bb324df/def/core.js#L205 "Source code of the ObjectExpression definition" [5]: https://github.com/benjamn/ast-types/tree/bb324df/def "the def directory" [6]: https://astexplorer.net/#/gist/5c7023307e1343232636752c849db2d4/9a7b7097d7a1e80a1f8c18ceeb69170cf1645254 "Link to the find by filter demo" Replace ------- Use `` builders `` to build replacement nodes. Note: buidlders always begin with a lower case leter. E.g. xmlDefaultDeclaration. ### Modify nodes ### [Demo 4: Modify nodes][7] Modify nodes in the path object directly to replace it. ### insertBefore ### [Demo 5: insertBefore][8] You can also use `` insertBefore `` to insert codes before the current scope. ### replaceWith ### [Demo 6: replaceWith][9] You can also use `` replaceWith `` to replace the whole scope. ### Tagged template literal ### [Demo 7: Use tagged template literal for replacement][10] You can also use tagged template literals to build replacement nodes. [7]: https://astexplorer.net/#/gist/d7bd91eb683b775f887d5d9370d30a6b/28572db283a9c1a38459738da9a42a8731af2abc "Link to the modify nodes demo" [8]: https://astexplorer.net/#/gist/651c07d99225457d757602181c8cb20c/3ee6d1a8a70d4d0a1ec7c979c8715460ba3d4729 "Link to the insert before demo" [9]: https://astexplorer.net/#/gist/c29390b2dbeb7137330500abba1b60d0/a2a46e884c5b4a28b56974465d097dc530da8873 "Link to the replace with demo" [10]: https://astexplorer.net/#/gist/4140f16980833c2365653bd836aef933/0a69f5e9f84bf6347cd6bf8ab509744ab0144147 "Link to the use tagged template literal for replacement demo" Test ---- ```sh git clone https://github.com/cpojer/js-codemod /tmp/js-codemod cd /tmp/js-codemod yarn yarn test ``` jscodeshift provides modules that integrates with Jest for unit testing. Please see [the official manual][11] for how to do utilise them. In terms of reference, I suggest look into the [cpojer/js-codemod][12] for what unit tests for codemodes look like. Note: Writing codemod is a very good way for practicing TDD. Note: If you write unit test for js but you are not using Jest, you are wasting your time. [11]: https://github.com/facebook/jscodeshift#unit-testing "Link to the official jscodeshift doc about unit testing" [12]: https://github.com/cpojer/js-codemod "Link to the cpojer/js-codemod repo" Extend the Collection class --------------------------- ### Use .registerMethods() ### [Demo 8: Use .registerMethods()][13] Use `` .registerMethods `` to add more methods to the Collection class. ### Use .registerMethods() for AST node type ### [Demo 9: Use .registerMethods() for AST node type][14] It's also possible to add methods to the Collection of a certain AST node type. [13]: https://astexplorer.net/#/gist/2be9309bd7b0a64819ac0fb3aad4808f/2a821248e04b239d33767630bab5073321000eb8 "Link to the use .registerMethods demo" [14]: https://astexplorer.net/#/gist/27e0e6e24c1240570df8fbe63fe7ed16/d319217dddd4b55bd86580b04fef0611a286b44e "Link to the use .registerMethods for ast node type demo" API --- ### Collection ### - [Collection.registerMethods()][15] - [Collection.prototype.at()][16] - [Collection.prototype.filter(callback)][17] - [Collection.prototype.forEach(callback)][18] - [Collection.prototype.get()][19] - [Collection.prototype.getType()][20] - [Collection.prototype.isOfType()][21] - [Collection.prototype.length][22] - [Collection.prototype.map(callback, type)][23] - [Collection.prototype.nodes()][24] - [Collection.prototype.size()][25] [15]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/Collection.js#L313-L361 "Link to the source of Collection.registerMethods" [16]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/Collection.js#L161-L180 "Link to the source of Collection.prototype.at" [17]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/Collection.js#L57-L66 "Link to the source of Collection.prototype.filter" [18]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/Collection.js#L68-L79 "Link to the source of Collection.prototype.forEach" [19]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/Collection.js#L182-L196 "Link to the source of Collection.prototype.get" [20]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/Collection.js#L198-L206 "Link to the source of Collection.prototype.getType" [21]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/Collection.js#L208-L216 "Link to the source of Collection.prototype.isOfType" [22]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/Collection.js#L121-L128 "Link to the source of Collection.prototype.length" [23]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/Collection.js#L81-L110 "Link to the source of Collection.prototype.map" [24]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/Collection.js#L130-L137 "Link to the source of Collection.prototype.nodes" [25]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/Collection.js#L112-L119 "Link to the source of Collection.prototype.size" ### JSXElement ### - [Collection.prototype.childElements()][26] - [Collection.prototype.childNodes()][27] - [Collection.prototype.findJSXElements()][28] - [Collection.prototype.findJSXElementsByModuleName()][29] - [Collection.prototype.getRootName()][30] - [Collection.prototype.hasAttributes()][31] - [Collection.prototype.hasChildren()][32] [26]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/JSXElement.js#L150-L168 "Link to the source of Collection.prototype.childElements" [27]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/JSXElement.js#L133-L148 "Link to the source of Collection.prototype.childNodes" [28]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/JSXElement.js#L31-L40 "Link to the source of Collection.prototype.findJSXElements" [29]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/JSXElement.js#L42-L68 "Link to the source of Collection.prototype.findJSXElementsByModuleName" [30]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/JSXElement.js#L171-L185 "Link to the source of Collection.prototype.getRootName" [31]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/JSXElement.js#L73-L109 "Link to the source of Collection.prototype.hasAttributes" [32]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/JSXElement.js#L111-L125 "Link to the source of Collection.prototype.hasChildren" ### Node ### - [Collection.prototype.closest()][33] - [Collection.prototype.closestScope()][34] - [Collection.prototype.find()][35] - [Collection.prototype.getVariableDeclarators()][36] - [Collection.prototype.insertAfter()][37] - [Collection.prototype.insertBefore()][38] - [Collection.prototype.replaceWith()][39] [33]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/Node.js#L71-L92 "Link to the source of Collection.prototype.closest" [34]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/Node.js#L61-L69 "Link to the source of Collection.prototype.closestScope" [35]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/Node.js#L27-L59 "Link to the source of Collection.prototype.find" [36]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/Node.js#L94-L123 "Link to the source of Collection.prototype.getVariableDeclarators" [37]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/Node.js#L163-L175 "Link to the source of Collection.prototype.insertAfter" [38]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/Node.js#L149-L161 "Link to the source of Collection.prototype.insertBefore" [39]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/Node.js#L133-L147 "Link to the source of Collection.prototype.replaceWith" ### VariableDeclarator ### - [Collection.prototype.findVariableDeclarators()][40] - [Collection.prototype.renameTo()][41] - [Collection.prototype.requiresModule()][42] [40]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/VariableDeclarator.js#L25-L39 "Link to the source of Collection.prototype.findVariableDeclarators" [41]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/VariableDeclarator.js#L73-L133 "Link to the source of Collection.prototype.renameTo" [42]: https://github.com/facebook/jscodeshift/blob/2bed715ef9ac849d5aac5c2f49427c37adc0b11a/src/collections/VariableDeclarator.js#L42-L66 "Link to the source of Collection.prototype.requiresModule" Reference --------- - [Effective JavaScript Codemods][15] - [facebook/jscodeshift Github][16] - [benjamn/recast Github][17] - [benjamn/ast-types Github][18] - [cpojer/js-codemod Github][19] - [jhgg/js-transforms Github][20] [15]: https://medium.com/@cpojer/effective-javascript-codemods-5a6686bb46fb "Link to the Effective JavaScript Codemods article by Christoph Pojer" [16]: https://github.com/facebook/jscodeshift "Link to the jscodeshift Github repository" [17]: https://github.com/benjamn/recast "Link to the recast Github repository" [18]: https://github.com/benjamn/ast-types "Link to the ast-types Github repository" [19]: https://github.com/cpojer/js-codemod "Link to the js-codemod Github repository" [20]: https://github.com/jhgg/js-transforms "Link to the js-transforms Github repository" jscodeshift In Depth ==================== AST Node Type ------------- AST node type is the same as `` require("ast-types").namedTypes ``. recast exposes them as `` require("recast").types.namedTypes `` then jscodeshift exposes them again under `` api.jscodeshift ``. This is why `` j.ObjectExpression `` and other AST node types exist.