ts-repo-utils 5.0.2 → 5.2.0

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.
Files changed (70) hide show
  1. package/dist/functions/assert-ext.d.mts +12 -7
  2. package/dist/functions/assert-ext.d.mts.map +1 -1
  3. package/dist/functions/assert-ext.mjs +17 -12
  4. package/dist/functions/assert-ext.mjs.map +1 -1
  5. package/dist/functions/assert-path-exists.d.mts +2 -0
  6. package/dist/functions/assert-path-exists.d.mts.map +1 -1
  7. package/dist/functions/assert-path-exists.mjs +2 -0
  8. package/dist/functions/assert-path-exists.mjs.map +1 -1
  9. package/dist/functions/assert-repo-is-clean.d.mts +3 -2
  10. package/dist/functions/assert-repo-is-clean.d.mts.map +1 -1
  11. package/dist/functions/assert-repo-is-clean.mjs +4 -2
  12. package/dist/functions/assert-repo-is-clean.mjs.map +1 -1
  13. package/dist/functions/diff.d.mts +2 -6
  14. package/dist/functions/diff.d.mts.map +1 -1
  15. package/dist/functions/diff.mjs +2 -6
  16. package/dist/functions/diff.mjs.map +1 -1
  17. package/dist/functions/exec-async.d.mts +1 -0
  18. package/dist/functions/exec-async.d.mts.map +1 -1
  19. package/dist/functions/exec-async.mjs +2 -0
  20. package/dist/functions/exec-async.mjs.map +1 -1
  21. package/dist/functions/format.d.mts +10 -3
  22. package/dist/functions/format.d.mts.map +1 -1
  23. package/dist/functions/format.mjs +10 -3
  24. package/dist/functions/format.mjs.map +1 -1
  25. package/dist/functions/gen-index.d.mts +19 -6
  26. package/dist/functions/gen-index.d.mts.map +1 -1
  27. package/dist/functions/gen-index.mjs +63 -42
  28. package/dist/functions/gen-index.mjs.map +1 -1
  29. package/dist/functions/should-run.d.mts +8 -5
  30. package/dist/functions/should-run.d.mts.map +1 -1
  31. package/dist/functions/should-run.mjs +8 -5
  32. package/dist/functions/should-run.mjs.map +1 -1
  33. package/dist/functions/workspace-utils/execute-parallel.d.mts +11 -6
  34. package/dist/functions/workspace-utils/execute-parallel.d.mts.map +1 -1
  35. package/dist/functions/workspace-utils/execute-parallel.mjs +50 -38
  36. package/dist/functions/workspace-utils/execute-parallel.mjs.map +1 -1
  37. package/dist/functions/workspace-utils/get-workspace-packages.d.mts +7 -4
  38. package/dist/functions/workspace-utils/get-workspace-packages.d.mts.map +1 -1
  39. package/dist/functions/workspace-utils/get-workspace-packages.mjs +7 -4
  40. package/dist/functions/workspace-utils/get-workspace-packages.mjs.map +1 -1
  41. package/dist/functions/workspace-utils/run-cmd-in-parallel.d.mts +7 -3
  42. package/dist/functions/workspace-utils/run-cmd-in-parallel.d.mts.map +1 -1
  43. package/dist/functions/workspace-utils/run-cmd-in-parallel.mjs +9 -5
  44. package/dist/functions/workspace-utils/run-cmd-in-parallel.mjs.map +1 -1
  45. package/dist/functions/workspace-utils/run-cmd-in-stages.d.mts +10 -6
  46. package/dist/functions/workspace-utils/run-cmd-in-stages.d.mts.map +1 -1
  47. package/dist/functions/workspace-utils/run-cmd-in-stages.mjs +12 -8
  48. package/dist/functions/workspace-utils/run-cmd-in-stages.mjs.map +1 -1
  49. package/dist/globals.d.mts +1 -0
  50. package/dist/node-global.d.mts.map +1 -1
  51. package/dist/node-global.mjs +1 -0
  52. package/dist/node-global.mjs.map +1 -1
  53. package/package.json +19 -8
  54. package/src/functions/assert-ext.mts +38 -19
  55. package/src/functions/assert-path-exists.mts +2 -0
  56. package/src/functions/assert-repo-is-clean.mts +4 -2
  57. package/src/functions/diff.mts +2 -6
  58. package/src/functions/diff.test.mts +31 -24
  59. package/src/functions/exec-async.mts +2 -0
  60. package/src/functions/format.mts +10 -3
  61. package/src/functions/format.test.mts +8 -4
  62. package/src/functions/gen-index.mts +146 -62
  63. package/src/functions/should-run.mts +8 -5
  64. package/src/functions/workspace-utils/execute-parallel.mts +52 -39
  65. package/src/functions/workspace-utils/get-workspace-packages.mts +7 -4
  66. package/src/functions/workspace-utils/run-cmd-in-parallel.mts +9 -5
  67. package/src/functions/workspace-utils/run-cmd-in-stages.mts +12 -8
  68. package/src/globals.d.mts +1 -0
  69. package/src/node-global.mts +3 -0
  70. package/src/functions/gen-index.test.mts +0 -18
