@kamaalio/codemod-kit 0.0.15 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -72,6 +72,103 @@ const result = findAndReplace(
72
72
  );
73
73
  ```
74
74
 
75
+ ### `findAndReplaceEdits(content, rule, transformer)`
76
+
77
+ A utility function for finding AST nodes and generating edit operations without committing them.
78
+
79
+ - `content`: An `SgRoot<TypesMap>` object representing the parsed AST.
80
+ - `rule`: A `Rule<TypesMap>` object defining the pattern to search for.
81
+ - `transformer`: A function that takes a matched node and returns an optional string replacement.
82
+
83
+ Returns an array of `Edit` objects that can be committed later using `commitEdits()`.
84
+
85
+ ```typescript
86
+ import { findAndReplaceEdits } from '@kamaalio/codemod-kit';
87
+ import { parseAsync } from '@ast-grep/napi';
88
+
89
+ const code = `
90
+ function oldFunction() {
91
+ return "hello";
92
+ }
93
+ `;
94
+
95
+ const ast = await parseAsync('javascript', code);
96
+ const edits = findAndReplaceEdits(
97
+ ast,
98
+ { pattern: 'function oldFunction() { $$$ }' },
99
+ node => 'function newFunction() { return "hello world"; }',
100
+ );
101
+
102
+ // Commit the edits later
103
+ const result = ast.root().commitEdits(edits);
104
+ ```
105
+
106
+ ### `findAndReplaceConfig(content, lang, config)`
107
+
108
+ A utility function for applying multiple find-and-replace operations sequentially on AST content.
109
+
110
+ - `content`: An `SgRoot<TypesMap>` object representing the parsed AST.
111
+ - `lang`: A `NapiLang` value specifying the language for re-parsing after each transformation.
112
+ - `config`: An array of objects containing `rule` and `transformer` pairs to apply sequentially.
113
+
114
+ Returns the final transformed content as a string after applying all transformations.
115
+
116
+ ```typescript
117
+ import { findAndReplaceConfig } from '@kamaalio/codemod-kit';
118
+ import { parseAsync } from '@ast-grep/napi';
119
+
120
+ const code = `
121
+ function oldFunction() {
122
+ return "hello";
123
+ }
124
+ const value = 42;
125
+ `;
126
+
127
+ const ast = await parseAsync('javascript', code);
128
+ const result = await findAndReplaceConfig(ast, 'javascript', [
129
+ {
130
+ rule: { pattern: 'function oldFunction() { $$$ }' },
131
+ transformer: node => 'function newFunction() { return "hello world"; }',
132
+ },
133
+ {
134
+ rule: { pattern: 'const value = $VAL' },
135
+ transformer: node => 'const value = 100',
136
+ },
137
+ ]);
138
+ ```
139
+
140
+ ### `findAndReplaceConfigEdits(content, lang, config)`
141
+
142
+ A utility function for applying multiple find-and-replace operations sequentially and returning the edit history.
143
+
144
+ - `content`: An `SgRoot<TypesMap>` object representing the parsed AST.
145
+ - `lang`: A `NapiLang` value specifying the language for re-parsing after each transformation.
146
+ - `config`: An array of objects containing `rule` and `transformer` pairs to apply sequentially.
147
+
148
+ Returns an array of objects containing the AST content and edits for each transformation step.
149
+
150
+ ```typescript
151
+ import { findAndReplaceConfigEdits } from '@kamaalio/codemod-kit';
152
+ import { parseAsync } from '@ast-grep/napi';
153
+
154
+ const code = `
155
+ function oldFunction() {
156
+ return "hello";
157
+ }
158
+ `;
159
+
160
+ const ast = await parseAsync('javascript', code);
161
+ const editsHistory = await findAndReplaceConfigEdits(ast, 'javascript', [
162
+ {
163
+ rule: { pattern: 'function oldFunction() { $$$ }' },
164
+ transformer: node => 'function newFunction() { return "hello world"; }',
165
+ },
166
+ ]);
167
+
168
+ // Each element contains { content: SgRoot, edits: Edit[] }
169
+ // Useful for tracking transformation history or debugging
170
+ ```
171
+
75
172
  ### `Codemod`
76
173
 
77
174
  A codemod is defined by the `Codemod` type:
@@ -1,2 +1,2 @@
1
- export { runCodemods, runCodemod, commitEditModifications, findAndReplace } from './utils.js';
1
+ export { runCodemods, runCodemod, commitEditModifications, findAndReplace, findAndReplaceEdits, findAndReplaceConfig, findAndReplaceConfigEdits, } from './utils.js';
2
2
  export type { Codemod, Modifications } from './types.js';
@@ -1,6 +1,7 @@
1
1
  import { type Result } from 'neverthrow';
2
2
  import { type Rule, type Edit, type SgNode, type SgRoot } from '@ast-grep/napi';
3
3
  import type { Kinds, TypesMap } from '@ast-grep/napi/types/staticTypes.js';
4
+ import type { NapiLang } from '@ast-grep/napi/types/lang.js';
4
5
  import type { Codemod, Modifications } from './types.js';
5
6
  import type { Optional } from '../utils/type-utils.js';
6
7
  type RunCodemodHooks<C extends Codemod> = {
@@ -21,6 +22,18 @@ export declare function runCodemod<C extends Codemod>(codemod: C, transformation
21
22
  hasChanges: boolean;
22
23
  content: string;
23
24
  }, Error>>>;
25
+ export declare function findAndReplaceConfigEdits(content: SgRoot<TypesMap>, lang: NapiLang, config: Array<{
26
+ rule: Rule<TypesMap>;
27
+ transformer: (node: SgNode<TypesMap, Kinds<TypesMap>>) => Optional<string>;
28
+ }>): Promise<Array<{
29
+ content: SgRoot<TypesMap>;
30
+ edits: Array<Edit>;
31
+ }>>;
32
+ export declare function findAndReplaceConfig(content: SgRoot<TypesMap>, lang: NapiLang, config: Array<{
33
+ rule: Rule<TypesMap>;
34
+ transformer: (node: SgNode<TypesMap, Kinds<TypesMap>>) => Optional<string>;
35
+ }>): Promise<string>;
36
+ export declare function findAndReplaceEdits(content: SgRoot<TypesMap>, rule: Rule<TypesMap>, transformer: (node: SgNode<TypesMap, Kinds<TypesMap>>) => Optional<string>): Array<Edit>;
24
37
  export declare function findAndReplace(content: SgRoot<TypesMap>, rule: Rule<TypesMap>, transformer: (node: SgNode<TypesMap, Kinds<TypesMap>>) => Optional<string>): string;
25
38
  export declare function commitEditModifications(edits: Array<Edit>, modifications: Modifications): Promise<Modifications>;
26
39
  export {};
package/dist/index.cjs CHANGED
@@ -34,9 +34,12 @@ var __webpack_exports__ = {};
34
34
  __webpack_require__.r(__webpack_exports__);
35
35
  __webpack_require__.d(__webpack_exports__, {
36
36
  commitEditModifications: ()=>commitEditModifications,
37
+ findAndReplaceConfigEdits: ()=>findAndReplaceConfigEdits,
37
38
  runCodemod: ()=>runCodemod,
38
39
  findAndReplace: ()=>findAndReplace,
39
- runCodemods: ()=>runCodemods
40
+ findAndReplaceEdits: ()=>findAndReplaceEdits,
41
+ runCodemods: ()=>runCodemods,
42
+ findAndReplaceConfig: ()=>findAndReplaceConfig
40
43
  });
41
44
  const external_node_path_namespaceObject = require("node:path");
42
45
  var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
@@ -128,15 +131,37 @@ async function runCodemod(codemod, transformationPath, options) {
128
131
  }
129
132
  }));
130
133
  }
131
- function findAndReplace(content, rule, transformer) {
132
- const root = content.root();
133
- const edits = kamaal_namespaceObject.arrays.compactMap(root.findAll({
134
+ async function findAndReplaceConfigEdits(content, lang, config) {
135
+ let currentContent = content;
136
+ const editsAndContent = [];
137
+ for (const { rule, transformer } of config){
138
+ const edits = findAndReplaceEdits(currentContent, rule, transformer);
139
+ const updatedContent = currentContent.root().commitEdits(edits);
140
+ editsAndContent.push({
141
+ content: currentContent,
142
+ edits
143
+ });
144
+ currentContent = await (0, napi_namespaceObject.parseAsync)(lang, updatedContent);
145
+ }
146
+ return editsAndContent;
147
+ }
148
+ async function findAndReplaceConfig(content, lang, config) {
149
+ const edits = await findAndReplaceConfigEdits(content, lang, config);
150
+ return edits.at(-1)?.content.root().text() ?? content.root().text();
151
+ }
152
+ function findAndReplaceEdits(content, rule, transformer) {
153
+ const nodes = content.root().findAll({
134
154
  rule
135
- }), (node)=>{
155
+ });
156
+ return kamaal_namespaceObject.arrays.compactMap(nodes, (node)=>{
136
157
  const transformed = transformer(node);
137
158
  if (null == transformed) return null;
138
159
  return node.replace(transformed);
139
160
  });
161
+ }
162
+ function findAndReplace(content, rule, transformer) {
163
+ const root = content.root();
164
+ const edits = findAndReplaceEdits(content, rule, transformer);
140
165
  return root.commitEdits(edits);
141
166
  }
142
167
  async function commitEditModifications(edits, modifications) {
@@ -174,11 +199,17 @@ function defaultedHooks(hooks) {
174
199
  }
175
200
  exports.commitEditModifications = __webpack_exports__.commitEditModifications;
176
201
  exports.findAndReplace = __webpack_exports__.findAndReplace;
202
+ exports.findAndReplaceConfig = __webpack_exports__.findAndReplaceConfig;
203
+ exports.findAndReplaceConfigEdits = __webpack_exports__.findAndReplaceConfigEdits;
204
+ exports.findAndReplaceEdits = __webpack_exports__.findAndReplaceEdits;
177
205
  exports.runCodemod = __webpack_exports__.runCodemod;
178
206
  exports.runCodemods = __webpack_exports__.runCodemods;
179
207
  for(var __webpack_i__ in __webpack_exports__)if (-1 === [
180
208
  "commitEditModifications",
181
209
  "findAndReplace",
210
+ "findAndReplaceConfig",
211
+ "findAndReplaceConfigEdits",
212
+ "findAndReplaceEdits",
182
213
  "runCodemod",
183
214
  "runCodemods"
184
215
  ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { runCodemods, runCodemod, commitEditModifications, findAndReplace, type Codemod, type Modifications, } from './codemods/index.js';
1
+ export { runCodemods, runCodemod, commitEditModifications, findAndReplace, findAndReplaceEdits, findAndReplaceConfig, findAndReplaceConfigEdits, type Codemod, type Modifications, } from './codemods/index.js';
package/dist/index.js CHANGED
@@ -85,15 +85,37 @@ async function runCodemod(codemod, transformationPath, options) {
85
85
  }
86
86
  }));
87
87
  }
88
- function findAndReplace(content, rule, transformer) {
89
- const root = content.root();
90
- const edits = arrays.compactMap(root.findAll({
88
+ async function findAndReplaceConfigEdits(content, lang, config) {
89
+ let currentContent = content;
90
+ const editsAndContent = [];
91
+ for (const { rule, transformer } of config){
92
+ const edits = findAndReplaceEdits(currentContent, rule, transformer);
93
+ const updatedContent = currentContent.root().commitEdits(edits);
94
+ editsAndContent.push({
95
+ content: currentContent,
96
+ edits
97
+ });
98
+ currentContent = await parseAsync(lang, updatedContent);
99
+ }
100
+ return editsAndContent;
101
+ }
102
+ async function findAndReplaceConfig(content, lang, config) {
103
+ const edits = await findAndReplaceConfigEdits(content, lang, config);
104
+ return edits.at(-1)?.content.root().text() ?? content.root().text();
105
+ }
106
+ function findAndReplaceEdits(content, rule, transformer) {
107
+ const nodes = content.root().findAll({
91
108
  rule
92
- }), (node)=>{
109
+ });
110
+ return arrays.compactMap(nodes, (node)=>{
93
111
  const transformed = transformer(node);
94
112
  if (null == transformed) return null;
95
113
  return node.replace(transformed);
96
114
  });
115
+ }
116
+ function findAndReplace(content, rule, transformer) {
117
+ const root = content.root();
118
+ const edits = findAndReplaceEdits(content, rule, transformer);
97
119
  return root.commitEdits(edits);
98
120
  }
99
121
  async function commitEditModifications(edits, modifications) {
@@ -129,4 +151,4 @@ function defaultedHooks(hooks) {
129
151
  preCodemodRun
130
152
  };
131
153
  }
132
- export { commitEditModifications, findAndReplace, runCodemod, runCodemods };
154
+ export { commitEditModifications, findAndReplace, findAndReplaceConfig, findAndReplaceConfigEdits, findAndReplaceEdits, runCodemod, runCodemods };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kamaalio/codemod-kit",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "type": "module",
5
5
  "author": "Kamaal Farah",
6
6
  "license": "MIT",