ts-repo-utils 7.7.2 → 7.8.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.
- package/README.md +197 -144
- package/dist/cmd/assert-repo-is-clean.d.mts +1 -1
- package/dist/cmd/assert-repo-is-clean.mjs +2 -2
- package/dist/cmd/assert-repo-is-clean.mjs.map +1 -1
- package/dist/cmd/check-should-run-type-checks.d.mts +1 -1
- package/dist/cmd/check-should-run-type-checks.mjs +2 -2
- package/dist/cmd/check-should-run-type-checks.mjs.map +1 -1
- package/dist/cmd/format-diff-from.d.mts +1 -1
- package/dist/cmd/format-diff-from.mjs +2 -2
- package/dist/cmd/format-diff-from.mjs.map +1 -1
- package/dist/cmd/format-uncommitted.d.mts +1 -1
- package/dist/cmd/format-uncommitted.mjs +2 -2
- package/dist/cmd/format-uncommitted.mjs.map +1 -1
- package/dist/cmd/gen-index-ts.d.mts +1 -1
- package/dist/cmd/gen-index-ts.mjs +2 -2
- package/dist/cmd/gen-index-ts.mjs.map +1 -1
- package/dist/entry-point.mjs +2 -1
- package/dist/entry-point.mjs.map +1 -1
- package/dist/functions/assert-ext.d.mts +20 -1
- package/dist/functions/assert-ext.d.mts.map +1 -1
- package/dist/functions/assert-ext.mjs +52 -35
- package/dist/functions/assert-ext.mjs.map +1 -1
- package/dist/functions/create-result-assert.d.mts +18 -0
- package/dist/functions/create-result-assert.d.mts.map +1 -0
- package/dist/functions/create-result-assert.mjs +40 -0
- package/dist/functions/create-result-assert.mjs.map +1 -0
- package/dist/functions/exec-async.d.mts +5 -6
- package/dist/functions/exec-async.d.mts.map +1 -1
- package/dist/functions/exec-async.mjs +26 -7
- package/dist/functions/exec-async.mjs.map +1 -1
- package/dist/functions/index.d.mts +1 -0
- package/dist/functions/index.d.mts.map +1 -1
- package/dist/functions/index.mjs +2 -1
- package/dist/functions/index.mjs.map +1 -1
- package/dist/functions/should-run.d.mts +1 -1
- package/dist/functions/should-run.mjs +1 -1
- package/dist/functions/workspace-utils/execute-parallel.mjs.map +1 -1
- package/dist/functions/workspace-utils/get-workspace-packages.d.mts.map +1 -1
- package/dist/functions/workspace-utils/get-workspace-packages.mjs +2 -2
- package/dist/functions/workspace-utils/get-workspace-packages.mjs.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/dist/node-global.d.mts +2 -0
- package/dist/node-global.d.mts.map +1 -1
- package/dist/node-global.mjs +3 -1
- package/dist/node-global.mjs.map +1 -1
- package/package.json +50 -38
- package/src/cmd/assert-repo-is-clean.mts +3 -3
- package/src/cmd/check-should-run-type-checks.mts +3 -3
- package/src/cmd/format-diff-from.mts +3 -3
- package/src/cmd/format-uncommitted.mts +3 -3
- package/src/cmd/gen-index-ts.mts +4 -4
- package/src/functions/assert-ext.mts +78 -52
- package/src/functions/create-result-assert.mts +59 -0
- package/src/functions/exec-async.mts +71 -14
- package/src/functions/exec-async.test.mts +5 -5
- package/src/functions/index.mts +1 -0
- package/src/functions/should-run.mts +1 -1
- package/src/functions/workspace-utils/execute-parallel.mts +1 -1
- package/src/functions/workspace-utils/get-workspace-packages.mts +12 -14
- package/src/functions/workspace-utils/run-cmd-in-stages.test.mts +5 -8
- package/src/node-global.mts +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-repo-utils",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript"
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
"module": "./dist/entry-point.mjs",
|
|
25
25
|
"types": "./dist/types.d.mts",
|
|
26
26
|
"bin": {
|
|
27
|
-
"assert-repo-is-clean": "./
|
|
28
|
-
"check-should-run-type-checks": "./
|
|
29
|
-
"format-diff-from": "./
|
|
30
|
-
"format-uncommitted": "./
|
|
31
|
-
"gen-index-ts": "./
|
|
27
|
+
"assert-repo-is-clean": "./dist/cmd/assert-repo-is-clean.mjs",
|
|
28
|
+
"check-should-run-type-checks": "./dist/cmd/check-should-run-type-checks.mjs",
|
|
29
|
+
"format-diff-from": "./dist/cmd/format-diff-from.mjs",
|
|
30
|
+
"format-uncommitted": "./dist/cmd/format-uncommitted.mjs",
|
|
31
|
+
"gen-index-ts": "./dist/cmd/gen-index-ts.mjs"
|
|
32
32
|
},
|
|
33
33
|
"files": [
|
|
34
34
|
"src",
|
|
@@ -38,13 +38,14 @@
|
|
|
38
38
|
],
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "tsx ./scripts/cmd/build.mts",
|
|
41
|
+
"build:min": "tsx ./scripts/cmd/build.mts --skip-check",
|
|
41
42
|
"check-all": "tsx ./scripts/cmd/check-all.mts",
|
|
42
43
|
"check:ext": "tsx ./scripts/cmd/check-ext.mts",
|
|
43
44
|
"cspell": "cspell \"**\" --gitignore --gitignore-root ./ --no-progress",
|
|
44
45
|
"doc": "tsx ./scripts/cmd/gen-docs.mts",
|
|
45
46
|
"doc:embed": "tsx ./scripts/cmd/embed-samples.mts",
|
|
46
|
-
"fmt": "format-uncommitted",
|
|
47
|
-
"fmt:diff": "format-diff-from origin/main",
|
|
47
|
+
"fmt": "pnpm run z:format-uncommitted",
|
|
48
|
+
"fmt:diff": "pnpm run z:format-diff-from origin/main",
|
|
48
49
|
"fmt:full": "prettier --write .",
|
|
49
50
|
"gh:apply-all": "run-s gh:apply-variables gh:apply-rulesets gh:apply-repository-settings",
|
|
50
51
|
"gh:apply-repository-settings": "tsx --env-file=.env scripts/github/repository/apply.mts",
|
|
@@ -54,72 +55,83 @@
|
|
|
54
55
|
"gh:backup-repository-settings": "tsx --env-file=.env scripts/github/repository/backup.mts",
|
|
55
56
|
"gh:backup-rulesets": "tsx --env-file=.env scripts/github/ruleset/backup.mts",
|
|
56
57
|
"gi": "run-s gi:scripts gi:src fmt",
|
|
57
|
-
"gi:scripts": "gen-index-ts ./scripts/github --index-ext .mts --export-ext .mjs --target-ext .mts --exclude cmd --exclude apply.mts --exclude octokit.mts",
|
|
58
|
-
"gi:src": "gen-index-ts ./src --index-ext .mts --export-ext .mjs --target-ext .mts --target-ext .tsx --exclude entry-point.mts --exclude cmd --exclude node-global.mts",
|
|
58
|
+
"gi:scripts": "pnpm run z:gen-index-ts ./scripts/github --index-ext .mts --export-ext .mjs --target-ext .mts --exclude cmd --exclude apply.mts --exclude octokit.mts",
|
|
59
|
+
"gi:src": "pnpm run z:gen-index-ts ./src --index-ext .mts --export-ext .mjs --target-ext .mts --target-ext .tsx --exclude entry-point.mts --exclude cmd --exclude node-global.mts",
|
|
59
60
|
"lint": "eslint .",
|
|
60
61
|
"lint:fix": "eslint . --fix",
|
|
61
62
|
"md": "markdownlint-cli2",
|
|
62
63
|
"prepare-release": "run-s sync-cli-versions fmt build doc",
|
|
63
64
|
"sync-cli-versions": "tsx scripts/cmd/sync-cli-versions.mts",
|
|
64
|
-
"test": "
|
|
65
|
-
"test:cov": "
|
|
65
|
+
"test": "pnpm run z:vitest run",
|
|
66
|
+
"test:cov": "pnpm run z:vitest run --coverage",
|
|
66
67
|
"test:cov:ui": "vite preview --outDir ./coverage",
|
|
67
|
-
"test:ui": "
|
|
68
|
-
"testw": "
|
|
68
|
+
"test:ui": "pnpm run z:vitest --ui",
|
|
69
|
+
"testw": "pnpm run z:vitest watch",
|
|
69
70
|
"tsc": "tsc --noEmit",
|
|
70
71
|
"tscw": "tsc --noEmit --watch -p ./tsconfig.json",
|
|
71
72
|
"type-check": "tsc --noEmit",
|
|
72
|
-
"update-packages": "
|
|
73
|
+
"update-packages": "pnpm update --latest",
|
|
74
|
+
"z:assert-repo-is-clean": "tsx ./src/cmd/assert-repo-is-clean.mjs",
|
|
75
|
+
"z:check-should-run-type-checks": "tsx ./src/cmd/check-should-run-type-checks.mjs",
|
|
76
|
+
"z:format-diff-from": "tsx ./src/cmd/format-diff-from.mjs",
|
|
77
|
+
"z:format-uncommitted": "tsx ./src/cmd/format-uncommitted.mjs",
|
|
78
|
+
"z:gen-index-ts": "tsx ./src/cmd/gen-index-ts.mjs",
|
|
73
79
|
"z:vitest": "vitest --config ./configs/vitest.config.ts"
|
|
74
80
|
},
|
|
75
81
|
"dependencies": {
|
|
76
|
-
"@types/micromatch": "^4.0.
|
|
77
|
-
"cmd-ts": "^0.14.
|
|
82
|
+
"@types/micromatch": "^4.0.10",
|
|
83
|
+
"cmd-ts": "^0.14.3",
|
|
78
84
|
"fast-glob": "^3.3.3",
|
|
79
85
|
"micromatch": "^4.0.8",
|
|
80
86
|
"prettier": "^3.6.2",
|
|
81
|
-
"ts-data-forge": "^3.3.
|
|
87
|
+
"ts-data-forge": "^3.3.1",
|
|
82
88
|
"tsx": "^4.20.6"
|
|
83
89
|
},
|
|
84
90
|
"devDependencies": {
|
|
85
|
-
"@octokit/core": "
|
|
86
|
-
"@rollup/plugin-replace": "^6.0.
|
|
91
|
+
"@octokit/core": "7.0.6",
|
|
92
|
+
"@rollup/plugin-replace": "^6.0.3",
|
|
87
93
|
"@rollup/plugin-strip": "^3.0.4",
|
|
88
|
-
"@rollup/plugin-typescript": "^12.
|
|
94
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
89
95
|
"@semantic-release/changelog": "^6.0.3",
|
|
90
96
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
91
97
|
"@semantic-release/exec": "^7.1.0",
|
|
92
98
|
"@semantic-release/git": "^10.0.1",
|
|
93
|
-
"@semantic-release/github": "^
|
|
94
|
-
"@semantic-release/npm": "^
|
|
99
|
+
"@semantic-release/github": "^12.0.1",
|
|
100
|
+
"@semantic-release/npm": "^13.1.1",
|
|
95
101
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
96
|
-
"@types/node": "^24.
|
|
97
|
-
"@vitest/coverage-v8": "^
|
|
98
|
-
"@vitest/ui": "^
|
|
102
|
+
"@types/node": "^24.9.2",
|
|
103
|
+
"@vitest/coverage-v8": "^4.0.6",
|
|
104
|
+
"@vitest/ui": "^4.0.6",
|
|
99
105
|
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
100
|
-
"cspell": "^9.2.
|
|
106
|
+
"cspell": "^9.2.2",
|
|
101
107
|
"dedent": "^1.7.0",
|
|
102
|
-
"eslint": "
|
|
103
|
-
"eslint-config-typed": "^1.
|
|
108
|
+
"eslint": "9.38.0",
|
|
109
|
+
"eslint-config-typed": "^3.1.1",
|
|
104
110
|
"fast-glob": "^3.3.3",
|
|
111
|
+
"jiti": "^2.6.1",
|
|
112
|
+
"markdownlint": "^0.39.0",
|
|
105
113
|
"markdownlint-cli2": "^0.18.1",
|
|
106
114
|
"npm-run-all2": "^8.0.4",
|
|
107
|
-
"octokit-safe-types": "^1.
|
|
115
|
+
"octokit-safe-types": "^1.1.2",
|
|
108
116
|
"prettier": "^3.6.2",
|
|
109
117
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
110
118
|
"prettier-plugin-packagejson": "^2.5.19",
|
|
111
|
-
"rollup": "^4.52.
|
|
112
|
-
"semantic-release": "^
|
|
113
|
-
"ts-fortress": "^
|
|
114
|
-
"ts-
|
|
115
|
-
"ts-type-forge": "^2.2.0",
|
|
119
|
+
"rollup": "^4.52.5",
|
|
120
|
+
"semantic-release": "^25.0.1",
|
|
121
|
+
"ts-fortress": "^5.2.0",
|
|
122
|
+
"ts-type-forge": "^2.3.0",
|
|
116
123
|
"tslib": "^2.8.1",
|
|
117
|
-
"
|
|
124
|
+
"tsx": "^4.20.6",
|
|
125
|
+
"typedoc": "^0.28.14",
|
|
118
126
|
"typedoc-plugin-markdown": "^4.9.0",
|
|
119
|
-
"typescript": "^5.9.
|
|
120
|
-
"vitest": "^
|
|
127
|
+
"typescript": "^5.9.3",
|
|
128
|
+
"vitest": "^4.0.6"
|
|
121
129
|
},
|
|
122
130
|
"peerDependencies": {
|
|
123
131
|
"prettier": "^3.6.2"
|
|
132
|
+
},
|
|
133
|
+
"volta": {
|
|
134
|
+
"node": "25.0.0",
|
|
135
|
+
"npm": "11.6.2"
|
|
124
136
|
}
|
|
125
137
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import * as cmd from 'cmd-ts';
|
|
4
4
|
import { assertRepoIsClean } from '../functions/index.mjs';
|
|
5
5
|
|
|
6
6
|
const cmdDef = cmd.command({
|
|
7
7
|
name: 'assert-repo-is-clean-cli',
|
|
8
|
-
version: '7.
|
|
8
|
+
version: '7.8.0',
|
|
9
9
|
args: {
|
|
10
10
|
silent: cmd.flag({
|
|
11
11
|
long: 'silent',
|
|
@@ -14,7 +14,7 @@ const cmdDef = cmd.command({
|
|
|
14
14
|
}),
|
|
15
15
|
},
|
|
16
16
|
handler: (args) => {
|
|
17
|
-
main(args).catch((error) => {
|
|
17
|
+
main(args).catch((error: unknown) => {
|
|
18
18
|
console.error('An error occurred:', error);
|
|
19
19
|
process.exit(1);
|
|
20
20
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import * as cmd from 'cmd-ts';
|
|
4
4
|
import { checkShouldRunTypeChecks } from '../functions/index.mjs';
|
|
5
5
|
|
|
6
6
|
const cmdDef = cmd.command({
|
|
7
7
|
name: 'check-should-run-type-checks-cli',
|
|
8
|
-
version: '7.
|
|
8
|
+
version: '7.8.0',
|
|
9
9
|
args: {
|
|
10
10
|
pathsIgnore: cmd.multioption({
|
|
11
11
|
long: 'paths-ignore',
|
|
@@ -21,7 +21,7 @@ const cmdDef = cmd.command({
|
|
|
21
21
|
}),
|
|
22
22
|
},
|
|
23
23
|
handler: (args) => {
|
|
24
|
-
main(args).catch((error) => {
|
|
24
|
+
main(args).catch((error: unknown) => {
|
|
25
25
|
console.error('An error occurred:', error);
|
|
26
26
|
process.exit(1);
|
|
27
27
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import * as cmd from 'cmd-ts';
|
|
4
4
|
import { Result } from 'ts-data-forge';
|
|
@@ -6,7 +6,7 @@ import { formatDiffFrom } from '../functions/index.mjs';
|
|
|
6
6
|
|
|
7
7
|
const cmdDef = cmd.command({
|
|
8
8
|
name: 'format-diff-from-cli',
|
|
9
|
-
version: '7.
|
|
9
|
+
version: '7.8.0',
|
|
10
10
|
args: {
|
|
11
11
|
base: cmd.positional({
|
|
12
12
|
type: cmd.string,
|
|
@@ -50,7 +50,7 @@ const cmdDef = cmd.command({
|
|
|
50
50
|
excludeStaged: args.excludeStaged ?? false,
|
|
51
51
|
ignoreUnknown: args.ignoreUnknown ?? true,
|
|
52
52
|
silent: args.silent ?? false,
|
|
53
|
-
}).catch((error) => {
|
|
53
|
+
}).catch((error: unknown) => {
|
|
54
54
|
console.error('An error occurred:', error);
|
|
55
55
|
process.exit(1);
|
|
56
56
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import * as cmd from 'cmd-ts';
|
|
4
4
|
import { Result } from 'ts-data-forge';
|
|
@@ -6,7 +6,7 @@ import { formatUncommittedFiles } from '../functions/index.mjs';
|
|
|
6
6
|
|
|
7
7
|
const cmdDef = cmd.command({
|
|
8
8
|
name: 'format-uncommitted-cli',
|
|
9
|
-
version: '7.
|
|
9
|
+
version: '7.8.0',
|
|
10
10
|
args: {
|
|
11
11
|
excludeUntracked: cmd.flag({
|
|
12
12
|
long: 'exclude-untracked',
|
|
@@ -44,7 +44,7 @@ const cmdDef = cmd.command({
|
|
|
44
44
|
excludeStaged: args.excludeStaged ?? false,
|
|
45
45
|
ignoreUnknown: args.ignoreUnknown ?? true,
|
|
46
46
|
silent: args.silent ?? false,
|
|
47
|
-
}).catch((error) => {
|
|
47
|
+
}).catch((error: unknown) => {
|
|
48
48
|
console.error('An error occurred:', error);
|
|
49
49
|
process.exit(1);
|
|
50
50
|
});
|
package/src/cmd/gen-index-ts.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import * as cmd from 'cmd-ts';
|
|
4
4
|
|
|
5
|
-
// eslint-disable-next-line import/no-internal-modules
|
|
5
|
+
// eslint-disable-next-line import-x/no-internal-modules
|
|
6
6
|
import { type InputOf, type OutputOf } from 'cmd-ts/dist/esm/from.js';
|
|
7
7
|
import { expectType } from 'ts-data-forge';
|
|
8
8
|
import { genIndex } from '../functions/index.mjs';
|
|
@@ -40,7 +40,7 @@ const nonEmptyArray = <T extends cmd.Type<any, any>>(
|
|
|
40
40
|
|
|
41
41
|
const cmdDef = cmd.command({
|
|
42
42
|
name: 'gen-index-ts-cli',
|
|
43
|
-
version: '7.
|
|
43
|
+
version: '7.8.0',
|
|
44
44
|
args: {
|
|
45
45
|
// required args
|
|
46
46
|
targetDirectory: cmd.positional({
|
|
@@ -109,7 +109,7 @@ const cmdDef = cmd.command({
|
|
|
109
109
|
expectType<typeof args.formatCommand, string | undefined>('=');
|
|
110
110
|
expectType<typeof args.silent, boolean | undefined>('=');
|
|
111
111
|
|
|
112
|
-
main(args).catch((error) => {
|
|
112
|
+
main(args).catch((error: unknown) => {
|
|
113
113
|
console.error('An error occurred:', error);
|
|
114
114
|
process.exit(1);
|
|
115
115
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Arr, type IMap, isString } from 'ts-data-forge';
|
|
1
|
+
import { Arr, type IMap, isString, Result } from 'ts-data-forge';
|
|
2
2
|
import '../node-global.mjs';
|
|
3
3
|
import { assertPathExists } from './assert-path-exists.mjs';
|
|
4
|
+
import { createResultAssert } from './create-result-assert.mjs';
|
|
4
5
|
|
|
5
6
|
/** Configuration for directory extension checking. */
|
|
6
7
|
export type CheckExtConfig = DeepReadonly<{
|
|
@@ -23,13 +24,21 @@ export type CheckExtConfig = DeepReadonly<{
|
|
|
23
24
|
}[];
|
|
24
25
|
}>;
|
|
25
26
|
|
|
27
|
+
export type CheckExtError = Readonly<{
|
|
28
|
+
message: string;
|
|
29
|
+
files: readonly string[];
|
|
30
|
+
}>;
|
|
31
|
+
|
|
26
32
|
/**
|
|
27
33
|
* Validates that all files in specified directories have the correct
|
|
28
|
-
* extensions.
|
|
34
|
+
* extensions.
|
|
29
35
|
*
|
|
30
36
|
* @param config - Configuration specifying directories and expected extensions.
|
|
37
|
+
* @returns Result.ok when all files pass, otherwise Result.err with details.
|
|
31
38
|
*/
|
|
32
|
-
export const
|
|
39
|
+
export const checkExt = async (
|
|
40
|
+
config: CheckExtConfig,
|
|
41
|
+
): Promise<Result<undefined, CheckExtError>> => {
|
|
33
42
|
// Check all directories in parallel
|
|
34
43
|
const results = await Promise.all(
|
|
35
44
|
config.directories.map(async ({ path: dir, extension, ignorePatterns }) => {
|
|
@@ -49,59 +58,43 @@ export const assertExt = async (config: CheckExtConfig): Promise<void> => {
|
|
|
49
58
|
// Collect all incorrect files
|
|
50
59
|
const allIncorrectFiles: readonly string[] = results.flat();
|
|
51
60
|
|
|
52
|
-
if (allIncorrectFiles.length
|
|
53
|
-
|
|
54
|
-
// Group directories by extension for a cleaner message
|
|
55
|
-
const extensionGroups: IMap<
|
|
56
|
-
string,
|
|
57
|
-
readonly Readonly<{
|
|
58
|
-
relativePath: string;
|
|
59
|
-
extKey: string;
|
|
60
|
-
}>[]
|
|
61
|
-
> = Arr.groupBy(
|
|
62
|
-
config.directories.map(({ path: dirPath, extension }) => {
|
|
63
|
-
const relativePath = path.relative(process.cwd(), dirPath);
|
|
64
|
-
const extKey = isString(extension)
|
|
65
|
-
? extension
|
|
66
|
-
: extension.join(' or ');
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
relativePath,
|
|
70
|
-
extKey,
|
|
71
|
-
};
|
|
72
|
-
}),
|
|
73
|
-
({ extKey }) => extKey,
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
// Generate message parts for each extension
|
|
77
|
-
const messageParts = Array.from(
|
|
78
|
-
extensionGroups.entries(),
|
|
79
|
-
([ext, dirs]) => {
|
|
80
|
-
const dirList =
|
|
81
|
-
dirs.length === 1
|
|
82
|
-
? dirs[0]?.relativePath
|
|
83
|
-
: dirs.map((d) => d.relativePath).join(', ');
|
|
84
|
-
return `${dirList} should have ${ext} extension`;
|
|
85
|
-
},
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
return `All files in ${messageParts.join(' and ')}.`;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const errorMessage = [
|
|
92
|
-
'Files with incorrect extensions found:',
|
|
93
|
-
...allIncorrectFiles.map((file) => ` - ${file}`),
|
|
94
|
-
'',
|
|
95
|
-
generateErrorMessage(),
|
|
96
|
-
].join('\n');
|
|
97
|
-
|
|
98
|
-
echo(errorMessage);
|
|
99
|
-
process.exit(1);
|
|
61
|
+
if (allIncorrectFiles.length === 0) {
|
|
62
|
+
return Result.ok(undefined);
|
|
100
63
|
}
|
|
101
64
|
|
|
102
|
-
|
|
65
|
+
const message = [
|
|
66
|
+
'Files with incorrect extensions found:',
|
|
67
|
+
...allIncorrectFiles.map((file) => ` - ${file}`),
|
|
68
|
+
'',
|
|
69
|
+
describeExpectedExtensions(config),
|
|
70
|
+
].join('\n');
|
|
71
|
+
|
|
72
|
+
return Result.err({
|
|
73
|
+
message,
|
|
74
|
+
files: allIncorrectFiles,
|
|
75
|
+
});
|
|
103
76
|
};
|
|
104
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Validates that all files in specified directories have the correct
|
|
80
|
+
* extensions. Exits with code 1 if any files have incorrect extensions.
|
|
81
|
+
*
|
|
82
|
+
* @param config - Configuration specifying directories and expected extensions.
|
|
83
|
+
*/
|
|
84
|
+
export const assertExt = createResultAssert<
|
|
85
|
+
CheckExtConfig,
|
|
86
|
+
undefined,
|
|
87
|
+
CheckExtError
|
|
88
|
+
>({
|
|
89
|
+
run: checkExt,
|
|
90
|
+
onError: (error) => {
|
|
91
|
+
echo(error.message);
|
|
92
|
+
},
|
|
93
|
+
onSuccess: () => {
|
|
94
|
+
echo('✓ All files have correct extensions');
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
105
98
|
/**
|
|
106
99
|
* Checks if all files in a directory have the expected extension.
|
|
107
100
|
*
|
|
@@ -134,3 +127,36 @@ const getFilesWithIncorrectExtension = async (
|
|
|
134
127
|
(file) => !expectedExtensions.some((ext) => file.endsWith(ext)),
|
|
135
128
|
);
|
|
136
129
|
};
|
|
130
|
+
|
|
131
|
+
const describeExpectedExtensions = (config: CheckExtConfig): string => {
|
|
132
|
+
// Group directories by extension for a cleaner message
|
|
133
|
+
const extensionGroups: IMap<
|
|
134
|
+
string,
|
|
135
|
+
readonly Readonly<{
|
|
136
|
+
relativePath: string;
|
|
137
|
+
extKey: string;
|
|
138
|
+
}>[]
|
|
139
|
+
> = Arr.groupBy(
|
|
140
|
+
config.directories.map(({ path: dirPath, extension }) => {
|
|
141
|
+
const relativePath = path.relative(process.cwd(), dirPath);
|
|
142
|
+
const extKey = isString(extension) ? extension : extension.join(' or ');
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
relativePath,
|
|
146
|
+
extKey,
|
|
147
|
+
};
|
|
148
|
+
}),
|
|
149
|
+
({ extKey }) => extKey,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// Generate message parts for each extension
|
|
153
|
+
const messageParts = Array.from(extensionGroups.entries(), ([ext, dirs]) => {
|
|
154
|
+
const dirList =
|
|
155
|
+
dirs.length === 1
|
|
156
|
+
? dirs[0]?.relativePath
|
|
157
|
+
: dirs.map((d) => d.relativePath).join(', ');
|
|
158
|
+
return `${dirList} should have ${ext} extension`;
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return `All files in ${messageParts.join(' and ')}.`;
|
|
162
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { hasKey, isRecord, isString, Result } from 'ts-data-forge';
|
|
2
|
+
import '../node-global.mjs';
|
|
3
|
+
|
|
4
|
+
type ResultProducer<TConfig, TOk, TErr> = (
|
|
5
|
+
config: TConfig,
|
|
6
|
+
) => Promise<Result<TOk, TErr>>;
|
|
7
|
+
|
|
8
|
+
export type CreateResultAssertOptions<Config, Ok, Err> = Readonly<{
|
|
9
|
+
run: ResultProducer<Config, Ok, Err>;
|
|
10
|
+
onSuccess?: (value: Ok, config: Config) => void | Promise<void>;
|
|
11
|
+
onError?: (error: Err, config: Config) => void | Promise<void>;
|
|
12
|
+
exitCode?: number;
|
|
13
|
+
}>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Converts a function that returns a Result into an assert-style variant that
|
|
17
|
+
* exits the process when the Result is Err. This is useful for building CLI
|
|
18
|
+
* commands that should stop execution on failure but remain composable when a
|
|
19
|
+
* Result is preferred.
|
|
20
|
+
*/
|
|
21
|
+
export const createResultAssert = <Config, Ok, Err>({
|
|
22
|
+
run,
|
|
23
|
+
onSuccess,
|
|
24
|
+
onError,
|
|
25
|
+
exitCode = 1,
|
|
26
|
+
}: CreateResultAssertOptions<Config, Ok, Err>): ((
|
|
27
|
+
config: Config,
|
|
28
|
+
) => Promise<Ok>) => {
|
|
29
|
+
const defaultOnError = (error: Err): void => {
|
|
30
|
+
if (
|
|
31
|
+
isRecord(error) &&
|
|
32
|
+
hasKey(error, 'message') &&
|
|
33
|
+
isString(error.message)
|
|
34
|
+
) {
|
|
35
|
+
echo(error.message);
|
|
36
|
+
} else {
|
|
37
|
+
console.error(error);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return async (config: Config): Promise<Ok> => {
|
|
42
|
+
const result = await run(config);
|
|
43
|
+
|
|
44
|
+
if (Result.isErr(result)) {
|
|
45
|
+
if (onError !== undefined) {
|
|
46
|
+
await onError(result.value, config);
|
|
47
|
+
} else {
|
|
48
|
+
defaultOnError(result.value);
|
|
49
|
+
}
|
|
50
|
+
process.exit(exitCode);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (onSuccess !== undefined) {
|
|
54
|
+
await onSuccess(result.value, config);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return result.value;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
@@ -5,6 +5,18 @@ type ExecOptionsCustom = Readonly<{
|
|
|
5
5
|
silent?: boolean;
|
|
6
6
|
}>;
|
|
7
7
|
|
|
8
|
+
type ExecOptionsWithStringEncoding = Readonly<
|
|
9
|
+
childProcess.ExecOptionsWithStringEncoding & ExecOptionsCustom
|
|
10
|
+
>;
|
|
11
|
+
|
|
12
|
+
type ExecOptionsWithBufferEncoding = Readonly<
|
|
13
|
+
childProcess.ExecOptionsWithBufferEncoding & ExecOptionsCustom
|
|
14
|
+
>;
|
|
15
|
+
|
|
16
|
+
type NormalizedExecOptions = Readonly<
|
|
17
|
+
childProcess.ExecOptions & { encoding?: BufferEncoding | 'buffer' | null }
|
|
18
|
+
>;
|
|
19
|
+
|
|
8
20
|
export type ExecOptions = childProcess.ExecOptions & ExecOptionsCustom;
|
|
9
21
|
|
|
10
22
|
export type ExecResult<T extends string | Buffer> = Result<
|
|
@@ -23,47 +35,92 @@ export type ExecResult<T extends string | Buffer> = Result<
|
|
|
23
35
|
export function $(
|
|
24
36
|
command: string,
|
|
25
37
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
26
|
-
options?:
|
|
27
|
-
| ExecOptionsCustom
|
|
28
|
-
| Readonly<{ encoding: BufferEncoding } & ExecOptions>,
|
|
38
|
+
options?: ExecOptionsWithStringEncoding,
|
|
29
39
|
): Promise<ExecResult<string>>;
|
|
30
40
|
|
|
31
41
|
export function $(
|
|
32
42
|
command: string,
|
|
33
43
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
34
|
-
options
|
|
44
|
+
options: ExecOptionsWithBufferEncoding,
|
|
35
45
|
): Promise<ExecResult<Buffer>>;
|
|
36
46
|
|
|
47
|
+
export function $<
|
|
48
|
+
TOptions extends
|
|
49
|
+
| ExecOptionsWithBufferEncoding
|
|
50
|
+
| ExecOptionsWithStringEncoding
|
|
51
|
+
| undefined = undefined,
|
|
52
|
+
>(
|
|
53
|
+
command: string,
|
|
54
|
+
options?: TOptions,
|
|
55
|
+
): Promise<
|
|
56
|
+
ExecResult<TOptions extends ExecOptionsWithBufferEncoding ? Buffer : string>
|
|
57
|
+
>;
|
|
58
|
+
|
|
37
59
|
export function $(
|
|
38
60
|
command: string,
|
|
39
61
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
40
|
-
options?:
|
|
41
|
-
{ encoding?: BufferEncoding | 'buffer' | null } & ExecOptions
|
|
42
|
-
>,
|
|
62
|
+
options?: ExecOptionsWithStringEncoding | ExecOptionsWithBufferEncoding,
|
|
43
63
|
): Promise<ExecResult<string | Buffer>> {
|
|
44
64
|
const { silent = false, ...restOptions } = options ?? {};
|
|
65
|
+
const normalizedOptions: NormalizedExecOptions = restOptions;
|
|
45
66
|
|
|
46
67
|
if (!silent) {
|
|
47
68
|
echo(`$ ${command}`);
|
|
48
69
|
}
|
|
49
70
|
|
|
50
71
|
return new Promise((resolve) => {
|
|
51
|
-
|
|
52
|
-
|
|
72
|
+
const handleResult = <T extends string | Buffer>(
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
74
|
+
error: childProcess.ExecException | null,
|
|
75
|
+
stdout: T,
|
|
76
|
+
stderr: T,
|
|
77
|
+
): void => {
|
|
53
78
|
if (!silent) {
|
|
54
|
-
if (stdout
|
|
79
|
+
if (!isEmpty(stdout)) {
|
|
55
80
|
echo(stdout);
|
|
56
81
|
}
|
|
57
|
-
if (stderr
|
|
82
|
+
if (!isEmpty(stderr)) {
|
|
58
83
|
console.error(stderr);
|
|
59
84
|
}
|
|
60
85
|
}
|
|
61
86
|
|
|
62
87
|
if (error !== null) {
|
|
63
88
|
resolve(Result.err(error));
|
|
64
|
-
|
|
65
|
-
resolve(Result.ok({ stdout, stderr }));
|
|
89
|
+
return;
|
|
66
90
|
}
|
|
67
|
-
|
|
91
|
+
|
|
92
|
+
resolve(
|
|
93
|
+
Result.ok<Readonly<{ stdout: T; stderr: T }>>({ stdout, stderr }),
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const encoding = normalizedOptions.encoding;
|
|
98
|
+
|
|
99
|
+
if (encoding === 'buffer' || encoding === null) {
|
|
100
|
+
// eslint-disable-next-line security/detect-child-process
|
|
101
|
+
childProcess.exec(
|
|
102
|
+
command,
|
|
103
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
104
|
+
normalizedOptions as childProcess.ExecOptionsWithBufferEncoding,
|
|
105
|
+
(error, stdout, stderr) => {
|
|
106
|
+
handleResult(error, stdout, stderr);
|
|
107
|
+
},
|
|
108
|
+
);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// eslint-disable-next-line security/detect-child-process
|
|
113
|
+
childProcess.exec(
|
|
114
|
+
command,
|
|
115
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
116
|
+
normalizedOptions as childProcess.ExecOptionsWithStringEncoding,
|
|
117
|
+
(error, stdout, stderr) => {
|
|
118
|
+
handleResult(error, stdout, stderr);
|
|
119
|
+
},
|
|
120
|
+
);
|
|
68
121
|
});
|
|
69
122
|
}
|
|
123
|
+
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
125
|
+
const isEmpty = (value: string | Buffer): boolean =>
|
|
126
|
+
typeof value === 'string' ? value === '' : value.length === 0;
|
|
@@ -368,8 +368,8 @@ describe('exec-async', () => {
|
|
|
368
368
|
// Type check for native exec with buffer encoding
|
|
369
369
|
exec('echo "test"', { encoding: 'buffer' }, (error, stdout, stderr) => {
|
|
370
370
|
expectType<ExecException | null, typeof error>('=');
|
|
371
|
-
expectType<Buffer, typeof stdout>('
|
|
372
|
-
expectType<Buffer, typeof stderr>('
|
|
371
|
+
expectType<Buffer, typeof stdout>('>=');
|
|
372
|
+
expectType<Buffer, typeof stderr>('>=');
|
|
373
373
|
});
|
|
374
374
|
|
|
375
375
|
// The $ function should produce the same types wrapped in Result (suppressed for clean output)
|
|
@@ -381,15 +381,15 @@ describe('exec-async', () => {
|
|
|
381
381
|
Promise<
|
|
382
382
|
Result<Readonly<{ stdout: Buffer; stderr: Buffer }>, ExecException>
|
|
383
383
|
>
|
|
384
|
-
>('
|
|
384
|
+
>('~=');
|
|
385
385
|
});
|
|
386
386
|
|
|
387
387
|
test('should match exec callback types for null encoding', () => {
|
|
388
388
|
// Type check for native exec with null encoding
|
|
389
389
|
exec('echo "test"', { encoding: null }, (error, stdout, stderr) => {
|
|
390
390
|
expectType<ExecException | null, typeof error>('=');
|
|
391
|
-
expectType<Buffer, typeof stdout>('
|
|
392
|
-
expectType<Buffer, typeof stderr>('
|
|
391
|
+
expectType<Buffer, typeof stdout>('>=');
|
|
392
|
+
expectType<Buffer, typeof stderr>('>=');
|
|
393
393
|
});
|
|
394
394
|
|
|
395
395
|
// The $ function should produce the same types wrapped in Result (suppressed for clean output)
|
package/src/functions/index.mts
CHANGED