ts-repo-utils 10.2.0 → 10.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,7 @@
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
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?token=S4688Q0CX3)](https://codecov.io/gh/noshiro-pf/mono)
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>>`
@@ -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"}
@@ -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":"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"}
@@ -10,7 +10,7 @@ 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';
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.mts","sourceRoot":"","sources":["../../src/functions/format.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAIxD,OAAO,EAA6B,MAAM,EAAE,MAAM,eAAe,CAAC;AAWlE;;;;GAIG;AACH,eAAO,MAAM,WAAW,UACf,SAAS,MAAM,EAAE,YACd,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IACjD,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC,KACD,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,OAAO,EAAE,CAAC,CAoI/C,CAAC;AA+DF;;;;GAIG;AACH,eAAO,MAAM,eAAe,aAChB,MAAM,YACN,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;CAClD,CAAC,KACD,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAmCpC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,aACvB,QAAQ,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IACjD,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC,KACD,OAAO,CACR,MAAM,CACJ,SAAS,EACT,aAAa,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,OAAO,EAAE,CACnE,CAgEF,CAAC;AA4BF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,SACnB,MAAM,YACF,QAAQ,CAAC;IACjB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IACjD;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC,KACD,OAAO,CACR,MAAM,CACJ,SAAS,EACP,aAAa,GACb,QAAQ,CAAC;IACP,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,GACF,SAAS,OAAO,EAAE,CACrB,CA2FF,CAAC"}
1
+ {"version":3,"file":"format.d.mts","sourceRoot":"","sources":["../../src/functions/format.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAIxD,OAAO,EAA6B,MAAM,EAAE,MAAM,eAAe,CAAC;AAWlE;;;;GAIG;AACH,eAAO,MAAM,WAAW,UACf,SAAS,MAAM,EAAE,YACd,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IACjD,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC,KACD,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,OAAO,EAAE,CAAC,CA8I/C,CAAC;AA+DF;;;;GAIG;AACH,eAAO,MAAM,eAAe,aAChB,MAAM,YACN,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;CAClD,CAAC,KACD,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAmCpC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,aACvB,QAAQ,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IACjD,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC,KACD,OAAO,CACR,MAAM,CACJ,SAAS,EACT,aAAa,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,OAAO,EAAE,CACnE,CAgEF,CAAC;AA4BF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,SACnB,MAAM,YACF,QAAQ,CAAC;IACjB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IACjD;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC,KACD,OAAO,CACR,MAAM,CACJ,SAAS,EACP,aAAa,GACb,QAAQ,CAAC;IACP,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,GACF,SAAS,OAAO,EAAE,CACrB,CA2FF,CAAC"}
@@ -25,36 +25,29 @@ const formatFiles = async (files, options) => {
25
25
  const relativePath = path.relative(gitRoot, filePath);
26
26
  return relativePath.startsWith("..") ? filePath : relativePath;
27
27
  };
28
+ const mut_skipped = { nonExistent: 0, ignored: 0, noParser: 0 };
28
29
  const results = (
29
30
  // NOTE: Using Promise.allSettled to ensure all files are processed even if some fail
30
31
  await Promise.allSettled(
31
32
  files.map(async (filePath) => {
32
33
  try {
33
34
  if (!await pathExists(filePath)) {
34
- conditionalEcho(
35
- `Skipping non-existent file: ${getDisplayPath(filePath)}`
36
- );
35
+ mut_skipped.nonExistent += 1;
37
36
  return Result.ok(void 0);
38
37
  }
39
38
  if (!noIgnore && (options?.ignore ?? defaultIgnoreFn)(filePath)) {
40
- conditionalEcho(
41
- `Skipping ignored file: ${getDisplayPath(filePath)}`
42
- );
39
+ mut_skipped.ignored += 1;
43
40
  return Result.ok(void 0);
44
41
  }
45
42
  const fileInfo = await prettier.getFileInfo(filePath, {
46
43
  ignorePath: ".prettierignore"
47
44
  });
48
45
  if (!noIgnore && fileInfo.ignored) {
49
- conditionalEcho(
50
- `Skipping ignored file: ${getDisplayPath(filePath)}`
51
- );
46
+ mut_skipped.ignored += 1;
52
47
  return Result.ok(void 0);
53
48
  }
54
49
  if (!noIgnore && (options?.ignoreUnknown ?? true) && fileInfo.inferredParser === null) {
55
- conditionalEcho(
56
- `Skipping file (no parser): ${getDisplayPath(filePath)}`
57
- );
50
+ mut_skipped.noParser += 1;
58
51
  return Result.ok(void 0);
59
52
  }
60
53
  const content = await fs.readFile(filePath, "utf8");
@@ -82,6 +75,14 @@ const formatFiles = async (files, options) => {
82
75
  })
83
76
  )
84
77
  );
78
+ const skipReport = [
79
+ mut_skipped.ignored === 0 ? "" : `${mut_skipped.ignored} ignored`,
80
+ mut_skipped.noParser === 0 ? "" : `${mut_skipped.noParser} with no parser`,
81
+ mut_skipped.nonExistent === 0 ? "" : `${mut_skipped.nonExistent} non-existent`
82
+ ].filter((part) => part !== "");
83
+ if (Arr.isNonEmpty(skipReport)) {
84
+ conditionalEcho(`Skipped ${skipReport.join(", ")}.`);
85
+ }
85
86
  if (results.every((r) => r.status === "fulfilled")) {
86
87
  const fulfilled = results.map((r) => r.value);
87
88
  if (fulfilled.every(Result.isOk)) {
@@ -1 +1 @@
1
- {"version":3,"file":"format.mjs","sources":["../../src/functions/format.mts"],"sourcesContent":["import { type ExecException } from 'node:child_process';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport * as prettier from 'prettier';\nimport { Arr, isNotUndefined, pipe, Result } from 'ts-data-forge';\nimport { pathExists } from './assert-path-exists.mjs';\nimport {\n getDiffFrom,\n getGitRoot,\n getModifiedFiles,\n getStagedFiles,\n getUntrackedFiles,\n} from './diff.mjs';\nimport { glob } from './glob.mjs';\n\n/**\n * Format a list of files using Prettier\n *\n * @param files - Array of file paths to format\n */\nexport const formatFiles = async (\n files: readonly string[],\n options?: Readonly<{\n silent?: boolean;\n ignore?: false | ((filePath: string) => boolean);\n ignoreUnknown?: boolean;\n }>,\n): Promise<Result<undefined, readonly unknown[]>> => {\n const silent = options?.silent ?? false;\n\n const noIgnore = options?.ignore === false;\n\n const conditionalEcho = silent ? () => {} : console.info;\n\n if (Arr.isEmpty(files)) {\n conditionalEcho('No files to format');\n\n return Result.ok(undefined);\n }\n\n conditionalEcho(`Formatting ${files.length} files...`);\n\n // Get git root for display purposes\n const gitRootResult = await getGitRoot({ silent: true });\n\n const gitRoot = Result.isOk(gitRootResult) ? gitRootResult.value : undefined;\n\n // Helper function to get display path (relative to git root if available)\n const getDisplayPath = (filePath: string): string => {\n if (gitRoot === undefined) {\n return filePath;\n }\n\n const relativePath = path.relative(gitRoot, filePath);\n\n return relativePath.startsWith('..') ? filePath : relativePath;\n };\n\n // Format each file\n const results: readonly PromiseSettledResult<Result<undefined, unknown>>[] =\n // NOTE: Using Promise.allSettled to ensure all files are processed even if some fail\n await Promise.allSettled(\n files.map(async (filePath) => {\n try {\n // Check if file exists first\n if (!(await pathExists(filePath))) {\n conditionalEcho(\n `Skipping non-existent file: ${getDisplayPath(filePath)}`,\n );\n\n return Result.ok(undefined);\n }\n\n if (!noIgnore && (options?.ignore ?? defaultIgnoreFn)(filePath)) {\n conditionalEcho(\n `Skipping ignored file: ${getDisplayPath(filePath)}`,\n );\n\n return Result.ok(undefined);\n }\n\n // Check if file is ignored by prettier\n const fileInfo = await prettier.getFileInfo(filePath, {\n ignorePath: '.prettierignore',\n });\n\n if (!noIgnore && fileInfo.ignored) {\n conditionalEcho(\n `Skipping ignored file: ${getDisplayPath(filePath)}`,\n );\n\n return Result.ok(undefined);\n }\n\n if (\n !noIgnore &&\n (options?.ignoreUnknown ?? true) &&\n fileInfo.inferredParser === null\n ) {\n // Silently skip files with no parser\n conditionalEcho(\n `Skipping file (no parser): ${getDisplayPath(filePath)}`,\n );\n\n return Result.ok(undefined);\n }\n\n // Read file content\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n const content = await fs.readFile(filePath, 'utf8');\n\n // Resolve prettier config for this file\n const prettierOptions = await prettier.resolveConfig(filePath);\n\n // Format the content\n const formatted = await prettier.format(content, {\n ...prettierOptions,\n filepath: filePath,\n });\n\n // Only write if content changed\n if (formatted === content) {\n conditionalEcho(`Unchanged: ${getDisplayPath(filePath)}`);\n } else {\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n await fs.writeFile(filePath, formatted, 'utf8');\n\n conditionalEcho(`Formatted: ${getDisplayPath(filePath)}`);\n }\n\n return Result.ok(undefined);\n } catch (error) {\n if (!silent) {\n console.error(\n `Error formatting ${getDisplayPath(filePath)}:`,\n error,\n );\n }\n\n return Result.err(error);\n }\n }),\n );\n\n if (results.every((r) => r.status === 'fulfilled')) {\n const fulfilled = results.map((r) => r.value);\n\n if (fulfilled.every(Result.isOk)) {\n return Result.ok(undefined);\n }\n\n return Result.err(fulfilled.filter(Result.isErr).map((r) => r.value));\n }\n\n return Result.err(\n results\n .filter((r) => r.status === 'rejected')\n .map((r): unknown => r.reason),\n );\n};\n\nconst defaultIgnoreFn = (filePath: string): boolean => {\n const filename = path.basename(filePath);\n\n return (\n ignoreFiles.has(filename) ||\n filename.startsWith('.env') ||\n ignoreExtensions.some((ext) => filePath.endsWith(ext)) ||\n pipe(filePath.split(path.sep)).map((pathSegments) =>\n pathSegments.some((segment) => ignoreDirs.includes(segment)),\n ).value\n );\n};\n\nconst ignoreFiles: ReadonlySet<string> = new Set([\n '.DS_Store',\n 'package-lock.json',\n 'yarn.lock',\n 'pnpm-lock.yaml',\n 'LICENSE',\n '.prettierignore',\n '.editorconfig',\n '.gitignore',\n '.npmignore',\n '.envrc',\n '.nvmrc',\n '.npmrc',\n]);\n\nconst ignoreExtensions: readonly `.${string}`[] = [\n '.svg',\n '.png',\n '.jpg',\n '.jpeg',\n '.gif',\n '.bmp',\n '.tiff',\n '.bak',\n '.log',\n '.zip',\n '.tar',\n '.gz',\n '.7z',\n '.mp3',\n '.mp4',\n '.avi',\n '.mkv',\n '.tsbuildinfo',\n] as const;\n\nconst ignoreDirs: readonly string[] = [\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n 'out',\n '.cache',\n '.vscode',\n '.yarn',\n '.wireit',\n] as const;\n\n/**\n * Format files matching the given glob pattern using Prettier\n *\n * @param pathGlob - Glob pattern to match files\n */\nexport const formatFilesGlob = async (\n pathGlob: string,\n options?: Readonly<{\n silent?: boolean;\n ignoreUnknown?: boolean;\n ignore?: false | ((filePath: string) => boolean);\n }>,\n): Promise<Result<undefined, unknown>> => {\n const silent = options?.silent ?? false;\n\n const ignoreUnknown = options?.ignoreUnknown ?? true;\n\n const ignore = options?.ignore;\n\n const conditionalEcho = silent ? () => {} : console.info;\n\n // Find all files matching the glob\n const globResult = await glob(pathGlob, {\n absolute: true,\n ignore: ignore === false ? [] : ['**/node_modules/**', '**/.git/**'],\n dot: true,\n });\n\n if (Result.isErr(globResult)) {\n const error = globResult.value;\n\n if (!silent) {\n console.error('Error in formatFiles:', error);\n }\n\n return Result.err(error);\n }\n\n const files = globResult.value;\n\n if (Arr.isEmpty(files)) {\n conditionalEcho('No files found matching pattern:', pathGlob);\n\n return Result.ok(undefined);\n }\n\n return formatFiles(files, { silent, ignoreUnknown, ignore });\n};\n\n/**\n * Format only files that have been changed (git status)\n *\n * @param options - Options for formatting\n * @param options.cwd - If provided, only files within this directory will be\n * formatted. Relative paths are resolved against `process.cwd()`. Defaults to\n * undefined (no filtering; all changed files in the repository are\n * formatted).\n */\nexport const formatUncommittedFiles = async (\n options?: Readonly<{\n untracked?: boolean;\n modified?: boolean;\n staged?: boolean;\n silent?: boolean;\n ignoreUnknown?: boolean;\n ignore?: false | ((filePath: string) => boolean);\n cwd?: string;\n }>,\n): Promise<\n Result<\n undefined,\n ExecException | Readonly<{ message: string }> | readonly unknown[]\n >\n> => {\n const {\n untracked = true,\n modified = true,\n staged = true,\n silent = false,\n ignoreUnknown = true,\n ignore,\n cwd,\n } = options ?? {};\n\n const mut_files: string[] = [];\n\n if (untracked) {\n const untrackedFilesResult = await getUntrackedFiles({ silent });\n\n if (Result.isErr(untrackedFilesResult)) {\n if (!silent) {\n console.error(\n 'Error getting changed files:',\n untrackedFilesResult.value,\n );\n }\n\n return untrackedFilesResult;\n }\n\n mut_files.push(...untrackedFilesResult.value);\n }\n\n if (modified) {\n const diffFilesResult = await getModifiedFiles({ silent });\n\n if (Result.isErr(diffFilesResult)) {\n if (!silent) {\n console.error('Error getting changed files:', diffFilesResult.value);\n }\n\n return diffFilesResult;\n }\n\n mut_files.push(...diffFilesResult.value);\n }\n\n if (staged) {\n const stagedFilesResult = await getStagedFiles({ silent });\n\n if (Result.isErr(stagedFilesResult)) {\n if (!silent) {\n console.error('Error getting changed files:', stagedFilesResult.value);\n }\n\n return stagedFilesResult;\n }\n\n mut_files.push(...stagedFilesResult.value);\n }\n\n return formatFiles(filterFilesByCwd(Arr.uniq(mut_files), cwd), {\n silent,\n ignoreUnknown,\n ignore,\n });\n};\n\n/**\n * Filter a list of file paths to those inside `cwd`. If `cwd` is undefined,\n * returns the original list unchanged. Relative `cwd` is resolved against\n * `process.cwd()`.\n */\nconst filterFilesByCwd = (\n files: readonly string[],\n cwd: string | undefined,\n): readonly string[] => {\n if (cwd === undefined) {\n return files;\n }\n\n const resolvedCwd = path.resolve(cwd);\n\n const cwdWithSep = resolvedCwd.endsWith(path.sep)\n ? resolvedCwd\n : (`${resolvedCwd}${path.sep}` as const);\n\n return files.filter((file) => {\n const absFile = path.isAbsolute(file) ? file : path.resolve(file);\n\n return absFile === resolvedCwd || absFile.startsWith(cwdWithSep);\n });\n};\n\n/**\n * Format only files that differ from the specified base branch or commit\n *\n * @param base - Base branch name or commit hash to compare against (defaults to\n * 'main')\n * @param options - Options for formatting\n * @param options.includeUntracked - Include untracked files in addition to diff\n * files (default is true)\n * @param options.includeStaged - Include staged files in addition to diff files\n * (default is true)\n * @param options.silent - Silent mode to suppress command output (default is\n * false)\n */\nexport const formatDiffFrom = async (\n base: string,\n options?: Readonly<{\n includeUntracked?: boolean;\n includeModified?: boolean;\n includeStaged?: boolean;\n silent?: boolean;\n ignoreUnknown?: boolean;\n ignore?: false | ((filePath: string) => boolean);\n /**\n * If provided, only files within this directory will be formatted.\n * Relative paths are resolved against `process.cwd()`. Defaults to\n * undefined (no filtering; all changed files in the repository are\n * formatted).\n */\n cwd?: string;\n }>,\n): Promise<\n Result<\n undefined,\n | ExecException\n | Readonly<{\n message: string;\n }>\n | readonly unknown[]\n >\n> => {\n // const silent = options?.silent ?? false;\n const {\n silent = false,\n includeUntracked = true,\n includeModified = true,\n includeStaged = true,\n ignoreUnknown = true,\n ignore,\n cwd,\n } = options ?? {};\n\n const conditionalEcho = silent ? () => {} : console.info;\n\n // Get files that differ from base branch/commit (excluding deleted files)\n const diffFromBaseResult = await getDiffFrom(base, {\n silent,\n });\n\n if (Result.isErr(diffFromBaseResult)) {\n if (!silent) {\n console.error('Error getting changed files:', diffFromBaseResult.value);\n }\n\n return diffFromBaseResult;\n }\n\n const diffFiles = diffFromBaseResult.value;\n\n const mut_allFiles: string[] = diffFiles.slice();\n\n // If includeUntracked is true, also get untracked files\n for (const { type, flag, fn } of [\n { type: 'untracked', flag: includeUntracked, fn: getUntrackedFiles },\n { type: 'modified', flag: includeModified, fn: getModifiedFiles },\n { type: 'staged', flag: includeStaged, fn: getStagedFiles },\n ]) {\n if (!flag) {\n continue;\n }\n\n // eslint-disable-next-line no-await-in-loop\n const filesResult = await fn({ silent });\n\n if (Result.isErr(filesResult)) {\n if (!silent) {\n console.error(`Error getting ${type} files:`, filesResult.value);\n }\n\n return filesResult;\n }\n\n const files = filesResult.value;\n\n // Combine and deduplicate files\n mut_allFiles.push(...files);\n }\n\n const allFiles = filterFilesByCwd(Arr.uniq(mut_allFiles), cwd);\n\n if (!silent) {\n const includedFileTypes = [\n includeUntracked ? 'untracked files' : undefined,\n includeModified ? 'modified files' : undefined,\n includeStaged ? 'staged files' : undefined,\n ].filter(isNotUndefined);\n\n const message = [\n `Formatting files that differ from ${base}`,\n includedFileTypes\n .map((s, i) =>\n i !== includedFileTypes.length - 1 ? `, ${s}` : ` and ${s}`,\n )\n .join(''),\n ].join('');\n\n conditionalEcho(`${message}:`, allFiles);\n }\n\n if (Arr.isEmpty(allFiles)) {\n conditionalEcho('No files to format');\n\n return Result.ok(undefined);\n }\n\n return formatFiles(allFiles, {\n silent,\n ignoreUnknown,\n ignore,\n });\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"format.mjs","sources":["../../src/functions/format.mts"],"sourcesContent":["import { type ExecException } from 'node:child_process';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport * as prettier from 'prettier';\nimport { Arr, isNotUndefined, pipe, Result } from 'ts-data-forge';\nimport { pathExists } from './assert-path-exists.mjs';\nimport {\n getDiffFrom,\n getGitRoot,\n getModifiedFiles,\n getStagedFiles,\n getUntrackedFiles,\n} from './diff.mjs';\nimport { glob } from './glob.mjs';\n\n/**\n * Format a list of files using Prettier\n *\n * @param files - Array of file paths to format\n */\nexport const formatFiles = async (\n files: readonly string[],\n options?: Readonly<{\n silent?: boolean;\n ignore?: false | ((filePath: string) => boolean);\n ignoreUnknown?: boolean;\n }>,\n): Promise<Result<undefined, readonly unknown[]>> => {\n const silent = options?.silent ?? false;\n\n const noIgnore = options?.ignore === false;\n\n const conditionalEcho = silent ? () => {} : console.info;\n\n if (Arr.isEmpty(files)) {\n conditionalEcho('No files to format');\n\n return Result.ok(undefined);\n }\n\n conditionalEcho(`Formatting ${files.length} files...`);\n\n // Get git root for display purposes\n const gitRootResult = await getGitRoot({ silent: true });\n\n const gitRoot = Result.isOk(gitRootResult) ? gitRootResult.value : undefined;\n\n // Helper function to get display path (relative to git root if available)\n const getDisplayPath = (filePath: string): string => {\n if (gitRoot === undefined) {\n return filePath;\n }\n\n const relativePath = path.relative(gitRoot, filePath);\n\n return relativePath.startsWith('..') ? filePath : relativePath;\n };\n\n // Skips are counted rather than named. A repository can hand this function\n // thousands of files it will not touch — a subtree formatted by another tool,\n // or a first diff that contains a whole imported directory — and a line each\n // is both unreadable and enough output to overflow the stdout buffer of a\n // parent process capturing this run.\n const mut_skipped = { nonExistent: 0, ignored: 0, noParser: 0 };\n\n // Format each file\n const results: readonly PromiseSettledResult<Result<undefined, unknown>>[] =\n // NOTE: Using Promise.allSettled to ensure all files are processed even if some fail\n await Promise.allSettled(\n files.map(async (filePath) => {\n try {\n // Check if file exists first\n if (!(await pathExists(filePath))) {\n mut_skipped.nonExistent += 1;\n\n return Result.ok(undefined);\n }\n\n if (!noIgnore && (options?.ignore ?? defaultIgnoreFn)(filePath)) {\n mut_skipped.ignored += 1;\n\n return Result.ok(undefined);\n }\n\n // Check if file is ignored by prettier\n const fileInfo = await prettier.getFileInfo(filePath, {\n ignorePath: '.prettierignore',\n });\n\n if (!noIgnore && fileInfo.ignored) {\n mut_skipped.ignored += 1;\n\n return Result.ok(undefined);\n }\n\n if (\n !noIgnore &&\n (options?.ignoreUnknown ?? true) &&\n fileInfo.inferredParser === null\n ) {\n mut_skipped.noParser += 1;\n\n return Result.ok(undefined);\n }\n\n // Read file content\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n const content = await fs.readFile(filePath, 'utf8');\n\n // Resolve prettier config for this file\n const prettierOptions = await prettier.resolveConfig(filePath);\n\n // Format the content\n const formatted = await prettier.format(content, {\n ...prettierOptions,\n filepath: filePath,\n });\n\n // Only write if content changed\n if (formatted === content) {\n conditionalEcho(`Unchanged: ${getDisplayPath(filePath)}`);\n } else {\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n await fs.writeFile(filePath, formatted, 'utf8');\n\n conditionalEcho(`Formatted: ${getDisplayPath(filePath)}`);\n }\n\n return Result.ok(undefined);\n } catch (error) {\n if (!silent) {\n console.error(\n `Error formatting ${getDisplayPath(filePath)}:`,\n error,\n );\n }\n\n return Result.err(error);\n }\n }),\n );\n\n const skipReport = [\n mut_skipped.ignored === 0 ? '' : `${mut_skipped.ignored} ignored`,\n mut_skipped.noParser === 0 ? '' : `${mut_skipped.noParser} with no parser`,\n mut_skipped.nonExistent === 0\n ? ''\n : `${mut_skipped.nonExistent} non-existent`,\n ].filter((part) => part !== '');\n\n if (Arr.isNonEmpty(skipReport)) {\n conditionalEcho(`Skipped ${skipReport.join(', ')}.`);\n }\n\n if (results.every((r) => r.status === 'fulfilled')) {\n const fulfilled = results.map((r) => r.value);\n\n if (fulfilled.every(Result.isOk)) {\n return Result.ok(undefined);\n }\n\n return Result.err(fulfilled.filter(Result.isErr).map((r) => r.value));\n }\n\n return Result.err(\n results\n .filter((r) => r.status === 'rejected')\n .map((r): unknown => r.reason),\n );\n};\n\nconst defaultIgnoreFn = (filePath: string): boolean => {\n const filename = path.basename(filePath);\n\n return (\n ignoreFiles.has(filename) ||\n filename.startsWith('.env') ||\n ignoreExtensions.some((ext) => filePath.endsWith(ext)) ||\n pipe(filePath.split(path.sep)).map((pathSegments) =>\n pathSegments.some((segment) => ignoreDirs.includes(segment)),\n ).value\n );\n};\n\nconst ignoreFiles: ReadonlySet<string> = new Set([\n '.DS_Store',\n 'package-lock.json',\n 'yarn.lock',\n 'pnpm-lock.yaml',\n 'LICENSE',\n '.prettierignore',\n '.editorconfig',\n '.gitignore',\n '.npmignore',\n '.envrc',\n '.nvmrc',\n '.npmrc',\n]);\n\nconst ignoreExtensions: readonly `.${string}`[] = [\n '.svg',\n '.png',\n '.jpg',\n '.jpeg',\n '.gif',\n '.bmp',\n '.tiff',\n '.bak',\n '.log',\n '.zip',\n '.tar',\n '.gz',\n '.7z',\n '.mp3',\n '.mp4',\n '.avi',\n '.mkv',\n '.tsbuildinfo',\n] as const;\n\nconst ignoreDirs: readonly string[] = [\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n 'out',\n '.cache',\n '.vscode',\n '.yarn',\n '.wireit',\n] as const;\n\n/**\n * Format files matching the given glob pattern using Prettier\n *\n * @param pathGlob - Glob pattern to match files\n */\nexport const formatFilesGlob = async (\n pathGlob: string,\n options?: Readonly<{\n silent?: boolean;\n ignoreUnknown?: boolean;\n ignore?: false | ((filePath: string) => boolean);\n }>,\n): Promise<Result<undefined, unknown>> => {\n const silent = options?.silent ?? false;\n\n const ignoreUnknown = options?.ignoreUnknown ?? true;\n\n const ignore = options?.ignore;\n\n const conditionalEcho = silent ? () => {} : console.info;\n\n // Find all files matching the glob\n const globResult = await glob(pathGlob, {\n absolute: true,\n ignore: ignore === false ? [] : ['**/node_modules/**', '**/.git/**'],\n dot: true,\n });\n\n if (Result.isErr(globResult)) {\n const error = globResult.value;\n\n if (!silent) {\n console.error('Error in formatFiles:', error);\n }\n\n return Result.err(error);\n }\n\n const files = globResult.value;\n\n if (Arr.isEmpty(files)) {\n conditionalEcho('No files found matching pattern:', pathGlob);\n\n return Result.ok(undefined);\n }\n\n return formatFiles(files, { silent, ignoreUnknown, ignore });\n};\n\n/**\n * Format only files that have been changed (git status)\n *\n * @param options - Options for formatting\n * @param options.cwd - If provided, only files within this directory will be\n * formatted. Relative paths are resolved against `process.cwd()`. Defaults to\n * undefined (no filtering; all changed files in the repository are\n * formatted).\n */\nexport const formatUncommittedFiles = async (\n options?: Readonly<{\n untracked?: boolean;\n modified?: boolean;\n staged?: boolean;\n silent?: boolean;\n ignoreUnknown?: boolean;\n ignore?: false | ((filePath: string) => boolean);\n cwd?: string;\n }>,\n): Promise<\n Result<\n undefined,\n ExecException | Readonly<{ message: string }> | readonly unknown[]\n >\n> => {\n const {\n untracked = true,\n modified = true,\n staged = true,\n silent = false,\n ignoreUnknown = true,\n ignore,\n cwd,\n } = options ?? {};\n\n const mut_files: string[] = [];\n\n if (untracked) {\n const untrackedFilesResult = await getUntrackedFiles({ silent });\n\n if (Result.isErr(untrackedFilesResult)) {\n if (!silent) {\n console.error(\n 'Error getting changed files:',\n untrackedFilesResult.value,\n );\n }\n\n return untrackedFilesResult;\n }\n\n mut_files.push(...untrackedFilesResult.value);\n }\n\n if (modified) {\n const diffFilesResult = await getModifiedFiles({ silent });\n\n if (Result.isErr(diffFilesResult)) {\n if (!silent) {\n console.error('Error getting changed files:', diffFilesResult.value);\n }\n\n return diffFilesResult;\n }\n\n mut_files.push(...diffFilesResult.value);\n }\n\n if (staged) {\n const stagedFilesResult = await getStagedFiles({ silent });\n\n if (Result.isErr(stagedFilesResult)) {\n if (!silent) {\n console.error('Error getting changed files:', stagedFilesResult.value);\n }\n\n return stagedFilesResult;\n }\n\n mut_files.push(...stagedFilesResult.value);\n }\n\n return formatFiles(filterFilesByCwd(Arr.uniq(mut_files), cwd), {\n silent,\n ignoreUnknown,\n ignore,\n });\n};\n\n/**\n * Filter a list of file paths to those inside `cwd`. If `cwd` is undefined,\n * returns the original list unchanged. Relative `cwd` is resolved against\n * `process.cwd()`.\n */\nconst filterFilesByCwd = (\n files: readonly string[],\n cwd: string | undefined,\n): readonly string[] => {\n if (cwd === undefined) {\n return files;\n }\n\n const resolvedCwd = path.resolve(cwd);\n\n const cwdWithSep = resolvedCwd.endsWith(path.sep)\n ? resolvedCwd\n : (`${resolvedCwd}${path.sep}` as const);\n\n return files.filter((file) => {\n const absFile = path.isAbsolute(file) ? file : path.resolve(file);\n\n return absFile === resolvedCwd || absFile.startsWith(cwdWithSep);\n });\n};\n\n/**\n * Format only files that differ from the specified base branch or commit\n *\n * @param base - Base branch name or commit hash to compare against (defaults to\n * 'main')\n * @param options - Options for formatting\n * @param options.includeUntracked - Include untracked files in addition to diff\n * files (default is true)\n * @param options.includeStaged - Include staged files in addition to diff files\n * (default is true)\n * @param options.silent - Silent mode to suppress command output (default is\n * false)\n */\nexport const formatDiffFrom = async (\n base: string,\n options?: Readonly<{\n includeUntracked?: boolean;\n includeModified?: boolean;\n includeStaged?: boolean;\n silent?: boolean;\n ignoreUnknown?: boolean;\n ignore?: false | ((filePath: string) => boolean);\n /**\n * If provided, only files within this directory will be formatted.\n * Relative paths are resolved against `process.cwd()`. Defaults to\n * undefined (no filtering; all changed files in the repository are\n * formatted).\n */\n cwd?: string;\n }>,\n): Promise<\n Result<\n undefined,\n | ExecException\n | Readonly<{\n message: string;\n }>\n | readonly unknown[]\n >\n> => {\n // const silent = options?.silent ?? false;\n const {\n silent = false,\n includeUntracked = true,\n includeModified = true,\n includeStaged = true,\n ignoreUnknown = true,\n ignore,\n cwd,\n } = options ?? {};\n\n const conditionalEcho = silent ? () => {} : console.info;\n\n // Get files that differ from base branch/commit (excluding deleted files)\n const diffFromBaseResult = await getDiffFrom(base, {\n silent,\n });\n\n if (Result.isErr(diffFromBaseResult)) {\n if (!silent) {\n console.error('Error getting changed files:', diffFromBaseResult.value);\n }\n\n return diffFromBaseResult;\n }\n\n const diffFiles = diffFromBaseResult.value;\n\n const mut_allFiles: string[] = diffFiles.slice();\n\n // If includeUntracked is true, also get untracked files\n for (const { type, flag, fn } of [\n { type: 'untracked', flag: includeUntracked, fn: getUntrackedFiles },\n { type: 'modified', flag: includeModified, fn: getModifiedFiles },\n { type: 'staged', flag: includeStaged, fn: getStagedFiles },\n ]) {\n if (!flag) {\n continue;\n }\n\n // eslint-disable-next-line no-await-in-loop\n const filesResult = await fn({ silent });\n\n if (Result.isErr(filesResult)) {\n if (!silent) {\n console.error(`Error getting ${type} files:`, filesResult.value);\n }\n\n return filesResult;\n }\n\n const files = filesResult.value;\n\n // Combine and deduplicate files\n mut_allFiles.push(...files);\n }\n\n const allFiles = filterFilesByCwd(Arr.uniq(mut_allFiles), cwd);\n\n if (!silent) {\n const includedFileTypes = [\n includeUntracked ? 'untracked files' : undefined,\n includeModified ? 'modified files' : undefined,\n includeStaged ? 'staged files' : undefined,\n ].filter(isNotUndefined);\n\n const message = [\n `Formatting files that differ from ${base}`,\n includedFileTypes\n .map((s, i) =>\n i !== includedFileTypes.length - 1 ? `, ${s}` : ` and ${s}`,\n )\n .join(''),\n ].join('');\n\n conditionalEcho(`${message}:`, allFiles);\n }\n\n if (Arr.isEmpty(allFiles)) {\n conditionalEcho('No files to format');\n\n return Result.ok(undefined);\n }\n\n return formatFiles(allFiles, {\n silent,\n ignoreUnknown,\n ignore,\n });\n};\n"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@ 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';
@@ -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