ts-repo-utils 10.1.8 → 10.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 (47) hide show
  1. package/README.md +73 -11
  2. package/dist/cmd/assert-repo-is-clean.mjs +0 -0
  3. package/dist/cmd/check-should-run-type-checks.mjs +0 -0
  4. package/dist/cmd/check-should-run.d.mts +3 -0
  5. package/dist/cmd/check-should-run.d.mts.map +1 -0
  6. package/dist/cmd/check-should-run.mjs +44 -0
  7. package/dist/cmd/check-should-run.mjs.map +1 -0
  8. package/dist/cmd/format-diff-from.mjs +0 -0
  9. package/dist/cmd/format-uncommitted.mjs +0 -0
  10. package/dist/cmd/gen-index-ts.mjs +1 -0
  11. package/dist/cmd/gen-index-ts.mjs.map +1 -1
  12. package/dist/entry-point.mjs +2 -1
  13. package/dist/entry-point.mjs.map +1 -1
  14. package/dist/functions/index.mjs +2 -1
  15. package/dist/functions/index.mjs.map +1 -1
  16. package/dist/functions/should-run.d.mts +62 -1
  17. package/dist/functions/should-run.d.mts.map +1 -1
  18. package/dist/functions/should-run.mjs +38 -34
  19. package/dist/functions/should-run.mjs.map +1 -1
  20. package/dist/functions/workspace-utils/get-workspace-packages.d.mts +5 -2
  21. package/dist/functions/workspace-utils/get-workspace-packages.d.mts.map +1 -1
  22. package/dist/functions/workspace-utils/get-workspace-packages.mjs +9 -11
  23. package/dist/functions/workspace-utils/get-workspace-packages.mjs.map +1 -1
  24. package/dist/functions/workspace-utils/index.mjs +1 -0
  25. package/dist/functions/workspace-utils/index.mjs.map +1 -1
  26. package/dist/functions/workspace-utils/run-cmd-in-stages.d.mts +7 -1
  27. package/dist/functions/workspace-utils/run-cmd-in-stages.d.mts.map +1 -1
  28. package/dist/functions/workspace-utils/run-cmd-in-stages.mjs +7 -2
  29. package/dist/functions/workspace-utils/run-cmd-in-stages.mjs.map +1 -1
  30. package/dist/functions/workspace-utils/types.d.mts +13 -0
  31. package/dist/functions/workspace-utils/types.d.mts.map +1 -1
  32. package/dist/functions/workspace-utils/types.mjs +6 -0
  33. package/dist/functions/workspace-utils/types.mjs.map +1 -1
  34. package/dist/index.mjs +2 -1
  35. package/dist/index.mjs.map +1 -1
  36. package/package.json +60 -81
  37. package/src/cmd/assert-repo-is-clean.mts +0 -0
  38. package/src/cmd/check-should-run-type-checks.mts +0 -0
  39. package/src/cmd/check-should-run.mts +45 -0
  40. package/src/cmd/format-diff-from.mts +0 -0
  41. package/src/cmd/format-uncommitted.mts +0 -0
  42. package/src/cmd/gen-index-ts.mts +0 -0
  43. package/src/functions/should-run.mts +134 -59
  44. package/src/functions/should-run.test.mts +196 -0
  45. package/src/functions/workspace-utils/get-workspace-packages.mts +17 -16
  46. package/src/functions/workspace-utils/run-cmd-in-stages.mts +11 -1
  47. package/src/functions/workspace-utils/types.mts +20 -0
package/README.md CHANGED
@@ -7,8 +7,8 @@
7
7
  - API reference: <https://noshiro-pf.github.io/ts-repo-utils/>
8
8
 
