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
@@ -4,11 +4,15 @@ import { getWorkspacePackages } from './get-workspace-packages.mjs';
4
4
 
5
5
  /**
6
6
  * Executes a npm script command across all workspace packages in parallel.
7
+ *
7
8
  * @param options - Configuration options for the parallel execution
8
- * @param options.rootPackageJsonDir - The directory containing the root package.json file
9
+ * @param options.rootPackageJsonDir - The directory containing the root
10
+ * package.json file
9
11
  * @param options.cmd - The npm script command to execute in each package
10
- * @param options.concurrency - Maximum number of packages to process simultaneously (default: 3)
11
- * @param options.filterWorkspacePattern - Optional function to filter packages by name
12
+ * @param options.concurrency - Maximum number of packages to process
13
+ * simultaneously (default: 3)
14
+ * @param options.filterWorkspacePattern - Optional function to filter packages
15
+ * by name
12
16
  * @returns A promise that resolves when all packages have completed execution
13
17
  */
14
18
  const runCmdInParallelAcrossWorkspaces = async ({ rootPackageJsonDir, cmd, concurrency = 3, filterWorkspacePattern, }) => {
@@ -20,8 +24,8 @@ const runCmdInParallelAcrossWorkspaces = async ({ rootPackageJsonDir, cmd, concu
20
24
  await executeParallel(filteredPackages, cmd, concurrency);
21
25
  console.log(`\n✅ ${cmd} completed successfully`);
22
26
  }
23
- catch (err) {
24
- console.error(`\n❌ ${cmd} failed:`, err instanceof Error ? err.message : (err?.toString() ?? ''));
27
+ catch (error) {
28
+ console.error(`\n❌ ${cmd} failed:`, error instanceof Error ? error.message : (error?.toString() ?? ''));
25
29
  process.exit(1);
26
30
  }
27
31
  };
@@ -1 +1 @@
1
- {"version":3,"file":"run-cmd-in-parallel.mjs","sources":["../../../src/functions/workspace-utils/run-cmd-in-parallel.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;AAKA;;;;;;;;AAQG;AACI,MAAM,gCAAgC,GAAG,OAAO,EACrD,kBAAkB,EAClB,GAAG,EACH,WAAW,GAAG,CAAC,EACf,sBAAsB,GAMtB,KAAmB;AACnB,IAAA,IAAI;AACF,QAAA,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,kBAAkB,CAAC;AAE/D,QAAA,MAAM,gBAAgB,GACpB,sBAAsB,KAAK;AACzB,cAAE;AACF,cAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAEhE,MAAM,eAAe,CAAC,gBAAgB,EAAE,GAAG,EAAE,WAAW,CAAC;AACzD,QAAA,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,CAAA,uBAAA,CAAyB,CAAC;IAClD;IAAE,OAAO,GAAG,EAAE;AACZ,QAAA,OAAO,CAAC,KAAK,CACX,CAAA,IAAA,EAAO,GAAG,CAAA,QAAA,CAAU,EACpB,GAAG,YAAY,KAAK,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAC7D;AACD,QAAA,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACjB;AACF;;;;"}
1
+ {"version":3,"file":"run-cmd-in-parallel.mjs","sources":["../../../src/functions/workspace-utils/run-cmd-in-parallel.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;AAKA;;;;;;;;;;;;AAYG;AACI,MAAM,gCAAgC,GAAG,OAAO,EACrD,kBAAkB,EAClB,GAAG,EACH,WAAW,GAAG,CAAC,EACf,sBAAsB,GAMtB,KAAmB;AACnB,IAAA,IAAI;AACF,QAAA,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,kBAAkB,CAAC;AAE/D,QAAA,MAAM,gBAAgB,GACpB,sBAAsB,KAAK;AACzB,cAAE;AACF,cAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAEhE,MAAM,eAAe,CAAC,gBAAgB,EAAE,GAAG,EAAE,WAAW,CAAC;AACzD,QAAA,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,CAAA,uBAAA,CAAyB,CAAC;IAClD;IAAE,OAAO,KAAK,EAAE;AACd,QAAA,OAAO,CAAC,KAAK,CACX,CAAA,IAAA,EAAO,GAAG,CAAA,QAAA,CAAU,EACpB,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CACnE;AACD,QAAA,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACjB;AACF;;;;"}
@@ -1,13 +1,17 @@
1
1
  #!/usr/bin/env tsx
2
2
  /**
3
- * Executes a npm script command across all workspace packages in dependency order stages.
4
- * Packages are grouped into stages where each stage contains packages whose
5
- * dependencies have been completed in previous stages.
3
+ * Executes a npm script command across all workspace packages in dependency
4
+ * order stages. Packages are grouped into stages where each stage contains
5
+ * packages whose dependencies have been completed in previous stages.
6
+ *
6
7
  * @param options - Configuration options for the staged execution
7
- * @param options.rootPackageJsonDir - The directory containing the root package.json file
8
+ * @param options.rootPackageJsonDir - The directory containing the root
9
+ * package.json file
8
10
  * @param options.cmd - The npm script command to execute in each package
9
- * @param options.concurrency - Maximum number of packages to process simultaneously within each stage (default: 3)
10
- * @param options.filterWorkspacePattern - Optional function to filter packages by name
11
+ * @param options.concurrency - Maximum number of packages to process
12
+ * simultaneously within each stage (default: 3)
13
+ * @param options.filterWorkspacePattern - Optional function to filter packages
14
+ * by name
11
15
  * @returns A promise that resolves when all stages have completed execution
12
16
  */
13
17
  export declare const runCmdInStagesAcrossWorkspaces: ({ rootPackageJsonDir, cmd, concurrency, filterWorkspacePattern, }: Readonly<{
@@ -1 +1 @@
1
- {"version":3,"file":"run-cmd-in-stages.d.mts","sourceRoot":"","sources":["../../../src/functions/workspace-utils/run-cmd-in-stages.mts"],"names":[],"mappings":";AAKA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,GAAU,mEAKlD,QAAQ,CAAC;IACV,kBAAkB,EAAE,MAAM,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC;CAC3D,CAAC,KAAG,OAAO,CAAC,IAAI,CAkBhB,CAAC"}
1
+ {"version":3,"file":"run-cmd-in-stages.d.mts","sourceRoot":"","sources":["../../../src/functions/workspace-utils/run-cmd-in-stages.mts"],"names":[],"mappings":";AAKA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,8BAA8B,GAAU,mEAKlD,QAAQ,CAAC;IACV,kBAAkB,EAAE,MAAM,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC;CAC3D,CAAC,KAAG,OAAO,CAAC,IAAI,CAkBhB,CAAC"}
@@ -3,14 +3,18 @@ import { executeStages } from './execute-parallel.mjs';
3
3
  import { getWorkspacePackages } from './get-workspace-packages.mjs';
4
4
 
5
5
  /**
6
- * Executes a npm script command across all workspace packages in dependency order stages.
7
- * Packages are grouped into stages where each stage contains packages whose
8
- * dependencies have been completed in previous stages.
6
+ * Executes a npm script command across all workspace packages in dependency
7
+ * order stages. Packages are grouped into stages where each stage contains
8
+ * packages whose dependencies have been completed in previous stages.
9
+ *
9
10
  * @param options - Configuration options for the staged execution
10
- * @param options.rootPackageJsonDir - The directory containing the root package.json file
11
+ * @param options.rootPackageJsonDir - The directory containing the root
12
+ * package.json file
11
13
  * @param options.cmd - The npm script command to execute in each package
12
- * @param options.concurrency - Maximum number of packages to process simultaneously within each stage (default: 3)
13
- * @param options.filterWorkspacePattern - Optional function to filter packages by name
14
+ * @param options.concurrency - Maximum number of packages to process
15
+ * simultaneously within each stage (default: 3)
16
+ * @param options.filterWorkspacePattern - Optional function to filter packages
17
+ * by name
14
18
  * @returns A promise that resolves when all stages have completed execution
15
19
  */
16
20
  const runCmdInStagesAcrossWorkspaces = async ({ rootPackageJsonDir, cmd, concurrency = 3, filterWorkspacePattern, }) => {
@@ -22,8 +26,8 @@ const runCmdInStagesAcrossWorkspaces = async ({ rootPackageJsonDir, cmd, concurr
22
26
  await executeStages(filteredPackages, cmd, concurrency);
23
27
  console.log(`\n✅ ${cmd} completed successfully`);
24
28
  }
25
- catch (err) {
26
- console.error(`\n❌ ${cmd} failed:`, err instanceof Error ? err.message : (err?.toString() ?? ''));
29
+ catch (error) {
30
+ console.error(`\n❌ ${cmd} failed:`, error instanceof Error ? error.message : (error?.toString() ?? ''));
27
31
  process.exit(1);
28
32
  }
29
33
  };
@@ -1 +1 @@
1
- {"version":3,"file":"run-cmd-in-stages.mjs","sources":["../../../src/functions/workspace-utils/run-cmd-in-stages.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;AAKA;;;;;;;;;;AAUG;AACI,MAAM,8BAA8B,GAAG,OAAO,EACnD,kBAAkB,EAClB,GAAG,EACH,WAAW,GAAG,CAAC,EACf,sBAAsB,GAMtB,KAAmB;AACnB,IAAA,IAAI;AACF,QAAA,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,kBAAkB,CAAC;AAE/D,QAAA,MAAM,gBAAgB,GACpB,sBAAsB,KAAK;AACzB,cAAE;AACF,cAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAEhE,MAAM,aAAa,CAAC,gBAAgB,EAAE,GAAG,EAAE,WAAW,CAAC;AACvD,QAAA,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,CAAA,uBAAA,CAAyB,CAAC;IAClD;IAAE,OAAO,GAAG,EAAE;AACZ,QAAA,OAAO,CAAC,KAAK,CACX,CAAA,IAAA,EAAO,GAAG,CAAA,QAAA,CAAU,EACpB,GAAG,YAAY,KAAK,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAC7D;AACD,QAAA,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACjB;AACF;;;;"}
1
+ {"version":3,"file":"run-cmd-in-stages.mjs","sources":["../../../src/functions/workspace-utils/run-cmd-in-stages.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;AAKA;;;;;;;;;;;;;;AAcG;AACI,MAAM,8BAA8B,GAAG,OAAO,EACnD,kBAAkB,EAClB,GAAG,EACH,WAAW,GAAG,CAAC,EACf,sBAAsB,GAMtB,KAAmB;AACnB,IAAA,IAAI;AACF,QAAA,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,kBAAkB,CAAC;AAE/D,QAAA,MAAM,gBAAgB,GACpB,sBAAsB,KAAK;AACzB,cAAE;AACF,cAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAEhE,MAAM,aAAa,CAAC,gBAAgB,EAAE,GAAG,EAAE,WAAW,CAAC;AACvD,QAAA,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,CAAA,uBAAA,CAAyB,CAAC;IAClD;IAAE,OAAO,KAAK,EAAE;AACd,QAAA,OAAO,CAAC,KAAK,CACX,CAAA,IAAA,EAAO,GAAG,CAAA,QAAA,CAAU,EACpB,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CACnE;AACD,QAAA,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACjB;AACF;;;;"}
@@ -1 +1,2 @@
1
+ /* eslint-disable import/unambiguous */
1
2
  /// <reference types="ts-type-forge" />
@@ -1 +1 @@
1
- {"version":3,"file":"node-global.d.mts","sourceRoot":"","sources":["../src/node-global.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,GAAG,MAAM,kBAAkB,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,MAAM,4BAA4B,CAAC;AAarD,OAAO,CAAC,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,MAAM,IAAI,EAAE,OAAO,OAAO,CAAC,GAAG,CAAC;IAE/B,MAAM,IAAI,EAAE,OAAO,KAAK,CAAC;IACzB,MAAM,EAAE,EAAE,OAAO,GAAG,CAAC;IACrB,MAAM,IAAI,EAAE,OAAO,KAAK,CAAC;CAC1B"}
1
+ {"version":3,"file":"node-global.d.mts","sourceRoot":"","sources":["../src/node-global.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,GAAG,MAAM,kBAAkB,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,WAAW,CAAC;AAGnC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,MAAM,4BAA4B,CAAC;AAcrD,OAAO,CAAC,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,MAAM,IAAI,EAAE,OAAO,OAAO,CAAC,GAAG,CAAC;IAE/B,MAAM,IAAI,EAAE,OAAO,KAAK,CAAC;IACzB,MAAM,EAAE,EAAE,OAAO,GAAG,CAAC;IACrB,MAAM,IAAI,EAAE,OAAO,KAAK,CAAC;CAC1B"}
@@ -10,5 +10,6 @@ const globalsDef = {
10
10
  fs: fs_,
11
11
  glob: glob_,
12
12
  };
13
+ // eslint-disable-next-line functional/immutable-data
13
14
  Object.assign(globalThis, globalsDef);
14
15
  //# sourceMappingURL=node-global.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"node-global.mjs","sources":["../src/node-global.mts"],"sourcesContent":[null],"names":["$_"],"mappings":";;;;;AAKA,MAAM,UAAU,GAAG;AACjB,IAAA,CAAC,EAAEA,CAAE;IACL,IAAI,EAAE,OAAO,CAAC,GAAG;AAEjB,IAAA,IAAI,EAAE,KAAK;AACX,IAAA,EAAE,EAAE,GAAG;AACP,IAAA,IAAI,EAAE,KAAK;CACH;AAEV,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC"}
1
+ {"version":3,"file":"node-global.mjs","sources":["../src/node-global.mts"],"sourcesContent":[null],"names":["$_"],"mappings":";;;;;AAOA,MAAM,UAAU,GAAG;AACjB,IAAA,CAAC,EAAEA,CAAE;IACL,IAAI,EAAE,OAAO,CAAC,GAAG;AAEjB,IAAA,IAAI,EAAE,KAAK;AACX,IAAA,EAAE,EAAE,GAAG;AACP,IAAA,IAAI,EAAE,KAAK;CACH;AAEV;AACA,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-repo-utils",
3
- "version": "5.0.2",
3
+ "version": "5.2.0",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "typescript"
@@ -16,13 +16,13 @@
16
16
  "exports": {
17
17
  ".": {
18
18
  "import": {
19
- "types": "./dist/index.d.mts",
19
+ "types": "./dist/type.d.mts",
20
20
  "default": "./dist/index.mjs"
21
21
  }
22
22
  }
23
23
  },
24
24
  "module": "./dist/index.mjs",
25
- "types": "./dist/index.d.mts",
25
+ "types": "./dist/type.d.mts",
26
26
  "files": [
27
27
  "src",
28
28
  "dist",
@@ -47,7 +47,7 @@
47
47
  "test:ui": "npm run z:vitest -- --ui",
48
48
  "testw": "npm run z:vitest -- watch",
49
49
  "tsc": "tsc --noEmit",
50
- "tscw": "tsc --noEmit --watch",
50
+ "tscw": "tsc --noEmit --watch -p ./tsconfig.json",
51
51
  "type-check": "tsc --noEmit",
52
52
  "update-packages": "npx npm-check-updates -u --install always",
53
53
  "z:vitest": "vitest --config ./configs/vitest.config.ts"
@@ -57,7 +57,7 @@
57
57
  "fast-glob": "^3.3.3",
58
58
  "micromatch": "^4.0.8",
59
59
  "prettier": "^3.6.2",
60
- "ts-data-forge": "^3.0.2"
60
+ "ts-data-forge": "^3.0.4"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@eslint/js": "^9.31.0",
@@ -69,21 +69,32 @@
69
69
  "@semantic-release/exec": "^7.1.0",
70
70
  "@semantic-release/git": "^10.0.1",
71
71
  "@semantic-release/github": "^11.0.3",
72
- "@semantic-release/npm": "^12.0.1",
72
+ "@semantic-release/npm": "^12.0.2",
73
73
  "@semantic-release/release-notes-generator": "^14.0.3",
74
74
  "@types/node": "^24.1.0",
75
75
  "@vitest/coverage-v8": "^3.2.4",
76
76
  "@vitest/ui": "^3.2.4",
77
77
  "conventional-changelog-conventionalcommits": "^9.1.0",
78
- "cspell": "^9.1.5",
78
+ "cspell": "^9.2.0",
79
79
  "dedent": "^1.6.0",
80
- "eslint": "^9.31.0",
80
+ "eslint": "^9.32.0",
81
+ "eslint-import-resolver-typescript": "4.4.4",
82
+ "eslint-plugin-array-func": "5.0.2",
83
+ "eslint-plugin-functional": "9.0.2",
84
+ "eslint-plugin-import": "2.32.0",
85
+ "eslint-plugin-prefer-arrow-functions": "3.6.2",
86
+ "eslint-plugin-promise": "7.2.1",
87
+ "eslint-plugin-security": "3.0.1",
88
+ "eslint-plugin-unicorn": "60.0.0",
89
+ "eslint-plugin-vitest": "0.5.4",
81
90
  "markdownlint-cli2": "^0.18.1",
91
+ "prettier-plugin-jsdoc": "^1.3.3",
82
92
  "prettier-plugin-organize-imports": "^4.2.0",
83
93
  "prettier-plugin-packagejson": "^2.5.19",
84
94
  "rollup": "^4.45.1",
85
95
  "semantic-release": "^24.2.7",
86
96
  "ts-type-forge": "^2.1.1",
97
+ "tslib": "^2.8.1",
87
98
  "tsx": "^4.20.3",
88
99
  "typedoc": "^0.28.7",
89
100
  "typedoc-plugin-markdown": "^4.7.1",
@@ -1,27 +1,32 @@
1
- import { isString } from 'ts-data-forge';
1
+ import { Arr, type IMap, isString } from 'ts-data-forge';
2
2
  import '../node-global.mjs';
3
3
  import { assertPathExists } from './assert-path-exists.mjs';
4
4
 
5
- /**
6
- * Configuration for directory extension checking.
7
- */
5
+ /** Configuration for directory extension checking. */
8
6
  export type CheckExtConfig = DeepReadonly<{
9
7
  /** Array of directory paths and their expected extensions */
10
8
  directories: {
11
9
  /** Directory path to check */
12
10
  path: string;
13
11
 
14
- /** Expected file extension(s) (including the dot). Can be a single extension or an array of valid extensions */
12
+ /**
13
+ * Expected file extension(s) (including the dot). Can be a single extension
14
+ * or an array of valid extensions
15
+ */
15
16
  extension: `.${string}` | `.${string}`[];
16
17
 
17
- /** Optional glob patterns to ignore (defaults to ['tsconfig.json', 'globals.d.*']) */
18
+ /**
19
+ * Optional glob patterns to ignore (defaults to ['tsconfig.json',
20
+ * 'globals.d.*'])
21
+ */
18
22
  ignorePatterns?: string[];
19
23
  }[];
20
24
  }>;
21
25
 
22
26
  /**
23
- * Validates that all files in specified directories have the correct extensions.
24
- * Exits with code 1 if any files have incorrect extensions.
27
+ * Validates that all files in specified directories have the correct
28
+ * extensions. Exits with code 1 if any files have incorrect extensions.
29
+ *
25
30
  * @param config - Configuration specifying directories and expected extensions.
26
31
  */
27
32
  export const assertExt = async (config: CheckExtConfig): Promise<void> => {
@@ -47,22 +52,35 @@ export const assertExt = async (config: CheckExtConfig): Promise<void> => {
47
52
  if (allIncorrectFiles.length > 0) {
48
53
  const generateErrorMessage = (): string => {
49
54
  // Group directories by extension for a cleaner message
50
- const extensionGroups = new Map<string, string[]>();
51
-
52
- for (const { path: dirPath, extension } of config.directories) {
53
- const relativePath = path.relative(process.cwd(), dirPath);
54
- const extKey = isString(extension) ? extension : extension.join(' or ');
55
- if (!extensionGroups.has(extKey)) {
56
- extensionGroups.set(extKey, []);
57
- }
58
- extensionGroups.get(extKey)?.push(relativePath);
59
- }
55
+ const extensionGroups: IMap<
56
+ string,
57
+ readonly Readonly<{
58
+ relativePath: string;
59
+ extKey: string;
60
+ }>[]
61
+ > = Arr.groupBy(
62
+ config.directories.map(({ path: dirPath, extension }) => {
63
+ const relativePath = path.relative(process.cwd(), dirPath);
64
+ const extKey = isString(extension)
65
+ ? extension
66
+ : extension.join(' or ');
67
+
68
+ return {
69
+ relativePath,
70
+ extKey,
71
+ };
72
+ }),
73
+ ({ extKey }) => extKey,
74
+ );
60
75
 
61
76
  // Generate message parts for each extension
62
77
  const messageParts = Array.from(
63
78
  extensionGroups.entries(),
64
79
  ([ext, dirs]) => {
65
- const dirList = dirs.length === 1 ? dirs[0] : dirs.join(', ');
80
+ const dirList =
81
+ dirs.length === 1
82
+ ? dirs[0]?.relativePath
83
+ : dirs.map((d) => d.relativePath).join(', ');
66
84
  return `${dirList} should have ${ext} extension`;
67
85
  },
68
86
  );
@@ -86,6 +104,7 @@ export const assertExt = async (config: CheckExtConfig): Promise<void> => {
86
104
 
87
105
  /**
88
106
  * Checks if all files in a directory have the expected extension.
107
+ *
89
108
  * @param dir - The directory to check.
90
109
  * @param expectedExtensions - The expected file extensions.
91
110
  * @param ignorePatterns - Optional glob patterns to ignore.
@@ -2,6 +2,7 @@ import '../node-global.mjs';
2
2
 
3
3
  /**
4
4
  * Checks if a file or directory exists.
5
+ *
5
6
  * @param filePath - The path to check.
6
7
  * @returns True if the path exists.
7
8
  */
@@ -16,6 +17,7 @@ export const pathExists = async (filePath: string): Promise<boolean> => {
16
17
 
17
18
  /**
18
19
  * Validates that a path exists and exits with code 1 if it doesn't.
20
+ *
19
21
  * @param filePath - The path to validate.
20
22
  * @param description - Description for error message (defaults to 'Path').
21
23
  */
@@ -3,6 +3,7 @@ import '../node-global.mjs';
3
3
 
4
4
  /**
5
5
  * Checks if the repository has uncommitted changes.
6
+ *
6
7
  * @returns True if the repo is dirty, false otherwise.
7
8
  * @throws Error if git command fails.
8
9
  */
@@ -14,8 +15,8 @@ export const repoIsDirty = async (
14
15
  };
15
16
 
16
17
  /**
17
- * Checks if the repository is clean and exits with code 1 if it is dirty.
18
- * Shows git status and diff output before exiting.
18
+ * Checks if the repository is clean and exits with code 1 if it is dirty. Shows
19
+ * git status and diff output before exiting.
19
20
  */
20
21
  export const assertRepoIsClean = async (
21
22
  options?: Readonly<{ silent?: boolean }>,
@@ -56,6 +57,7 @@ export const assertRepoIsClean = async (
56
57
 
57
58
  /**
58
59
  * Gets the git status of the repository.
60
+ *
59
61
  * @returns An object containing status information.
60
62
  */
61
63
  const getGitStatus = async (
@@ -2,9 +2,7 @@ import { type ExecException } from 'node:child_process';
2
2
  import { Result } from 'ts-data-forge';
3
3
  import '../node-global.mjs';
4
4
 
5
- /**
6
- * Get files that have been changed (git status).
7
- */
5
+ /** Get files that have been changed (git status). */
8
6
  export const getUntrackedFiles = async (
9
7
  options?: Readonly<{
10
8
  /** @default true */
@@ -42,9 +40,7 @@ export const getUntrackedFiles = async (
42
40
  return Result.ok(files);
43
41
  };
44
42
 
45
- /**
46
- * Get files that differ from the specified base branch or commit
47
- */
43
+ /** Get files that differ from the specified base branch or commit */
48
44
  export const getDiffFrom = async (
49
45
  base: string,
50
46
  options?: Readonly<{
@@ -3,12 +3,9 @@ import '../node-global.mjs';
3
3
  import { getDiffFrom, getUntrackedFiles } from './diff.mjs';
4
4
 
5
5
  describe('diff', () => {
6
- // Use project root for test files to ensure git tracking
7
- const testFiles: string[] = [];
8
-
9
- afterEach(async () => {
10
- // Clean up test files
11
- for (const file of testFiles) {
6
+ // Helper function to clean up test files
7
+ const cleanupTestFiles = async (files: Set<string>): Promise<void> => {
8
+ for (const file of files) {
12
9
  try {
13
10
  // eslint-disable-next-line no-await-in-loop
14
11
  await fs.rm(file, { force: true });
@@ -16,8 +13,7 @@ describe('diff', () => {
16
13
  // Ignore cleanup errors
17
14
  }
18
15
  }
19
- testFiles.length = 0;
20
- });
16
+ };
21
17
 
22
18
  describe('getUntrackedFiles', () => {
23
19
  test('should return empty array when no files are changed', async () => {
@@ -30,10 +26,12 @@ describe('diff', () => {
30
26
  });
31
27
 
32
28
  test('should detect newly created files', async () => {
29
+ const mut_testFiles = new Set<string>();
30
+
33
31
  // Create a new file in project root
34
32
  const testFileName = 'test-new-file.tmp';
35
33
  const testFilePath = path.join(process.cwd(), testFileName);
36
- testFiles.push(testFilePath);
34
+ mut_testFiles.add(testFilePath);
37
35
 
38
36
  await fs.writeFile(testFilePath, 'test content');
39
37
 
@@ -42,42 +40,51 @@ describe('diff', () => {
42
40
  expect(Result.isOk(result)).toBe(true);
43
41
  if (Result.isOk(result)) {
44
42
  const files = result.value;
45
- expect(files.some((file) => file === testFileName)).toBe(true);
43
+ expect(files).toContain(testFileName);
46
44
  }
45
+
46
+ await cleanupTestFiles(mut_testFiles);
47
47
  });
48
48
 
49
49
  test('should detect modified existing files', async () => {
50
+ const mut_testFiles = new Set<string>();
51
+
50
52
  // Use an existing file in the project that we can modify safely
51
53
  const testFileName = 'test-modify-file.tmp';
52
- const testFilePath = path.join(process.cwd(), testFileName);
53
- testFiles.push(testFilePath);
54
+ const mut_testFilePath = path.join(process.cwd(), testFileName);
55
+ mut_testFiles.add(mut_testFilePath);
54
56
 
55
57
  // Create and commit the file first
56
- await fs.writeFile(testFilePath, 'initial content');
58
+ await fs.writeFile(mut_testFilePath, 'initial content');
57
59
 
58
60
  // Add to git to track it
59
61
  await $(`git add ${testFileName}`, { silent: true });
60
62
 
61
63
  // Modify the file
62
- await fs.writeFile(testFilePath, 'modified content');
64
+ await fs.writeFile(mut_testFilePath, 'modified content');
63
65
 
64
66
  const result = await getUntrackedFiles({ silent: true });
65
67
 
66
68
  expect(Result.isOk(result)).toBe(true);
67
69
  if (Result.isOk(result)) {
68
70
  const files = result.value;
69
- expect(files.some((file) => file === testFileName)).toBe(false);
71
+ expect(files).not.toContain(testFileName);
70
72
  }
71
73
 
72
74
  // Reset git state
73
75
  await $(`git reset HEAD ${testFileName}`, { silent: true });
76
+
77
+ await cleanupTestFiles(mut_testFiles);
74
78
  });
75
79
 
76
80
  test('should detect multiple types of changes', async () => {
81
+ const mut_testFiles = new Set<string>();
82
+
77
83
  // Create multiple test files
78
84
  const newFile = path.join(process.cwd(), 'test-new-file.tmp');
79
85
  const modifyFile = path.join(process.cwd(), 'test-modify-file.tmp');
80
- testFiles.push(newFile, modifyFile);
86
+ mut_testFiles.add(newFile);
87
+ mut_testFiles.add(modifyFile);
81
88
 
82
89
  // Create new file
83
90
  await fs.writeFile(newFile, 'new file content');
@@ -94,14 +101,14 @@ describe('diff', () => {
94
101
  expect(Result.isOk(result)).toBe(true);
95
102
  if (Result.isOk(result)) {
96
103
  const files = result.value;
97
- expect(files.some((file) => file === 'test-new-file.tmp')).toBe(true);
98
- expect(files.some((file) => file === 'test-modify-file.tmp')).toBe(
99
- false,
100
- );
104
+ expect(files).toContain('test-new-file.tmp');
105
+ expect(files).not.toContain('test-modify-file.tmp');
101
106
  }
102
107
 
103
108
  // Reset git state
104
109
  await $(`git reset HEAD test-modify-file.tmp`, { silent: true });
110
+
111
+ await cleanupTestFiles(mut_testFiles);
105
112
  });
106
113
 
107
114
  test('should exclude deleted files from results', async () => {
@@ -113,10 +120,10 @@ describe('diff', () => {
113
120
  if (Result.isOk(result)) {
114
121
  const files = result.value;
115
122
  // Verify no deleted files are included (status 'D')
116
- files.forEach((file) => {
123
+ for (const file of files) {
117
124
  expect(typeof file).toBe('string');
118
125
  expect(file.length).toBeGreaterThan(0);
119
- });
126
+ }
120
127
  }
121
128
  });
122
129
 
@@ -137,11 +144,11 @@ describe('diff', () => {
137
144
  const files = result.value;
138
145
 
139
146
  // Each file should be a non-empty string
140
- files.forEach((file) => {
147
+ for (const file of files) {
141
148
  expect(typeof file).toBe('string');
142
149
  expect(file.trim()).toBe(file); // No leading/trailing whitespace
143
150
  expect(file.length).toBeGreaterThan(0);
144
- });
151
+ }
145
152
  }
146
153
  });
147
154
 
@@ -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.
@@ -22,6 +23,7 @@ export const $ = (
22
23
  return new Promise((resolve) => {
23
24
  const execOptions = { timeout };
24
25
 
26
+ // eslint-disable-next-line security/detect-child-process
25
27
  exec(cmd, execOptions, (error, stdout, stderr) => {
26
28
  if (!silent) {
27
29
  if (stdout !== '') {
@@ -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 (
@@ -27,6 +27,7 @@ describe('formatFiles', () => {
27
27
  fs.readFile(filePath, 'utf8');
28
28
 
29
29
  test('should format files matching glob pattern', async () => {
30
+ vi.clearAllMocks();
30
31
  // Setup test directory
31
32
  await fs.mkdir(testDir, { recursive: true });
32
33
 
@@ -84,6 +85,7 @@ describe('formatFiles', () => {
84
85
  });
85
86
 
86
87
  test('should return ok when no files match pattern', async () => {
88
+ vi.clearAllMocks();
87
89
  const result = await formatFiles('/non-existent-path/*.ts', {
88
90
  silent: true,
89
91
  });
@@ -91,6 +93,7 @@ describe('formatFiles', () => {
91
93
  });
92
94
 
93
95
  test('should handle nested directories with glob pattern', async () => {
96
+ vi.clearAllMocks();
94
97
  // Setup test directory with nested structure
95
98
  await fs.mkdir(path.join(testDir, 'src', 'utils'), { recursive: true });
96
99
 
@@ -143,6 +146,7 @@ describe('formatFilesList', () => {
143
146
  fs.readFile(filePath, 'utf8');
144
147
 
145
148
  test('should format a list of files', async () => {
149
+ vi.clearAllMocks();
146
150
  await fs.mkdir(testDir, { recursive: true });
147
151
 
148
152
  try {
@@ -189,6 +193,7 @@ describe('formatFilesList', () => {
189
193
  });
190
194
 
191
195
  test('should return ok for empty file list', async () => {
196
+ vi.clearAllMocks();
192
197
  const result = await formatFilesList([], {
193
198
  silent: true,
194
199
  });
@@ -211,11 +216,8 @@ describe('formatDiffFrom', () => {
211
216
  const readTestFile = async (filePath: string): Promise<string> =>
212
217
  fs.readFile(filePath, 'utf8');
213
218
 
214
- beforeEach(() => {
215
- vi.clearAllMocks();
216
- });
217
-
218
219
  test('should format files from diff', async () => {
220
+ vi.clearAllMocks();
219
221
  await fs.mkdir(testDir, { recursive: true });
220
222
 
221
223
  try {
@@ -250,6 +252,7 @@ describe('formatDiffFrom', () => {
250
252
  });
251
253
 
252
254
  test('should include untracked files when option is set', async () => {
255
+ vi.clearAllMocks();
253
256
  await fs.mkdir(testDir, { recursive: true });
254
257
 
255
258
  try {
@@ -302,6 +305,7 @@ describe('formatDiffFrom', () => {
302
305
  });
303
306
 
304
307
  test('should deduplicate files when including untracked', async () => {
308
+ vi.clearAllMocks();
305
309
  await fs.mkdir(testDir, { recursive: true });
306
310
 
307
311
  try {