ts-repo-utils 7.1.1 → 7.3.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 (38) hide show
  1. package/README.md +264 -249
  2. package/dist/cmd/assert-repo-is-clean.mjs +2 -1
  3. package/dist/cmd/assert-repo-is-clean.mjs.map +1 -1
  4. package/dist/cmd/check-should-run-type-checks.mjs +2 -1
  5. package/dist/cmd/check-should-run-type-checks.mjs.map +1 -1
  6. package/dist/cmd/format-diff-from.mjs +9 -1
  7. package/dist/cmd/format-diff-from.mjs.map +1 -1
  8. package/dist/cmd/format-uncommitted.mjs +9 -1
  9. package/dist/cmd/format-uncommitted.mjs.map +1 -1
  10. package/dist/cmd/gen-index-ts.mjs +2 -1
  11. package/dist/cmd/gen-index-ts.mjs.map +1 -1
  12. package/dist/functions/format.d.mts +7 -0
  13. package/dist/functions/format.d.mts.map +1 -1
  14. package/dist/functions/format.mjs +21 -5
  15. package/dist/functions/format.mjs.map +1 -1
  16. package/dist/functions/index.d.mts +1 -0
  17. package/dist/functions/index.d.mts.map +1 -1
  18. package/dist/functions/index.mjs +1 -0
  19. package/dist/functions/index.mjs.map +1 -1
  20. package/dist/functions/is-directly-executed.d.mts +2 -0
  21. package/dist/functions/is-directly-executed.d.mts.map +1 -0
  22. package/dist/functions/is-directly-executed.mjs +6 -0
  23. package/dist/functions/is-directly-executed.mjs.map +1 -0
  24. package/dist/index.mjs +1 -0
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/node-global.d.mts.map +1 -1
  27. package/dist/node-global.mjs +3 -0
  28. package/dist/node-global.mjs.map +1 -1
  29. package/package.json +6 -6
  30. package/src/cmd/assert-repo-is-clean.mts +1 -1
  31. package/src/cmd/check-should-run-type-checks.mts +1 -1
  32. package/src/cmd/format-diff-from.mts +9 -1
  33. package/src/cmd/format-uncommitted.mts +9 -1
  34. package/src/cmd/gen-index-ts.mts +1 -1
  35. package/src/functions/format.mts +36 -4
  36. package/src/functions/index.mts +1 -0
  37. package/src/functions/is-directly-executed.mts +4 -0
  38. package/src/node-global.mts +4 -2
package/README.md CHANGED
@@ -12,47 +12,24 @@ A comprehensive toolkit for managing TypeScript projects with strict ESM support
12
12
  ## Installation
13
13
 
14
14
  ```bash
15
- npm install ts-repo-utils
15
+ npm add --save-dev ts-repo-utils
16
16
  ```
17
17
 
18
- ## CLI Commands
19
-
20
- `ts-repo-utils` provides several CLI commands that can be used directly or through npm scripts.
21
-
22
- ### `gen-index-ts`
23
-
24
- Generates index.ts files recursively in target directories with automatic barrel exports.
25
-
26
18
  ```bash
27
- # Basic usage with required options
28
- npm exec -- gen-index-ts ./src --target-ext .mts --index-ext .mts --export-ext .mjs
29
-
30
- # With formatting command
31
- npm exec -- gen-index-ts ./src --target-ext .mts --index-ext .mts --export-ext .mjs --fmt 'npm run fmt'
32
-
33
- # Multiple target extensions
34
- npm exec -- gen-index-ts ./src --target-ext .mts --target-ext .tsx --index-ext .mts --export-ext .mjs
35
-
36
- # With exclude patterns
37
- npm exec -- gen-index-ts ./src --target-ext .ts --index-ext .ts --export-ext .js --exclude '*.test.ts' --exclude '*.spec.ts'
19
+ yarn add --dev ts-repo-utils
20
+ ```
38
21
 
39
- # Example in npm scripts
40
- "gi": "gen-index-ts ./src --index-ext .mts --export-ext .mjs --target-ext .mts --target-ext .tsx --fmt 'npm run fmt'"
22
+ ```bash
23
+ pnpm add --save-dev ts-repo-utils
41
24
  ```
42
25
 
43
- **Options:**
26
+ ## CLI Commands
44
27
 
45
- - `<target-directory>` - Directory where the index file will be generated (comma-separated list can be used)
46
- - `--target-ext` - File extensions to include in the index file (required, can be specified multiple times)
47
- - `--index-ext` - Extension of the index file to be generated (required)
48
- - `--export-ext` - Extension of the export statements in the index file (required, or 'none')
49
- - `--exclude` - Glob patterns of files to exclude (optional, can be specified multiple times)
50
- - `--fmt` - Command to format after generating the index file (optional)
51
- - `--silent` - Suppress output messages (optional)
28
+ `ts-repo-utils` provides several CLI commands that can be used directly or through npm scripts.
52
29
 
