@kamaalio/codemod-kit 0.0.26 → 0.0.28

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.
@@ -1,11 +1,21 @@
1
1
  import type { Edit, Rule, SgNode, SgRoot } from '@ast-grep/napi';
2
2
  import type { NapiLang } from '@ast-grep/napi/types/lang.js';
3
3
  import type { Kinds, TypesMap } from '@ast-grep/napi/types/staticTypes.js';
4
- import type { Optional } from '../utils/type-utils.js';
4
+ import type { types } from '@kamaalio/kamaal';
5
+ export type RunCodemodOkResult = {
6
+ hasChanges: boolean;
7
+ content: string;
8
+ fullPath: string;
9
+ root: string;
10
+ };
5
11
  export type Codemod = {
6
12
  name: string;
7
13
  languages: Set<NapiLang> | Array<NapiLang>;
8
- transformer: (content: string, filename?: Optional<string>) => Promise<string>;
14
+ transformer: (content: string, filename?: types.Optional<string>) => Promise<string>;
15
+ postTransform?: (rootPath: {
16
+ root: string;
17
+ results: Array<RunCodemodOkResult>;
18
+ }) => Promise<void>;
9
19
  };
10
20
  export type ModificationsReport = {
11
21
  changesApplied: number;
@@ -14,10 +24,10 @@ export type Modifications = {
14
24
  ast: SgRoot<TypesMap>;
15
25
  report: ModificationsReport;
16
26
  lang: NapiLang;
17
- filename: Optional<string>;
27
+ filename: types.Optional<string>;
18
28
  history: Array<SgRoot<TypesMap>>;
19
29
  };
20
30
  export type FindAndReplaceConfig = {
21
31
  rule: Rule<TypesMap>;
22
- transformer: ((node: SgNode<TypesMap, Kinds<TypesMap>>, rule: Rule<TypesMap>) => Optional<Edit | string> | Array<Edit | string>) | string;
32
+ transformer: ((node: SgNode<TypesMap, Kinds<TypesMap>>, rule: Rule<TypesMap>) => types.Optional<Edit | string> | Array<Edit | string>) | string;
23
33
  };
@@ -3,7 +3,7 @@ import { type Edit, type SgRoot, type SgNode } from '@ast-grep/napi';
3
3
  import type { Kinds, TypesMap } from '@ast-grep/napi/types/staticTypes.js';
4
4
  import type { NapiLang } from '@ast-grep/napi/types/lang.js';
5
5
  import { type types } from '@kamaalio/kamaal';
6
- import type { Codemod, FindAndReplaceConfig, Modifications } from './types.js';
6
+ import type { Codemod, FindAndReplaceConfig, Modifications, RunCodemodOkResult } from './types.js';
7
7
  type RunCodemodHooks<C extends Codemod> = {
8
8
  targetFiltering?: (filepath: string, codemod: C) => boolean;
9
9
  preCodemodRun?: (codemod: C) => Promise<void>;
@@ -13,15 +13,11 @@ type RunCodemodOptions<C extends Codemod> = {
13
13
  hooks?: RunCodemodHooks<C>;
14
14
  log?: boolean;
15
15
  dry?: boolean;
16
+ rootPaths?: Array<string>;
16
17
  };
17
- export declare function runCodemods<C extends Codemod>(codemods: Array<C>, transformationPath: string, options?: RunCodemodOptions<C>): Promise<Record<string, Array<Result<{
18
- hasChanges: boolean;
19
- content: string;
20
- }, Error>>>>;
21
- export declare function runCodemod<C extends Codemod>(codemod: C, transformationPath: string, options?: RunCodemodOptions<C>): Promise<Array<Result<{
22
- hasChanges: boolean;
23
- content: string;
24
- }, Error>>>;
18
+ type RunCodemodResult = Result<RunCodemodOkResult, Error>;
19
+ export declare function runCodemods<C extends Codemod>(codemods: Array<C>, transformationPath: string, options?: RunCodemodOptions<C>): Promise<Record<string, Array<RunCodemodResult>>>;
20
+ export declare function runCodemod<C extends Codemod>(codemod: C, transformationPath: string, options?: RunCodemodOptions<C>): Promise<Array<RunCodemodResult>>;
25
21
  export declare function traverseUp(node: SgNode<TypesMap, Kinds<TypesMap>>, until: (node: SgNode<TypesMap, Kinds<TypesMap>>) => boolean): types.Optional<SgNode<TypesMap, Kinds<TypesMap>>>;
26
22
  export declare function findAndReplaceConfigModifications(modifications: Modifications, config: Array<FindAndReplaceConfig>): Promise<Modifications>;
27
23
  export declare function findAndReplaceConfig(content: SgRoot<TypesMap>, lang: NapiLang, config: Array<FindAndReplaceConfig>): Promise<string>;
package/dist/index.cjs CHANGED
@@ -83,13 +83,26 @@ function getCollectionCount(collection) {
83
83
  function collectionIsEmpty(collection) {
84
84
  return 0 === getCollectionCount(collection);
85
85
  }
86
+ function groupBy(array, key) {
87
+ const arrayCopy = [
88
+ ...array
89
+ ];
90
+ return arrayCopy.reduce((acc, current)=>{
91
+ const keyValue = String(current[key]);
92
+ if (null == acc[keyValue]) acc[keyValue] = [
93
+ current
94
+ ];
95
+ else acc[keyValue].push(current);
96
+ return acc;
97
+ }, {});
98
+ }
86
99
  async function runCodemods(codemods, transformationPath, options) {
87
100
  const results = {};
88
101
  for (const codemod of codemods)results[codemod.name] = await runCodemod(codemod, transformationPath, options);
89
102
  return results;
90
103
  }
91
104
  async function runCodemod(codemod, transformationPath, options) {
92
- const { hooks, log: enableLogging, dry: runInDryMode } = defaultedOptions(options);
105
+ const { hooks, log: enableLogging, dry: runInDryMode, rootPaths } = defaultedOptions(options);
93
106
  await hooks.preCodemodRun(codemod);
94
107
  const globItems = await external_fast_glob_default().glob([
95
108
  '**/*'
@@ -107,7 +120,7 @@ async function runCodemod(codemod, transformationPath, options) {
107
120
  });
108
121
  if (0 === targets.length) return [];
109
122
  if (enableLogging) console.log(`\u{1F9C9} '${codemod.name}' targeting ${targets.length} ${1 === targets.length ? 'file' : 'files'} to transform, chill and grab some mat\xe9`);
110
- return Promise.all(targets.map(async (filepath)=>{
123
+ const results = await Promise.all(targets.map(async (filepath)=>{
111
124
  const fullPath = external_node_path_default().join(transformationPath, filepath);
112
125
  try {
113
126
  const content = await promises_default().readFile(fullPath, {
@@ -124,13 +137,26 @@ async function runCodemod(codemod, transformationPath, options) {
124
137
  }
125
138
  return (0, external_neverthrow_namespaceObject.ok)({
126
139
  hasChanges,
127
- content: modifiedContent
140
+ content: modifiedContent,
141
+ fullPath,
142
+ root: filepath.split('/')[0]
128
143
  });
129
144
  } catch (error) {
130
145
  if (enableLogging) console.error(`\u{274C} '${codemod.name}' failed to parse file`, filepath, error);
131
146
  return (0, external_neverthrow_namespaceObject.err)(error);
132
147
  }
133
148
  }));
149
+ const successes = kamaal_namespaceObject.arrays.compactMap(results, (result)=>{
150
+ if (result.isErr()) return null;
151
+ return result.value;
152
+ });
153
+ const successesGroupedByRoot = groupBy(successes, 'root');
154
+ const rootPathsWithResults = rootPaths.map((root)=>({
155
+ root,
156
+ results: successesGroupedByRoot[root] ?? []
157
+ }));
158
+ await Promise.all(rootPathsWithResults.map((r)=>(codemod.postTransform ?? (async ()=>{}))(r)));
159
+ return results;
134
160
  }
135
161
  function traverseUp(node, until) {
136
162
  let current = node.parent();
@@ -262,7 +288,8 @@ function defaultedOptions(options) {
262
288
  return {
263
289
  hooks: defaultedHooks(options?.hooks),
264
290
  log: options?.log ?? true,
265
- dry: options?.dry ?? false
291
+ dry: options?.dry ?? false,
292
+ rootPaths: options?.rootPaths ?? []
266
293
  };
267
294
  }
268
295
  function defaultedHooks(hooks) {
package/dist/index.js CHANGED
@@ -36,13 +36,26 @@ function getCollectionCount(collection) {
36
36
  function collectionIsEmpty(collection) {
37
37
  return 0 === getCollectionCount(collection);
38
38
  }
39
+ function groupBy(array, key) {
40
+ const arrayCopy = [
41
+ ...array
42
+ ];
43
+ return arrayCopy.reduce((acc, current)=>{
44
+ const keyValue = String(current[key]);
45
+ if (null == acc[keyValue]) acc[keyValue] = [
46
+ current
47
+ ];
48
+ else acc[keyValue].push(current);
49
+ return acc;
50
+ }, {});
51
+ }
39
52
  async function runCodemods(codemods, transformationPath, options) {
40
53
  const results = {};
41
54
  for (const codemod of codemods)results[codemod.name] = await runCodemod(codemod, transformationPath, options);
42
55
  return results;
43
56
  }
44
57
  async function runCodemod(codemod, transformationPath, options) {
45
- const { hooks, log: enableLogging, dry: runInDryMode } = defaultedOptions(options);
58
+ const { hooks, log: enableLogging, dry: runInDryMode, rootPaths } = defaultedOptions(options);
46
59
  await hooks.preCodemodRun(codemod);
47
60
  const globItems = await fast_glob.glob([
48
61
  '**/*'
@@ -60,7 +73,7 @@ async function runCodemod(codemod, transformationPath, options) {
60
73
  });
61
74
  if (0 === targets.length) return [];
62
75
  if (enableLogging) console.log(`\u{1F9C9} '${codemod.name}' targeting ${targets.length} ${1 === targets.length ? 'file' : 'files'} to transform, chill and grab some mat\xe9`);
63
- return Promise.all(targets.map(async (filepath)=>{
76
+ const results = await Promise.all(targets.map(async (filepath)=>{
64
77
  const fullPath = node_path.join(transformationPath, filepath);
65
78
  try {
66
79
  const content = await promises.readFile(fullPath, {
@@ -77,13 +90,26 @@ async function runCodemod(codemod, transformationPath, options) {
77
90
  }
78
91
  return ok({
79
92
  hasChanges,
80
- content: modifiedContent
93
+ content: modifiedContent,
94
+ fullPath,
95
+ root: filepath.split('/')[0]
81
96
  });
82
97
  } catch (error) {
83
98
  if (enableLogging) console.error(`\u{274C} '${codemod.name}' failed to parse file`, filepath, error);
84
99
  return err(error);
85
100
  }
86
101
  }));
102
+ const successes = arrays.compactMap(results, (result)=>{
103
+ if (result.isErr()) return null;
104
+ return result.value;
105
+ });
106
+ const successesGroupedByRoot = groupBy(successes, 'root');
107
+ const rootPathsWithResults = rootPaths.map((root)=>({
108
+ root,
109
+ results: successesGroupedByRoot[root] ?? []
110
+ }));
111
+ await Promise.all(rootPathsWithResults.map((r)=>(codemod.postTransform ?? (async ()=>{}))(r)));
112
+ return results;
87
113
  }
88
114
  function traverseUp(node, until) {
89
115
  let current = node.parent();
@@ -215,7 +241,8 @@ function defaultedOptions(options) {
215
241
  return {
216
242
  hooks: defaultedHooks(options?.hooks),
217
243
  log: options?.log ?? true,
218
- dry: options?.dry ?? false
244
+ dry: options?.dry ?? false,
245
+ rootPaths: options?.rootPaths ?? []
219
246
  };
220
247
  }
221
248
  function defaultedHooks(hooks) {
@@ -0,0 +1 @@
1
+ export declare function groupBy<T, K extends keyof T>(array: Array<T>, key: K): Record<string, Array<T>>;
@@ -1,4 +1,3 @@
1
- export type Optional<T> = T | undefined | null;
2
1
  export type ReplaceObjectProperty<A extends Record<string, unknown>, K extends keyof A, B> = Omit<A, K> & {
3
2
  [P in K]: B;
4
3
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kamaalio/codemod-kit",
3
- "version": "0.0.26",
3
+ "version": "0.0.28",
4
4
  "type": "module",
5
5
  "author": "Kamaal Farah",
6
6
  "license": "MIT",