9
9
  [![npm downloads](https://img.shields.io/npm/dm/ts-repo-utils.svg)](https://www.npmjs.com/package/ts-repo-utils)
10
- [![License](https://img.shields.io/npm/l/ts-repo-utils.svg)](./LICENSE)
11
- [![codecov](https://codecov.io/gh/noshiro-pf/ts-repo-utils/graph/badge.svg?token=S4688Q0CX3)](https://codecov.io/gh/noshiro-pf/ts-repo-utils)
10
+ [![License](https://img.shields.io/npm/l/ts-repo-utils.svg)](https://github.com/noshiro-pf/mono/blob/main/libs/ts-repo-utils/LICENSE)
11
+ [![codecov](https://codecov.io/gh/noshiro-pf/mono/graph/badge.svg?component=ts-repo-utils)](https://codecov.io/gh/noshiro-pf/mono)
12
12
 
13
13
  Utilities for TypeScript Repositories.
14
14
 
@@ -161,10 +161,56 @@ npm exec -- gen-index-ts ./src --target-ext .mts --index-ext .mts --export-ext .
161
161
  - `--min-depth` - Minimum depth to start generating index files (default: 0)
162
162
  - `--silent` - Suppress output messages (optional)
163
163
 
164
+ ### `check-should-run`
165
+
166
+ Checks whether a CI step should run, based on which files differ from the base branch. Reports `false` when every changed file matches one of the `--paths-ignore` patterns, so a step can be skipped when nothing it checks has changed.
167
+
168
+ Prefer this over a workflow-level `paths-ignore` trigger filter: a workflow that a path filter skips never reports a status check, so a pull request that changed only the filtered paths waits forever on a required check that never arrives. Gating the individual steps keeps the workflow running — and reporting — while doing no work.
169
+
170
+ ```bash
171
+ # Skip when the diff only touches a directory nothing checks
172
+ npm exec -- check-should-run --paths-ignore 'experimental/'
173
+
174
+ # Several patterns, and a custom base branch
175
+ npm exec -- check-should-run \
176
+ --paths-ignore 'experimental/' \
177
+ --paths-ignore 'docs/' \
178
+ --paths-ignore '**.md' \
179
+ --base-branch origin/develop
180
+ ```
181
+
182
+ ```yaml
183
+ # Example in GitHub Actions
184
+ - name: Check diff
185
+ id: check_diff
186
+ run: npm exec -- check-should-run --paths-ignore 'experimental/'
187
+
188
+ - name: Run the checks
189
+ if: steps.check_diff.outputs.should_run == 'true'
190
+ run: npm run check
191
+ ```
192
+
193
+ **Options:**
194
+
195
+ - `--paths-ignore` - Patterns whose files do not affect the gated step (optional, can be specified multiple times). Matched with [micromatch](https://github.com/micromatch/micromatch) (with `dot: true`) against paths relative to the repository root, with one shorthand: a pattern ending with `/` is a directory prefix.
196
+ - Exact file matches: `.editorconfig`
197
+ - Directory prefixes: `docs/` (the same as `docs/**`)
198
+ - Globs: `**.md`, `libs/*/samples/**`
199
+ - Default: `[]` — nothing is ignored, so the step always runs when anything changed
200
+ - `--base-branch` - Base branch to compare against for determining changed files (default: `origin/main`)
201
+
202
+ An empty diff reads as "nothing changed", so it reports `should_run=false`. On a push to the base branch itself the diff against that branch is empty for that reason; pass the commit the branch pointed at before the push as `--base-branch` there.
203
+
204
+ **GitHub Actions Integration:**
205
+
206
+ When running in GitHub Actions, the command appends `should_run=true` or `should_run=false` to the file named by the `GITHUB_OUTPUT` environment variable, which can be used in subsequent steps.
207
+
164
208
  ### `check-should-run-type-checks`
165
209
 
166
210
  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.
167
211
 
212
+ This is [`check-should-run`](#check-should-run) with a default ignore list of the files a TypeScript project typically has but never type-checks. For a step that is not a type check, use `check-should-run` and state its paths.
213
+
168
214
  ```bash
169
215
  # Basic usage (compares against origin/main)
170
216
  npm exec -- check-should-run-type-checks
@@ -193,11 +239,11 @@ npm exec -- check-should-run-type-checks \
193
239
 
194
240
  **Options:**
195
241
 
196
- - `--paths-ignore` - Patterns to ignore when checking if type checks should run (optional, can be specified multiple times)
242
+ - `--paths-ignore` - Patterns to ignore when checking if type checks should run (optional, can be specified multiple times). Matched as described for [`check-should-run`](#check-should-run); passing it replaces the default list rather than adding to it.
197
243
  - Supports exact file matches: `.cspell.config.yaml`
198
244
  - Directory prefixes: `docs/` (matches any file in docs directory)
199
245
  - File extensions: `**.md` (matches any markdown file)
200
- - Default: `['LICENSE', '.editorconfig', '.gitignore', '.cspell.config.yaml', '.markdownlint-cli2.mjs', '.npmignore', '.prettierignore', '.prettierrc', 'docs/', '**.md', '**.txt']`
246
+ - Default: `['LICENSE', '.editorconfig', '.gitignore', '.cspell.json', '.cspell.config.yaml', '.markdownlint-cli2.mjs', '.npmignore', '.prettierignore', '.prettierrc', 'docs/', '**.md', '**.txt']`
201
247
  - `--base-branch` - Base branch to compare against for determining changed files (default: `origin/main`)
202
248
 
203
249
  **GitHub Actions Integration:**
@@ -498,13 +544,27 @@ type Ret = Result<
498
544
 
499
545
  #### Build Optimization Utilities
500
546
 
547
+ ##### `checkShouldRun(options?): Promise<boolean>`
548
+
549
+ Checks whether a CI step should run, based on which files differ from the base branch. Reports `false` when every changed file matches one of the ignored patterns.
550
+ (Function version of the `check-should-run` command)
551
+
501
552
  ##### `checkShouldRunTypeChecks(options?): Promise<boolean>`
502
553
 
503
- 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.
554
+ `checkShouldRun` with a default ignore list of the files a TypeScript project typically has but never type-checks. Optimizes CI/CD pipelines by skipping type checks when only non-TypeScript files have changed.
504
555
  (Function version of the `check-should-run-type-checks` command)
505
556
 
506
557
  ```tsx
507
- import { $, checkShouldRunTypeChecks } from 'ts-repo-utils';
558
+ import { $, checkShouldRun, checkShouldRunTypeChecks } from 'ts-repo-utils';
559
+
560
+ // Gate any step on the paths it cares about
561
+ const shouldRunStyleChecks = await checkShouldRun({
562
+ pathsIgnore: ['experimental/'],
563
+ });
564
+
565
+ if (shouldRunStyleChecks) {
566
+ await $('npm run fmt:check');
567
+ }
508
568
 
509
569
  // Use default settings (compare against origin/main)
510
570
  const shouldRun = await checkShouldRunTypeChecks();
@@ -520,15 +580,17 @@ const shouldRun2 = await checkShouldRunTypeChecks({
520
580
  });
521
581
  ```
522
582
 
523
- **Options:**
583
+ **Options (both functions):**
524
584
 
525
- - `pathsIgnore?` - Patterns to ignore when checking if type checks should run:
585
+ - `pathsIgnore?` - Patterns whose files do not affect the gated step. Matched with [micromatch](https://github.com/micromatch/micromatch) (with `dot: true`) against paths relative to the repository root, with a trailing `/` as the shorthand for a directory:
526
586
  - Exact file matches: `.cspell.config.yaml`
527
- - Directory prefixes: `docs/` (matches any file in docs directory)
528
- - File extensions: `**.md` (matches any markdown file)
529
- - Default: `['LICENSE', '.editorconfig', '.gitignore', '.cspell.config.yaml', '.markdownlint-cli2.mjs', '.npmignore', '.prettierignore', '.prettierrc', 'docs/', '**.md', '**.txt']`
587
+ - Directory prefixes: `docs/` (the same as `docs/**`)
588
+ - Globs: `**.md` (matches any markdown file), `libs/*/samples/**`
589
+ - Default: `[]` for `checkShouldRun`; for `checkShouldRunTypeChecks`, `['LICENSE', '.editorconfig', '.gitignore', '.cspell.json', '.cspell.config.yaml', '.markdownlint-cli2.mjs', '.npmignore', '.prettierignore', '.prettierrc', 'docs/', '**.md', '**.txt']` — passing the option replaces that list rather than adding to it
530
590
  - `baseBranch?` - Base branch to compare against (default: `origin/main`)
531
591
 
592
+ Both return whether the step should run, and, in GitHub Actions, append `should_run=<result>` to the file named by `GITHUB_OUTPUT`. An empty diff reads as "nothing changed" and so returns `false`.
593
+
532
594
  ### Code Formatting Utilities
533
595
 
534
596
  #### `formatFilesGlob(pathGlob: string, options?): Promise<Result<undefined, unknown>>`
File without changes
File without changes
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=check-should-run.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-should-run.d.mts","sourceRoot":"","sources":["../../src/cmd/check-should-run.mts"],"names":[],"mappings":""}
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ import * as cmd from 'cmd-ts';
3
+ import { Arr } from 'ts-data-forge';
4
+ import 'node:path';
5
+ import 'node:fs/promises';
6
+ import 'fast-glob';
7
+ import 'node:child_process';
8
+ import 'prettier';
9
+ import 'micromatch';
10
+ import 'node:fs';
11
+ import 'node:url';
12
+ import { checkShouldRun } from '../functions/should-run.mjs';
13
+ import '@sindresorhus/is';
14
+
15
+ const cmdDef = cmd.command({
16
+ name: "check-should-run-cli",
17
+ version: "10.2.0",
18
+ args: {
19
+ pathsIgnore: cmd.multioption({
20
+ long: "paths-ignore",
21
+ type: cmd.optional(cmd.array(cmd.string)),
22
+ description: 'Patterns whose files do not affect the gated step. Matched with micromatch against repository-relative paths, with a trailing "/" as the shorthand for a directory (e.g. "experimental/", ".editorconfig", "**.md"). Defaults to ignoring nothing.'
23
+ }),
24
+ baseBranch: cmd.option({
25
+ long: "base-branch",
26
+ type: cmd.optional(cmd.string),
27
+ description: 'Base branch to compare against for determining changed files. Defaults to "origin/main"'
28
+ })
29
+ },
30
+ handler: (args) => {
31
+ main(args).catch((error) => {
32
+ console.error("An error occurred:", error);
33
+ process.exit(1);
34
+ });
35
+ }
36
+ });
37
+ const main = async (args) => {
38
+ await checkShouldRun({
39
+ pathsIgnore: args.pathsIgnore,
40
+ baseBranch: args.baseBranch
41
+ });
42
+ };
43
+ await cmd.run(cmdDef, Arr.skip(process.argv, 2));
44
+ //# sourceMappingURL=check-should-run.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-should-run.mjs","sources":["../../src/cmd/check-should-run.mts"],"sourcesContent":["#!/usr/bin/env node\n\nimport * as cmd from 'cmd-ts';\nimport { Arr } from 'ts-data-forge';\nimport { checkShouldRun } from '../functions/index.mjs';\n\nconst cmdDef = cmd.command({\n name: 'check-should-run-cli',\n version: '10.2.0',\n args: {\n pathsIgnore: cmd.multioption({\n long: 'paths-ignore',\n type: cmd.optional(cmd.array(cmd.string)),\n description:\n 'Patterns whose files do not affect the gated step. Matched with micromatch against repository-relative paths, with a trailing \"/\" as the shorthand for a directory (e.g. \"experimental/\", \".editorconfig\", \"**.md\"). Defaults to ignoring nothing.',\n }),\n baseBranch: cmd.option({\n long: 'base-branch',\n type: cmd.optional(cmd.string),\n description:\n 'Base branch to compare against for determining changed files. Defaults to \"origin/main\"',\n }),\n },\n handler: (args) => {\n main(args).catch((error: unknown) => {\n console.error('An error occurred:', error);\n\n process.exit(1);\n });\n },\n});\n\nconst main = async (\n args: Readonly<{\n pathsIgnore?: readonly string[];\n baseBranch?: string;\n }>,\n): Promise<void> => {\n await checkShouldRun({\n pathsIgnore: args.pathsIgnore,\n baseBranch: args.baseBranch,\n });\n};\n\nawait cmd.run(cmdDef, Arr.skip(process.argv, 2));\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAMA,MAAM,MAAA,GAAS,IAAI,OAAA,CAAQ;AAAA,EACzB,IAAA,EAAM,sBAAA;AAAA,EACN,OAAA,EAAS,QAAA;AAAA,EACT,IAAA,EAAM;AAAA,IACJ,WAAA,EAAa,IAAI,WAAA,CAAY;AAAA,MAC3B,IAAA,EAAM,cAAA;AAAA,MACN,MAAM,GAAA,CAAI,QAAA,CAAS,IAAI,KAAA,CAAM,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,MACxC,WAAA,EACE;AAAA,KACH,CAAA;AAAA,IACD,UAAA,EAAY,IAAI,MAAA,CAAO;AAAA,MACrB,IAAA,EAAM,aAAA;AAAA,MACN,IAAA,EAAM,GAAA,CAAI,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA;AAAA,MAC7B,WAAA,EACE;AAAA,KACH;AAAA,GACH;AAAA,EACA,OAAA,EAAS,CAAC,IAAA,KAAS;AACjB,IAAA,IAAA,CAAK,IAAI,CAAA,CAAE,KAAA,CAAM,CAAC,KAAA,KAAmB;AACnC,MAAA,OAAA,CAAQ,KAAA,CAAM,sBAAsB,KAAK,CAAA;AAEzC,MAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,IAChB,CAAC,CAAA;AAAA,EACH;AACF,CAAC,CAAA;AAED,MAAM,IAAA,GAAO,OACX,IAAA,KAIkB;AAClB,EAAA,MAAM,cAAA,CAAe;AAAA,IACnB,aAAa,IAAA,CAAK,WAAA;AAAA,IAClB,YAAY,IAAA,CAAK;AAAA,GAClB,CAAA;AACH,CAAA;AAEA,MAAM,GAAA,CAAI,IAAI,MAAA,EAAQ,GAAA,CAAI,KAAK,OAAA,CAAQ,IAAA,EAAM,CAAC,CAAC,CAAA"}
File without changes
File without changes
@@ -9,6 +9,7 @@ import 'prettier';
9
9
  import { genIndex } from '../functions/gen-index.mjs';
10
10
  import 'node:fs';
11
11
  import 'node:url';
12
+ import 'micromatch';
12
13
  import '@sindresorhus/is';
13
14
 
14
15
  const extensionType = cmd.extendType(cmd.string, {
@@ -1 +1 @@
1
- {"version":3,"file":"gen-index-ts.mjs","sources":["../../src/cmd/gen-index-ts.mts"],"sourcesContent":["#!/usr/bin/env node\n\n/* transformer-ignore convert-to-readonly */\n\nimport * as cmd from 'cmd-ts';\nimport { Arr, expectType } from 'ts-data-forge';\n// eslint-disable-next-line import-x/no-internal-modules, import-x/extensions\nimport { type InputOf, type OutputOf } from 'cmd-ts/dist/esm/from.js';\nimport { type NonEmptyArray } from 'ts-type-forge';\nimport { genIndex } from '../functions/index.mjs';\n\ntype Ext = `.${string}`;\n\nconst extensionType = cmd.extendType(cmd.string, {\n from: (s) => {\n if (!s.startsWith('.')) {\n throw new Error(`ext should start with '.'`);\n }\n\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n return Promise.resolve(s as Ext);\n },\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst nonEmptyArray = <T extends cmd.Type<any, any>>(\n t: T,\n commandName: string,\n): cmd.Type<readonly InputOf<T>[], NonEmptyArray<OutputOf<T>>> =>\n cmd.extendType(\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n cmd.array(t) as cmd.Type<readonly InputOf<T>[], OutputOf<T>[]>,\n {\n from: (arr) => {\n if (Arr.isEmpty(arr)) {\n throw new Error(\n `No value provided for --${commandName}. At least one value is required.`,\n );\n }\n\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n return Promise.resolve(arr as unknown as NonEmptyArray<OutputOf<T>>);\n },\n },\n );\n\nconst cmdDef = cmd.command({\n name: 'gen-index-ts-cli',\n version: '10.1.8',\n args: {\n // required args\n targetDirectory: cmd.positional({\n type: cmd.string,\n displayName: 'target-directory',\n description:\n 'Directory where the index file will be generated (Comma-separated list can be used)',\n }),\n\n targetExtensions: cmd.multioption({\n long: 'target-ext',\n type: nonEmptyArray(extensionType, 'target-ext'),\n description: 'File extensions to include in the index file',\n }),\n indexFileExtension: cmd.option({\n long: 'index-ext',\n type: extensionType,\n description: 'Extension of the index file to be generated',\n }),\n exportStatementExtension: cmd.option({\n long: 'export-ext',\n type: cmd.union([\n extensionType,\n cmd.extendType(cmd.string, {\n from: (s) => {\n if (s !== 'none') {\n throw new Error(\n `export-ext should be 'none' or a valid extension`,\n );\n }\n\n return Promise.resolve('none' as const);\n },\n }),\n ]),\n description: 'Extension of the export statements in the index file',\n }),\n\n // optional args\n exclude: cmd.multioption({\n long: 'exclude',\n type: cmd.optional(cmd.array(cmd.string)),\n description:\n 'Glob patterns of files to exclude from the index file (e.g., \"*.d.mts\", \"*.test.mts\")',\n }),\n formatCommand: cmd.option({\n long: 'fmt',\n type: cmd.optional(cmd.string),\n description:\n 'Command to format after generating the index file (e.g., \"npm run fmt\")',\n }),\n silent: cmd.flag({\n long: 'silent',\n type: cmd.optional(cmd.boolean),\n description: 'If true, suppresses output messages (default: false)',\n }),\n minDepth: cmd.option({\n long: 'min-depth',\n type: cmd.optional(cmd.number),\n description: 'Minimum depth to start generating index files (default: 0)',\n }),\n },\n handler: (args) => {\n expectType<typeof args.targetDirectory, string>('=');\n\n expectType<typeof args.targetExtensions, NonEmptyArray<Ext>>('=');\n\n expectType<typeof args.exportStatementExtension, Ext | 'none'>('=');\n\n expectType<typeof args.indexFileExtension, Ext>('=');\n\n expectType<typeof args.exclude, string[] | undefined>('=');\n\n expectType<typeof args.formatCommand, string | undefined>('=');\n\n expectType<typeof args.silent, boolean | undefined>('=');\n\n expectType<typeof args.minDepth, number | undefined>('=');\n\n main(args).catch((error: unknown) => {\n console.error('An error occurred:', error);\n\n process.exit(1);\n });\n },\n});\n\nconst main = async ({\n targetDirectory,\n targetExtensions,\n exportStatementExtension,\n indexFileExtension,\n exclude,\n formatCommand,\n silent,\n minDepth,\n}: Readonly<{\n targetDirectory: string;\n targetExtensions: readonly Ext[];\n exportStatementExtension: Ext | 'none';\n indexFileExtension: Ext;\n exclude?: readonly string[] | undefined;\n formatCommand?: string | undefined;\n silent?: boolean | undefined;\n minDepth?: number | undefined;\n}>): Promise<void> => {\n await genIndex({\n targetDirectory: targetDirectory.includes(',')\n ? targetDirectory.split(',').map((dir) => dir.trim())\n : targetDirectory,\n exportStatementExtension,\n targetExtensions,\n exclude,\n indexFileExtension,\n formatCommand,\n silent,\n minDepth,\n });\n};\n\nawait cmd.run(cmdDef, Arr.skip(process.argv, 2));\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gen-index-ts.mjs","sources":["../../src/cmd/gen-index-ts.mts"],"sourcesContent":["#!/usr/bin/env node\n\n/* transformer-ignore convert-to-readonly */\n\nimport * as cmd from 'cmd-ts';\nimport { Arr, expectType } from 'ts-data-forge';\n// eslint-disable-next-line import-x/no-internal-modules, import-x/extensions\nimport { type InputOf, type OutputOf } from 'cmd-ts/dist/esm/from.js';\nimport { type NonEmptyArray } from 'ts-type-forge';\nimport { genIndex } from '../functions/index.mjs';\n\ntype Ext = `.${string}`;\n\nconst extensionType = cmd.extendType(cmd.string, {\n from: (s) => {\n if (!s.startsWith('.')) {\n throw new Error(`ext should start with '.'`);\n }\n\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n return Promise.resolve(s as Ext);\n },\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst nonEmptyArray = <T extends cmd.Type<any, any>>(\n t: T,\n commandName: string,\n): cmd.Type<readonly InputOf<T>[], NonEmptyArray<OutputOf<T>>> =>\n cmd.extendType(\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n cmd.array(t) as cmd.Type<readonly InputOf<T>[], OutputOf<T>[]>,\n {\n from: (arr) => {\n if (Arr.isEmpty(arr)) {\n throw new Error(\n `No value provided for --${commandName}. At least one value is required.`,\n );\n }\n\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n return Promise.resolve(arr as unknown as NonEmptyArray<OutputOf<T>>);\n },\n },\n );\n\nconst cmdDef = cmd.command({\n name: 'gen-index-ts-cli',\n version: '10.1.8',\n args: {\n // required args\n targetDirectory: cmd.positional({\n type: cmd.string,\n displayName: 'target-directory',\n description:\n 'Directory where the index file will be generated (Comma-separated list can be used)',\n }),\n\n targetExtensions: cmd.multioption({\n long: 'target-ext',\n type: nonEmptyArray(extensionType, 'target-ext'),\n description: 'File extensions to include in the index file',\n }),\n indexFileExtension: cmd.option({\n long: 'index-ext',\n type: extensionType,\n description: 'Extension of the index file to be generated',\n }),\n exportStatementExtension: cmd.option({\n long: 'export-ext',\n type: cmd.union([\n extensionType,\n cmd.extendType(cmd.string, {\n from: (s) => {\n if (s !== 'none') {\n throw new Error(\n `export-ext should be 'none' or a valid extension`,\n );\n }\n\n return Promise.resolve('none' as const);\n },\n }),\n ]),\n description: 'Extension of the export statements in the index file',\n }),\n\n // optional args\n exclude: cmd.multioption({\n long: 'exclude',\n type: cmd.optional(cmd.array(cmd.string)),\n description:\n 'Glob patterns of files to exclude from the index file (e.g., \"*.d.mts\", \"*.test.mts\")',\n }),\n formatCommand: cmd.option({\n long: 'fmt',\n type: cmd.optional(cmd.string),\n description:\n 'Command to format after generating the index file (e.g., \"npm run fmt\")',\n }),\n silent: cmd.flag({\n long: 'silent',\n type: cmd.optional(cmd.boolean),\n description: 'If true, suppresses output messages (default: false)',\n }),\n minDepth: cmd.option({\n long: 'min-depth',\n type: cmd.optional(cmd.number),\n description: 'Minimum depth to start generating index files (default: 0)',\n }),\n },\n handler: (args) => {\n expectType<typeof args.targetDirectory, string>('=');\n\n expectType<typeof args.targetExtensions, NonEmptyArray<Ext>>('=');\n\n expectType<typeof args.exportStatementExtension, Ext | 'none'>('=');\n\n expectType<typeof args.indexFileExtension, Ext>('=');\n\n expectType<typeof args.exclude, string[] | undefined>('=');\n\n expectType<typeof args.formatCommand, string | undefined>('=');\n\n expectType<typeof args.silent, boolean | undefined>('=');\n\n expectType<typeof args.minDepth, number | undefined>('=');\n\n main(args).catch((error: unknown) => {\n console.error('An error occurred:', error);\n\n process.exit(1);\n });\n },\n});\n\nconst main = async ({\n targetDirectory,\n targetExtensions,\n exportStatementExtension,\n indexFileExtension,\n exclude,\n formatCommand,\n silent,\n minDepth,\n}: Readonly<{\n targetDirectory: string;\n targetExtensions: readonly Ext[];\n exportStatementExtension: Ext | 'none';\n indexFileExtension: Ext;\n exclude?: readonly string[] | undefined;\n formatCommand?: string | undefined;\n silent?: boolean | undefined;\n minDepth?: number | undefined;\n}>): Promise<void> => {\n await genIndex({\n targetDirectory: targetDirectory.includes(',')\n ? targetDirectory.split(',').map((dir) => dir.trim())\n : targetDirectory,\n exportStatementExtension,\n targetExtensions,\n exclude,\n indexFileExtension,\n formatCommand,\n silent,\n minDepth,\n });\n};\n\nawait cmd.run(cmdDef, Arr.skip(process.argv, 2));\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAaA,MAAM,aAAA,GAAgB,GAAA,CAAI,UAAA,CAAW,GAAA,CAAI,MAAA,EAAQ;AAAA,EAC/C,IAAA,EAAM,CAAC,CAAA,KAAM;AACX,IAAA,IAAI,CAAC,CAAA,CAAE,UAAA,CAAW,GAAG,CAAA,EAAG;AACtB,MAAA,MAAM,IAAI,MAAM,CAAA,yBAAA,CAA2B,CAAA;AAAA,IAAA;AAI7C,IAAA,OAAO,OAAA,CAAQ,QAAQ,CAAQ,CAAA;AAAA,EAAA;AAEnC,CAAC,CAAA;AAGD,MAAM,aAAA,GAAgB,CACpB,CAAA,EACA,WAAA,KAEA,GAAA,CAAI,UAAA;AAAA;AAAA,EAEF,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,EACX;AAAA,IACE,IAAA,EAAM,CAAC,GAAA,KAAQ;AACb,MAAA,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA,EAAG;AACpB,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,2BAA2B,WAAW,CAAA,iCAAA;AAAA,SACxC;AAAA,MAAA;AAIF,MAAA,OAAO,OAAA,CAAQ,QAAQ,GAA4C,CAAA;AAAA,IAAA;AACrE;AAEJ,CAAA;AAEF,MAAM,MAAA,GAAS,IAAI,OAAA,CAAQ;AAAA,EACzB,IAAA,EAAM,kBAAA;AAAA,EACN,OAAA,EAAS,QAAA;AAAA,EACT,IAAA,EAAM;AAAA;AAAA,IAEJ,eAAA,EAAiB,IAAI,UAAA,CAAW;AAAA,MAC9B,MAAM,GAAA,CAAI,MAAA;AAAA,MACV,WAAA,EAAa,kBAAA;AAAA,MACb,WAAA,EACE;AAAA,KACH,CAAA;AAAA,IAED,gBAAA,EAAkB,IAAI,WAAA,CAAY;AAAA,MAChC,IAAA,EAAM,YAAA;AAAA,MACN,IAAA,EAAM,aAAA,CAAc,aAAA,EAAe,YAAY,CAAA;AAAA,MAC/C,WAAA,EAAa;AAAA,KACd,CAAA;AAAA,IACD,kBAAA,EAAoB,IAAI,MAAA,CAAO;AAAA,MAC7B,IAAA,EAAM,WAAA;AAAA,MACN,IAAA,EAAM,aAAA;AAAA,MACN,WAAA,EAAa;AAAA,KACd,CAAA;AAAA,IACD,wBAAA,EAA0B,IAAI,MAAA,CAAO;AAAA,MACnC,IAAA,EAAM,YAAA;AAAA,MACN,IAAA,EAAM,IAAI,KAAA,CAAM;AAAA,QACd,aAAA;AAAA,QACA,GAAA,CAAI,UAAA,CAAW,GAAA,CAAI,MAAA,EAAQ;AAAA,UACzB,IAAA,EAAM,CAAC,CAAA,KAAM;AACX,YAAA,IAAI,MAAM,MAAA,EAAQ;AAChB,cAAA,MAAM,IAAI,KAAA;AAAA,gBACR,CAAA,gDAAA;AAAA,eACF;AAAA,YAAA;AAGF,YAAA,OAAO,OAAA,CAAQ,QAAQ,MAAe,CAAA;AAAA,UAAA;AACxC,SACD;AAAA,OACF,CAAA;AAAA,MACD,WAAA,EAAa;AAAA,KACd,CAAA;AAAA;AAAA,IAGD,OAAA,EAAS,IAAI,WAAA,CAAY;AAAA,MACvB,IAAA,EAAM,SAAA;AAAA,MACN,MAAM,GAAA,CAAI,QAAA,CAAS,IAAI,KAAA,CAAM,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,MACxC,WAAA,EACE;AAAA,KACH,CAAA;AAAA,IACD,aAAA,EAAe,IAAI,MAAA,CAAO;AAAA,MACxB,IAAA,EAAM,KAAA;AAAA,MACN,IAAA,EAAM,GAAA,CAAI,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA;AAAA,MAC7B,WAAA,EACE;AAAA,KACH,CAAA;AAAA,IACD,MAAA,EAAQ,IAAI,IAAA,CAAK;AAAA,MACf,IAAA,EAAM,QAAA;AAAA,MACN,IAAA,EAAM,GAAA,CAAI,QAAA,CAAS,GAAA,CAAI,OAAO,CAAA;AAAA,MAC9B,WAAA,EAAa;AAAA,KACd,CAAA;AAAA,IACD,QAAA,EAAU,IAAI,MAAA,CAAO;AAAA,MACnB,IAAA,EAAM,WAAA;AAAA,MACN,IAAA,EAAM,GAAA,CAAI,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA;AAAA,MAC7B,WAAA,EAAa;AAAA,KACd;AAAA,GACH;AAAA,EACA,OAAA,EAAS,CAAC,IAAA,KAAS;AAiBjB,IAAA,IAAA,CAAK,IAAI,CAAA,CAAE,KAAA,CAAM,CAAC,KAAA,KAAmB;AACnC,MAAA,OAAA,CAAQ,KAAA,CAAM,sBAAsB,KAAK,CAAA;AAEzC,MAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,IAAA,CACf,CAAA;AAAA,EAAA;AAEL,CAAC,CAAA;AAED,MAAM,OAAO,OAAO;AAAA,EAClB,eAAA;AAAA,EACA,gBAAA;AAAA,EACA,wBAAA;AAAA,EACA,kBAAA;AAAA,EACA,OAAA;AAAA,EACA,aAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA,KASsB;AACpB,EAAA,MAAM,QAAA,CAAS;AAAA,IACb,eAAA,EAAiB,eAAA,CAAgB,QAAA,CAAS,GAAG,IACzC,eAAA,CAAgB,KAAA,CAAM,GAAG,CAAA,CAAE,IAAI,CAAC,GAAA,KAAQ,GAAA,CAAI,IAAA,EAAM,CAAA,GAClD,eAAA;AAAA,IACJ,wBAAA;AAAA,IACA,gBAAA;AAAA,IACA,OAAA;AAAA,IACA,kBAAA;AAAA,IACA,aAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH,CAAA;AAEA,MAAM,GAAA,CAAI,IAAI,MAAA,EAAQ,GAAA,CAAI,KAAK,OAAA,CAAQ,IAAA,EAAM,CAAC,CAAC,CAAA"}
@@ -10,9 +10,10 @@ export { genIndex } from './functions/gen-index.mjs';
10
10
  export { glob } from './functions/glob.mjs';
11
11
  export { isDirectlyExecuted } from './functions/is-directly-executed.mjs';
12
12
  export { makeEmptyDir } from './functions/make-empty-dir.mjs';
13
- export { checkShouldRunTypeChecks } from './functions/should-run.mjs';
13
+ export { checkShouldRun, checkShouldRunTypeChecks } from './functions/should-run.mjs';
14
14
  export { executeParallel, executeStages } from './functions/workspace-utils/execute-parallel.mjs';
15
15
  export { getWorkspacePackages } from './functions/workspace-utils/get-workspace-packages.mjs';
16
16
  export { runCmdInParallelAcrossWorkspaces } from './functions/workspace-utils/run-cmd-in-parallel.mjs';
17
17
  export { runCmdInStagesAcrossWorkspaces } from './functions/workspace-utils/run-cmd-in-stages.mjs';
18
+ export { defaultDependencyFields } from './functions/workspace-utils/types.mjs';
18
19
  //# sourceMappingURL=entry-point.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"entry-point.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"entry-point.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;"}
@@ -9,9 +9,10 @@ export { genIndex } from './gen-index.mjs';
9
9
  export { glob } from './glob.mjs';
10
10
  export { isDirectlyExecuted } from './is-directly-executed.mjs';
11
11
  export { makeEmptyDir } from './make-empty-dir.mjs';
12
- export { checkShouldRunTypeChecks } from './should-run.mjs';
12
+ export { checkShouldRun, checkShouldRunTypeChecks } from './should-run.mjs';
13
13
  export { executeParallel, executeStages } from './workspace-utils/execute-parallel.mjs';
14
14
  export { getWorkspacePackages } from './workspace-utils/get-workspace-packages.mjs';
15
15
  export { runCmdInParallelAcrossWorkspaces } from './workspace-utils/run-cmd-in-parallel.mjs';
16
16
  export { runCmdInStagesAcrossWorkspaces } from './workspace-utils/run-cmd-in-stages.mjs';
17
+ export { defaultDependencyFields } from './workspace-utils/types.mjs';
17
18
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
@@ -1,7 +1,68 @@
1
+ /**
2
+ * Checks whether a CI step should run, based on which files differ from a base
3
+ * branch. Reports `false` when every changed file matches one of the ignored
4
+ * patterns.
5
+ *
6
+ * This is the general form of {@link checkShouldRunTypeChecks}: it carries no
7
+ * default ignore list, so the caller states exactly which paths are irrelevant
8
+ * to the step being gated.
9
+ *
10
+ * Prefer gating the individual steps of a workflow with this over a
11
+ * `paths-ignore` trigger filter: a workflow that a path filter skips never
12
+ * reports a status check, so a pull request that changed only the filtered
13
+ * paths waits forever on a required check that will never arrive.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * // Skip when the diff only touches a directory nothing checks
18
+ * await checkShouldRun({ pathsIgnore: ['experimental/'] });
19
+ *
20
+ * // Custom base branch
21
+ * await checkShouldRun({
22
+ * pathsIgnore: ['docs/', '**.md'],
23
+ * baseBranch: 'origin/develop',
24
+ * });
25
+ * ```;
26
+ *
27
+ * @example
28
+ * GitHub Actions usage
29
+ * ```yaml
30
+ * - name: Check diff
31
+ * id: check_diff
32
+ * run: npm exec -- check-should-run --paths-ignore 'experimental/'
33
+ *
34
+ * - name: Run the checks
35
+ * if: steps.check_diff.outputs.should_run == 'true'
36
+ * run: npm run check
37
+ * ```
38
+ *
39
+ * @param options - Configuration options
40
+ * @param options.pathsIgnore - Patterns whose files do not affect the step.
41
+ * Matched with micromatch (with `dot: true`) against paths relative to the
42
+ * repository root, with one shorthand: a pattern ending with `/` is a
43
+ * directory prefix, so `docs/` is `docs/**`. Exact file names
44
+ * (`.editorconfig`) and ordinary globs (`**.md`, `docs/**`) both work.
45
+ * Defaults to `[]`, which ignores nothing.
46
+ * @param options.baseBranch - Base branch to compare against for determining
47
+ * changed files. Defaults to `'origin/main'`
48
+ * @returns Whether the step should run. An empty diff reads as "nothing
49
+ * changed" and so returns `false`. When running in GitHub Actions, also
50
+ * appends `should_run=true` or `should_run=false` to the file named by the
51
+ * `GITHUB_OUTPUT` environment variable.
52
+ */
53
+ export declare const checkShouldRun: (options?: Readonly<{
54
+ pathsIgnore?: readonly string[];
55
+ baseBranch?: string;
56
+ }>) => Promise<boolean>;
1
57
  /**
2
58
  * Checks if TypeScript type checks should run based on the diff from a base
3
59
  * branch. Skips type checks if all changed files match the ignored patterns.
4
60
  *
61
+ * This is {@link checkShouldRun} with an ignore list of the files that a
62
+ * TypeScript project typically has but never type-checks — documentation,
63
+ * tooling configuration, license text. Pass `pathsIgnore` to replace that list,
64
+ * or call {@link checkShouldRun} directly for a step that is not a type check.
65
+ *
5
66
  * This function is typically used in CI/CD pipelines to determine whether
6
67
  * expensive type checking operations should be performed. If all changed files
7
68
  * are documentation, configuration, or other non-TypeScript files, type checks
@@ -37,7 +98,7 @@
37
98
  *
38
99
  * @param options - Configuration options
39
100
  * @param options.pathsIgnore - Array of patterns to ignore when determining if
40
- * type checks should run. Supports three pattern types:
101
+ * type checks should run. Matched as described in {@link checkShouldRun}:
41
102
  *
42
103
  * - **Exact file matches**: e.g., `.cspell.config.yaml` matches only that file
43
104
  * - **Directory prefixes**: e.g., `docs/` matches any file in docs directory
@@ -1 +1 @@
1
- {"version":3,"file":"should-run.d.mts","sourceRoot":"","sources":["../../src/functions/should-run.mts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,eAAO,MAAM,wBAAwB,aACzB,QAAQ,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC,KACD,OAAO,CAAC,OAAO,CAyDjB,CAAC"}
1
+ {"version":3,"file":"should-run.d.mts","sourceRoot":"","sources":["../../src/functions/should-run.mts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,eAAO,MAAM,cAAc,aACf,QAAQ,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC,KACD,OAAO,CAAC,OAAO,CAyCjB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,eAAO,MAAM,wBAAwB,aACzB,QAAQ,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC,KACD,OAAO,CAAC,OAAO,CAId,CAAC"}
@@ -1,51 +1,55 @@
1
+ import micromatch from 'micromatch';
1
2
  import * as fs from 'node:fs/promises';
2
3
  import * as path from 'node:path';
3
- import { Result } from 'ts-data-forge';
4
- import { getDiffFrom } from './diff.mjs';
4
+ import { Result, castMutable } from 'ts-data-forge';
5
+ import { getGitRoot, getDiffFrom } from './diff.mjs';
5
6
 
6
- const checkShouldRunTypeChecks = async (options) => {
7
- const pathsIgnore = options?.pathsIgnore ?? [
8
- "LICENSE",
9
- ".editorconfig",
10
- ".gitignore",
11
- ".cspell.json",
12
- ".cspell.config.yaml",
13
- ".markdownlint-cli2.mjs",
14
- ".npmignore",
15
- ".prettierignore",
16
- ".prettierrc",
17
- "docs/",
18
- "**.md",
19
- "**.txt"
20
- ];
7
+ const checkShouldRun = async (options) => {
8
+ const pathsIgnore = options?.pathsIgnore ?? [];
21
9
  const baseBranch = options?.baseBranch ?? "origin/main";
22
10
  const GITHUB_OUTPUT = process.env["GITHUB_OUTPUT"];
11
+ const gitRoot = await getGitRoot();
12
+ if (Result.isErr(gitRoot)) {
13
+ console.error("Error getting the git root:", gitRoot.value);
14
+ process.exit(1);
15
+ }
23
16
  const files = await getDiffFrom(baseBranch);
24
17
  if (Result.isErr(files)) {
25
18
  console.error("Error getting diff:", files.value);
26
19
  process.exit(1);
27
20
  }
28
- const shouldRunTsChecks = !files.value.every(
29
- (file) => pathsIgnore.some((pattern) => {
30
- if (pattern === file) {
31
- return true;
32
- }
33
- if (pattern.endsWith("/") && file.startsWith(pattern)) {
34
- return true;
35
- }
36
- if (pattern.startsWith("**.")) {
37
- const extension = pattern.slice(2);
38
- return path.basename(file).endsWith(extension);
39
- }
40
- return false;
41
- })
21
+ const changedFiles = files.value.map(
22
+ (file) => path.relative(gitRoot.value, file).split(path.sep).join("/")
23
+ );
24
+ const patterns = pathsIgnore.map(toMatchPattern);
25
+ const shouldRun = changedFiles.some(
26
+ (file) => !micromatch.isMatch(file, castMutable(patterns), { dot: true })
42
27
  );
43
28
  if (GITHUB_OUTPUT !== void 0) {
44
- await fs.appendFile(GITHUB_OUTPUT, `should_run=${shouldRunTsChecks}
29
+ await fs.appendFile(GITHUB_OUTPUT, `should_run=${shouldRun}
45
30
  `);
46
31
  }
47
- return shouldRunTsChecks;
32
+ return shouldRun;
48
33
  };
34
+ const checkShouldRunTypeChecks = async (options) => checkShouldRun({
35
+ pathsIgnore: options?.pathsIgnore ?? defaultTypeCheckPathsIgnore,
36
+ baseBranch: options?.baseBranch
37
+ });
38
+ const defaultTypeCheckPathsIgnore = [
39
+ "LICENSE",
40
+ ".editorconfig",
41
+ ".gitignore",
42
+ ".cspell.json",
43
+ ".cspell.config.yaml",
44
+ ".markdownlint-cli2.mjs",
45
+ ".npmignore",
46
+ ".prettierignore",
47
+ ".prettierrc",
48
+ "docs/",
49
+ "**.md",
50
+ "**.txt"
51
+ ];
52
+ const toMatchPattern = (pattern) => pattern.endsWith("/") ? `${pattern}**` : pattern;
49
53
 
50
- export { checkShouldRunTypeChecks };
54
+ export { checkShouldRun, checkShouldRunTypeChecks };
51
55
  //# sourceMappingURL=should-run.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"should-run.mjs","sources":["../../src/functions/should-run.mts"],"sourcesContent":["import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { Result } from 'ts-data-forge';\nimport { getDiffFrom } from './diff.mjs';\n\n/**\n * Checks if TypeScript type checks should run based on the diff from a base\n * branch. Skips type checks if all changed files match the ignored patterns.\n *\n * This function is typically used in CI/CD pipelines to determine whether\n * expensive type checking operations should be performed. If all changed files\n * are documentation, configuration, or other non-TypeScript files, type checks\n * can be safely skipped to improve build performance.\n *\n * @example\n * ```typescript\n * // Use default settings (compare against origin/main, ignore docs/md/txt files)\n * await checkShouldRunTypeChecks();\n *\n * // Custom ignore patterns\n * await checkShouldRunTypeChecks({\n * pathsIgnore: ['.eslintrc.json', 'docs/', '**.md', 'scripts/'],\n * });\n *\n * // Custom base branch\n * await checkShouldRunTypeChecks({\n * baseBranch: 'origin/develop',\n * });\n * ```;\n *\n * @example\n * GitHub Actions usage\n * ```yaml\n * - name: Check if type checks should run\n * id: check_diff\n * run: npm exec check-should-run-type-checks\n *\n * - name: Run type checks\n * if: steps.check_diff.outputs.should_run == 'true'\n * run: npm run type-check\n * ```\n *\n * @param options - Configuration options\n * @param options.pathsIgnore - Array of patterns to ignore when determining if\n * type checks should run. Supports three pattern types:\n *\n * - **Exact file matches**: e.g., `.cspell.config.yaml` matches only that file\n * - **Directory prefixes**: e.g., `docs/` matches any file in docs directory\n * - **File extensions**: e.g., `**.md` matches any markdown file Defaults to:\n * `['LICENSE', '.editorconfig', '.gitignore', '.cspell.json', '.cspell.config.yaml',\n * '.markdownlint-cli2.mjs', '.npmignore', '.prettierignore',\n * '.prettierrc', 'docs/', '**.md', '**.txt']`\n *\n * @param options.baseBranch - Base branch to compare against for determining\n * changed files. Defaults to `'origin/main'`\n * @returns A promise that resolves when the check is complete. The function\n * will set the GITHUB_OUTPUT environment variable with `should_run=true` or\n * `should_run=false` if running in GitHub Actions environment.\n */\nexport const checkShouldRunTypeChecks = async (\n options?: Readonly<{\n pathsIgnore?: readonly string[];\n baseBranch?: string;\n }>,\n): Promise<boolean> => {\n const pathsIgnore = options?.pathsIgnore ?? [\n 'LICENSE',\n '.editorconfig',\n '.gitignore',\n '.cspell.json',\n '.cspell.config.yaml',\n '.markdownlint-cli2.mjs',\n '.npmignore',\n '.prettierignore',\n '.prettierrc',\n 'docs/',\n '**.md',\n '**.txt',\n ];\n\n const baseBranch = options?.baseBranch ?? 'origin/main';\n\n const GITHUB_OUTPUT = process.env['GITHUB_OUTPUT'];\n\n const files = await getDiffFrom(baseBranch);\n\n if (Result.isErr(files)) {\n console.error('Error getting diff:', files.value);\n\n process.exit(1);\n }\n\n const shouldRunTsChecks: boolean = !files.value.every((file) =>\n pathsIgnore.some((pattern) => {\n // Exact file match\n if (pattern === file) {\n return true;\n }\n\n // Directory prefix match (pattern ends with '/')\n if (pattern.endsWith('/') && file.startsWith(pattern)) {\n return true;\n }\n\n // File extension pattern match (pattern starts with '**.')\n if (pattern.startsWith('**.')) {\n const extension = pattern.slice(2); // Remove '**'\n\n return path.basename(file).endsWith(extension);\n }\n\n return false;\n }),\n );\n\n if (GITHUB_OUTPUT !== undefined) {\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n await fs.appendFile(GITHUB_OUTPUT, `should_run=${shouldRunTsChecks}\\n`);\n }\n\n return shouldRunTsChecks;\n};\n"],"names":[],"mappings":";;;;;AA2DO,MAAM,wBAAA,GAA2B,OACtC,OAAA,KAIqB;AACrB,EAAA,MAAM,WAAA,GAAc,SAAS,WAAA,IAAe;AAAA,IAC1C,SAAA;AAAA,IACA,eAAA;AAAA,IACA,YAAA;AAAA,IACA,cAAA;AAAA,IACA,qBAAA;AAAA,IACA,wBAAA;AAAA,IACA,YAAA;AAAA,IACA,iBAAA;AAAA,IACA,aAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,UAAA,GAAa,SAAS,UAAA,IAAc,aAAA;AAE1C,EAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,GAAA,CAAI,eAAe,CAAA;AAEjD,EAAA,MAAM,KAAA,GAAQ,MAAM,WAAA,CAAY,UAAU,CAAA;AAE1C,EAAA,IAAI,MAAA,CAAO,KAAA,CAAM,KAAK,CAAA,EAAG;AACvB,IAAA,OAAA,CAAQ,KAAA,CAAM,qBAAA,EAAuB,KAAA,CAAM,KAAK,CAAA;AAEhD,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB;AAEA,EAAA,MAAM,iBAAA,GAA6B,CAAC,KAAA,CAAM,KAAA,CAAM,KAAA;AAAA,IAAM,CAAC,IAAA,KACrD,WAAA,CAAY,IAAA,CAAK,CAAC,OAAA,KAAY;AAE5B,MAAA,IAAI,YAAY,IAAA,EAAM;AACpB,QAAA,OAAO,IAAA;AAAA,MACT;AAGA,MAAA,IAAI,QAAQ,QAAA,CAAS,GAAG,KAAK,IAAA,CAAK,UAAA,CAAW,OAAO,CAAA,EAAG;AACrD,QAAA,OAAO,IAAA;AAAA,MACT;AAGA,MAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,KAAK,CAAA,EAAG;AAC7B,QAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,KAAA,CAAM,CAAC,CAAA;AAEjC,QAAA,OAAO,IAAA,CAAK,QAAA,CAAS,IAAI,CAAA,CAAE,SAAS,SAAS,CAAA;AAAA,MAC/C;AAEA,MAAA,OAAO,KAAA;AAAA,IACT,CAAC;AAAA,GACH;AAEA,EAAA,IAAI,kBAAkB,MAAA,EAAW;AAE/B,IAAA,MAAM,EAAA,CAAG,UAAA,CAAW,aAAA,EAAe,CAAA,WAAA,EAAc,iBAAiB;AAAA,CAAI,CAAA;AAAA,EACxE;AAEA,EAAA,OAAO,iBAAA;AACT;;;;"}
1
+ {"version":3,"file":"should-run.mjs","sources":["../../src/functions/should-run.mts"],"sourcesContent":["import micromatch from 'micromatch';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { castMutable, Result } from 'ts-data-forge';\nimport { getDiffFrom, getGitRoot } from './diff.mjs';\n\n/**\n * Checks whether a CI step should run, based on which files differ from a base\n * branch. Reports `false` when every changed file matches one of the ignored\n * patterns.\n *\n * This is the general form of {@link checkShouldRunTypeChecks}: it carries no\n * default ignore list, so the caller states exactly which paths are irrelevant\n * to the step being gated.\n *\n * Prefer gating the individual steps of a workflow with this over a\n * `paths-ignore` trigger filter: a workflow that a path filter skips never\n * reports a status check, so a pull request that changed only the filtered\n * paths waits forever on a required check that will never arrive.\n *\n * @example\n * ```typescript\n * // Skip when the diff only touches a directory nothing checks\n * await checkShouldRun({ pathsIgnore: ['experimental/'] });\n *\n * // Custom base branch\n * await checkShouldRun({\n * pathsIgnore: ['docs/', '**.md'],\n * baseBranch: 'origin/develop',\n * });\n * ```;\n *\n * @example\n * GitHub Actions usage\n * ```yaml\n * - name: Check diff\n * id: check_diff\n * run: npm exec -- check-should-run --paths-ignore 'experimental/'\n *\n * - name: Run the checks\n * if: steps.check_diff.outputs.should_run == 'true'\n * run: npm run check\n * ```\n *\n * @param options - Configuration options\n * @param options.pathsIgnore - Patterns whose files do not affect the step.\n * Matched with micromatch (with `dot: true`) against paths relative to the\n * repository root, with one shorthand: a pattern ending with `/` is a\n * directory prefix, so `docs/` is `docs/**`. Exact file names\n * (`.editorconfig`) and ordinary globs (`**.md`, `docs/**`) both work.\n * Defaults to `[]`, which ignores nothing.\n * @param options.baseBranch - Base branch to compare against for determining\n * changed files. Defaults to `'origin/main'`\n * @returns Whether the step should run. An empty diff reads as \"nothing\n * changed\" and so returns `false`. When running in GitHub Actions, also\n * appends `should_run=true` or `should_run=false` to the file named by the\n * `GITHUB_OUTPUT` environment variable.\n */\nexport const checkShouldRun = async (\n options?: Readonly<{\n pathsIgnore?: readonly string[];\n baseBranch?: string;\n }>,\n): Promise<boolean> => {\n const pathsIgnore = options?.pathsIgnore ?? [];\n\n const baseBranch = options?.baseBranch ?? 'origin/main';\n\n const GITHUB_OUTPUT = process.env['GITHUB_OUTPUT'];\n\n const gitRoot = await getGitRoot();\n\n if (Result.isErr(gitRoot)) {\n console.error('Error getting the git root:', gitRoot.value);\n\n process.exit(1);\n }\n\n const files = await getDiffFrom(baseBranch);\n\n if (Result.isErr(files)) {\n console.error('Error getting diff:', files.value);\n\n process.exit(1);\n }\n\n // `getDiffFrom` returns absolute paths, while the patterns are written\n // relative to the repository root.\n const changedFiles = files.value.map((file) =>\n path.relative(gitRoot.value, file).split(path.sep).join('/'),\n );\n\n const patterns = pathsIgnore.map(toMatchPattern);\n\n const shouldRun: boolean = changedFiles.some(\n (file) => !micromatch.isMatch(file, castMutable(patterns), { dot: true }),\n );\n\n if (GITHUB_OUTPUT !== undefined) {\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n await fs.appendFile(GITHUB_OUTPUT, `should_run=${shouldRun}\\n`);\n }\n\n return shouldRun;\n};\n\n/**\n * Checks if TypeScript type checks should run based on the diff from a base\n * branch. Skips type checks if all changed files match the ignored patterns.\n *\n * This is {@link checkShouldRun} with an ignore list of the files that a\n * TypeScript project typically has but never type-checks — documentation,\n * tooling configuration, license text. Pass `pathsIgnore` to replace that list,\n * or call {@link checkShouldRun} directly for a step that is not a type check.\n *\n * This function is typically used in CI/CD pipelines to determine whether\n * expensive type checking operations should be performed. If all changed files\n * are documentation, configuration, or other non-TypeScript files, type checks\n * can be safely skipped to improve build performance.\n *\n * @example\n * ```typescript\n * // Use default settings (compare against origin/main, ignore docs/md/txt files)\n * await checkShouldRunTypeChecks();\n *\n * // Custom ignore patterns\n * await checkShouldRunTypeChecks({\n * pathsIgnore: ['.eslintrc.json', 'docs/', '**.md', 'scripts/'],\n * });\n *\n * // Custom base branch\n * await checkShouldRunTypeChecks({\n * baseBranch: 'origin/develop',\n * });\n * ```;\n *\n * @example\n * GitHub Actions usage\n * ```yaml\n * - name: Check if type checks should run\n * id: check_diff\n * run: npm exec check-should-run-type-checks\n *\n * - name: Run type checks\n * if: steps.check_diff.outputs.should_run == 'true'\n * run: npm run type-check\n * ```\n *\n * @param options - Configuration options\n * @param options.pathsIgnore - Array of patterns to ignore when determining if\n * type checks should run. Matched as described in {@link checkShouldRun}:\n *\n * - **Exact file matches**: e.g., `.cspell.config.yaml` matches only that file\n * - **Directory prefixes**: e.g., `docs/` matches any file in docs directory\n * - **File extensions**: e.g., `**.md` matches any markdown file Defaults to:\n * `['LICENSE', '.editorconfig', '.gitignore', '.cspell.json', '.cspell.config.yaml',\n * '.markdownlint-cli2.mjs', '.npmignore', '.prettierignore',\n * '.prettierrc', 'docs/', '**.md', '**.txt']`\n *\n * @param options.baseBranch - Base branch to compare against for determining\n * changed files. Defaults to `'origin/main'`\n * @returns A promise that resolves when the check is complete. The function\n * will set the GITHUB_OUTPUT environment variable with `should_run=true` or\n * `should_run=false` if running in GitHub Actions environment.\n */\nexport const checkShouldRunTypeChecks = async (\n options?: Readonly<{\n pathsIgnore?: readonly string[];\n baseBranch?: string;\n }>,\n): Promise<boolean> =>\n checkShouldRun({\n pathsIgnore: options?.pathsIgnore ?? defaultTypeCheckPathsIgnore,\n baseBranch: options?.baseBranch,\n });\n\nconst defaultTypeCheckPathsIgnore = [\n 'LICENSE',\n '.editorconfig',\n '.gitignore',\n '.cspell.json',\n '.cspell.config.yaml',\n '.markdownlint-cli2.mjs',\n '.npmignore',\n '.prettierignore',\n '.prettierrc',\n 'docs/',\n '**.md',\n '**.txt',\n] as const;\n\n/**\n * Expands the trailing-slash shorthand for a directory into the glob\n * micromatch needs. Every other pattern is already one.\n */\nconst toMatchPattern = (pattern: string): string =>\n pattern.endsWith('/') ? (`${pattern}**` as const) : pattern;\n"],"names":[],"mappings":";;;;;;AA0DO,MAAM,cAAA,GAAiB,OAC5B,OAAA,KAIqB;AACrB,EAAA,MAAM,WAAA,GAAc,OAAA,EAAS,WAAA,IAAe,EAAC;AAE7C,EAAA,MAAM,UAAA,GAAa,SAAS,UAAA,IAAc,aAAA;AAE1C,EAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,GAAA,CAAI,eAAe,CAAA;AAEjD,EAAA,MAAM,OAAA,GAAU,MAAM,UAAA,EAAW;AAEjC,EAAA,IAAI,MAAA,CAAO,KAAA,CAAM,OAAO,CAAA,EAAG;AACzB,IAAA,OAAA,CAAQ,KAAA,CAAM,6BAAA,EAA+B,OAAA,CAAQ,KAAK,CAAA;AAE1D,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB;AAEA,EAAA,MAAM,KAAA,GAAQ,MAAM,WAAA,CAAY,UAAU,CAAA;AAE1C,EAAA,IAAI,MAAA,CAAO,KAAA,CAAM,KAAK,CAAA,EAAG;AACvB,IAAA,OAAA,CAAQ,KAAA,CAAM,qBAAA,EAAuB,KAAA,CAAM,KAAK,CAAA;AAEhD,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB;AAIA,EAAA,MAAM,YAAA,GAAe,MAAM,KAAA,CAAM,GAAA;AAAA,IAAI,CAAC,IAAA,KACpC,IAAA,CAAK,QAAA,CAAS,OAAA,CAAQ,KAAA,EAAO,IAAI,CAAA,CAAE,KAAA,CAAM,IAAA,CAAK,GAAG,CAAA,CAAE,KAAK,GAAG;AAAA,GAC7D;AAEA,EAAA,MAAM,QAAA,GAAW,WAAA,CAAY,GAAA,CAAI,cAAc,CAAA;AAE/C,EAAA,MAAM,YAAqB,YAAA,CAAa,IAAA;AAAA,IACtC,CAAC,IAAA,KAAS,CAAC,UAAA,CAAW,OAAA,CAAQ,IAAA,EAAM,WAAA,CAAY,QAAQ,CAAA,EAAG,EAAE,GAAA,EAAK,IAAA,EAAM;AAAA,GAC1E;AAEA,EAAA,IAAI,kBAAkB,MAAA,EAAW;AAE/B,IAAA,MAAM,EAAA,CAAG,UAAA,CAAW,aAAA,EAAe,CAAA,WAAA,EAAc,SAAS;AAAA,CAAI,CAAA;AAAA,EAChE;AAEA,EAAA,OAAO,SAAA;AACT;AA6DO,MAAM,wBAAA,GAA2B,OACtC,OAAA,KAKA,cAAA,CAAe;AAAA,EACb,WAAA,EAAa,SAAS,WAAA,IAAe,2BAAA;AAAA,EACrC,YAAY,OAAA,EAAS;AACvB,CAAC;AAEH,MAAM,2BAAA,GAA8B;AAAA,EAClC,SAAA;AAAA,EACA,eAAA;AAAA,EACA,YAAA;AAAA,EACA,cAAA;AAAA,EACA,qBAAA;AAAA,EACA,wBAAA;AAAA,EACA,YAAA;AAAA,EACA,iBAAA;AAAA,EACA,aAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA;AAMA,MAAM,cAAA,GAAiB,CAAC,OAAA,KACtB,OAAA,CAAQ,SAAS,GAAG,CAAA,GAAK,CAAA,EAAG,OAAO,CAAA,EAAA,CAAA,GAAiB,OAAA;;;;"}
@@ -1,12 +1,15 @@
1
- import { type Package } from './types.mjs';
1
+ import { type DependencyField, type Package } from './types.mjs';
2
2
  /**
3
3
  * Retrieves all workspace packages from a monorepo based on the workspace
4
4
  * patterns defined in the root package.json file.
5
5
  *
6
6
  * @param rootPackageJsonDir - The directory containing the root package.json
7
7
  * file
8
+ * @param dependencyFields - Which `package.json` fields contribute to each
9
+ * package's `dependencies` map. Defaults to
10
+ * {@link defaultDependencyFields}.
8
11
  * @returns A promise that resolves to an array of Package objects containing
9
12
  * package metadata
10
13
  */
11
- export declare const getWorkspacePackages: (rootPackageJsonDir: string) => Promise<readonly Package[]>;
14
+ export declare const getWorkspacePackages: (rootPackageJsonDir: string, dependencyFields?: readonly DependencyField[]) => Promise<readonly Package[]>;
12
15
  //# sourceMappingURL=get-workspace-packages.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-workspace-packages.d.mts","sourceRoot":"","sources":["../../../src/functions/workspace-utils/get-workspace-packages.mts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,uBACX,MAAM,KACzB,OAAO,CAAC,SAAS,OAAO,EAAE,CAqE5B,CAAC"}
1
+ {"version":3,"file":"get-workspace-packages.d.mts","sourceRoot":"","sources":["../../../src/functions/workspace-utils/get-workspace-packages.mts"],"names":[],"mappings":"AAcA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,OAAO,EACb,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,uBACX,MAAM,qBACR,SAAS,eAAe,EAAE,KAC3C,OAAO,CAAC,SAAS,OAAO,EAAE,CAqE5B,CAAC"}
@@ -1,9 +1,10 @@
1
1
  import * as fs from 'node:fs/promises';
2
2
  import * as path from 'node:path';
3
- import { Result, Json, isNotUndefined, isRecord, hasKey, Arr, isString } from 'ts-data-forge';
3
+ import { Result, Json, isNotUndefined, isRecord, hasKey, Arr, isString, Obj } from 'ts-data-forge';
4
4
  import { glob } from '../glob.mjs';
5
+ import { defaultDependencyFields } from './types.mjs';
5
6
 
6
- const getWorkspacePackages = async (rootPackageJsonDir) => {
7
+ const getWorkspacePackages = async (rootPackageJsonDir, dependencyFields = defaultDependencyFields) => {
7
8
  const rootPackageJson = JSON.parse(
8
9
  // eslint-disable-next-line security/detect-non-literal-fs-filename
9
10
  await fs.readFile(path.join(rootPackageJsonDir, "package.json"), "utf8")
@@ -40,11 +41,11 @@ const getWorkspacePackages = async (rootPackageJsonDir) => {
40
41
  name: getStrFromJsonValue(packageJson, "name"),
41
42
  path: path.dirname(packagePath),
42
43
  packageJson,
43
- dependencies: {
44
- ...getKeyValueRecordFromJsonValue(packageJson, "dependencies"),
45
- ...getKeyValueRecordFromJsonValue(packageJson, "devDependencies"),
46
- ...getKeyValueRecordFromJsonValue(packageJson, "peerDependencies")
47
- }
44
+ dependencies: Object.fromEntries(
45
+ dependencyFields.flatMap(
46
+ (field) => Object.entries(getKeyValueRecordFromJsonValue(packageJson, field))
47
+ )
48
+ )
48
49
  }));
49
50
  return packageInfos;
50
51
  });
@@ -62,10 +63,7 @@ const getKeyValueRecordFromJsonValue = (value, key) => {
62
63
  if (!isRecord(obj)) {
63
64
  return {};
64
65
  }
65
- const entries = Object.entries(obj).filter(
66
- (entry) => isString(entry[1])
67
- );
68
- return Object.fromEntries(entries);
66
+ return Obj.filter(obj, isString);
69
67
  };
70
68
 
71
69
  export { getWorkspacePackages };
@@ -1 +1 @@
1
- {"version":3,"file":"get-workspace-packages.mjs","sources":["../../../src/functions/workspace-utils/get-workspace-packages.mts"],"sourcesContent":["import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport {\n Arr,\n hasKey,\n isNotUndefined,\n isRecord,\n isString,\n Json,\n Result,\n} from 'ts-data-forge';\nimport {\n type JsonValue,\n type MutableFixedLengthTuple,\n type ReadonlyRecord,\n} from 'ts-type-forge';\nimport { glob } from '../glob.mjs';\nimport { type Package } from './types.mjs';\n\n/**\n * Retrieves all workspace packages from a monorepo based on the workspace\n * patterns defined in the root package.json file.\n *\n * @param rootPackageJsonDir - The directory containing the root package.json\n * file\n * @returns A promise that resolves to an array of Package objects containing\n * package metadata\n */\nexport const getWorkspacePackages = async (\n rootPackageJsonDir: string,\n): Promise<readonly Package[]> => {\n // Read root package.json\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const rootPackageJson: JsonValue = JSON.parse(\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n await fs.readFile(path.join(rootPackageJsonDir, 'package.json'), 'utf8'),\n );\n\n const workspacePatterns: readonly string[] = getStrArrayFromJsonValue(\n rootPackageJson,\n 'workspaces',\n );\n\n const packagePromises = workspacePatterns.map(async (pattern) => {\n const globResult = await glob(pattern, {\n cwd: rootPackageJsonDir,\n ignore: ['**/node_modules/**'],\n onlyDirectories: true,\n absolute: true,\n });\n\n if (Result.isErr(globResult)) {\n return [];\n }\n\n const matches = globResult.value;\n\n const packageJsonList: readonly (\n readonly [string, JsonValue] | undefined\n )[] = await Promise.all(\n matches.map(async (match) => {\n const maybePackagePath = path.join(match, 'package.json');\n\n const result = await Result.fromPromise(\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n fs.readFile(maybePackagePath, 'utf8'),\n );\n\n if (Result.isErr(result)) return undefined;\n\n const parsed = Json.parse(result.value);\n\n if (Result.isErr(parsed)) return undefined;\n\n return [maybePackagePath, parsed.value] as const;\n }),\n );\n\n const packageInfos: readonly Package[] = packageJsonList\n .filter(isNotUndefined)\n .map(([packagePath, packageJson]) => ({\n name: getStrFromJsonValue(packageJson, 'name'),\n path: path.dirname(packagePath),\n packageJson,\n dependencies: {\n ...getKeyValueRecordFromJsonValue(packageJson, 'dependencies'),\n ...getKeyValueRecordFromJsonValue(packageJson, 'devDependencies'),\n ...getKeyValueRecordFromJsonValue(packageJson, 'peerDependencies'),\n },\n }));\n\n return packageInfos;\n });\n\n const allPackageArrays = await Promise.all(packagePromises);\n\n const finalPackages = allPackageArrays.flat();\n\n return finalPackages;\n};\n\nconst getStrFromJsonValue = (value: JsonValue, key: string): string =>\n isRecord(value) && hasKey(value, key) && isString(value[key])\n ? value[key]\n : '';\n\nconst getStrArrayFromJsonValue = (\n value: JsonValue,\n key: string,\n): readonly string[] =>\n isRecord(value) &&\n hasKey(value, key) &&\n Arr.isArray(value[key]) &&\n value[key].every(isString)\n ? value[key]\n : ([] as const);\n\nconst getKeyValueRecordFromJsonValue = (\n value: JsonValue,\n key: string,\n): ReadonlyRecord<string, string> => {\n if (!isRecord(value) || !hasKey(value, key)) {\n return {};\n }\n\n const obj = value[key];\n\n if (!isRecord(obj)) {\n return {};\n }\n\n const entries = Object.entries(obj).filter(\n (entry): entry is MutableFixedLengthTuple<2, string> => isString(entry[1]),\n );\n\n return Object.fromEntries(entries);\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"get-workspace-packages.mjs","sources":["../../../src/functions/workspace-utils/get-workspace-packages.mts"],"sourcesContent":["import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport {\n Arr,\n hasKey,\n isNotUndefined,\n isRecord,\n isString,\n Json,\n Obj,\n Result,\n} from 'ts-data-forge';\nimport { type JsonValue, type ReadonlyRecord } from 'ts-type-forge';\nimport { glob } from '../glob.mjs';\nimport {\n defaultDependencyFields,\n type DependencyField,\n type Package,\n} from './types.mjs';\n\n/**\n * Retrieves all workspace packages from a monorepo based on the workspace\n * patterns defined in the root package.json file.\n *\n * @param rootPackageJsonDir - The directory containing the root package.json\n * file\n * @param dependencyFields - Which `package.json` fields contribute to each\n * package's `dependencies` map. Defaults to\n * {@link defaultDependencyFields}.\n * @returns A promise that resolves to an array of Package objects containing\n * package metadata\n */\nexport const getWorkspacePackages = async (\n rootPackageJsonDir: string,\n dependencyFields: readonly DependencyField[] = defaultDependencyFields,\n): Promise<readonly Package[]> => {\n // Read root package.json\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const rootPackageJson: JsonValue = JSON.parse(\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n await fs.readFile(path.join(rootPackageJsonDir, 'package.json'), 'utf8'),\n );\n\n const workspacePatterns: readonly string[] = getStrArrayFromJsonValue(\n rootPackageJson,\n 'workspaces',\n );\n\n const packagePromises = workspacePatterns.map(async (pattern) => {\n const globResult = await glob(pattern, {\n cwd: rootPackageJsonDir,\n ignore: ['**/node_modules/**'],\n onlyDirectories: true,\n absolute: true,\n });\n\n if (Result.isErr(globResult)) {\n return [];\n }\n\n const matches = globResult.value;\n\n const packageJsonList: readonly (\n readonly [string, JsonValue] | undefined\n )[] = await Promise.all(\n matches.map(async (match) => {\n const maybePackagePath = path.join(match, 'package.json');\n\n const result = await Result.fromPromise(\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n fs.readFile(maybePackagePath, 'utf8'),\n );\n\n if (Result.isErr(result)) return undefined;\n\n const parsed = Json.parse(result.value);\n\n if (Result.isErr(parsed)) return undefined;\n\n return [maybePackagePath, parsed.value] as const;\n }),\n );\n\n const packageInfos: readonly Package[] = packageJsonList\n .filter(isNotUndefined)\n .map(([packagePath, packageJson]) => ({\n name: getStrFromJsonValue(packageJson, 'name'),\n path: path.dirname(packagePath),\n packageJson,\n dependencies: Object.fromEntries(\n dependencyFields.flatMap((field) =>\n Object.entries(getKeyValueRecordFromJsonValue(packageJson, field)),\n ),\n ),\n }));\n\n return packageInfos;\n });\n\n const allPackageArrays = await Promise.all(packagePromises);\n\n const finalPackages = allPackageArrays.flat();\n\n return finalPackages;\n};\n\nconst getStrFromJsonValue = (value: JsonValue, key: string): string =>\n isRecord(value) && hasKey(value, key) && isString(value[key])\n ? value[key]\n : '';\n\nconst getStrArrayFromJsonValue = (\n value: JsonValue,\n key: string,\n): readonly string[] =>\n isRecord(value) &&\n hasKey(value, key) &&\n Arr.isArray(value[key]) &&\n value[key].every(isString)\n ? value[key]\n : ([] as const);\n\nconst getKeyValueRecordFromJsonValue = (\n value: JsonValue,\n key: string,\n): ReadonlyRecord<string, string> => {\n if (!isRecord(value) || !hasKey(value, key)) {\n return {};\n }\n\n const obj = value[key];\n\n if (!isRecord(obj)) {\n return {};\n }\n\n return Obj.filter(obj, isString);\n};\n"],"names":[],"mappings":"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"}