53
30
  ### `assert-repo-is-clean`
54
31
 
55
- Checks if repository is clean and exits with code 1 if it has uncommitted changes.
32
+ Checks if the repository is clean (i.e., there are no uncommitted changes, untracked files, or staged files) and exits with code 1 if any are present.
56
33
 
57
34
  ```bash
58
35
  # Basic usage
@@ -64,6 +41,8 @@ npm exec -- assert-repo-is-clean --silent
64
41
 
65
42
  ```yaml
66
43
  # Example in GitHub Actions
44
+ - name: Format check
45
+ run: npm run fmt
67
46
  - name: Check if there is no file diff
68
47
  run: npm exec -- assert-repo-is-clean
69
48
  ```
@@ -90,6 +69,7 @@ npm exec -- format-uncommitted --silent
90
69
  - `--exclude-modified` - Exclude modified files (default: false)
91
70
  - `--exclude-staged` - Exclude staged files (default: false)
92
71
  - `--silent` - Suppress output messages (default: false)
72
+ - `--ignore-unknown` - Skip files without a Prettier parser instead of erroring (default: true)
93
73
 
94
74
  ### `format-diff-from`
95
75
 
@@ -107,9 +87,16 @@ npm exec -- format-diff-from main --exclude-untracked
107
87
 
108
88
  # Silent mode
109
89
  npm exec -- format-diff-from main --silent
90
+ ```
110
91
 
111
- # Example in npm scripts
112
- "fmt": "format-diff-from origin/main"
92
+ Example in npm scripts:
93
+
94
+ ```json
95
+ {
96
+ "scripts": {
97
+ "fmt": "npm exec -- format-diff-from origin/main"
98
+ }
99
+ }
113
100
  ```
114
101
 
115
102
  **Options:**
@@ -119,10 +106,55 @@ npm exec -- format-diff-from main --silent
119
106
  - `--exclude-modified` - Exclude modified files (default: false)
120
107
  - `--exclude-staged` - Exclude staged files (default: false)
121
108
  - `--silent` - Suppress output messages (default: false)
109
+ - `--ignore-unknown` - Skip files without a Prettier parser instead of erroring (default: true)
110
+
111
+ ### `gen-index-ts`
112
+
113
+ Generates index.ts files recursively in target directories with automatic barrel exports.
114
+
115
+ ```bash
116
+ # Basic usage with required options
117
+ npm exec -- gen-index-ts ./src --target-ext .mts --index-ext .mts --export-ext .mjs
118
+
119
+ # With formatting command
120
+ npm exec -- gen-index-ts ./src --target-ext .mts --index-ext .mts --export-ext .mjs --fmt 'npm run fmt'
121
+
122
+ # Multiple target extensions
123
+ npm exec -- gen-index-ts ./src --target-ext .mts --target-ext .tsx --index-ext .mts --export-ext .mjs
124
+
125
+ # With exclude patterns
126
+ npm exec -- gen-index-ts ./src --target-ext .ts --index-ext .ts --export-ext .js --exclude '*.test.ts' --exclude '*.spec.ts'
127
+
128
+ # Example in npm scripts
129
+ "gi": "gen-index-ts ./src --index-ext .mts --export-ext .mjs --target-ext .mts --target-ext .tsx --fmt 'npm run fmt'"
130
+ ```
131
+
132
+ **Features:**
133
+
134
+ - Creates barrel exports for all subdirectories
135
+ - Supports complex glob exclusion patterns (using micromatch)
136
+ - Automatically formats generated files using the project's Prettier config
137
+ - Works with both single directories and directory arrays
138
+ - Respects source and export extension configuration
139
+
140
+ **Benefits:**
141
+
142
+ - Prevents forgetting to export modules
143
+ - TypeScript can detect duplicate variables, type names, etc.
144
+
145
+ **Options:**
146
+
147
+ - `<target-directory>` - Directory where the index file will be generated (comma-separated list can be used)
148
+ - `--target-ext` - File extensions to include in the index file (required, can be specified multiple times)
149
+ - `--index-ext` - Extension of the index file to be generated (required)
150
+ - `--export-ext` - Extension of the export statements in the index file (required, or 'none')
151
+ - `--exclude` - Glob patterns of files to exclude (optional, can be specified multiple times)
152
+ - `--fmt` - Command to format after generating the index file (optional)
153
+ - `--silent` - Suppress output messages (optional)
122
154
 
