@williamthorsen/nmr 0.37.0 → 0.39.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/.meta/changelog.json +114 -0
- package/.readyup/kits/default.js +92 -87
- package/.readyup/manifest.json +8 -8
- package/CHANGELOG.md +89 -0
- package/README.md +37 -28
- package/agents/guidance/rulebooks/nmr.md +2 -2
- package/dist/esm/check-cache.d.ts +4 -3
- package/dist/esm/check-cache.js +28 -28
- package/dist/esm/cli-build.js +3 -0
- package/dist/esm/cli-clean.js +3 -1
- package/dist/esm/cli-ensure-prepublish-hooks.js +7 -5
- package/dist/esm/cli-report-catalog.js +2 -1
- package/dist/esm/cli-report-overrides.js +2 -1
- package/dist/esm/commands/build-output.d.ts +2 -2
- package/dist/esm/commands/build-output.js +9 -9
- package/dist/esm/commands/build.d.ts +6 -1
- package/dist/esm/commands/build.js +73 -72
- package/dist/esm/commands/clean.d.ts +3 -2
- package/dist/esm/commands/clean.js +25 -24
- package/dist/esm/commands/ensure-prepublish-hooks.d.ts +5 -4
- package/dist/esm/commands/ensure-prepublish-hooks.js +44 -42
- package/dist/esm/commands/fmt.js +33 -33
- package/dist/esm/commands/report-catalog.d.ts +2 -1
- package/dist/esm/commands/report-catalog.js +14 -11
- package/dist/esm/commands/report-overrides.d.ts +2 -1
- package/dist/esm/commands/report-overrides.js +11 -10
- package/dist/esm/config.js +39 -29
- package/dist/esm/context.js +8 -8
- package/dist/esm/default-scripts.d.ts +2 -2
- package/dist/esm/default-scripts.js +5 -5
- package/dist/esm/glyphs.d.ts +9 -0
- package/dist/esm/glyphs.js +9 -0
- package/dist/esm/help.d.ts +2 -2
- package/dist/esm/help.js +36 -30
- package/dist/esm/helpers/clampToBytes.js +3 -3
- package/dist/esm/helpers/createBoundedBuffer.d.ts +1 -1
- package/dist/esm/helpers/createBoundedBuffer.js +2 -2
- package/dist/esm/helpers/duration.d.ts +2 -2
- package/dist/esm/helpers/duration.js +7 -7
- package/dist/esm/helpers/filter-selection.js +12 -12
- package/dist/esm/helpers/findClosestName.js +15 -15
- package/dist/esm/helpers/package-json.d.ts +2 -2
- package/dist/esm/helpers/package-json.js +26 -25
- package/dist/esm/helpers/transcript.d.ts +2 -2
- package/dist/esm/helpers/transcript.js +14 -14
- package/dist/esm/output-style.d.ts +41 -0
- package/dist/esm/output-style.js +17 -0
- package/dist/esm/prettier.js +5 -5
- package/dist/esm/recording.d.ts +8 -5
- package/dist/esm/recording.js +27 -17
- package/dist/esm/report-format.d.ts +1 -1
- package/dist/esm/report-format.js +7 -7
- package/dist/esm/resolveBinStyles.d.ts +12 -0
- package/dist/esm/resolveBinStyles.js +16 -0
- package/dist/esm/resolver.d.ts +2 -2
- package/dist/esm/resolver.js +16 -16
- package/dist/esm/runCli.js +309 -221
- package/dist/esm/runner.d.ts +1 -1
- package/dist/esm/runner.js +18 -18
- package/dist/esm/steps.d.ts +17 -5
- package/dist/esm/steps.js +37 -28
- package/dist/esm/tests.d.ts +7 -7
- package/dist/esm/tests.js +20 -8
- package/dist/esm/tiers.d.ts +1 -1
- package/dist/esm/tiers.js +8 -8
- package/dist/esm/verbosity.d.ts +1 -1
- package/dist/esm/verbosity.js +7 -7
- package/dist/esm/verdict.d.ts +15 -4
- package/dist/esm/verdict.js +78 -59
- package/dist/esm/vitest-source-resolution.js +48 -46
- package/dist/esm/vitest.d.ts +2 -2
- package/dist/esm/vitest.js +74 -25
- package/dist/esm/workspace.d.ts +2 -18
- package/dist/esm/workspace.js +9 -67
- package/docs/check-cache.md +5 -5
- package/docs/reporting.md +20 -2
- package/docs/scripts.md +2 -2
- package/docs/vitest.md +6 -6
- package/docs/workspace.md +24 -1
- package/package.json +9 -10
- package/dist/esm/helpers/workspace-patterns.d.ts +0 -8
- package/dist/esm/helpers/workspace-patterns.js +0 -29
package/.meta/changelog.json
CHANGED
|
@@ -1,4 +1,118 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.39.0",
|
|
4
|
+
"date": "2026-09-22",
|
|
5
|
+
"sections": [
|
|
6
|
+
{
|
|
7
|
+
"title": "🎉 Features",
|
|
8
|
+
"audience": "all",
|
|
9
|
+
"items": [
|
|
10
|
+
{
|
|
11
|
+
"description": "Report why a workspace selects no package, and no-op nmr's own -R steps (#841)",
|
|
12
|
+
"body": "- Drops a `-R` step that nmr composed into a script where the workspace holds no package, so `typecheck` and the five `test` scripts run there instead of failing the run.\n- Adds a no-op verdict for a command that the drop leaves with no step at all.\n- Replaces the single refusal for a workspace that selects no package with one message per cause.\n\nMigration: In a repo that declares a workspace package pattern and deliberately holds no package, delete the `.config/nmr.config.ts` entries added to work around the `-R` refusal, the `build: ':'` override included.",
|
|
13
|
+
"migration": "In a repo that declares a workspace package pattern and deliberately holds no package, delete the `.config/nmr.config.ts` entries added to work around the `-R` refusal, the `build: ':'` override included.",
|
|
14
|
+
"hash": "0b21f4b29a40aab5c56f10026789e8c0e72161ab"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"description": "Prefix the booleans and reject unrecognized Vitest option keys (#849)",
|
|
18
|
+
"body": "- Renames two boolean options of `defineVitestConfig` and `defineRootVitestConfig`, the factories that build a repo's Vitest config: `isolateGit` becomes `shouldIsolateGit`, and `resolveFromSource` becomes `shouldResolveFromSource`.\n- Makes both factories reject an option key outside the recognized set, naming the offending keys and the recognized ones, rather than ignoring a misspelled or retired key and letting its option fall back to the default with nothing in the run reporting it.\n- Adds a retired-key check that runs ahead of the unrecognized-key check, reporting the new name for a config that still sets `isolateGit` or `resolveFromSource`.\n\nMigration: In each options object passed to `defineVitestConfig` or `defineRootVitestConfig`, rename `isolateGit` to `shouldIsolateGit` and `resolveFromSource` to `shouldResolveFromSource`, and correct any other key that the factory now rejects. `defineRootVitestConfig` recognizes `monorepoRoot` on the last layer alone, so a config that sets it on an earlier layer now throws where the old code ignored it.",
|
|
19
|
+
"breaking": true,
|
|
20
|
+
"migration": "In each options object passed to `defineVitestConfig` or `defineRootVitestConfig`, rename `isolateGit` to `shouldIsolateGit` and `resolveFromSource` to `shouldResolveFromSource`, and correct any other key that the factory now rejects. `defineRootVitestConfig` recognizes `monorepoRoot` on the last layer alone, so a config that sets it on an earlier layer now throws where the old code ignored it.",
|
|
21
|
+
"hash": "ed2d1ba9a505c3a6d9fd5455c23bd110b9b8a7c5"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"description": "End nmr's source value names with the kind of the value (#850)",
|
|
25
|
+
"body": "- Renames two fields of `TestFileConventionsReport`, `misplaced` and `untiered`, to `misplacedFiles` and `untieredFiles`, and renames `TestFileConventionsOptions`'s `exclude` to `excludedBasenames`, in the `@williamthorsen/nmr/tests` entry point.\n- Adds a guard to `checkTestFileConventions` and `reportTestFileConventions` that throws a `TypeError` naming `excludedBasenames` when a call still sets `exclude`.\n\nMigration: Read `report.misplacedFiles` and `report.untieredFiles` in place of `report.misplaced` and `report.untiered`, and pass `excludedBasenames` in place of `exclude`. A call that still sets `exclude` throws rather than running with no exclusions, so the failure surfaces where Vitest loads the call site.",
|
|
26
|
+
"breaking": true,
|
|
27
|
+
"migration": "Read `report.misplacedFiles` and `report.untieredFiles` in place of `report.misplaced` and `report.untiered`, and pass `excludedBasenames` in place of `exclude`. A call that still sets `exclude` throws rather than running with no exclusions, so the failure surfaces where Vitest loads the call site.",
|
|
28
|
+
"hash": "059c249e2857bed87cefcce02e894ce3a9a65970"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"description": "Hoist the pnpm-workspace resolver into nmr-core as a total API (#864)",
|
|
32
|
+
"body": "- Adds a `./workspace` subpath export to `@williamthorsen/nmr-core`, whose `resolveWorkspace` returns a pnpm workspace's package directories, or the cause of an empty result, as a value rather than as a thrown error.\n- Reports a `pnpm-workspace.yaml` that the YAML parser rejects under a cause of its own, `unreadable-manifest`, so `nmr -R build` names the syntax error and its remedy in place of failing with the parser's own error.\n- Removes `diagnoseEmptyWorkspace` and `EmptyWorkspaceDiagnosis` from `@williamthorsen/nmr/workspace`, since `resolveWorkspace` reports the same cause in one call.\n\nMigration: Replace each `diagnoseEmptyWorkspace` call with `resolveWorkspace`, which reports the cause on the empty result it already returns, and rename the `no-manifest` cause to `no-package`. `EmptyWorkspaceCause` also gains `unreadable-manifest`, so an exhaustive switch over it stops compiling until it handles the new member; every other export of `@williamthorsen/nmr/workspace` keeps its signature and its `UserError` throws.",
|
|
33
|
+
"breaking": true,
|
|
34
|
+
"migration": "Replace each `diagnoseEmptyWorkspace` call with `resolveWorkspace`, which reports the cause on the empty result it already returns, and rename the `no-manifest` cause to `no-package`. `EmptyWorkspaceCause` also gains `unreadable-manifest`, so an exhaustive switch over it stops compiling until it handles the new member; every other export of `@williamthorsen/nmr/workspace` keeps its signature and its `UserError` throws.",
|
|
35
|
+
"hash": "3196b84c9d5f037ead0e98ee8547fe71e80fb5c6"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"description": "Adopt the shared workspace resolver in release-kit, honoring exclusions (#865)",
|
|
39
|
+
"body": "- 🚨 **Breaking:** Narrows `no-pattern` in `@williamthorsen/nmr-core`'s `EmptyWorkspaceCause`, moving an absent or empty `packages` list onto `no-packages-list` and a value that is not a list of strings onto `unreadable-packages`.\n- Stops `release-kit` from tagging, publishing, and label-syncing a package that a `!` entry in `pnpm-workspace.yaml` excludes, which its previous `glob`-based resolution could not remove from the match.\n- Replaces the release of the root package alone with a failure naming the condition that emptied the workspace, when `pnpm-workspace.yaml` does not hold valid YAML or declares a workspace that does not resolve to a package.\n- Replaces the `no-pattern` remedy that `nmr` printed for a `pnpm-workspace.yaml` declaring no `packages` list, and for one declaring a `packages` value that is not a list of strings.\n- Raises `engines.node` to `>=24.16.0` in `@williamthorsen/nmr-core` and `@williamthorsen/release-kit`, because Node 24.0 through 24.15 ignores the `followSymlinks` option that the workspace matcher passes to `globSync` and drops a symlinked package directory from the resolution without reporting it.\n\nMigration: Add a `no-packages-list` arm and an `unreadable-packages` arm to any `switch` that covers `EmptyWorkspaceCause` exhaustively. `no-pattern` now names only a list that the resolver read in full and whose entries all failed to become a positive pattern, so move the handling of an absent `packages` key, an empty list, and a value that is not a list of strings out of the `no-pattern` arm into the two new ones. Route `no-packages-list` to single-package behavior rather than to a failure path: pnpm resolves such a manifest to the root package alone.",
|
|
40
|
+
"breaking": true,
|
|
41
|
+
"migration": "Add a `no-packages-list` arm and an `unreadable-packages` arm to any `switch` that covers `EmptyWorkspaceCause` exhaustively. `no-pattern` now names only a list that the resolver read in full and whose entries all failed to become a positive pattern, so move the handling of an absent `packages` key, an empty list, and a value that is not a list of strings out of the `no-pattern` arm into the two new ones. Route `no-packages-list` to single-package behavior rather than to a failure path: pnpm resolves such a manifest to the root package alone.",
|
|
42
|
+
"hash": "b1126866159402b8f027a9b82f2a19591a6e9e1b"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"title": "♻️ Refactoring",
|
|
48
|
+
"audience": "dev",
|
|
49
|
+
"items": [
|
|
50
|
+
{
|
|
51
|
+
"description": "Align nmr's function names with naming conventions (#852)",
|
|
52
|
+
"body": "- Renames the functions in `packages/nmr` to align with naming conventions.",
|
|
53
|
+
"hash": "8a41a206ebe0374c844b20dca0eded7da6240133"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"description": "Suffix nmr's unit-measuring numbers and expand its abbreviations (#854)",
|
|
57
|
+
"body": "- Renames every unit-measuring number and abbreviated identifier under `packages/nmr/src`, adding `Ms`, `Bytes`, and `Offset` suffixes to the quantities and expanding `pkg`, `Len`, and `Idx` to `package`, `Length`, and `Index`. The renames are not part of any public API.",
|
|
58
|
+
"hash": "f8292228095a475b9a187ba64d700cb222f80305"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"title": "🧪 Tests",
|
|
64
|
+
"audience": "dev",
|
|
65
|
+
"items": [
|
|
66
|
+
{
|
|
67
|
+
"description": "End nmr's test value names with the kind of the value (#851)",
|
|
68
|
+
"body": "- Aligns the name of every non-boolean value in `packages/nmr/src/**/__tests__` with naming conventions.\n- Renames `inline` to `inlineBody` in the repo-root `__tests__/reusable-workflow-runtime.app.unit.test.ts`.",
|
|
69
|
+
"hash": "d12494b2be31f0e73cda1afb7113e7508e9969ee"
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"title": "👷 CI",
|
|
75
|
+
"audience": "dev",
|
|
76
|
+
"items": [
|
|
77
|
+
{
|
|
78
|
+
"description": "Make the release workflow Corepack-agnostic (#842)",
|
|
79
|
+
"body": "- Removes the `Disable Corepack` step from `release.reusable.yaml`, which Node 25's removal of Corepack from the distribution would make fail with `command not found` once `actions/setup-node` moves past Node 24, and keeps `COREPACK_ENABLE_STRICT: '0'`, which guards the source-build path where the pnpm `packageManager` pin survives.\n- Adds a case to `reusable-workflow-runtime.app.unit.test.ts` that fails the test run on any reusable workflow whose `run:` commands invoke `corepack`, enumerated from `.github/workflows` so that a workflow added later is covered without editing a list.\n- Drops the Corepack recommendation from the README's pnpm prerequisite, which now points at pnpm's installation page, and from the fix text of the `.tool-versions does not list pnpm` check in nmr's readyup kit, which now names the `packageManager` field.",
|
|
80
|
+
"hash": "dd46b629c3b4448b701ded3301f6794bdea7af6b"
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"version": "0.38.0",
|
|
88
|
+
"date": "2026-09-18",
|
|
89
|
+
"sections": [
|
|
90
|
+
{
|
|
91
|
+
"title": "🎉 Features",
|
|
92
|
+
"audience": "all",
|
|
93
|
+
"items": [
|
|
94
|
+
{
|
|
95
|
+
"description": "Give nmr plain output in pipes and CI logs (#838)",
|
|
96
|
+
"body": "- Stops `nmr` from printing emoji to a pipe or a CI log: When the output stream is not a terminal or `CI` is set, a verdict reads `PASS`, `FAIL`, `SKIP`, or `NOOP` and a command icon is omitted, so that a log can be searched for a verdict.\n- Adds `--output-style <auto|plain|rich>` to `nmr` and the `NMR_OUTPUT_STYLE` variable, each forcing either style; the flag outranks the variable, a value that names no style exits 1 with a usage error, and neither enters a check-cache key: A rich run and a plain run share a recorded pass.\n- Replaces several rich glyphs, taking the status markers from the set that release-kit and v11y-check print so that one status has one glyph across the three tools: a recalled pass shows ⏩ in place of ⏭️, a skipped empty override shows ⚪ in place of ⛔, a warning and the `--log` refusal show 🟠 in place of ⚠️ and 📭, the `--log` header shows 💾 in place of 📼, and `nmr-ensure-prepublish-hooks` reports ✅ and ❌ in place of ✓ and ✗.",
|
|
97
|
+
"hash": "693b3a7f57def3d299ced240c19bda90f6253bdc"
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"title": "🪦 Removed",
|
|
103
|
+
"audience": "all",
|
|
104
|
+
"items": [
|
|
105
|
+
{
|
|
106
|
+
"description": "Rename the declinesArgs step declaration to shouldDeclineArguments (#822)",
|
|
107
|
+
"body": "- Makes a `.config/nmr.config.ts` that still declares `declinesArgs` on a composite script's `{ run, declinesArgs }` step fail to load, with an error that names `shouldDeclineArguments` as the replacement.\n- Renames the matching field of the `StepSpec` type exported by `@williamthorsen/nmr/config` and `@williamthorsen/nmr/scripts`.\n\nMigration: Rename `declinesArgs` to `shouldDeclineArguments` in each composite script step of `.config/nmr.config.ts`, and in any code that sets or reads the field on a `StepSpec`.",
|
|
108
|
+
"breaking": true,
|
|
109
|
+
"migration": "Rename `declinesArgs` to `shouldDeclineArguments` in each composite script step of `.config/nmr.config.ts`, and in any code that sets or reads the field on a `StepSpec`.",
|
|
110
|
+
"hash": "b12333390e731c1a9d7055d293b686028c89ad26"
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
},
|
|
2
116
|
{
|
|
3
117
|
"version": "0.37.0",
|
|
4
118
|
"date": "2026-09-15",
|
package/.readyup/kits/default.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/** @noformat -- @generated. Do not edit. Compiled by rdy. */
|
|
2
2
|
/* eslint-disable */
|
|
3
|
-
export const __readyupVersion = "0.
|
|
3
|
+
export const __readyupVersion = "0.38.0";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
// .readyup/kits/default.ts
|
|
7
7
|
import { existsSync, globSync, readdirSync as readdirSync2 } from "node:fs";
|
|
8
8
|
import { basename, dirname, join, posix, sep } from "node:path";
|
|
9
9
|
|
|
10
|
-
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.errors@0.
|
|
10
|
+
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.errors@0.8.1/node_modules/@williamthorsen/toolbelt.errors/dist/esm/4-release/isError.js
|
|
11
11
|
function isError(error) {
|
|
12
12
|
return error instanceof Error || Object.prototype.toString.call(error) === "[object Error]";
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.errors@0.
|
|
15
|
+
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.errors@0.8.1/node_modules/@williamthorsen/toolbelt.errors/dist/esm/4-release/describeError.js
|
|
16
16
|
function describeError(error) {
|
|
17
17
|
try {
|
|
18
18
|
if (isError(error) && typeof error.message === "string" && error.message !== "") {
|
|
@@ -41,8 +41,8 @@ import {
|
|
|
41
41
|
|
|
42
42
|
// src/default-scripts.ts
|
|
43
43
|
var GATE_PROJECTS = "--project unit --project tool";
|
|
44
|
-
var TYPECHECK_STEP = { run: "typecheck",
|
|
45
|
-
var ROOT_TYPECHECK_STEP = { run: "root:typecheck",
|
|
44
|
+
var TYPECHECK_STEP = { run: "typecheck", shouldDeclineArguments: true };
|
|
45
|
+
var ROOT_TYPECHECK_STEP = { run: "root:typecheck", shouldDeclineArguments: true };
|
|
46
46
|
var workspaceScripts = {
|
|
47
47
|
build: ["compile"],
|
|
48
48
|
check: [TYPECHECK_STEP, "fmt:check", "lint:check", "test"],
|
|
@@ -77,7 +77,7 @@ var rootScripts = {
|
|
|
77
77
|
"check:strict": [TYPECHECK_STEP, "fmt:check", "lint:strict", "test"],
|
|
78
78
|
// Excludes the audit, which in CI has a workflow of its own. The build is what the narrowed check runs
|
|
79
79
|
// against, so it declines the arguments rather than being narrowed by them.
|
|
80
|
-
ci: [{ run: "build",
|
|
80
|
+
ci: [{ run: "build", shouldDeclineArguments: true }, "check:strict"],
|
|
81
81
|
clean: "nmr-clean",
|
|
82
82
|
fix: ["lint", "fmt"],
|
|
83
83
|
"fix:check": ["fmt:check", "lint:check"],
|
|
@@ -88,7 +88,7 @@ var rootScripts = {
|
|
|
88
88
|
"lint:strict": "strict-lint",
|
|
89
89
|
// The audit costs seconds and `ci` costs minutes, so the cheap gate fails first. The audit reads the
|
|
90
90
|
// dependency tree, which no argument narrowing the code under test says anything about.
|
|
91
|
-
prepush: [{ run: "audit",
|
|
91
|
+
prepush: [{ run: "audit", shouldDeclineArguments: true }, "ci"],
|
|
92
92
|
"report-overrides": "nmr-report-overrides",
|
|
93
93
|
"root:check": [ROOT_TYPECHECK_STEP, "fmt:check", "root:lint:check", "root:test"],
|
|
94
94
|
"root:lint": "eslint --fix --ignore-pattern 'packages/**' .",
|
|
@@ -110,7 +110,7 @@ var rootScripts = {
|
|
|
110
110
|
"test:watch": `vitest ${GATE_PROJECTS} --watch`,
|
|
111
111
|
// Neither step is narrowable, so `nmr typecheck <file>` is rejected rather than checking that file under
|
|
112
112
|
// default options at the root and hunting for it in every package.
|
|
113
|
-
typecheck: [ROOT_TYPECHECK_STEP, { run: "-R typecheck",
|
|
113
|
+
typecheck: [ROOT_TYPECHECK_STEP, { run: "-R typecheck", shouldDeclineArguments: true }],
|
|
114
114
|
// The command is a string because neither half names an nmr command: both are binaries, and a composite
|
|
115
115
|
// element can name only a command.
|
|
116
116
|
upgrade: "nmr-report-overrides && nmr-taze --recursive"
|
|
@@ -144,26 +144,26 @@ function hasTierInfix(filePath) {
|
|
|
144
144
|
}
|
|
145
145
|
var TEST_COLLECTION_EXCLUDE = [".git", "coverage", "dist", "node_modules"];
|
|
146
146
|
var TIER_NAMES = ["unit", "tool", "localhost", "remote"];
|
|
147
|
-
function collectTestFiles(dir, relativeDir,
|
|
147
|
+
function collectTestFiles(dir, relativeDir, isInTestDir, context) {
|
|
148
148
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
149
149
|
for (const entry of entries) {
|
|
150
150
|
const relativePath = relativeDir === "" ? entry.name : `${relativeDir}/${entry.name}`;
|
|
151
151
|
if (entry.isDirectory()) {
|
|
152
152
|
if (context.pruned.has(entry.name)) continue;
|
|
153
|
-
collectTestFiles(path.join(dir, entry.name), relativePath,
|
|
154
|
-
} else if (
|
|
155
|
-
context.
|
|
153
|
+
collectTestFiles(path.join(dir, entry.name), relativePath, isInTestDir || entry.name === TEST_DIR, context);
|
|
154
|
+
} else if (isInTestDir !== context.misplaced && TEST_FILE_PATTERN.test(entry.name)) {
|
|
155
|
+
context.foundPaths.push(relativePath);
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
|
-
function walkTestFiles(rootDir, {
|
|
159
|
+
function walkTestFiles(rootDir, { excludedBasenames = [] }, misplaced) {
|
|
160
160
|
const context = {
|
|
161
|
-
|
|
161
|
+
foundPaths: [],
|
|
162
162
|
misplaced,
|
|
163
|
-
pruned: /* @__PURE__ */ new Set([...TEST_COLLECTION_EXCLUDE, ...
|
|
163
|
+
pruned: /* @__PURE__ */ new Set([...TEST_COLLECTION_EXCLUDE, ...excludedBasenames])
|
|
164
164
|
};
|
|
165
165
|
collectTestFiles(rootDir, "", false, context);
|
|
166
|
-
return context.
|
|
166
|
+
return context.foundPaths.toSorted();
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
// .readyup/kits/default.ts
|
|
@@ -210,7 +210,7 @@ var default_default = defineRdyKit({
|
|
|
210
210
|
severity: "warn",
|
|
211
211
|
quiet: true,
|
|
212
212
|
check: toolVersionsHasNoPnpm,
|
|
213
|
-
fix: "Remove pnpm from .tool-versions \u2014 manage via packageManager field
|
|
213
|
+
fix: "Remove pnpm from .tool-versions \u2014 manage via the packageManager field"
|
|
214
214
|
},
|
|
215
215
|
{
|
|
216
216
|
name: "no package.json declares a pnpm field",
|
|
@@ -316,7 +316,7 @@ var default_default = defineRdyKit({
|
|
|
316
316
|
name: "the test suite gates the test-file conventions",
|
|
317
317
|
severity: "warn",
|
|
318
318
|
check: () => testSuiteGatesTestFileConventions(),
|
|
319
|
-
fix: "Add a test of the repo's own under a __tests__ directory that declares the check: import { checkTestFileConventions } from '@williamthorsen/nmr/tests'; checkTestFileConventions(); -- passing `
|
|
319
|
+
fix: "Add a test of the repo's own under a __tests__ directory that declares the check: import { checkTestFileConventions } from '@williamthorsen/nmr/tests'; checkTestFileConventions(); -- passing `excludedBasenames` the directory names that the repo passes to `testCollectionExclude`. Without it no test run reports an untiered or misplaced test file, and this kit reports them in its place against nmr's built-in exclusions alone"
|
|
320
320
|
},
|
|
321
321
|
{
|
|
322
322
|
name: "every test file names its isolation tier",
|
|
@@ -330,7 +330,7 @@ var default_default = defineRdyKit({
|
|
|
330
330
|
severity: "error",
|
|
331
331
|
skip: () => describeConventionsGuardSkip(),
|
|
332
332
|
check: () => everyTestFileSitsUnderTestsDir(),
|
|
333
|
-
fix: "Move each into a __tests__ directory, the only place from which the shared Vitest config collects. No project collects a file outside one, so it runs nowhere and reports nothing. For a file that is not a test, declare checkTestFileConventions with its directory in `
|
|
333
|
+
fix: "Move each into a __tests__ directory, the only place from which the shared Vitest config collects. No project collects a file outside one, so it runs nowhere and reports nothing. For a file that is not a test, declare checkTestFileConventions with its directory in `excludedBasenames` instead, which this check then defers to"
|
|
334
334
|
},
|
|
335
335
|
{
|
|
336
336
|
name: "no package re-exports the ancestor Vitest config",
|
|
@@ -424,7 +424,7 @@ function allWorkspacePackagesCanBuild() {
|
|
|
424
424
|
const packagesDir = join(process.cwd(), "packages");
|
|
425
425
|
if (!existsSync(packagesDir)) return true;
|
|
426
426
|
const entries = readdirSync2(packagesDir, { withFileTypes: true });
|
|
427
|
-
const
|
|
427
|
+
const failingPackages = [];
|
|
428
428
|
for (const entry of entries) {
|
|
429
429
|
if (!entry.isDirectory()) continue;
|
|
430
430
|
const pkgPath = `packages/${entry.name}/package.json`;
|
|
@@ -433,30 +433,33 @@ function allWorkspacePackagesCanBuild() {
|
|
|
433
433
|
const hasBuildOverride = /"build"\s*:/.test(content);
|
|
434
434
|
const hasDefaultBuildInputs = fileExists(`packages/${entry.name}/tsconfig.json`) && existsSync(join(packagesDir, entry.name, "src"));
|
|
435
435
|
if (!hasBuildOverride && !hasDefaultBuildInputs) {
|
|
436
|
-
|
|
436
|
+
failingPackages.push(entry.name);
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
|
-
if (
|
|
439
|
+
if (failingPackages.length === 0) return true;
|
|
440
440
|
return {
|
|
441
441
|
ok: false,
|
|
442
|
-
detail: `missing build override or tsconfig.json + src/: ${
|
|
442
|
+
detail: `missing build override or tsconfig.json + src/: ${failingPackages.join(", ")}`
|
|
443
443
|
};
|
|
444
444
|
}
|
|
445
445
|
function checkNoMatchingFiles(patterns, cwd) {
|
|
446
|
-
const
|
|
447
|
-
if (
|
|
448
|
-
return { ok: false, detail: formatPaths(
|
|
446
|
+
const foundFiles = findFiles(patterns, cwd);
|
|
447
|
+
if (foundFiles.length === 0) return true;
|
|
448
|
+
return { ok: false, detail: formatPaths(foundFiles) };
|
|
449
449
|
}
|
|
450
450
|
function checkRootVitestConfig(baseName, exportName, cwd) {
|
|
451
451
|
const matches = findFiles([`${baseName}.${CONFIG_EXTENSIONS}`], cwd);
|
|
452
452
|
if (matches.length === 0) {
|
|
453
453
|
return { ok: false, detail: `${baseName}.ts is missing` };
|
|
454
454
|
}
|
|
455
|
-
const
|
|
455
|
+
const staleConfigs = matches.filter(
|
|
456
456
|
(relativePath) => !importsSharedExport(readFileIn(cwd, relativePath), exportName, SHARED_VITEST_MODULE)
|
|
457
457
|
);
|
|
458
|
-
if (
|
|
459
|
-
return {
|
|
458
|
+
if (staleConfigs.length === 0) return true;
|
|
459
|
+
return {
|
|
460
|
+
ok: false,
|
|
461
|
+
detail: `does not import ${exportName} from ${SHARED_VITEST_MODULE}: ${staleConfigs.join(", ")}`
|
|
462
|
+
};
|
|
460
463
|
}
|
|
461
464
|
function prettierConfigBuildsOnSharedConfig(cwd = process.cwd()) {
|
|
462
465
|
const configs = findFiles(
|
|
@@ -467,18 +470,18 @@ function prettierConfigBuildsOnSharedConfig(cwd = process.cwd()) {
|
|
|
467
470
|
if (configs.length === 0) {
|
|
468
471
|
return { ok: false, detail: describeMissingPrettierConfig(cwd) };
|
|
469
472
|
}
|
|
470
|
-
const
|
|
473
|
+
const staleConfigs = configs.filter(
|
|
471
474
|
(relativePath) => !importsSharedExport(readFileIn(cwd, relativePath), "definePrettierConfig", SHARED_PRETTIER_MODULE)
|
|
472
475
|
);
|
|
473
|
-
if (
|
|
476
|
+
if (staleConfigs.length === 0) return true;
|
|
474
477
|
return {
|
|
475
478
|
ok: false,
|
|
476
|
-
detail: `does not import definePrettierConfig from ${SHARED_PRETTIER_MODULE}: ${
|
|
479
|
+
detail: `does not import definePrettierConfig from ${SHARED_PRETTIER_MODULE}: ${staleConfigs.join(", ")}`
|
|
477
480
|
};
|
|
478
481
|
}
|
|
479
482
|
function describeMissingPrettierConfig(cwd) {
|
|
480
|
-
const
|
|
481
|
-
if (
|
|
483
|
+
const inertConfigs = findFiles(INERT_PRETTIER_CONFIGS, cwd);
|
|
484
|
+
if (inertConfigs.length > 0) return `holds no code to call the factory: ${inertConfigs.join(", ")}`;
|
|
482
485
|
if (hasPrettierConfigKey(cwd)) {
|
|
483
486
|
return 'holds no code to call the factory: the "prettier" key in package.json';
|
|
484
487
|
}
|
|
@@ -488,8 +491,8 @@ function hasPrettierConfigKey(cwd) {
|
|
|
488
491
|
const manifest = readFileIn(cwd, "package.json");
|
|
489
492
|
if (manifest === void 0) return false;
|
|
490
493
|
try {
|
|
491
|
-
const
|
|
492
|
-
return isRecord(
|
|
494
|
+
const parsedManifest = JSON.parse(manifest);
|
|
495
|
+
return isRecord(parsedManifest) && parsedManifest["prettier"] !== void 0;
|
|
493
496
|
} catch {
|
|
494
497
|
return false;
|
|
495
498
|
}
|
|
@@ -502,11 +505,11 @@ function discoverMemberWorkspaces() {
|
|
|
502
505
|
}
|
|
503
506
|
}
|
|
504
507
|
async function everyBinTargetIsACommittedWrapper() {
|
|
505
|
-
const
|
|
506
|
-
if (!
|
|
507
|
-
const
|
|
508
|
-
const trackedPaths =
|
|
509
|
-
const offenders =
|
|
508
|
+
const workspaceDiscovery = discoverMemberWorkspaces();
|
|
509
|
+
if (!workspaceDiscovery.ok) return workspaceDiscovery;
|
|
510
|
+
const trackedFiles = await listTrackedFiles();
|
|
511
|
+
const trackedPaths = trackedFiles === void 0 ? void 0 : new Set(trackedFiles);
|
|
512
|
+
const offenders = workspaceDiscovery.workspaces.flatMap(
|
|
510
513
|
(workspace) => readBinEntries(workspace).flatMap((entry) => {
|
|
511
514
|
const defect = describeBinTargetDefect(workspace, entry, trackedPaths);
|
|
512
515
|
return defect === void 0 ? [] : [`${describeBinEntry(workspace, entry)} (${defect})`];
|
|
@@ -516,16 +519,18 @@ async function everyBinTargetIsACommittedWrapper() {
|
|
|
516
519
|
return { ok: false, detail: formatPaths(offenders) };
|
|
517
520
|
}
|
|
518
521
|
function everyBinWrapperTargetIsCoveredByFiles() {
|
|
519
|
-
const
|
|
520
|
-
if (!
|
|
522
|
+
const workspaceDiscovery = discoverMemberWorkspaces();
|
|
523
|
+
if (!workspaceDiscovery.ok) return workspaceDiscovery;
|
|
521
524
|
const cwd = process.cwd();
|
|
522
|
-
const offenders =
|
|
525
|
+
const offenders = workspaceDiscovery.workspaces.flatMap((workspace) => {
|
|
523
526
|
const files = workspace.packageJson["files"];
|
|
524
527
|
if (!Array.isArray(files)) return [];
|
|
525
|
-
const
|
|
528
|
+
const publishedSegments = new Set(
|
|
529
|
+
files.flatMap((entry) => typeof entry === "string" ? [readFirstSegment(entry)] : [])
|
|
530
|
+
);
|
|
526
531
|
return readBinEntries(workspace).flatMap((entry) => {
|
|
527
532
|
const target = readWrapperTarget(cwd, workspace, entry);
|
|
528
|
-
if (target === void 0 ||
|
|
533
|
+
if (target === void 0 || publishedSegments.has(readFirstSegment(target))) return [];
|
|
529
534
|
return [`${describeBinEntry(workspace, entry)} -> ${target}`];
|
|
530
535
|
});
|
|
531
536
|
});
|
|
@@ -566,25 +571,25 @@ function readWrapperTarget(cwd, workspace, entry) {
|
|
|
566
571
|
return posix.normalize(posix.join(posix.dirname(entry.target), specifier));
|
|
567
572
|
}
|
|
568
573
|
function everyTestFileNamesItsTier(cwd = process.cwd()) {
|
|
569
|
-
const
|
|
570
|
-
if (
|
|
571
|
-
return { ok: false, detail: formatPaths(
|
|
574
|
+
const untieredFiles = findUntieredTestFiles(cwd);
|
|
575
|
+
if (untieredFiles.length === 0) return true;
|
|
576
|
+
return { ok: false, detail: formatPaths(untieredFiles) };
|
|
572
577
|
}
|
|
573
578
|
function everyTestFileSitsUnderTestsDir(cwd = process.cwd()) {
|
|
574
|
-
const
|
|
575
|
-
if (
|
|
576
|
-
return { ok: false, detail: formatPaths(
|
|
579
|
+
const misplacedFiles = findMisplacedTestFiles(cwd);
|
|
580
|
+
if (misplacedFiles.length === 0) return true;
|
|
581
|
+
return { ok: false, detail: formatPaths(misplacedFiles) };
|
|
577
582
|
}
|
|
578
583
|
function everyViteConfigHasVitestConfig() {
|
|
579
|
-
const
|
|
580
|
-
if (!
|
|
581
|
-
const
|
|
584
|
+
const workspaceDiscovery = discoverMemberWorkspaces();
|
|
585
|
+
if (!workspaceDiscovery.ok) return workspaceDiscovery;
|
|
586
|
+
const unpairedConfigs = workspaceDiscovery.workspaces.flatMap((workspace) => {
|
|
582
587
|
const viteConfigs = findWorkspaceConfigs(workspace, VITE_CONFIG_PATTERN);
|
|
583
588
|
if (viteConfigs.length === 0) return [];
|
|
584
589
|
return findWorkspaceConfigs(workspace, VITEST_CONFIG_PATTERN).length > 0 ? [] : viteConfigs;
|
|
585
590
|
});
|
|
586
|
-
if (
|
|
587
|
-
return { ok: false, detail: formatPaths(
|
|
591
|
+
if (unpairedConfigs.length === 0) return true;
|
|
592
|
+
return { ok: false, detail: formatPaths(unpairedConfigs) };
|
|
588
593
|
}
|
|
589
594
|
function findFiles(patterns, cwd) {
|
|
590
595
|
return globSync(patterns, { cwd, exclude: (path2) => SCAN_EXCLUDE_DIRS.has(basename(path2)) }).map((path2) => path2.split(sep).join("/")).toSorted();
|
|
@@ -597,11 +602,11 @@ function formatPaths(paths) {
|
|
|
597
602
|
${paths.map((path2) => ` ${path2}`).join("\n")}`;
|
|
598
603
|
}
|
|
599
604
|
function getMinVersion() {
|
|
600
|
-
const
|
|
601
|
-
if (typeof
|
|
605
|
+
const pickedFields = { "version": "0.39.0" };
|
|
606
|
+
if (typeof pickedFields["version"] !== "string") {
|
|
602
607
|
throw new TypeError("nmr/package.json: 'version' must be a string");
|
|
603
608
|
}
|
|
604
|
-
return
|
|
609
|
+
return pickedFields["version"];
|
|
605
610
|
}
|
|
606
611
|
function hasSupportedEslintVersion() {
|
|
607
612
|
return hasMinDevDependencyVersion("eslint", MIN_ESLINT_VERSION, {
|
|
@@ -630,13 +635,13 @@ function isReExportOnly(content) {
|
|
|
630
635
|
return statements.length > 0 && statements.every((line) => RE_EXPORT_LINE_PATTERN.test(line));
|
|
631
636
|
}
|
|
632
637
|
function noPnpmFieldInPackageJson(cwd = process.cwd()) {
|
|
633
|
-
const
|
|
638
|
+
const declaringFiles = findFiles(["**/package.json"], cwd).flatMap((relativePath) => {
|
|
634
639
|
const keys = readPnpmFieldKeys(readFileIn(cwd, relativePath));
|
|
635
640
|
if (keys === void 0) return [];
|
|
636
641
|
return [keys.length > 0 ? `${relativePath} (${keys.join(", ")})` : relativePath];
|
|
637
642
|
});
|
|
638
|
-
if (
|
|
639
|
-
return { ok: false, detail: formatPaths(
|
|
643
|
+
if (declaringFiles.length === 0) return true;
|
|
644
|
+
return { ok: false, detail: formatPaths(declaringFiles) };
|
|
640
645
|
}
|
|
641
646
|
function noReExportOnlyVitestConfigs(cwd = process.cwd()) {
|
|
642
647
|
const nonRootConfigs = findFiles([`**/${VITEST_CONFIG_PATTERN}`], cwd).filter((path2) => path2.includes("/"));
|
|
@@ -650,11 +655,11 @@ function noRedundantRootScripts() {
|
|
|
650
655
|
const scripts = pkg["scripts"];
|
|
651
656
|
if (!isRecord(scripts)) return true;
|
|
652
657
|
const builtInNames = Object.keys(getDefaultRootScripts());
|
|
653
|
-
const
|
|
654
|
-
if (
|
|
658
|
+
const redundantNames = Object.keys(scripts).filter((name) => builtInNames.includes(name));
|
|
659
|
+
if (redundantNames.length === 0) return true;
|
|
655
660
|
return {
|
|
656
661
|
ok: false,
|
|
657
|
-
detail: `redundant: ${
|
|
662
|
+
detail: `redundant: ${redundantNames.join(", ")}`
|
|
658
663
|
};
|
|
659
664
|
}
|
|
660
665
|
function noRetiredVitestConfigs(cwd = process.cwd()) {
|
|
@@ -665,14 +670,14 @@ function noRetiredVitestConfigs(cwd = process.cwd()) {
|
|
|
665
670
|
}
|
|
666
671
|
function noUnguardedLefthookInstall(cwd = process.cwd()) {
|
|
667
672
|
const scripts = readRootScripts(cwd);
|
|
668
|
-
const
|
|
673
|
+
const unguardedScripts = INSTALL_LIFECYCLE_SCRIPTS.flatMap((name) => {
|
|
669
674
|
const command = scripts[name];
|
|
670
675
|
if (typeof command !== "string") return [];
|
|
671
676
|
const isUnguarded = LEFTHOOK_INSTALL_PATTERN.test(command) && !LEFTHOOK_CHECK_INSTALL_PATTERN.test(command);
|
|
672
677
|
return isUnguarded ? [`${name}: ${command}`] : [];
|
|
673
678
|
});
|
|
674
|
-
if (
|
|
675
|
-
return { ok: false, detail: formatPaths(
|
|
679
|
+
if (unguardedScripts.length === 0) return true;
|
|
680
|
+
return { ok: false, detail: formatPaths(unguardedScripts) };
|
|
676
681
|
}
|
|
677
682
|
function noWorkspaceRunScriptReferences() {
|
|
678
683
|
const packagesDir = join(process.cwd(), "packages");
|
|
@@ -698,27 +703,27 @@ function readFileIn(cwd, relativePath) {
|
|
|
698
703
|
}
|
|
699
704
|
function readPnpmFieldKeys(content) {
|
|
700
705
|
if (content === void 0) return void 0;
|
|
701
|
-
let
|
|
706
|
+
let parsedManifest;
|
|
702
707
|
try {
|
|
703
|
-
|
|
708
|
+
parsedManifest = JSON.parse(content);
|
|
704
709
|
} catch {
|
|
705
710
|
return void 0;
|
|
706
711
|
}
|
|
707
|
-
if (!isRecord(
|
|
708
|
-
const pnpm =
|
|
712
|
+
if (!isRecord(parsedManifest)) return void 0;
|
|
713
|
+
const pnpm = parsedManifest["pnpm"];
|
|
709
714
|
return isRecord(pnpm) ? Object.keys(pnpm).toSorted() : void 0;
|
|
710
715
|
}
|
|
711
716
|
function readRootScripts(cwd) {
|
|
712
717
|
const content = readFileIn(cwd, "package.json");
|
|
713
718
|
if (content === void 0) return {};
|
|
714
|
-
let
|
|
719
|
+
let parsedManifest;
|
|
715
720
|
try {
|
|
716
|
-
|
|
721
|
+
parsedManifest = JSON.parse(content);
|
|
717
722
|
} catch {
|
|
718
723
|
return {};
|
|
719
724
|
}
|
|
720
|
-
if (!isRecord(
|
|
721
|
-
const scripts =
|
|
725
|
+
if (!isRecord(parsedManifest)) return {};
|
|
726
|
+
const scripts = parsedManifest["scripts"];
|
|
722
727
|
return isRecord(scripts) ? scripts : {};
|
|
723
728
|
}
|
|
724
729
|
function resolvesVersionViaWorkspace(range) {
|
|
@@ -729,11 +734,11 @@ function tazeConfigBuildsOnSharedConfig(cwd = process.cwd()) {
|
|
|
729
734
|
if (configs.length === 0) {
|
|
730
735
|
return { ok: false, detail: describeMissingTazeConfig(cwd) };
|
|
731
736
|
}
|
|
732
|
-
const
|
|
737
|
+
const staleConfigs = configs.filter(
|
|
733
738
|
(relativePath) => !importsSharedExport(readFileIn(cwd, relativePath), "defineConfig", SHARED_TAZE_MODULE)
|
|
734
739
|
);
|
|
735
|
-
if (
|
|
736
|
-
return { ok: false, detail: `does not import defineConfig from ${SHARED_TAZE_MODULE}: ${
|
|
740
|
+
if (staleConfigs.length === 0) return true;
|
|
741
|
+
return { ok: false, detail: `does not import defineConfig from ${SHARED_TAZE_MODULE}: ${staleConfigs.join(", ")}` };
|
|
737
742
|
}
|
|
738
743
|
function tazeConfigAvoidsClobberedOptions(cwd = process.cwd()) {
|
|
739
744
|
const configs = findFiles([`taze.config.${CONFIG_EXTENSIONS}`], cwd);
|
|
@@ -741,15 +746,15 @@ function tazeConfigAvoidsClobberedOptions(cwd = process.cwd()) {
|
|
|
741
746
|
for (const relativePath of configs) {
|
|
742
747
|
const content = readFileIn(cwd, relativePath);
|
|
743
748
|
if (content === void 0) continue;
|
|
744
|
-
const
|
|
745
|
-
if (
|
|
749
|
+
const discardedKeys = CLOBBERED_TAZE_OPTIONS.filter(({ pattern }) => pattern.test(content)).map(({ key }) => key);
|
|
750
|
+
if (discardedKeys.length > 0) findings.push(`${relativePath}: ${discardedKeys.join(", ")}`);
|
|
746
751
|
}
|
|
747
752
|
if (findings.length === 0) return true;
|
|
748
753
|
return { ok: false, detail: formatPaths(findings) };
|
|
749
754
|
}
|
|
750
755
|
function describeMissingTazeConfig(cwd) {
|
|
751
|
-
const
|
|
752
|
-
if (
|
|
756
|
+
const inertConfigs = findFiles(INERT_TAZE_CONFIGS, cwd);
|
|
757
|
+
if (inertConfigs.length > 0) return `holds no code to call the factory: ${inertConfigs.join(", ")}`;
|
|
753
758
|
return "taze.config.ts is missing";
|
|
754
759
|
}
|
|
755
760
|
function testSuiteGatesTestFileConventions(cwd = process.cwd()) {
|
|
@@ -777,9 +782,9 @@ function vitestConfigBuildsOnSharedConfig() {
|
|
|
777
782
|
const cwd = process.cwd();
|
|
778
783
|
const rootConfigs = findFiles([VITEST_CONFIG_PATTERN], cwd);
|
|
779
784
|
if (rootConfigs.length === 0) return { ok: false, detail: "vitest.config.ts is missing" };
|
|
780
|
-
const
|
|
781
|
-
if (!
|
|
782
|
-
const workspaceConfigs =
|
|
785
|
+
const workspaceDiscovery = discoverMemberWorkspaces();
|
|
786
|
+
if (!workspaceDiscovery.ok) return workspaceDiscovery;
|
|
787
|
+
const workspaceConfigs = workspaceDiscovery.workspaces.flatMap((workspace) => findWorkspaceConfigs(workspace, VITEST_CONFIG_PATTERN)).filter((relativePath) => !isOwnedByReExportCheck(cwd, relativePath));
|
|
783
788
|
const stale = [...rootConfigs, ...workspaceConfigs].filter(
|
|
784
789
|
(relativePath) => !importsSharedExport(readFileIn(cwd, relativePath), "defineVitestConfig", SHARED_VITEST_MODULE)
|
|
785
790
|
);
|
package/.readyup/manifest.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"kits": [
|
|
4
4
|
{
|
|
5
5
|
"bundledDependencies": {
|
|
6
|
-
"@williamthorsen/toolbelt.errors": "0.
|
|
6
|
+
"@williamthorsen/toolbelt.errors": "0.8.1"
|
|
7
7
|
},
|
|
8
8
|
"checklists": [
|
|
9
9
|
"nmr"
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
"esbuildVersion": "0.28.2",
|
|
12
12
|
"inputs": [
|
|
13
13
|
{
|
|
14
|
-
"hash": "
|
|
14
|
+
"hash": "0ae7745f",
|
|
15
15
|
"kind": "module",
|
|
16
16
|
"path": "kits/default.ts"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
|
-
"hash": "
|
|
19
|
+
"hash": "30230373",
|
|
20
20
|
"kind": "inline",
|
|
21
21
|
"path": "../package.json",
|
|
22
22
|
"paths": [
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
]
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
|
-
"hash": "
|
|
27
|
+
"hash": "06bffeed",
|
|
28
28
|
"kind": "module",
|
|
29
29
|
"path": "../src/default-scripts.ts"
|
|
30
30
|
},
|
|
@@ -34,17 +34,17 @@
|
|
|
34
34
|
"path": "../src/resolve-scripts.ts"
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
|
-
"hash": "
|
|
37
|
+
"hash": "beb92e94",
|
|
38
38
|
"kind": "module",
|
|
39
39
|
"path": "../src/tiers.ts"
|
|
40
40
|
}
|
|
41
41
|
],
|
|
42
42
|
"name": "default",
|
|
43
43
|
"path": "kits/default.js",
|
|
44
|
-
"readyupVersion": "0.
|
|
44
|
+
"readyupVersion": "0.38.0",
|
|
45
45
|
"source": "kits/default.ts",
|
|
46
|
-
"sourceHash": "
|
|
47
|
-
"targetHash": "
|
|
46
|
+
"sourceHash": "0ae7745f",
|
|
47
|
+
"targetHash": "06f47835"
|
|
48
48
|
}
|
|
49
49
|
]
|
|
50
50
|
}
|