ts-repo-utils 5.1.0 → 5.2.1

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 (62) 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 +4 -2
  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 +1 -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 +18 -5
  26. package/dist/functions/gen-index.d.mts.map +1 -1
  27. package/dist/functions/gen-index.mjs +48 -32
  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 +40 -28
  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 +7 -3
  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 +10 -6
  48. package/dist/functions/workspace-utils/run-cmd-in-stages.mjs.map +1 -1
  49. package/package.json +4 -3
  50. package/src/functions/assert-ext.mts +13 -7
  51. package/src/functions/assert-path-exists.mts +2 -0
  52. package/src/functions/assert-repo-is-clean.mts +4 -2
  53. package/src/functions/diff.mts +2 -6
  54. package/src/functions/diff.test.mts +13 -13
  55. package/src/functions/exec-async.mts +1 -0
  56. package/src/functions/format.mts +10 -3
  57. package/src/functions/gen-index.mts +107 -48
  58. package/src/functions/should-run.mts +8 -5
  59. package/src/functions/workspace-utils/execute-parallel.mts +42 -29
  60. package/src/functions/workspace-utils/get-workspace-packages.mts +7 -4
  61. package/src/functions/workspace-utils/run-cmd-in-parallel.mts +7 -3
  62. package/src/functions/workspace-utils/run-cmd-in-stages.mts +10 -6
@@ -26,12 +26,12 @@ describe('diff', () => {
26
26
  });
27
27
 