123
155
  ### `check-should-run-type-checks`
124
156
 
125
- Checks if TypeScript type checks should run based on the diff from a base branch. Optimizes CI/CD pipelines by skipping type checks when only non-TypeScript files are changed.
157
+ Checks whether TypeScript type checks should run based on file changes from the base branch. Optimizes CI/CD pipelines by skipping type checks when only non-TypeScript files have changed. The determination of "non-TypeScript files" is based on configurable ignore patterns, which can be specified using the `--paths-ignore` option.
126
158
 
127
159
  ```bash
128
160
  # Basic usage (compares against origin/main)
@@ -163,39 +195,46 @@ npm exec -- check-should-run-type-checks \
163
195
 
164
196
  When running in GitHub Actions, the command sets the `GITHUB_OUTPUT` environment variable with `should_run=true` or `should_run=false`, which can be used in subsequent steps.
165
197
 
166
- ### Usage in npm scripts
198
+ ## API Reference
167
199
 
168
- The CLI commands are commonly used in npm scripts for automation:
200
+ ### Command Execution
169
201
 
170
- ```json
171
- {
172
- "scripts": {
173
- "fmt": "format-diff-from origin/main",
174
- "gi": "gen-index-ts ./src --index-ext .mts --export-ext .mjs --target-ext .mts --target-ext .tsx --fmt 'npm run fmt'",
175
- "check:clean": "assert-repo-is-clean"
176
- }
202
+ #### `$(command: string, options?: ExecOptions): Promise<ExecResult>`
203
+
204
+ Executes a shell command asynchronously with type-safe results.
205
+
206
+ ```typescript
207
+ import { $, Result } from 'ts-repo-utils';
208
+
209
+ // or
210
+ // import "ts-repo-utils"; // $ and Result are globally defined in ts-repo-utils
211
+
212
+ const result = await $('npm test');
213
+
214
+ if (Result.isOk(result)) {
215
+ console.log('Tests passed:', result.value.stdout);
216
+ } else {
217
+ console.error('Tests failed:', result.value.message);
177
218
  }
178
219
  ```
179
220
 
180
- ### Usage in CI/CD
221
+ **Options:**
181
222
 
182
- These commands are particularly useful in CI/CD pipelines:
223
+ - `silent?: boolean` - Don't log command/output (default: false)
224
+ - `'node:child_process'` `exec` function options
183
225
 
184
- ```yaml
185
- # GitHub Actions example
186
- - name: Format check
187
- run: |
188
- npm run fmt
189
- npm exec -- assert-repo-is-clean
226
+ **Return Type:**
190
227
 
191
- # Check for uncommitted changes after build
192
- - name: Build
193
- run: npm run build
194
- - name: Check if there is no file diff
195
- run: npm exec -- assert-repo-is-clean
228
+ ```typescript
229
+ type Ret = Promise<
230
+ Result<
231
+ Readonly<{ stdout: string | Buffer; stderr: string | Buffer }>,
232
+ import('node:child_process').ExecException
233
+ >
234
+ >;
196
235
  ```
197
236
 
198
- ## API Reference
237
+ ### Script Execution Utilities
199
238
 
200
239
  ### Path and File System Utilities
201
240
 
@@ -207,7 +246,7 @@ Checks if a file or directory exists at the specified path.
207
246
  import { pathExists } from 'ts-repo-utils';
208
247
 
209
248
  const exists = await pathExists('./src/index.ts');
210
- console.log(exists); // true or false
249
+ console.log(exists satisfies boolean); // true or false
211
250
  ```
212
251
 
213
252
  #### `assertPathExists(filePath: string, description?: string): Promise<void>`
@@ -221,8 +260,6 @@ import { assertPathExists } from 'ts-repo-utils';
221
260
  await assertPathExists('./src/index.ts', 'Entry point file');
222
261
  ```
223
262
 
224
- ### File Extension Validation
225
-
226
263
  #### `assertExt(config: CheckExtConfig): Promise<void>`
227
264
 
228
265
  Validates that all files in specified directories have the correct extensions. Exits with code 1 if any files have incorrect extensions.
@@ -248,12 +285,12 @@ await assertExt({
248
285
  **Configuration Type:**
249
286
 
250
287
  ```typescript
251
- type CheckExtConfig = DeepReadonly<{
252
- directories: {
288
+ type CheckExtConfig = Readonly<{
289
+ directories: readonly Readonly<{
253
290
  path: string; // Directory path to check
254
291
  extension: string; // Expected file extension (including the dot)
255
- ignorePatterns?: string[]; // Optional glob patterns to ignore
256
- }[];
292
+ ignorePatterns?: readonly string[]; // Optional glob patterns to ignore
293
+ }>[];
257
294
  }>;
258
295
  ```
259
296
 
@@ -274,7 +311,8 @@ if (isDirty) {
274
311
 
275
312
  #### `assertRepoIsClean(): Promise<void>`
276
313
 
277
- Checks if repository is clean and exits with code 1 if it has uncommitted changes (shows changes and diff).
314
+ Checks if the repository is clean and exits with code 1 if it has uncommitted changes (shows changes and diff).
315
+ (Function version of the `assert-repo-is-clean` command)
278
316
 
279
317
  ```typescript
280
318
  import { assertRepoIsClean } from 'ts-repo-utils';
@@ -291,22 +329,22 @@ await assertRepoIsClean();
291
329
 
292
330
  ##### `getUntrackedFiles(options?)`
293
331
 
294
- Get untracked files from the working tree (files not added to git).
332
+ Gets untracked files from the working tree (files not added to git).
295
333
  Runs `git ls-files --others --exclude-standard [--deleted]`
296
334
 
297
335
  ##### `getModifiedFiles(options?)`
298
336
 
299
- Get modified files from the working tree (files that have been changed but not staged).
337
+ Gets modified files from the working tree (files that have been changed but not staged).
300
338
  Runs `git diff --name-only [--diff-filter=d]`
301
339
 
302
340
  ##### `getStagedFiles(options?)`
303
341
 
304
- Get files that are staged for commit (files added with git add).
342
+ Gets files that are staged for commit (files added with git add).
305
343
  Runs `git diff --staged --name-only [--diff-filter=d]`
306
344
 
307
345
  ##### `getDiffFrom(base: string, options?)`
308
346
 
309
- Get files that differ from the specified base branch or commit.
347
+ Gets files that differ from the specified base branch or commit.
310
348
  Runs `git diff --name-only <base> [--diff-filter=d]`
311
349
 
312
350
  **Common options:**
@@ -319,95 +357,15 @@ Runs `git diff --name-only <base> [--diff-filter=d]`
319
357
  ```typescript
320
358
  type Ret = Result<
321
359
  readonly string[],
322
- ExecException | Readonly<{ message: string }>
323
- >;
324
- ```
325
-
326
- ### Build Optimization Utilities
327
-
328
- #### `checkShouldRunTypeChecks(options?): Promise<boolean>`
329
-
330
- Checks if TypeScript type checks should run based on the diff from a base branch. Optimizes CI/CD pipelines by skipping type checks when only non-TypeScript files are changed.
331
-
332
- ```typescript
333
- import { checkShouldRunTypeChecks } from 'ts-repo-utils';
334
-
335
- // Use default settings (compare against origin/main)
336
- const shouldRun = await checkShouldRunTypeChecks();
337
-
338
- if (shouldRun) {
339
- await $('npm run type-check');
340
- }
341
-
342
- // Custom ignore patterns and base branch
343
- const shouldRun = await checkShouldRunTypeChecks({
344
- pathsIgnore: ['.eslintrc.json', 'docs/', '**.md', 'scripts/'],
345
- baseBranch: 'origin/develop',
346
- });
347
- ```
348
-
349
- **Options:**
350
-
351
- - `pathsIgnore?` - Patterns to ignore when checking if type checks should run:
352
- - Exact file matches: `.cspell.json`
353
- - Directory prefixes: `docs/` (matches any file in docs directory)
354
- - File extensions: `**.md` (matches any markdown file)
355
- - Default: `['LICENSE', '.editorconfig', '.gitignore', '.cspell.json', '.markdownlint-cli2.mjs', '.npmignore', '.prettierignore', '.prettierrc', 'docs/', '**.md', '**.txt']`
356
- - `baseBranch?` - Base branch to compare against (default: `origin/main`)
357
-
358
- **GitHub Actions Integration:**
359
-
360
- When running in GitHub Actions, sets `GITHUB_OUTPUT` with `should_run=true/false`:
361
-
362
- ```yaml
363
- - name: Check if type checks should run
364
- id: check_diff
365
- run: npx check-should-run-type-checks
366
-
367
- - name: Run type checks
368
- if: steps.check_diff.outputs.should_run == 'true'
369
- run: npm run type-check
370
- ```
371
-
372
- ### Command Execution
373
-
374
- #### `$(command: string, options?: ExecOptions): Promise<ExecResult>`
375
-
376
- Executes a shell command asynchronously with timeout support and type-safe results.
377
-
378
- ```typescript
379
- import { $ } from 'ts-repo-utils';
380
-
381
- const result = await $('npm test', { timeout: 60000 });
382
-
383
- if (result.type === 'ok') {
384
- console.log('Tests passed:', result.stdout);
385
- } else {
386
- console.error('Tests failed:', result.exception.message);
387
- }
388
- ```
389
-
390
- **Options:**
391
-
392
- - `silent?: boolean` - Don't log command/output (default: false)
393
- - `'node:child_process'` `exec` function options
394
-
395
- **Return Type:**
396
-
397
- ```typescript
398
- type Ret = Promise<
399
- Result<
400
- Readonly<{ stdout: string | Buffer; stderr: string | Buffer }>,
401
- import('node:child_process').ExecException
402
- >
360
+ import('node:child_process').ExecException | Readonly<{ message: string }>
403
361
  >;
404
362
  ```
405
363
 
406
364
  ### Code Formatting Utilities
407
365
 
408
- #### `formatFilesGlob(pathGlob: string): Promise<Result<undefined, unknown>>`
366
+ #### `formatFilesGlob(pathGlob: string, options?): Promise<Result<undefined, unknown>>`
409
367
 
410
- Format files matching a glob pattern using Prettier.
368
+ Formats files matching a glob pattern using Prettier.
411
369
 
412
370
  ```typescript
413
371
  import { formatFilesGlob } from 'ts-repo-utils';
@@ -417,21 +375,36 @@ await formatFilesGlob('src/**/*.ts');
417
375
 
418
376
  // Format specific files
419
377
  await formatFilesGlob('src/{index,utils}.ts');
378
+
379
+ // With custom ignore function
380
+ await formatFilesGlob('src/**/*.ts', {
381
+ ignore: (filePath) => filePath.includes('generated'),
382
+ ignoreUnknown: false, // Error on files without parser
383
+ });
420
384
  ```
421
385
 
422
386
  **Options:**
423
387
 
424
388
  - `silent?` - Suppress output messages (default: false)
389
+ - `ignoreUnknown?` - Skip files without a Prettier parser instead of erroring (default: true)
390
+ - `ignore?` - Custom function to ignore files (default: built-in ignore list)
425
391
 
426
- #### `formatUncommittedFiles(): Promise<Result>`
392
+ #### `formatUncommittedFiles(options?): Promise<Result>`
427
393
 
428
- Format only files that have been changed according to git status.
394
+ Formats only files that have been changed according to git status.
395
+ (Function version of the `format-uncommitted` command)
429
396
 
430
397
  ```typescript
431
398
  import { formatUncommittedFiles } from 'ts-repo-utils';
432
399
 
433
400
  // Format only modified files
434
401
  await formatUncommittedFiles();
402
+
403
+ // With custom options
404
+ await formatUncommittedFiles({
405
+ untracked: false, // Skip untracked files
406
+ ignore: (filePath) => filePath.includes('test'),
407
+ });
435
408
  ```
436
409
 
437
410
  **Options:**
@@ -440,6 +413,8 @@ await formatUncommittedFiles();
440
413
  - `modified?` - Format modified files (default: true)
441
414
  - `staged?` - Format staged files (default: true)
442
415
  - `silent?` - Suppress output messages (default: false)
416
+ - `ignoreUnknown?` - Skip files without a Prettier parser instead of erroring (default: true)
417
+ - `ignore?` - Custom function to ignore files (default: built-in ignore list)
443
418
 
444
419
  **Return Type:**
445
420
 
@@ -447,14 +422,17 @@ await formatUncommittedFiles();
447
422
  type Ret = Promise<
448
423
  Result<
449
424
  undefined,
450
- ExecException | Readonly<{ message: string }> | readonly unknown[]
425
+ | import('node:child_process').ExecException
426
+ | Readonly<{ message: string }>
427
+ | readonly unknown[]
451
428
  >
452
429
  >;
453
430
  ```
454
431
 
455
- #### `formatDiffFrom(base: string): Promise<Result>`
432
+ #### `formatDiffFrom(base: string, options?): Promise<Result>`
456
433
 
457
- Format only files that differ from the specified base branch or commit.
434
+ Formats only files that differ from the specified base branch or commit.
435
+ (Function version of the `format-diff-from` command)
458
436
 
459
437
  ```typescript
460
438
  import { formatDiffFrom } from 'ts-repo-utils';
@@ -464,6 +442,13 @@ await formatDiffFrom('main');
464
442
 
465
443
  // Format files different from specific commit
466
444
  await formatDiffFrom('abc123');
445
+
446
+ // With custom options
447
+ await formatDiffFrom('main', {
448
+ includeUntracked: false,
449
+ ignore: (filePath) => filePath.includes('vendor'),
450
+ ignoreUnknown: false, // Error on files without parser
451
+ });
467
452
  ```
468
453
 
469
454
  **Options:**
@@ -472,6 +457,8 @@ await formatDiffFrom('abc123');
472
457
  - `includeModified?` - Include modified files in addition to diff files (default: true)
473
458
  - `includeStaged?` - Include staged files in addition to diff files (default: true)
474
459
  - `silent?` - Suppress output messages (default: false)
460
+ - `ignoreUnknown?` - Skip files without a Prettier parser instead of erroring (default: true)
461
+ - `ignore?` - Custom function to ignore files (default: built-in ignore list)
475
462
 
476
463
  **Return Type:**
477
464
 
@@ -479,26 +466,102 @@ await formatDiffFrom('abc123');
479
466
  type Ret = Promise<
480
467
  Result<
481
468
  undefined,
482
- ExecException | Readonly<{ message: string }> | readonly unknown[]
469
+ | import('node:child_process').ExecException
470
+ | Readonly<{ message: string }>
471
+ | readonly unknown[]
483
472
  >
484
473
  >;
485
474
  ```
486
475
 
487
- ### Workspace Management Utilities
476
+ ### Index File Generation
477
+
478
+ #### `genIndex(config: GenIndexConfig): Promise<Result<undefined, unknown>>`
479
+
480
+ Generates index files recursively in target directories with automatic barrel exports.
481
+ (Function version of the `gen-index-ts` command)
482
+
483
+ ```typescript
484
+ import { genIndex } from 'ts-repo-utils';
485
+
486
+ await genIndex({
487
+ targetDirectory: './src',
488
+ exclude: ['*.test.ts', '*.spec.ts'],
489
+ });
490
+ ```
491
+
492
+ **Configuration Type:**
493
+
494
+ ```typescript
495
+ type GenIndexConfig = Readonly<{
496
+ /**
497
+ * Target directories to generate index files for (string or array of
498
+ * strings)
499
+ */
500
+ targetDirectory: string | readonly string[];
501
+
502
+ /**
503
+ * Glob patterns for files or predicate function to exclude from exports
504
+ * (default: excludes `'**\/*.{test,spec}.?(c|m)[jt]s?(x)'` and
505
+ * `'**\/*.d.?(c|m)ts'`)
506
+ */
507
+ exclude?:
508
+ | readonly string[]
509
+ | ((
510
+ args: Readonly<{
511
+ absolutePath: string;
512
+ relativePath: string;
513
+ fileName: string;
514
+ }>,
515
+ ) => boolean);
516
+
517
+ /**
518
+ * File extensions of source files to include in exports (default: ['.ts',
519
+ * '.tsx'])
520
+ */
521
+ targetExtensions?: readonly `.${string}`[];
522
+
523
+ /** File extension of index files to generate (default: '.ts') */
524
+ indexFileExtension?: `.${string}`;
525
+
526
+ /** File extension to use in export statements (default: '.js') */
527
+ exportStatementExtension?: `.${string}` | 'none';
528
+
529
+ /** Command to run for formatting generated files (optional) */
530
+ formatCommand?: string;
531
+
532
+ /** Whether to suppress output during execution (default: false) */
533
+ silent?: boolean;
534
+ }>;
535
+ ```
536
+
537
+ **Features:**
538
+
539
+ - Creates barrel exports for all subdirectories
540
+ - Supports complex glob exclusion patterns (using micromatch)
541
+ - Automatically formats generated files using the project's Prettier config
542
+ - Works with both single directories and directory arrays
543
+ - Respects source and export extension configuration
544
+
545
+ **Benefits:**
546
+
547
+ - Prevents forgetting to export modules
548
+ - TypeScript can detect duplicate variables, type names, etc.
549
+
550
+ ### Monorepo Workspace Management Utilities
488
551
 
489
552
  #### `runCmdInStagesAcrossWorkspaces(options): Promise<void>`
490
553
 
491
- Executes a npm script command across all workspace packages in dependency order stages. Packages are grouped into stages where each stage contains packages whose dependencies have been completed in previous stages. Uses fail-fast behavior.
554
+ Executes an npm script command across all workspace packages in dependency order stages. Packages are grouped into stages where each stage contains packages whose dependencies have been completed in previous stages. Uses fail-fast behavior.
492
555
 
493
556
  ```typescript
494
557
  import { runCmdInStagesAcrossWorkspaces } from 'ts-repo-utils';
495
558
 
496
559
  // Run build in dependency order
497
560
  await runCmdInStagesAcrossWorkspaces({
498
- rootPackageJsonDir: '.',
561
+ rootPackageJsonDir: '../',
499
562
  cmd: 'build',
500
563
  concurrency: 3,
501
- filterWorkspacePattern: (name) => !name.includes('deprecated'),
564
+ filterWorkspacePattern: (name) => !name.includes('experimental'),
502
565
  });
503
566
  ```
504
567
 
@@ -511,16 +574,17 @@ await runCmdInStagesAcrossWorkspaces({
511
574
 
512
575
  #### `runCmdInParallelAcrossWorkspaces(options): Promise<void>`
513
576
 
514
- Executes a npm script command across all workspace packages in parallel. Uses fail-fast behavior - stops execution immediately when any package fails.
577
+ Executes an npm script command across all workspace packages in parallel. Uses fail-fast behavior - stops execution immediately when any package fails.
515
578
 
516
579
  ```typescript
517
580
  import { runCmdInParallelAcrossWorkspaces } from 'ts-repo-utils';
518
581
 
519
582
  // Run tests in parallel across all packages
520
583
  await runCmdInParallelAcrossWorkspaces({
521
- rootPackageJsonDir: '.',
584
+ rootPackageJsonDir: '../',
522
585
  cmd: 'test',
523
586
  concurrency: 5,
587
+ filterWorkspacePattern: (name) => !name.includes('experimental'),
524
588
  });
525
589
  ```
526
590
 
@@ -546,17 +610,17 @@ console.log(packages.map((pkg) => pkg.name));
546
610
  **Return Type:**
547
611
 
548
612
  ```typescript
549
- type Package = {
613
+ type Package = Readonly<{
550
614
  name: string;
551
615
  path: string;
552
616
  packageJson: JsonValue;
553
- dependencies: Record<string, string>;
554
- };
617
+ dependencies: Readonly<Record<string, string>>;
618
+ }>;
555
619
  ```
556
620
 
557
621
  #### `executeParallel(packages, scriptName, concurrency?): Promise<readonly Result[]>`
558
622
 
559
- Executes a npm script across multiple packages in parallel with a concurrency limit. Lower-level function used by `runCmdInParallelAcrossWorkspaces`.
623
+ Executes an npm script across multiple packages in parallel with a concurrency limit. Lower-level function used by `runCmdInParallelAcrossWorkspaces`.
560
624
 
561
625
  ```typescript
562
626
  import { executeParallel, getWorkspacePackages } from 'ts-repo-utils';
@@ -567,7 +631,7 @@ await executeParallel(packages, 'lint', 4);
567
631
 
568
632
  #### `executeStages(packages, scriptName, concurrency?): Promise<void>`
569
633
 
570
- Executes a npm script across packages in dependency order stages. Lower-level function used by `runCmdInStagesAcrossWorkspaces`.
634
+ Executes an npm script across packages in dependency order stages. Lower-level function used by `runCmdInStagesAcrossWorkspaces`.
571
635
 
572
636
  ```typescript
573
637
  import { executeStages, getWorkspacePackages } from 'ts-repo-utils';
@@ -584,93 +648,44 @@ await executeStages(packages, 'build', 3);
584
648
  - Fail-fast behavior on errors
585
649
  - Circular dependency detection
586
650
 
587
- ### Index File Generation
588
-
589
- #### `genIndex(config: GenIndexConfig): Promise<Result<undefined, unknown>>`
651
+ ### Globals
590
652
 
591
- Generates index files recursively in target directories with automatic barrel exports.
653
+ When you import `ts-repo-utils` without destructuring, several utilities become globally available. This is useful for scripts where you want quick access to common functions without explicit imports.
592
654
 
593
655
  ```typescript
594
- import { genIndex } from 'ts-repo-utils';
595
-
596
- await genIndex({
597
- targetDirectory: './src',
598
- exclude: ['*.test.ts', '*.spec.ts'],
599
- });
600
- ```
656
+ import 'ts-repo-utils';
601
657
 
602
- **Configuration Type:**
658
+ // Now these functions are globally available
603
659
 
604
- ```typescript
605
- type GenIndexConfig = DeepReadonly<{
606
- /**
607
- * Target directories to generate index files for (string or array of
608
- * strings)
609
- */
610
- targetDirectory: string | readonly string[];
660
+ await $('npm test');
611
661
 
612
- /**
613
- * Glob patterns of files or predicate function to exclude from exports
614
- * (default: excludes `'**\/*.{test,spec}.?(c|m)[jt]s?(x)'` and
615
- * `'**\/*.d.?(c|m)ts'`)
616
- */
617
- exclude?:
618
- | readonly string[]
619
- | ((
620
- args: Readonly<{
621
- absolutePath: string;
622
- relativePath: string;
623
- fileName: string;
624
- }>,
625
- ) => boolean);
662
+ echo('Building project...');
626
663
 
627
- /** File extensions of source files to export (default: ['.ts', '.tsx']) */
628
- targetExtensions?: readonly `.${string}`[];
664
+ const filePath: string = path.join('src', 'index.ts');
629
665
 
630
- /** File extension of index files to generate (default: '.ts') */
631
- indexFileExtension?: `.${string}`;
632
-
633
- /** File extension to use in export statements (default: '.js') */
634
- exportStatementExtension?: `.${string}` | 'none';
635
-
636
- /** Command to run for formatting generated files (optional) */
637
- formatCommand?: string;
666
+ const configJson: string = await fs.readFile('./config.json', {
667
+ encoding: 'utf8',
668
+ });
638
669
 
639
- /** Whether to suppress output during execution (default: false) */
640
- silent?: boolean;
641
- }>;
670
+ const files: readonly string[] = await glob('**/*.ts');
642
671
  ```
643
672
 
644
- **Features:**
645
-
646
- - Creates barrel exports for all subdirectories
647
- - Supports complex glob exclusion patterns (using micromatch)
648
- - Automatically formats generated files using project's prettier config
649
- - Works with both single directories and directory arrays
650
- - Respects source and export extension configuration
651
-
652
- **Benefits:**
653
-
654
- - Prevents forgetting to export libraries
655
- - tsc can detect duplicate variables, type names, etc.
656
-
657
- ## Key Features
658
-
659
- - **Type Safety**: All functions use strict TypeScript types with readonly parameters
660
- - **Error Handling**: Comprehensive error handling with descriptive messages
661
- - **Git Integration**: Built-in git status and diff utilities
662
- - **Formatting**: Prettier integration with configuration resolution
663
- - **ESM Support**: Designed for ES modules with .mts/.mjs extension handling
664
- - **Concurrent Processing**: Uses Promise.all for performance optimization
665
- - **Configurable**: Flexible configuration options with sensible defaults
666
- - **Console Feedback**: Informative logging throughout operations
673
+ - `$` - The command execution utility described above.
674
+ - `echo` - Equivalent to `console.log`
675
+ - `path` - `node:path`
676
+ - `fs` - `node:fs/promises`
677
+ - `glob` - `fast-glob`
667
678
 
668
679
  ## Common Patterns
669
680
 
670
681
  ### Pre-commit Hook
671
682
 
672
683
  ```typescript
673
- import { formatUntracked, assertExt, assertRepoIsClean } from 'ts-repo-utils';
684
+ import {
685
+ assertExt,
686
+ assertRepoIsClean,
687
+ formatUncommittedFiles,
688
+ } from 'ts-repo-utils';
674
689
 
675
690
  // Validate file extensions
676
691
  await assertExt({
@@ -687,15 +702,7 @@ await assertRepoIsClean();
687
702
  ### Build Pipeline
688
703
 
689
704
  ```typescript
690
- import { assertExt, genIndex, $, formatFilesGlob } from 'ts-repo-utils';
691
-
692
- // Validate extensions
693
- await assertExt({
694
- directories: [
695
- { path: './src', extension: '.ts' },
696
- { path: './scripts', extension: '.mjs' },
697
- ],
698
- });
705
+ import { formatFilesGlob, genIndex } from 'ts-repo-utils';
699
706
 
700
707
  // Generate barrel exports
701
708
  await genIndex({ targetDirectory: './src' });
@@ -713,12 +720,20 @@ await formatFilesGlob('dist/**/*.js');
713
720
  ### Project Validation
714
721
 
715
722
  ```typescript
716
- import { pathExists, assertPathExists, assertRepoIsClean } from 'ts-repo-utils';
723
+ import { assertExt, assertPathExists, assertRepoIsClean } from 'ts-repo-utils';
717
724
 
718
725
  // Check required files exist (exits with code 1 if files don't exist)
719
726
  await assertPathExists('./package.json', 'Package manifest');
720
727
  await assertPathExists('./tsconfig.json', 'TypeScript config');
721
728
 
729
+ // Validate extensions
730
+ await assertExt({
731
+ directories: [
732
+ { path: './src', extension: '.ts' },
733
+ { path: './scripts', extension: '.mjs' },
734
+ ],
735
+ });
736
+
722
737
  // Verify clean repository state (exits with code 1 if repo is dirty)
723
738
  await assertRepoIsClean();
724
739
  ```