@@ -1,16 +1,33 @@
1
1
  import micromatch from 'micromatch';
2
- import { Arr, ISet, isString, Result } from 'ts-data-forge';
2
+ import { Arr, ISet, isString, pipe, Result } from 'ts-data-forge';
3
3
  import '../node-global.mjs';
4
4
  import { assertPathExists } from './assert-path-exists.mjs';
5
5
 
6
- /**
7
- * Configuration for index file generation.
8
- */
6
+ /** Configuration for index file generation. */
9
7
  export type GenIndexConfig = DeepReadonly<{
10
8
  /** Target directories to generate index files for (string or array of strings) */
11
9
  targetDirectory: string | readonly string[];
12
10
 
13
- /** Glob patterns of files to exclude from exports (default: excludes `'**\/*.{test,spec}.?(c|m)[jt]s?(x)'`) */
11
+ /**
12
+ * Glob patterns of files or predicate function to exclude from exports
13
+ * (default: excludes `'**\/*.{test,spec}.?(c|m)[jt]s?(x)'`)
14
+ */
15
+ exclude?:
16
+ | readonly string[]
17
+ | ((
18
+ args: Readonly<{
19
+ absolutePath: string;
20
+ relativePath: string;
21
+ fileName: string;
22
+ }>,
23
+ ) => boolean);
24
+
25
+ /**
26
+ * Glob patterns of files or predicate function to exclude from exports
27
+ * (default: excludes `'**\/*.{test,spec}.?(c|m)[jt]s?(x)'`)
28
+ *
29
+ * @deprecated Use `exclude` instead.
30
+ */
14
31
  excludePatterns?: readonly string[];
15
32
 
16
33
  /** File extensions of source files to export (default: ['.ts', '.tsx']) */
@@ -20,9 +37,9 @@ export type GenIndexConfig = DeepReadonly<{
20
37
  indexExtension?: `.${string}`;
21
38
 
22
39
  /** File extension to use in export statements (default: '.js') */
23
- exportExtension?: `.${string}`;
40
+ exportExtension?: `.${string}` | 'none';
24
41
 
25
- /** Command to run for formatting generated files (default: 'npm run fmt') */
42
+ /** Command to run for formatting generated files (optional) */
26
43
  formatCommand?: string;
27
44
 
28
45
  /** Whether to suppress output during execution (default: false) */
@@ -32,15 +49,22 @@ export type GenIndexConfig = DeepReadonly<{
32
49
  type GenIndexConfigInternal = DeepReadonly<{
33
50
  formatCommand: string | undefined;
34
51
  targetDirectory: ISet<string>;
35
- excludePatterns: ISet<string>;
52
+ exclude: (
53
+ args: Readonly<{
54
+ absolutePath: string;
55
+ relativePath: string;
56
+ fileName: string;
57
+ }>,
58
+ ) => boolean;
36
59
  sourceExtensions: ISet<`.${string}`>;
37
60
  indexExtension: `.${string}`;
38
- exportExtension: `.${string}`;
61
+ exportExtension: `.${string}` | 'none';
39
62
  silent: boolean;
40
63
  }>;
41
64
 
42
65
  /**
43
66
  * Generates index.ts files recursively in `config.targetDirectory`.
67
+ *
44
68
  * @param config - Configuration for index file generation
45
69
  * @throws Error if any step fails.
46
70
  */
@@ -65,7 +89,7 @@ export const genIndex = async (config: GenIndexConfig): Promise<void> => {
65
89
  }
66
90
 
67
91
  // Step 2: Generate index files
68
- echo('2. Generating index files...');
92
+ echo('Generating index files...');
69
93
  for (const dir of targetDirs) {
70
94
  const resolvedDir = path.resolve(dir);
71
95
  // eslint-disable-next-line no-await-in-loop
@@ -75,7 +99,7 @@ export const genIndex = async (config: GenIndexConfig): Promise<void> => {
75
99
 
76
100
  // Step 3: Format generated files
77
101
  if (filledConfig.formatCommand !== undefined) {
78
- echo('3. Formatting generated files...');
102
+ echo('Formatting generated files...');
79
103
  const fmtResult = await $(filledConfig.formatCommand, {
80
104
  silent: filledConfig.silent,
81
105
  });
@@ -92,17 +116,6 @@ export const genIndex = async (config: GenIndexConfig): Promise<void> => {
92
116
  }
93
117
  };
94
118
 
95
- /**
96
- * Fills the configuration with default values.
97
- * Default values:
98
- * - sourceExtensions: ['.ts']
99
- * - indexExtension: '.ts'
100
- * - exportExtension: '.js'
101
- * - excludePatterns: ['**\/*.{test,spec}.?(c|m)[jt]s?(x)']
102
- * - silent: false
103
- * @param config - The input configuration object.
104
- * @returns The configuration object with all required properties filled with defaults.
105
- */
106
119
  const fillConfig = (config: GenIndexConfig): GenIndexConfigInternal => {
107
120
  const sourceExtensions = config.sourceExtensions ?? ['.ts'];
108
121
  const exportExtension = config.exportExtension ?? '.js'; // For ESM imports, .mts resolves to .mjs
@@ -114,14 +127,41 @@ const fillConfig = (config: GenIndexConfig): GenIndexConfigInternal => {
114
127
  ? [config.targetDirectory]
115
128
  : config.targetDirectory,
116
129
  ),
117
- excludePatterns: ISet.create(
118
- Arr.generate(function* () {
119
- if (config.excludePatterns !== undefined) {
120
- yield* config.excludePatterns;
121
- }
122
- yield '**/*.{test,spec}.?(c|m)[jt]s?(x)';
123
- }),
124
- ),
130
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
131
+ exclude: pipe(config.exclude ?? config.excludePatterns).map((exclude) =>
132
+ typeof exclude === 'function'
133
+ ? exclude
134
+ : pipe(
135
+ ISet.create<string>(
136
+ Arr.generate(function* () {
137
+ if (exclude !== undefined && Array.isArray(exclude)) {
138
+ yield* exclude;
139
+ }
140
+ yield '**/*.{test,spec}.?(c|m)[jt]s?(x)';
141
+ }),
142
+ ),
143
+ ).map(
144
+ (set) =>
145
+ ({
146
+ relativePath,
147
+ fileName,
148
+ }: Readonly<{
149
+ absolutePath: string;
150
+ relativePath: string;
151
+ fileName: string;
152
+ }>) => {
153
+ for (const pattern of set.values()) {
154
+ if (
155
+ micromatch.isMatch(relativePath, pattern) ||
156
+ micromatch.isMatch(fileName, pattern)
157
+ ) {
158
+ return true;
159
+ }
160
+ }
161
+ return false;
162
+ },
163
+ ).value,
164
+ ).value,
125
165
  sourceExtensions: ISet.create(sourceExtensions),
126
166
  indexExtension: config.indexExtension ?? '.ts',
127
167
  exportExtension,
@@ -130,11 +170,13 @@ const fillConfig = (config: GenIndexConfig): GenIndexConfigInternal => {
130
170
  };
131
171
 
132
172
  /**
133
- * Generates an index.ts file for the given directory.
134
- * Recursively calls itself for subdirectories.
173
+ * Generates an index.ts file for the given directory. Recursively calls itself
174
+ * for subdirectories.
175
+ *
135
176
  * @param dirPath - The absolute path to the directory to process.
136
177
  * @param config - The merged configuration object.
137
- * @param baseDir - The base directory path for calculating relative paths (optional, defaults to dirPath).
178
+ * @param baseDir - The base directory path for calculating relative paths
179
+ * (optional, defaults to dirPath).
138
180
  * @throws Error if directory processing fails.
139
181
  */
140
182
  const generateIndexFileForDir = async (
@@ -146,8 +188,8 @@ const generateIndexFileForDir = async (
146
188
  const actualBaseDir = baseDir ?? dirPath;
147
189
  const entries = await fs.readdir(dirPath, { withFileTypes: true });
148
190
 
149
- const subDirectories: string[] = [];
150
- const filesToExport: string[] = [];
191
+ const mut_subDirectories: string[] = [];
192
+ const mut_filesToExport: string[] = [];
151
193
 
152
194
  for (const entry of entries) {
153
195
  const entryName = entry.name;
@@ -155,28 +197,35 @@ const generateIndexFileForDir = async (
155
197
  const relativePath = path.relative(actualBaseDir, entryPath);
156
198
 
157
199
  if (
158
- config.excludePatterns.some(
159
- (pat) =>
160
- micromatch.isMatch(relativePath, pat) ||
161
- micromatch.isMatch(entryName, pat),
162
- )
200
+ config.exclude({
201
+ absolutePath: entryPath,
202
+ relativePath,
203
+ fileName: entryName,
204
+ })
163
205
  ) {
164
206
  continue; // Skip excluded directories/files
165
207
  }
166
208
 
167
209
  if (entry.isDirectory()) {
168
- subDirectories.push(entryName);
210
+ mut_subDirectories.push(entryName);
169
211
  // Recursively call for subdirectories first
170
212
  // eslint-disable-next-line no-await-in-loop
171
213
  await generateIndexFileForDir(entryPath, config, actualBaseDir);
172
- } else if (entry.isFile() && shouldExportFile(relativePath, config)) {
173
- filesToExport.push(entryName);
214
+ } else if (
215
+ entry.isFile() &&
216
+ shouldExportFile({
217
+ absolutePath: entryPath,
218
+ filePath: relativePath,
219
+ config,
220
+ })
221
+ ) {
222
+ mut_filesToExport.push(entryName);
174
223
  }
175
224
  }
176
225
 
177
226
  const indexContent = generateIndexContent(
178
- subDirectories,
179
- filesToExport,
227
+ mut_subDirectories,
228
+ mut_filesToExport,
180
229
  config,
181
230
  );
182
231
 
@@ -191,20 +240,30 @@ const generateIndexFileForDir = async (
191
240
  }
192
241
  };
193
242
 
243
+ const indexRegex = /^index\.[cm]?[jt]s[x]?$/u;
244
+
194
245
  /**
195
- * Determines if a file should be exported in the index file.
196
- * A file is exported if:
246
+ * Determines if a file should be exported in the index file. A file is exported
247
+ * if:
248
+ *
197
249
  * - It has one of the configured source extensions
198
250
  * - It's not an index file itself
199
251
  * - It doesn't match any exclusion patterns
252
+ *
200
253
  * @param filePath - The relative path to the file from the target directory.
254
+ * @param absolutePath - The absolute path to the file.
201
255
  * @param config - The merged configuration object.
202
256
  * @returns True if the file should be exported.
203
257
  */
204
- const shouldExportFile = (
205
- filePath: string,
206
- config: GenIndexConfigInternal,
207
- ): boolean => {
258
+ const shouldExportFile = ({
259
+ absolutePath,
260
+ filePath,
261
+ config,
262
+ }: Readonly<{
263
+ absolutePath: string;
264
+ filePath: string;
265
+ config: GenIndexConfigInternal;
266
+ }>): boolean => {
208
267
  const fileName = path.basename(filePath);
209
268
 
210
269
  const ext = path.extname(fileName);
@@ -216,26 +275,47 @@ const shouldExportFile = (
216
275
 
217
276
  // Don't export the index file itself
218
277
  if (
219
- /^index\.[cm]?[jt]s[x]?$/u.test(fileName) // Matches index.ts, index.mts, index.js, index.tsx
278
+ indexRegex.test(fileName) // Matches index.ts, index.mts, index.js, index.tsx
220
279
  ) {
221
280
  return false;
222
281
  }
223
282
 
224
283
  // Check against exclusion patterns
225
- for (const pattern of config.excludePatterns.values()) {
226
- if (
227
- micromatch.isMatch(filePath, pattern) ||
228
- micromatch.isMatch(fileName, pattern)
229
- ) {
230
- return false;
231
- }
284
+ if (
285
+ config.exclude({
286
+ absolutePath,
287
+ relativePath: filePath,
288
+ fileName,
289
+ })
290
+ ) {
291
+ return false;
232
292
  }
233
293
 
234
294
  return true;
235
295
  };
236
296
 
297
+ if (import.meta.vitest !== undefined) {
298
+ describe('index file regex', () => {
299
+ test.each([
300
+ ['index.ts', true],
301
+ ['index.js', true],
302
+ ['index.mts', true],
303
+ ['index.mjs', true],
304
+ ['index.cts', true],
305
+ ['index.cjs', true],
306
+ ['index.tsx', true],
307
+ ['index.jsx', true],
308
+ ['not-index.ts', false],
309
+ ['index.txt', false],
310
+ ] as const)('indexRegex.test($0) to be $1', (fileName, expected) => {
311
+ expect(indexRegex.test(fileName)).toBe(expected);
312
+ });
313
+ });
314
+ }
315
+
237
316
  /**
238
317
  * Generates the content for an index file.
318
+ *
239
319
  * @param subDirectories - Array of subdirectory names.
240
320
  * @param filesToExport - Array of file names to export.
241
321
  * @param config - The merged configuration object.
@@ -247,13 +327,17 @@ const generateIndexContent = (
247
327
  config: GenIndexConfigInternal,
248
328
  ): string => {
249
329
  const exportStatements = [
250
- ...subDirectories.map(
251
- (subDir) => `export * from "./${subDir}/index${config.exportExtension}";`,
330
+ ...subDirectories.map((subDir) =>
331
+ config.exportExtension === 'none'
332
+ ? `export * from "./${subDir}";`
333
+ : `export * from "./${subDir}/index${config.exportExtension}";`,
252
334
  ),
253
335
  ...filesToExport.map((file) => {
254
336
  const fileNameWithoutExt = path.basename(file, path.extname(file));
255
337
 
256
- return `export * from "./${fileNameWithoutExt}${config.exportExtension}";`;
338
+ return config.exportExtension === 'none'
339
+ ? `export * from "./${fileNameWithoutExt}";`
340
+ : `export * from "./${fileNameWithoutExt}${config.exportExtension}";`;
257
341
  }),
258
342
  ];
259
343
 
@@ -3,18 +3,21 @@ import '../node-global.mjs';
3
3
  import { getDiffFrom } from './diff.mjs';
4
4
 
5
5
  /**
6
- * Checks if TypeScript type checks should run based on the diff from origin/main.
7
- * Skips type checks if all changed files are documentation files, spell check config,
8
- * or other non-TypeScript files that don't affect type checking.
6
+ * Checks if TypeScript type checks should run based on the diff from
7
+ * origin/main. Skips type checks if all changed files are documentation files,
8
+ * spell check config, or other non-TypeScript files that don't affect type
9
+ * checking.
9
10
  *
10
11
  * Ignored file patterns:
12
+ *
11
13
  * - '.cspell.json'
12
14
  * - '**.md'
13
15
  * - '**.txt'
14
16
  * - 'docs/**'
15
17
  *
16
- * @returns A promise that resolves when the check is complete. Sets GITHUB_OUTPUT
17
- * environment variable with should_run=true/false if running in GitHub Actions.
18
+ * @returns A promise that resolves when the check is complete. Sets
19
+ * GITHUB_OUTPUT environment variable with should_run=true/false if running in
20
+ * GitHub Actions.
18
21
  */
19
22
  export const checkShouldRunTypeChecks = async (): Promise<void> => {
20
23
  // paths-ignore:
@@ -13,10 +13,13 @@ import { type Package } from './types.mjs';
13
13
  const DEBUG = false as boolean;
14
14
 
15
15
  /**
16
- * Executes a npm script across multiple packages in parallel with a concurrency limit.
16
+ * Executes a npm script across multiple packages in parallel with a concurrency
17
+ * limit.
18
+ *
17
19
  * @param packages - Array of Package objects to execute the script in
18
20
  * @param scriptName - The name of the npm script to execute
19
- * @param concurrency - Maximum number of packages to process simultaneously (default: 3)
21
+ * @param concurrency - Maximum number of packages to process simultaneously
22
+ * (default: 3)
20
23
  * @returns A promise that resolves to an array of execution results
21
24
  */
22
25
  export const executeParallel = async (
@@ -30,7 +33,7 @@ export const executeParallel = async (
30
33
  Result<Readonly<{ code?: number; skipped?: boolean }>, Error>
31
34
  >[] = [];
32
35
 
33
- const executing = new Set<Promise<unknown>>();
36
+ const mut_executing = new Set<Promise<unknown>>();
34
37
 
35
38
  for (const pkg of packages) {
36
39
  const promise = executeScript(pkg, scriptName);
@@ -38,22 +41,22 @@ export const executeParallel = async (
38
41
  mut_resultPromises.push(promise);
39
42
 
40
43
  const wrappedPromise = promise.finally(() => {
41
- executing.delete(wrappedPromise);
44
+ mut_executing.delete(wrappedPromise);
42
45
  if (DEBUG) {
43
- console.debug('executing size', executing.size);
46
+ console.debug('executing size', mut_executing.size);
44
47
  }
45
48
  });
46
49
 
47
- executing.add(wrappedPromise);
50
+ mut_executing.add(wrappedPromise);
48
51
 
49
52
  if (DEBUG) {
50
- console.debug('executing size', executing.size);
53
+ console.debug('executing size', mut_executing.size);
51
54
  }
52
55
 
53
56
  // If we reach concurrency limit, wait for one to finish
54
- if (executing.size >= concurrency) {
57
+ if (mut_executing.size >= concurrency) {
55
58
  // eslint-disable-next-line no-await-in-loop
56
- await Promise.race(executing);
59
+ await Promise.race(mut_executing);
57
60
  }
58
61
  }
59
62
 
@@ -61,12 +64,14 @@ export const executeParallel = async (
61
64
  };
62
65
 
63
66
  /**
64
- * Executes a npm script across packages in dependency order stages.
65
- * Packages are grouped into stages where each stage contains packages whose
66
- * dependencies have been completed in previous stages.
67
+ * Executes a npm script across packages in dependency order stages. Packages
68
+ * are grouped into stages where each stage contains packages whose dependencies
69
+ * have been completed in previous stages.
70
+ *
67
71
  * @param packages - Array of Package objects to execute the script in
68
72
  * @param scriptName - The name of the npm script to execute
69
- * @param concurrency - Maximum number of packages to process simultaneously within each stage (default: 3)
73
+ * @param concurrency - Maximum number of packages to process simultaneously
74
+ * within each stage (default: 3)
70
75
  * @returns A promise that resolves when all stages are complete
71
76
  */
72
77
  export const executeStages = async (
@@ -78,34 +83,36 @@ export const executeStages = async (
78
83
 
79
84
  const sorted = topologicalSortPackages(packages, dependencyGraph);
80
85
 
81
- const stages: (readonly Package[])[] = [];
82
- const completed = new Set<string>();
86
+ const mut_stages: (readonly Package[])[] = [];
87
+ const mut_completed = new Set<string>();
83
88
 
84
- while (completed.size < sorted.length) {
85
- const stage: Package[] = [];
89
+ while (mut_completed.size < sorted.length) {
90
+ const mut_stage: Package[] = [];
86
91
 
87
92
  for (const pkg of sorted) {
88
- if (completed.has(pkg.name)) continue;
93
+ if (mut_completed.has(pkg.name)) continue;
89
94
 
90
95
  const deps = dependencyGraph.get(pkg.name) ?? [];
91
- const depsCompleted = deps.every((dep) => completed.has(dep));
96
+ const depsCompleted = deps.every((dep) => mut_completed.has(dep));
92
97
 
93
98
  if (depsCompleted) {
94
- stage.push(pkg);
99
+ mut_stage.push(pkg);
95
100
  }
96
101
  }
97
102
 
98
- if (stage.length === 0) {
103
+ if (mut_stage.length === 0) {
99
104
  throw new Error('Circular dependency detected');
100
105
  }
101
106
 
102
- stages.push(stage);
103
- for (const pkg of stage) completed.add(pkg.name);
107
+ mut_stages.push(mut_stage);
108
+ for (const pkg of mut_stage) {
109
+ mut_completed.add(pkg.name);
110
+ }
104
111
  }
105
112
 
106
- console.log(`\nExecuting ${scriptName} in ${stages.length} stages...\n`);
113
+ console.log(`\nExecuting ${scriptName} in ${mut_stages.length} stages...\n`);
107
114
 
108
- for (const [i, stage] of stages.entries()) {
115
+ for (const [i, stage] of mut_stages.entries()) {
109
116
  if (stage.length > 0) {
110
117
  console.log(`Stage ${i + 1}: ${stage.map((p) => p.name).join(', ')}`);
111
118
  // eslint-disable-next-line no-await-in-loop
@@ -116,11 +123,14 @@ export const executeStages = async (
116
123
 
117
124
  /**
118
125
  * Executes a npm script in a specific package directory.
126
+ *
119
127
  * @param pkg - The package object containing path and metadata
120
128
  * @param scriptName - The name of the npm script to execute
121
129
  * @param options - Configuration options
122
- * @param options.prefix - Whether to prefix output with package name (default: true)
123
- * @returns A promise that resolves to execution result with exit code or skipped flag
130
+ * @param options.prefix - Whether to prefix output with package name (default:
131
+ * true)
132
+ * @returns A promise that resolves to execution result with exit code or
133
+ * skipped flag
124
134
  */
125
135
  const executeScript = (
126
136
  pkg: Package,
@@ -189,8 +199,9 @@ const executeScript = (
189
199
  ).value;
190
200
 
191
201
  /**
192
- * Performs a topological sort on packages based on their dependencies,
193
- * ensuring dependencies are ordered before their dependents.
202
+ * Performs a topological sort on packages based on their dependencies, ensuring
203
+ * dependencies are ordered before their dependents.
204
+ *
194
205
  * @param packages - Array of Package objects to sort
195
206
  * @returns An array of packages in dependency order (dependencies first)
196
207
  */
@@ -198,28 +209,28 @@ const topologicalSortPackages = (
198
209
  packages: readonly Package[],
199
210
  dependencyGraph: ReadonlyMap<string, readonly string[]>,
200
211
  ): readonly Package[] => {
201
- const visited = new Set<string>();
202
- const result: string[] = [];
212
+ const mut_visited = new Set<string>();
213
+ const mut_result: string[] = [];
203
214
 
204
215
  const packageMap = new Map(packages.map((p) => [p.name, p]));
205
216
 
206
217
  const visit = (pkgName: string): void => {
207
- if (visited.has(pkgName)) return;
208
- visited.add(pkgName);
218
+ if (mut_visited.has(pkgName)) return;
219
+ mut_visited.add(pkgName);
209
220
 
210
221
  const deps = dependencyGraph.get(pkgName) ?? [];
211
222
  for (const dep of deps) {
212
223
  visit(dep);
213
224
  }
214
225
 
215
- result.push(pkgName);
226
+ mut_result.push(pkgName);
216
227
  };
217
228
 
218
229
  for (const pkg of packages) {
219
230
  visit(pkg.name);
220
231
  }
221
232
 
222
- return result
233
+ return mut_result
223
234
  .map((pkgName) => packageMap.get(pkgName))
224
235
  .filter(isNotUndefined);
225
236
  };
@@ -227,21 +238,23 @@ const topologicalSortPackages = (
227
238
  /**
228
239
  * Builds a dependency graph from the given packages, mapping each package name
229
240
  * to its internal workspace dependencies.
241
+ *
230
242
  * @param packages - Array of Package objects to analyze
231
- * @returns A readonly map where keys are package names and values are arrays of their dependency package names
243
+ * @returns A readonly map where keys are package names and values are arrays of
244
+ * their dependency package names
232
245
  */
233
246
  const buildDependencyGraph = (
234
247
  packages: readonly Package[],
235
248
  ): ReadonlyMap<string, readonly string[]> => {
236
249
  const packageMap = new Map(packages.map((p) => [p.name, p]));
237
- const graph = new Map<string, readonly string[]>();
250
+ const mut_graph = new Map<string, readonly string[]>();
238
251
 
239
252
  for (const pkg of packages) {
240
253
  const deps = Object.keys(pkg.dependencies).filter((depName) =>
241
254
  packageMap.has(depName),
242
255
  );
243
- graph.set(pkg.name, deps);
256
+ mut_graph.set(pkg.name, deps);
244
257
  }
245
258
 
246
- return graph;
259
+ return mut_graph;
247
260
  };
@@ -12,10 +12,13 @@ import '../../node-global.mjs';
12
12
  import { type Package } from './types.mjs';
13
13
 
14
14
  /**
15
- * Retrieves all workspace packages from a monorepo based on the workspace patterns
16
- * defined in the root package.json file.
17
- * @param rootPackageJsonDir - The directory containing the root package.json file
18
- * @returns A promise that resolves to an array of Package objects containing package metadata
15
+ * Retrieves all workspace packages from a monorepo based on the workspace
16
+ * patterns defined in the root package.json file.
17
+ *
18
+ * @param rootPackageJsonDir - The directory containing the root package.json
19
+ * file
20
+ * @returns A promise that resolves to an array of Package objects containing
21
+ * package metadata
19
22
  */
20
23
  export const getWorkspacePackages = async (
21
24
  rootPackageJsonDir: string,
@@ -5,11 +5,15 @@ import { getWorkspacePackages } from './get-workspace-packages.mjs';
5
5
 
6
6
  /**
7
7
  * Executes a npm script command across all workspace packages in parallel.
8
+ *
8
9
  * @param options - Configuration options for the parallel execution
9
- * @param options.rootPackageJsonDir - The directory containing the root package.json file
10
+ * @param options.rootPackageJsonDir - The directory containing the root
11
+ * package.json file
10
12
  * @param options.cmd - The npm script command to execute in each package
11
- * @param options.concurrency - Maximum number of packages to process simultaneously (default: 3)
12
- * @param options.filterWorkspacePattern - Optional function to filter packages by name
13
+ * @param options.concurrency - Maximum number of packages to process
14
+ * simultaneously (default: 3)
15
+ * @param options.filterWorkspacePattern - Optional function to filter packages
16
+ * by name
13
17
  * @returns A promise that resolves when all packages have completed execution
14
18
  */
15
19
  export const runCmdInParallelAcrossWorkspaces = async ({
@@ -33,10 +37,10 @@ export const runCmdInParallelAcrossWorkspaces = async ({
33
37
 
34
38
  await executeParallel(filteredPackages, cmd, concurrency);
35
39
  console.log(`\n✅ ${cmd} completed successfully`);
36
- } catch (err) {
40
+ } catch (error) {
37
41
  console.error(
38
42
  `\n❌ ${cmd} failed:`,
39
- err instanceof Error ? err.message : (err?.toString() ?? ''),
43
+ error instanceof Error ? error.message : (error?.toString() ?? ''),
40
44
  );
41
45
  process.exit(1);
42
46
  }
@@ -4,14 +4,18 @@ import { executeStages } from './execute-parallel.mjs';
4
4
  import { getWorkspacePackages } from './get-workspace-packages.mjs';
5
5
 
6
6
  /**
7
- * Executes a npm script command across all workspace packages in dependency order stages.
8
- * Packages are grouped into stages where each stage contains packages whose
9
- * dependencies have been completed in previous stages.
7
+ * Executes a npm script command across all workspace packages in dependency
8
+ * order stages. Packages are grouped into stages where each stage contains
9
+ * packages whose dependencies have been completed in previous stages.
10
+ *
10
11
  * @param options - Configuration options for the staged execution
11
- * @param options.rootPackageJsonDir - The directory containing the root package.json file
12
+ * @param options.rootPackageJsonDir - The directory containing the root
13
+ * package.json file
12
14
  * @param options.cmd - The npm script command to execute in each package
13
- * @param options.concurrency - Maximum number of packages to process simultaneously within each stage (default: 3)
14
- * @param options.filterWorkspacePattern - Optional function to filter packages by name
15
+ * @param options.concurrency - Maximum number of packages to process
16
+ * simultaneously within each stage (default: 3)
17
+ * @param options.filterWorkspacePattern - Optional function to filter packages
18
+ * by name
15
19
  * @returns A promise that resolves when all stages have completed execution
16
20
  */
17
21
  export const runCmdInStagesAcrossWorkspaces = async ({
@@ -35,10 +39,10 @@ export const runCmdInStagesAcrossWorkspaces = async ({
35
39
 
36
40
  await executeStages(filteredPackages, cmd, concurrency);
37
41
  console.log(`\n✅ ${cmd} completed successfully`);
38
- } catch (err) {
42
+ } catch (error) {
39
43
  console.error(
40
44
  `\n❌ ${cmd} failed:`,
41
- err instanceof Error ? err.message : (err?.toString() ?? ''),
45
+ error instanceof Error ? error.message : (error?.toString() ?? ''),
42
46
  );
43
47
  process.exit(1);
44
48
  }
package/src/globals.d.mts CHANGED
@@ -1 +1,2 @@
1
+ /* eslint-disable import/unambiguous */
1
2
  /// <reference types="ts-type-forge" />