28
28
  test('should detect newly created files', async () => {
29
- const testFiles = new Set<string>();
29
+ const mut_testFiles = new Set<string>();
30
30
 
31
31
  // Create a new file in project root
32
32
  const testFileName = 'test-new-file.tmp';
33
33
  const testFilePath = path.join(process.cwd(), testFileName);
34
- testFiles.add(testFilePath);
34
+ mut_testFiles.add(testFilePath);
35
35
 
36
36
  await fs.writeFile(testFilePath, 'test content');
37
37
 
@@ -43,25 +43,25 @@ describe('diff', () => {
43
43
  expect(files).toContain(testFileName);
44
44
  }
45
45
 
46
- await cleanupTestFiles(testFiles);
46
+ await cleanupTestFiles(mut_testFiles);
47
47
  });
48
48
 
49
49
  test('should detect modified existing files', async () => {
50
- const testFiles = new Set<string>();
50
+ const mut_testFiles = new Set<string>();
51
51
 
52
52
  // Use an existing file in the project that we can modify safely
53
53
  const testFileName = 'test-modify-file.tmp';
54
- const testFilePath = path.join(process.cwd(), testFileName);
55
- testFiles.add(testFilePath);
54
+ const mut_testFilePath = path.join(process.cwd(), testFileName);
55
+ mut_testFiles.add(mut_testFilePath);
56
56
 
57
57
  // Create and commit the file first
58
- await fs.writeFile(testFilePath, 'initial content');
58
+ await fs.writeFile(mut_testFilePath, 'initial content');
59
59
 
60
60
  // Add to git to track it
61
61
  await $(`git add ${testFileName}`, { silent: true });
62
62
 
63
63
  // Modify the file
64
- await fs.writeFile(testFilePath, 'modified content');
64
+ await fs.writeFile(mut_testFilePath, 'modified content');
65
65
 
66
66
  const result = await getUntrackedFiles({ silent: true });
67
67
 
@@ -74,17 +74,17 @@ describe('diff', () => {
74
74
  // Reset git state
75
75
  await $(`git reset HEAD ${testFileName}`, { silent: true });
76
76
 
77
- await cleanupTestFiles(testFiles);
77
+ await cleanupTestFiles(mut_testFiles);
78
78
  });
79
79
 
80
80
  test('should detect multiple types of changes', async () => {
81
- const testFiles = new Set<string>();
81
+ const mut_testFiles = new Set<string>();
82
82
 
83
83
  // Create multiple test files
84
84
  const newFile = path.join(process.cwd(), 'test-new-file.tmp');
85
85
  const modifyFile = path.join(process.cwd(), 'test-modify-file.tmp');
86
- testFiles.add(newFile);
87
- testFiles.add(modifyFile);
86
+ mut_testFiles.add(newFile);
87
+ mut_testFiles.add(modifyFile);
88
88
 
89
89
  // Create new file
90
90
  await fs.writeFile(newFile, 'new file content');
@@ -108,7 +108,7 @@ describe('diff', () => {
108
108
  // Reset git state
109
109
  await $(`git reset HEAD test-modify-file.tmp`, { silent: true });
110
110
 
111
- await cleanupTestFiles(testFiles);
111
+ await cleanupTestFiles(mut_testFiles);
112
112
  });
113
113
 
114
114
  test('should exclude deleted files from results', async () => {
@@ -3,6 +3,7 @@ import { Result } from 'ts-data-forge';
3
3
 
4
4
  /**
5
5
  * Executes a shell command asynchronously.
6
+ *
6
7
  * @param cmd - The command to execute.
7
8
  * @param options - Optional configuration for command execution.
8
9
  * @returns A promise that resolves with the command result.
@@ -5,6 +5,7 @@ import { getDiffFrom, getUntrackedFiles } from './diff.mjs';
5
5
 
6
6
  /**
7
7
  * Format a list of files using Prettier
8
+ *
8
9
  * @param files - Array of file paths to format
9
10
  * @returns 'ok' if successful, 'err' if any errors occurred
10
11
  */
@@ -74,6 +75,7 @@ export const formatFilesList = async (
74
75
 
75
76
  /**
76
77
  * Format files matching the given glob pattern using Prettier
78
+ *
77
79
  * @param pathGlob - Glob pattern to match files
78
80
  * @returns 'ok' if successful, 'err' if any errors occurred
79
81
  */
@@ -107,6 +109,7 @@ export const formatFiles = async (
107
109
 
108
110
  /**
109
111
  * Format only files that have been changed (git status)
112
+ *
110
113
  * @param options - Options for formatting
111
114
  * @returns 'ok' if successful, 'err' if any errors occurred
112
115
  */
@@ -169,10 +172,14 @@ export const formatUntracked = async (
169
172
 
170
173
  /**
171
174
  * Format only files that differ from the specified base branch or commit
172
- * @param base - Base branch name or commit hash to compare against (defaults to 'main')
175
+ *
176
+ * @param base - Base branch name or commit hash to compare against (defaults to
177
+ * 'main')
173
178
  * @param options - Options for formatting
174
- * @param options.includeUntracked - Include untracked files in addition to diff files (default is true)
175
- * @param options.silent - Silent mode to suppress command output (default is false)
179
+ * @param options.includeUntracked - Include untracked files in addition to diff
180
+ * files (default is true)
181
+ * @param options.silent - Silent mode to suppress command output (default is
182
+ * false)
176
183
  * @returns 'ok' if successful, 'err' if any errors occurred
177
184
  */
178
185
  export const formatDiffFrom = async (
@@ -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']) */
@@ -22,7 +39,7 @@ export type GenIndexConfig = DeepReadonly<{
22
39
  /** File extension to use in export statements (default: '.js') */
23
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,7 +49,13 @@ 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
61
  exportExtension: `.${string}` | 'none';
@@ -41,6 +64,7 @@ type GenIndexConfigInternal = DeepReadonly<{
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
  */
@@ -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 (
@@ -155,11 +197,11 @@ 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
  }
@@ -169,7 +211,14 @@ const generateIndexFileForDir = async (
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)) {
214
+ } else if (
215
+ entry.isFile() &&
216
+ shouldExportFile({
217
+ absolutePath: entryPath,
218
+ filePath: relativePath,
219
+ config,
220
+ })
221
+ ) {
173
222
  mut_filesToExport.push(entryName);
174
223
  }
175
224
  }
@@ -194,19 +243,27 @@ const generateIndexFileForDir = async (
194
243
  const indexRegex = /^index\.[cm]?[jt]s[x]?$/u;
195
244
 
196
245
  /**
197
- * Determines if a file should be exported in the index file.
198
- * A file is exported if:
246
+ * Determines if a file should be exported in the index file. A file is exported
247
+ * if:
248
+ *
199
249
  * - It has one of the configured source extensions
200
250
  * - It's not an index file itself
201
251
  * - It doesn't match any exclusion patterns
252
+ *
202
253
  * @param filePath - The relative path to the file from the target directory.
254
+ * @param absolutePath - The absolute path to the file.
203
255
  * @param config - The merged configuration object.
204
256
  * @returns True if the file should be exported.
205
257
  */
206
- const shouldExportFile = (
207
- filePath: string,
208
- config: GenIndexConfigInternal,
209
- ): boolean => {
258
+ const shouldExportFile = ({
259
+ absolutePath,
260
+ filePath,
261
+ config,
262
+ }: Readonly<{
263
+ absolutePath: string;
264
+ filePath: string;
265
+ config: GenIndexConfigInternal;
266
+ }>): boolean => {
210
267
  const fileName = path.basename(filePath);
211
268
 
212
269
  const ext = path.extname(fileName);
@@ -224,13 +281,14 @@ const shouldExportFile = (
224
281
  }
225
282
 
226
283
  // Check against exclusion patterns
227
- for (const pattern of config.excludePatterns.values()) {
228
- if (
229
- micromatch.isMatch(filePath, pattern) ||
230
- micromatch.isMatch(fileName, pattern)
231
- ) {
232
- return false;
233
- }
284
+ if (
285
+ config.exclude({
286
+ absolutePath,
287
+ relativePath: filePath,
288
+ fileName,
289
+ })
290
+ ) {
291
+ return false;
234
292
  }
235
293
 
236
294
  return true;
@@ -257,6 +315,7 @@ if (import.meta.vitest !== undefined) {
257
315
 
258
316
  /**
259
317
  * Generates the content for an index file.
318
+ *
260
319
  * @param subDirectories - Array of subdirectory names.
261
320
  * @param filesToExport - Array of file names to export.
262
321
  * @param config - The merged configuration object.
@@ -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 (
@@ -79,16 +84,16 @@ export const executeStages = async (
79
84
  const sorted = topologicalSortPackages(packages, dependencyGraph);
80
85
 
81
86
  const mut_stages: (readonly Package[])[] = [];
82
- const completed = new Set<string>();
87
+ const mut_completed = new Set<string>();
83
88
 
84
- while (completed.size < sorted.length) {
89
+ while (mut_completed.size < sorted.length) {
85
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
99
  mut_stage.push(pkg);
@@ -100,7 +105,9 @@ export const executeStages = async (
100
105
  }
101
106
 
102
107
  mut_stages.push(mut_stage);
103
- for (const pkg of mut_stage) completed.add(pkg.name);
108
+ for (const pkg of mut_stage) {
109
+ mut_completed.add(pkg.name);
110
+ }
104
111
  }
105
112
 
106
113
  console.log(`\nExecuting ${scriptName} in ${mut_stages.length} stages...\n`);
@@ -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,14 +209,14 @@ const topologicalSortPackages = (
198
209
  packages: readonly Package[],
199
210
  dependencyGraph: ReadonlyMap<string, readonly string[]>,
200
211
  ): readonly Package[] => {
201
- const visited = new Set<string>();
212
+ const mut_visited = new Set<string>();
202
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) {
@@ -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 ({
@@ -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 ({