@williamthorsen/nmr 0.38.0 → 0.40.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 +212 -4
- package/.readyup/kits/default.js +124 -104
- package/.readyup/manifest.json +12 -7
- package/CHANGELOG.md +108 -0
- package/README.md +41 -47
- package/agents/README.md +8 -8
- package/agents/guidance/rulebooks/nmr.md +8 -8
- package/bin/ensure-prepublish-hooks.js +1 -1
- package/bin/nmr-clean.js +1 -1
- package/bin/nmr-compile.js +1 -1
- package/bin/nmr-fmt.js +1 -1
- package/bin/nmr-report-catalog.js +1 -1
- package/bin/nmr-report-overrides.js +1 -1
- package/bin/nmr-taze.js +1 -1
- package/bin/nmr.js +1 -1
- package/dist/esm/UserError.d.ts +1 -2
- package/dist/esm/check-cache.d.ts +61 -63
- package/dist/esm/check-cache.js +24 -24
- package/dist/esm/cli-ensure-prepublish-hooks.js +4 -4
- package/dist/esm/commands/build-output.d.ts +37 -38
- package/dist/esm/commands/build-output.js +9 -9
- package/dist/esm/commands/build.d.ts +13 -14
- package/dist/esm/commands/build.js +67 -65
- package/dist/esm/commands/clean.d.ts +6 -6
- package/dist/esm/commands/clean.js +11 -11
- package/dist/esm/commands/ensure-prepublish-hooks.d.ts +7 -7
- package/dist/esm/commands/ensure-prepublish-hooks.js +41 -41
- package/dist/esm/commands/fmt.d.ts +8 -8
- package/dist/esm/commands/fmt.js +33 -33
- package/dist/esm/commands/report-catalog.d.ts +5 -7
- package/dist/esm/commands/report-catalog.js +10 -8
- package/dist/esm/commands/report-overrides.d.ts +0 -2
- package/dist/esm/commands/report-overrides.js +8 -8
- package/dist/esm/commands/taze.d.ts +9 -8
- package/dist/esm/config.d.ts +5 -5
- package/dist/esm/config.js +27 -27
- package/dist/esm/context.d.ts +1 -2
- package/dist/esm/context.js +8 -8
- package/dist/esm/default-scripts.d.ts +5 -6
- package/dist/esm/defineConfig.d.ts +1 -1
- package/dist/esm/git-ignored-paths.d.ts +9 -0
- package/dist/esm/git-ignored-paths.js +10 -0
- package/dist/esm/glyphs.d.ts +3 -3
- package/dist/esm/help.d.ts +4 -4
- package/dist/esm/help.js +21 -21
- package/dist/esm/helpers/clampToBytes.d.ts +4 -5
- package/dist/esm/helpers/clampToBytes.js +3 -3
- package/dist/esm/helpers/config-path.d.ts +0 -2
- package/dist/esm/helpers/createBoundedBuffer.d.ts +3 -3
- package/dist/esm/helpers/createBoundedBuffer.js +2 -2
- package/dist/esm/helpers/deriveExcerpt.d.ts +4 -5
- package/dist/esm/helpers/duration.d.ts +10 -11
- package/dist/esm/helpers/duration.js +7 -7
- package/dist/esm/helpers/filter-selection.d.ts +19 -19
- package/dist/esm/helpers/filter-selection.js +12 -12
- package/dist/esm/helpers/findClosestName.d.ts +1 -1
- package/dist/esm/helpers/findClosestName.js +15 -15
- package/dist/esm/helpers/hook-name.d.ts +2 -5
- package/dist/esm/helpers/package-json.d.ts +11 -10
- package/dist/esm/helpers/package-json.js +26 -25
- package/dist/esm/helpers/readStringValues.d.ts +2 -3
- package/dist/esm/helpers/reportClosing.d.ts +8 -9
- package/dist/esm/helpers/transcript.d.ts +15 -15
- package/dist/esm/helpers/transcript.js +14 -14
- package/dist/esm/helpers/type-guards.d.ts +1 -0
- package/dist/esm/output-style.d.ts +12 -13
- package/dist/esm/prettier.d.ts +2 -2
- package/dist/esm/prettier.js +5 -5
- package/dist/esm/recording.d.ts +25 -20
- package/dist/esm/recording.js +28 -21
- package/dist/esm/replay-assembly.d.ts +10 -10
- package/dist/esm/report-format.d.ts +15 -20
- package/dist/esm/report-format.js +7 -7
- package/dist/esm/reportCliFailure.d.ts +2 -3
- package/dist/esm/resolve-scripts.d.ts +4 -5
- package/dist/esm/resolveBinStyles.d.ts +4 -5
- package/dist/esm/resolveBinStyles.js +3 -3
- package/dist/esm/resolver.d.ts +16 -20
- package/dist/esm/resolver.js +15 -15
- package/dist/esm/runCli.d.ts +6 -6
- package/dist/esm/runCli.js +261 -195
- package/dist/esm/runner.d.ts +20 -20
- package/dist/esm/runner.js +18 -18
- package/dist/esm/steps.d.ts +51 -38
- package/dist/esm/steps.js +32 -27
- package/dist/esm/taze.d.ts +1 -1
- package/dist/esm/tests.d.ts +23 -18
- package/dist/esm/tests.js +21 -9
- package/dist/esm/tiers.d.ts +28 -24
- package/dist/esm/tiers.js +13 -9
- package/dist/esm/types.d.ts +5 -5
- package/dist/esm/verbosity.d.ts +23 -21
- package/dist/esm/verbosity.js +7 -7
- package/dist/esm/verdict.d.ts +39 -32
- package/dist/esm/verdict.js +63 -50
- package/dist/esm/vitest-source-resolution.d.ts +11 -11
- package/dist/esm/vitest-source-resolution.js +49 -47
- package/dist/esm/vitest.d.ts +27 -26
- package/dist/esm/vitest.js +90 -33
- package/dist/esm/workspace.d.ts +6 -22
- package/dist/esm/workspace.js +9 -67
- package/docs/check-cache.md +57 -57
- package/docs/prettier.md +10 -10
- package/docs/reporting.md +48 -44
- package/docs/scripts.md +29 -29
- package/docs/upgrades.md +18 -18
- package/docs/utilities.md +13 -13
- package/docs/vitest.md +68 -66
- package/docs/workspace.md +27 -4
- package/package.json +6 -6
- package/dist/esm/helpers/workspace-patterns.d.ts +0 -8
- package/dist/esm/helpers/workspace-patterns.js +0 -29
package/.readyup/kits/default.js
CHANGED
|
@@ -64,7 +64,7 @@ var workspaceScripts = {
|
|
|
64
64
|
"test:unit": "pnpm exec vitest --project unit",
|
|
65
65
|
"test:watch": `pnpm exec vitest ${GATE_PROJECTS} --watch`,
|
|
66
66
|
typecheck: "tsgo --noEmit",
|
|
67
|
-
// The command is a string because neither half names an nmr command:
|
|
67
|
+
// The command is a string because neither half names an nmr command: Both are binaries.
|
|
68
68
|
upgrade: "nmr-report-catalog && nmr-taze",
|
|
69
69
|
"view-coverage": "open coverage/index.html"
|
|
70
70
|
};
|
|
@@ -75,8 +75,8 @@ var rootScripts = {
|
|
|
75
75
|
build: ["-R build"],
|
|
76
76
|
check: [TYPECHECK_STEP, "fmt:check", "lint:check", "test"],
|
|
77
77
|
"check:strict": [TYPECHECK_STEP, "fmt:check", "lint:strict", "test"],
|
|
78
|
-
// Excludes the audit, which in CI has a workflow of its own. The
|
|
79
|
-
//
|
|
78
|
+
// Excludes the audit, which in CI has a workflow of its own. The narrowed check runs against the build, so
|
|
79
|
+
// the build declines the arguments rather than being narrowed by them.
|
|
80
80
|
ci: [{ run: "build", shouldDeclineArguments: true }, "check:strict"],
|
|
81
81
|
clean: "nmr-clean",
|
|
82
82
|
fix: ["lint", "fmt"],
|
|
@@ -86,8 +86,8 @@ var rootScripts = {
|
|
|
86
86
|
lint: "eslint --fix .",
|
|
87
87
|
"lint:check": "eslint .",
|
|
88
88
|
"lint:strict": "strict-lint",
|
|
89
|
-
// The audit
|
|
90
|
-
// dependency tree,
|
|
89
|
+
// The audit takes seconds and `ci` takes minutes, so the cheap gate fails first. The audit reads the
|
|
90
|
+
// dependency tree, and an argument narrowing the code under test does not say anything about that tree.
|
|
91
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"],
|
|
@@ -99,7 +99,7 @@ var rootScripts = {
|
|
|
99
99
|
"root:test:tool": "vitest --config ./vitest.root.config.ts --project tool",
|
|
100
100
|
"root:test:unit": "vitest --config ./vitest.root.config.ts --project unit",
|
|
101
101
|
"root:typecheck": "tsgo --noEmit",
|
|
102
|
-
//
|
|
102
|
+
// Includes the override report for the same reason `upgrade` does: Both end in the tool that rewrites a
|
|
103
103
|
// `pnpm.overrides` block, so both need the reporter's rejection ahead of them.
|
|
104
104
|
"root:upgrade": "nmr-report-overrides && nmr-taze",
|
|
105
105
|
test: ["root:test", "-R test"],
|
|
@@ -109,9 +109,9 @@ var rootScripts = {
|
|
|
109
109
|
"test:unit": ["root:test:unit", "-R test:unit"],
|
|
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
|
-
// default options at the root and
|
|
112
|
+
// default options at the root and searching for it in every package.
|
|
113
113
|
typecheck: [ROOT_TYPECHECK_STEP, { run: "-R typecheck", shouldDeclineArguments: true }],
|
|
114
|
-
// The command is a string because neither half names an nmr command:
|
|
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"
|
|
117
117
|
};
|
|
@@ -124,6 +124,20 @@ function getDefaultRootScripts() {
|
|
|
124
124
|
// src/tiers.ts
|
|
125
125
|
import { readdirSync } from "node:fs";
|
|
126
126
|
import path from "node:path";
|
|
127
|
+
|
|
128
|
+
// src/git-ignored-paths.ts
|
|
129
|
+
import { spawnSync } from "node:child_process";
|
|
130
|
+
function listGitIgnoredPaths(dir) {
|
|
131
|
+
const result = spawnSync(
|
|
132
|
+
"git",
|
|
133
|
+
["ls-files", "-z", "--others", "--ignored", "--exclude-standard", "--directory", "--no-empty-directory"],
|
|
134
|
+
{ cwd: dir, encoding: "utf8" }
|
|
135
|
+
);
|
|
136
|
+
if (result.error !== void 0 || result.status !== 0) return [];
|
|
137
|
+
return result.stdout.split("\0").filter((entry) => entry !== "").toSorted();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/tiers.ts
|
|
127
141
|
var TEST_DIR = "__tests__";
|
|
128
142
|
var TEST_EXTENSIONS = "{ts,tsx}";
|
|
129
143
|
var TEST_GLOB_PREFIX = `**/${TEST_DIR}/**`;
|
|
@@ -144,26 +158,27 @@ function hasTierInfix(filePath) {
|
|
|
144
158
|
}
|
|
145
159
|
var TEST_COLLECTION_EXCLUDE = [".git", "coverage", "dist", "node_modules"];
|
|
146
160
|
var TIER_NAMES = ["unit", "tool", "localhost", "remote"];
|
|
147
|
-
function collectTestFiles(dir, relativeDir,
|
|
161
|
+
function collectTestFiles(dir, relativeDir, isInTestDir, context) {
|
|
148
162
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
149
163
|
for (const entry of entries) {
|
|
150
164
|
const relativePath = relativeDir === "" ? entry.name : `${relativeDir}/${entry.name}`;
|
|
151
165
|
if (entry.isDirectory()) {
|
|
152
|
-
if (context.pruned.has(entry.name)) continue;
|
|
153
|
-
collectTestFiles(path.join(dir, entry.name), relativePath,
|
|
154
|
-
} else if (
|
|
155
|
-
context.
|
|
166
|
+
if (context.pruned.has(entry.name) || context.ignoredPaths.has(`${relativePath}/`)) continue;
|
|
167
|
+
collectTestFiles(path.join(dir, entry.name), relativePath, isInTestDir || entry.name === TEST_DIR, context);
|
|
168
|
+
} else if (isInTestDir !== context.misplaced && TEST_FILE_PATTERN.test(entry.name) && !context.ignoredPaths.has(relativePath)) {
|
|
169
|
+
context.foundPaths.push(relativePath);
|
|
156
170
|
}
|
|
157
171
|
}
|
|
158
172
|
}
|
|
159
|
-
function walkTestFiles(rootDir, {
|
|
173
|
+
function walkTestFiles(rootDir, { excludedBasenames = [] }, misplaced) {
|
|
160
174
|
const context = {
|
|
161
|
-
|
|
175
|
+
foundPaths: [],
|
|
176
|
+
ignoredPaths: new Set(listGitIgnoredPaths(rootDir)),
|
|
162
177
|
misplaced,
|
|
163
|
-
pruned: /* @__PURE__ */ new Set([...TEST_COLLECTION_EXCLUDE, ...
|
|
178
|
+
pruned: /* @__PURE__ */ new Set([...TEST_COLLECTION_EXCLUDE, ...excludedBasenames])
|
|
164
179
|
};
|
|
165
180
|
collectTestFiles(rootDir, "", false, context);
|
|
166
|
-
return context.
|
|
181
|
+
return context.foundPaths.toSorted();
|
|
167
182
|
}
|
|
168
183
|
|
|
169
184
|
// .readyup/kits/default.ts
|
|
@@ -210,21 +225,21 @@ var default_default = defineRdyKit({
|
|
|
210
225
|
severity: "warn",
|
|
211
226
|
quiet: true,
|
|
212
227
|
check: toolVersionsHasNoPnpm,
|
|
213
|
-
fix: "Remove pnpm from .tool-versions
|
|
228
|
+
fix: "Remove pnpm from .tool-versions, and manage it through the packageManager field"
|
|
214
229
|
},
|
|
215
230
|
{
|
|
216
231
|
name: "no package.json declares a pnpm field",
|
|
217
232
|
severity: "error",
|
|
218
233
|
quiet: true,
|
|
219
234
|
check: () => noPnpmFieldInPackageJson(),
|
|
220
|
-
fix: "Move these settings into pnpm-workspace.yaml, quoting each version under `overrides`, or run `pnpx codemod run pnpm-v10-to-v11`. pnpm 11
|
|
235
|
+
fix: "Move these settings into pnpm-workspace.yaml, quoting each version under `overrides`, or run `pnpx codemod run pnpm-v10-to-v11`. pnpm 11 does not read any key from the `pnpm` field, so an override left there pins nothing while an upgrade run with `--write` goes on rewriting it"
|
|
221
236
|
},
|
|
222
237
|
{
|
|
223
238
|
name: ".config/nmr.config.ts uses defineConfig",
|
|
224
239
|
severity: "recommend",
|
|
225
240
|
skip: () => !fileExists(".config/nmr.config.ts") ? "no nmr config file" : false,
|
|
226
241
|
check: () => fileContains(".config/nmr.config.ts", /defineConfig/),
|
|
227
|
-
fix: "Wrap
|
|
242
|
+
fix: "Wrap the config export with defineConfig() from @williamthorsen/nmr/config for type safety"
|
|
228
243
|
},
|
|
229
244
|
// `error` because falling short produces wrong results rather than a failure. Names and fixes are
|
|
230
245
|
// getters because their version constants are declared below the kit.
|
|
@@ -236,7 +251,7 @@ var default_default = defineRdyKit({
|
|
|
236
251
|
skip: () => !hasDevDependency("eslint") ? "eslint not installed" : false,
|
|
237
252
|
check: hasSupportedEslintVersion,
|
|
238
253
|
get fix() {
|
|
239
|
-
return `pnpm add --save-dev eslint@^${MIN_ESLINT_VERSION}
|
|
254
|
+
return `pnpm add --save-dev eslint@^${MIN_ESLINT_VERSION}. Earlier releases resolve config from the working directory, so nmr's root lint and lint:check would apply the root config to every package`;
|
|
240
255
|
}
|
|
241
256
|
},
|
|
242
257
|
{
|
|
@@ -247,7 +262,7 @@ var default_default = defineRdyKit({
|
|
|
247
262
|
skip: () => !hasDevDependency("@williamthorsen/strict-lint") ? "strict-lint not installed" : false,
|
|
248
263
|
check: hasSupportedStrictLintVersion,
|
|
249
264
|
get fix() {
|
|
250
|
-
return `pnpm add --save-dev @williamthorsen/strict-lint@^${MIN_STRICT_LINT_VERSION}
|
|
265
|
+
return `pnpm add --save-dev @williamthorsen/strict-lint@^${MIN_STRICT_LINT_VERSION}. Earlier releases pin ESLint to one config and resolve ceilings from the working directory, so nmr's root lint:strict would report the wrong rules for every package`;
|
|
251
266
|
}
|
|
252
267
|
},
|
|
253
268
|
// -- Root script cleanup -------------------------------------------------
|
|
@@ -256,7 +271,7 @@ var default_default = defineRdyKit({
|
|
|
256
271
|
severity: "warn",
|
|
257
272
|
quiet: true,
|
|
258
273
|
check: noRedundantRootScripts,
|
|
259
|
-
fix: "Remove scripts from root package.json that nmr provides as built-in root scripts
|
|
274
|
+
fix: "Remove scripts from root package.json that nmr provides as built-in root scripts, and invoke them through nmr directly"
|
|
260
275
|
},
|
|
261
276
|
// -- Git hooks -----------------------------------------------------------
|
|
262
277
|
{
|
|
@@ -278,13 +293,13 @@ var default_default = defineRdyKit({
|
|
|
278
293
|
name: "every bin target is a committed wrapper",
|
|
279
294
|
severity: "error",
|
|
280
295
|
check: () => everyBinTargetIsACommittedWrapper(),
|
|
281
|
-
fix: `Point each listed entry at a committed wrapper under bin/ that loads the build output at runtime. pnpm links a workspace package's bins during the install's link phase, which runs before anything is built, so a target that is not committed does not exist when pnpm
|
|
296
|
+
fix: `Point each listed entry at a committed wrapper under bin/ that loads the build output at runtime. pnpm links a workspace package's bins during the install's link phase, which runs before anything is built, so a target that is not committed does not exist when pnpm tries to link it. pnpm never retries, which leaves the link missing for the life of the node_modules tree`
|
|
282
297
|
},
|
|
283
298
|
{
|
|
284
299
|
name: "every bin wrapper's build-output target is covered by files",
|
|
285
300
|
severity: "warn",
|
|
286
301
|
check: () => everyBinWrapperTargetIsCoveredByFiles(),
|
|
287
|
-
fix: "Add the build output directory to `files` in each listed package. npm and pnpm publish the bin target itself whatever `files` says, so the wrapper
|
|
302
|
+
fix: "Add the build output directory to `files` in each listed package. npm and pnpm publish the bin target itself whatever `files` says, so the wrapper is published pointing at build output missing from the tarball"
|
|
288
303
|
},
|
|
289
304
|
// -- Vitest projects -----------------------------------------------------
|
|
290
305
|
{
|
|
@@ -298,7 +313,7 @@ var default_default = defineRdyKit({
|
|
|
298
313
|
name: "every vitest.config builds on @williamthorsen/nmr/vitest",
|
|
299
314
|
severity: "error",
|
|
300
315
|
check: () => vitestConfigBuildsOnSharedConfig(),
|
|
301
|
-
fix: "Replace each listed config with: import { defineVitestConfig } from '@williamthorsen/nmr/vitest'; export default defineVitestConfig(); -- a config that does not call the factory
|
|
316
|
+
fix: "Replace each listed config with: import { defineVitestConfig } from '@williamthorsen/nmr/vitest'; export default defineVitestConfig(); -- a config that does not call the factory does not declare any projects, so every tier-selecting test command fails against it. Pass the repo's own settings to the factory as layers to keep them"
|
|
302
317
|
},
|
|
303
318
|
{
|
|
304
319
|
name: "vitest.root.config.ts builds on @williamthorsen/nmr/vitest",
|
|
@@ -310,27 +325,27 @@ var default_default = defineRdyKit({
|
|
|
310
325
|
name: "every workspace with a Vite config has a Vitest config",
|
|
311
326
|
severity: "error",
|
|
312
327
|
check: () => everyViteConfigHasVitestConfig(),
|
|
313
|
-
fix: "Add a vitest.config.ts calling defineVitestConfig() from @williamthorsen/nmr/vitest beside each listed vite.config -- Vitest stops its config search at the first directory
|
|
328
|
+
fix: "Add a vitest.config.ts calling defineVitestConfig() from @williamthorsen/nmr/vitest beside each listed vite.config -- Vitest stops its config search at the first directory containing either name, so the Vite config otherwise wins and the projects model is never reached"
|
|
314
329
|
},
|
|
315
330
|
{
|
|
316
331
|
name: "the test suite gates the test-file conventions",
|
|
317
332
|
severity: "warn",
|
|
318
333
|
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 `
|
|
334
|
+
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, the repo's test run does not report an untiered or misplaced test file, and this kit reports them in its place against only nmr's built-in exclusions and the paths that git ignores"
|
|
320
335
|
},
|
|
321
336
|
{
|
|
322
337
|
name: "every test file names its isolation tier",
|
|
323
338
|
severity: "error",
|
|
324
339
|
skip: () => describeConventionsGuardSkip(),
|
|
325
340
|
check: () => everyTestFileNamesItsTier(),
|
|
326
|
-
fix: `Rename each to <subject>[.<aspect>].<tier>.test.ts, naming one of ${TIER_NAMES.join(", ")}. Use tool for a test that reaches a program the environment
|
|
341
|
+
fix: `Rename each to <subject>[.<aspect>].<tier>.test.ts, naming one of ${TIER_NAMES.join(", ")}. Use tool for a test that reaches a program supplied by the environment, which is the tier for a retired .int. or .integration. file. Only the segment before .test. selects a project, so an untiered file runs under the residual unit project and reports success`
|
|
327
342
|
},
|
|
328
343
|
{
|
|
329
|
-
name: "every test file
|
|
344
|
+
name: "every test file is under a __tests__ directory",
|
|
330
345
|
severity: "error",
|
|
331
346
|
skip: () => describeConventionsGuardSkip(),
|
|
332
347
|
check: () => everyTestFileSitsUnderTestsDir(),
|
|
333
|
-
fix: "Move each into a __tests__ directory, the only place from which the shared Vitest config collects.
|
|
348
|
+
fix: "Move each into a __tests__ directory, the only place from which the shared Vitest config collects. Vitest does not collect 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
349
|
},
|
|
335
350
|
{
|
|
336
351
|
name: "no package re-exports the ancestor Vitest config",
|
|
@@ -351,14 +366,14 @@ var default_default = defineRdyKit({
|
|
|
351
366
|
name: "taze.config.ts builds on @williamthorsen/nmr/taze",
|
|
352
367
|
severity: "warn",
|
|
353
368
|
check: () => tazeConfigBuildsOnSharedConfig(),
|
|
354
|
-
fix: "Replace the taze config with: import { defineConfig } from '@williamthorsen/nmr/taze'; export default defineConfig();
|
|
369
|
+
fix: "Replace the taze config with: import { defineConfig } from '@williamthorsen/nmr/taze'; export default defineConfig(); -- nmr's upgrade policy applies to a repo only through this file, so without it `nmr upgrade` does not report a dependency pinned to an exact version"
|
|
355
370
|
},
|
|
356
371
|
{
|
|
357
372
|
name: "taze config declares no option taze discards",
|
|
358
373
|
severity: "warn",
|
|
359
374
|
quiet: true,
|
|
360
375
|
check: () => tazeConfigAvoidsClobberedOptions(),
|
|
361
|
-
fix: "Set these through the upgrade script instead, as rootScripts: { upgrade: 'nmr-report-overrides && nmr-taze --recursive --request-timeout 90000' } in .config/nmr.config.ts, keeping the rest of the default script
|
|
376
|
+
fix: "Set these through the upgrade script instead, as rootScripts: { upgrade: 'nmr-report-overrides && nmr-taze --recursive --request-timeout 90000' } in .config/nmr.config.ts, keeping the rest of the default script. taze's CLI writes a default for each of them over whatever the config file declares, so taze never uses the file's value (antfu-collective/taze#317). nmr already forwards a 30-second request timeout"
|
|
362
377
|
},
|
|
363
378
|
// -- Audit dependency --------------------------------------------------------
|
|
364
379
|
{
|
|
@@ -373,7 +388,7 @@ var default_default = defineRdyKit({
|
|
|
373
388
|
severity: "error",
|
|
374
389
|
quiet: true,
|
|
375
390
|
check: () => !fileExists("scripts/run-workspace-script.ts"),
|
|
376
|
-
fix: "Delete scripts/run-workspace-script.ts
|
|
391
|
+
fix: "Delete scripts/run-workspace-script.ts, because nmr replaces this custom script runner"
|
|
377
392
|
},
|
|
378
393
|
{
|
|
379
394
|
name: 'no workspace packages reference run-workspace-script or "pnpm run ws"',
|
|
@@ -424,7 +439,7 @@ function allWorkspacePackagesCanBuild() {
|
|
|
424
439
|
const packagesDir = join(process.cwd(), "packages");
|
|
425
440
|
if (!existsSync(packagesDir)) return true;
|
|
426
441
|
const entries = readdirSync2(packagesDir, { withFileTypes: true });
|
|
427
|
-
const
|
|
442
|
+
const failingPackages = [];
|
|
428
443
|
for (const entry of entries) {
|
|
429
444
|
if (!entry.isDirectory()) continue;
|
|
430
445
|
const pkgPath = `packages/${entry.name}/package.json`;
|
|
@@ -433,30 +448,33 @@ function allWorkspacePackagesCanBuild() {
|
|
|
433
448
|
const hasBuildOverride = /"build"\s*:/.test(content);
|
|
434
449
|
const hasDefaultBuildInputs = fileExists(`packages/${entry.name}/tsconfig.json`) && existsSync(join(packagesDir, entry.name, "src"));
|
|
435
450
|
if (!hasBuildOverride && !hasDefaultBuildInputs) {
|
|
436
|
-
|
|
451
|
+
failingPackages.push(entry.name);
|
|
437
452
|
}
|
|
438
453
|
}
|
|
439
|
-
if (
|
|
454
|
+
if (failingPackages.length === 0) return true;
|
|
440
455
|
return {
|
|
441
456
|
ok: false,
|
|
442
|
-
detail: `missing build override or tsconfig.json + src/: ${
|
|
457
|
+
detail: `missing build override or tsconfig.json + src/: ${failingPackages.join(", ")}`
|
|
443
458
|
};
|
|
444
459
|
}
|
|
445
460
|
function checkNoMatchingFiles(patterns, cwd) {
|
|
446
|
-
const
|
|
447
|
-
if (
|
|
448
|
-
return { ok: false, detail: formatPaths(
|
|
461
|
+
const foundFiles = findFiles(patterns, cwd);
|
|
462
|
+
if (foundFiles.length === 0) return true;
|
|
463
|
+
return { ok: false, detail: formatPaths(foundFiles) };
|
|
449
464
|
}
|
|
450
465
|
function checkRootVitestConfig(baseName, exportName, cwd) {
|
|
451
466
|
const matches = findFiles([`${baseName}.${CONFIG_EXTENSIONS}`], cwd);
|
|
452
467
|
if (matches.length === 0) {
|
|
453
468
|
return { ok: false, detail: `${baseName}.ts is missing` };
|
|
454
469
|
}
|
|
455
|
-
const
|
|
470
|
+
const staleConfigs = matches.filter(
|
|
456
471
|
(relativePath) => !importsSharedExport(readFileIn(cwd, relativePath), exportName, SHARED_VITEST_MODULE)
|
|
457
472
|
);
|
|
458
|
-
if (
|
|
459
|
-
return {
|
|
473
|
+
if (staleConfigs.length === 0) return true;
|
|
474
|
+
return {
|
|
475
|
+
ok: false,
|
|
476
|
+
detail: `does not import ${exportName} from ${SHARED_VITEST_MODULE}: ${staleConfigs.join(", ")}`
|
|
477
|
+
};
|
|
460
478
|
}
|
|
461
479
|
function prettierConfigBuildsOnSharedConfig(cwd = process.cwd()) {
|
|
462
480
|
const configs = findFiles(
|
|
@@ -467,20 +485,20 @@ function prettierConfigBuildsOnSharedConfig(cwd = process.cwd()) {
|
|
|
467
485
|
if (configs.length === 0) {
|
|
468
486
|
return { ok: false, detail: describeMissingPrettierConfig(cwd) };
|
|
469
487
|
}
|
|
470
|
-
const
|
|
488
|
+
const staleConfigs = configs.filter(
|
|
471
489
|
(relativePath) => !importsSharedExport(readFileIn(cwd, relativePath), "definePrettierConfig", SHARED_PRETTIER_MODULE)
|
|
472
490
|
);
|
|
473
|
-
if (
|
|
491
|
+
if (staleConfigs.length === 0) return true;
|
|
474
492
|
return {
|
|
475
493
|
ok: false,
|
|
476
|
-
detail: `does not import definePrettierConfig from ${SHARED_PRETTIER_MODULE}: ${
|
|
494
|
+
detail: `does not import definePrettierConfig from ${SHARED_PRETTIER_MODULE}: ${staleConfigs.join(", ")}`
|
|
477
495
|
};
|
|
478
496
|
}
|
|
479
497
|
function describeMissingPrettierConfig(cwd) {
|
|
480
|
-
const
|
|
481
|
-
if (
|
|
498
|
+
const inertConfigs = findFiles(INERT_PRETTIER_CONFIGS, cwd);
|
|
499
|
+
if (inertConfigs.length > 0) return `does not contain code that calls the factory: ${inertConfigs.join(", ")}`;
|
|
482
500
|
if (hasPrettierConfigKey(cwd)) {
|
|
483
|
-
return '
|
|
501
|
+
return 'does not contain code that calls the factory: the "prettier" key in package.json';
|
|
484
502
|
}
|
|
485
503
|
return ".prettierrc.js is missing";
|
|
486
504
|
}
|
|
@@ -488,8 +506,8 @@ function hasPrettierConfigKey(cwd) {
|
|
|
488
506
|
const manifest = readFileIn(cwd, "package.json");
|
|
489
507
|
if (manifest === void 0) return false;
|
|
490
508
|
try {
|
|
491
|
-
const
|
|
492
|
-
return isRecord(
|
|
509
|
+
const parsedManifest = JSON.parse(manifest);
|
|
510
|
+
return isRecord(parsedManifest) && parsedManifest["prettier"] !== void 0;
|
|
493
511
|
} catch {
|
|
494
512
|
return false;
|
|
495
513
|
}
|
|
@@ -502,11 +520,11 @@ function discoverMemberWorkspaces() {
|
|
|
502
520
|
}
|
|
503
521
|
}
|
|
504
522
|
async function everyBinTargetIsACommittedWrapper() {
|
|
505
|
-
const
|
|
506
|
-
if (!
|
|
507
|
-
const
|
|
508
|
-
const trackedPaths =
|
|
509
|
-
const offenders =
|
|
523
|
+
const workspaceDiscovery = discoverMemberWorkspaces();
|
|
524
|
+
if (!workspaceDiscovery.ok) return workspaceDiscovery;
|
|
525
|
+
const trackedFiles = await listTrackedFiles();
|
|
526
|
+
const trackedPaths = trackedFiles === void 0 ? void 0 : new Set(trackedFiles);
|
|
527
|
+
const offenders = workspaceDiscovery.workspaces.flatMap(
|
|
510
528
|
(workspace) => readBinEntries(workspace).flatMap((entry) => {
|
|
511
529
|
const defect = describeBinTargetDefect(workspace, entry, trackedPaths);
|
|
512
530
|
return defect === void 0 ? [] : [`${describeBinEntry(workspace, entry)} (${defect})`];
|
|
@@ -516,16 +534,18 @@ async function everyBinTargetIsACommittedWrapper() {
|
|
|
516
534
|
return { ok: false, detail: formatPaths(offenders) };
|
|
517
535
|
}
|
|
518
536
|
function everyBinWrapperTargetIsCoveredByFiles() {
|
|
519
|
-
const
|
|
520
|
-
if (!
|
|
537
|
+
const workspaceDiscovery = discoverMemberWorkspaces();
|
|
538
|
+
if (!workspaceDiscovery.ok) return workspaceDiscovery;
|
|
521
539
|
const cwd = process.cwd();
|
|
522
|
-
const offenders =
|
|
540
|
+
const offenders = workspaceDiscovery.workspaces.flatMap((workspace) => {
|
|
523
541
|
const files = workspace.packageJson["files"];
|
|
524
542
|
if (!Array.isArray(files)) return [];
|
|
525
|
-
const
|
|
543
|
+
const publishedSegments = new Set(
|
|
544
|
+
files.flatMap((entry) => typeof entry === "string" ? [readFirstSegment(entry)] : [])
|
|
545
|
+
);
|
|
526
546
|
return readBinEntries(workspace).flatMap((entry) => {
|
|
527
547
|
const target = readWrapperTarget(cwd, workspace, entry);
|
|
528
|
-
if (target === void 0 ||
|
|
548
|
+
if (target === void 0 || publishedSegments.has(readFirstSegment(target))) return [];
|
|
529
549
|
return [`${describeBinEntry(workspace, entry)} -> ${target}`];
|
|
530
550
|
});
|
|
531
551
|
});
|
|
@@ -566,25 +586,25 @@ function readWrapperTarget(cwd, workspace, entry) {
|
|
|
566
586
|
return posix.normalize(posix.join(posix.dirname(entry.target), specifier));
|
|
567
587
|
}
|
|
568
588
|
function everyTestFileNamesItsTier(cwd = process.cwd()) {
|
|
569
|
-
const
|
|
570
|
-
if (
|
|
571
|
-
return { ok: false, detail: formatPaths(
|
|
589
|
+
const untieredFiles = findUntieredTestFiles(cwd);
|
|
590
|
+
if (untieredFiles.length === 0) return true;
|
|
591
|
+
return { ok: false, detail: formatPaths(untieredFiles) };
|
|
572
592
|
}
|
|
573
593
|
function everyTestFileSitsUnderTestsDir(cwd = process.cwd()) {
|
|
574
|
-
const
|
|
575
|
-
if (
|
|
576
|
-
return { ok: false, detail: formatPaths(
|
|
594
|
+
const misplacedFiles = findMisplacedTestFiles(cwd);
|
|
595
|
+
if (misplacedFiles.length === 0) return true;
|
|
596
|
+
return { ok: false, detail: formatPaths(misplacedFiles) };
|
|
577
597
|
}
|
|
578
598
|
function everyViteConfigHasVitestConfig() {
|
|
579
|
-
const
|
|
580
|
-
if (!
|
|
581
|
-
const
|
|
599
|
+
const workspaceDiscovery = discoverMemberWorkspaces();
|
|
600
|
+
if (!workspaceDiscovery.ok) return workspaceDiscovery;
|
|
601
|
+
const unpairedConfigs = workspaceDiscovery.workspaces.flatMap((workspace) => {
|
|
582
602
|
const viteConfigs = findWorkspaceConfigs(workspace, VITE_CONFIG_PATTERN);
|
|
583
603
|
if (viteConfigs.length === 0) return [];
|
|
584
604
|
return findWorkspaceConfigs(workspace, VITEST_CONFIG_PATTERN).length > 0 ? [] : viteConfigs;
|
|
585
605
|
});
|
|
586
|
-
if (
|
|
587
|
-
return { ok: false, detail: formatPaths(
|
|
606
|
+
if (unpairedConfigs.length === 0) return true;
|
|
607
|
+
return { ok: false, detail: formatPaths(unpairedConfigs) };
|
|
588
608
|
}
|
|
589
609
|
function findFiles(patterns, cwd) {
|
|
590
610
|
return globSync(patterns, { cwd, exclude: (path2) => SCAN_EXCLUDE_DIRS.has(basename(path2)) }).map((path2) => path2.split(sep).join("/")).toSorted();
|
|
@@ -597,11 +617,11 @@ function formatPaths(paths) {
|
|
|
597
617
|
${paths.map((path2) => ` ${path2}`).join("\n")}`;
|
|
598
618
|
}
|
|
599
619
|
function getMinVersion() {
|
|
600
|
-
const
|
|
601
|
-
if (typeof
|
|
620
|
+
const pickedFields = { "version": "0.40.0" };
|
|
621
|
+
if (typeof pickedFields["version"] !== "string") {
|
|
602
622
|
throw new TypeError("nmr/package.json: 'version' must be a string");
|
|
603
623
|
}
|
|
604
|
-
return
|
|
624
|
+
return pickedFields["version"];
|
|
605
625
|
}
|
|
606
626
|
function hasSupportedEslintVersion() {
|
|
607
627
|
return hasMinDevDependencyVersion("eslint", MIN_ESLINT_VERSION, {
|
|
@@ -630,13 +650,13 @@ function isReExportOnly(content) {
|
|
|
630
650
|
return statements.length > 0 && statements.every((line) => RE_EXPORT_LINE_PATTERN.test(line));
|
|
631
651
|
}
|
|
632
652
|
function noPnpmFieldInPackageJson(cwd = process.cwd()) {
|
|
633
|
-
const
|
|
653
|
+
const declaringFiles = findFiles(["**/package.json"], cwd).flatMap((relativePath) => {
|
|
634
654
|
const keys = readPnpmFieldKeys(readFileIn(cwd, relativePath));
|
|
635
655
|
if (keys === void 0) return [];
|
|
636
656
|
return [keys.length > 0 ? `${relativePath} (${keys.join(", ")})` : relativePath];
|
|
637
657
|
});
|
|
638
|
-
if (
|
|
639
|
-
return { ok: false, detail: formatPaths(
|
|
658
|
+
if (declaringFiles.length === 0) return true;
|
|
659
|
+
return { ok: false, detail: formatPaths(declaringFiles) };
|
|
640
660
|
}
|
|
641
661
|
function noReExportOnlyVitestConfigs(cwd = process.cwd()) {
|
|
642
662
|
const nonRootConfigs = findFiles([`**/${VITEST_CONFIG_PATTERN}`], cwd).filter((path2) => path2.includes("/"));
|
|
@@ -650,11 +670,11 @@ function noRedundantRootScripts() {
|
|
|
650
670
|
const scripts = pkg["scripts"];
|
|
651
671
|
if (!isRecord(scripts)) return true;
|
|
652
672
|
const builtInNames = Object.keys(getDefaultRootScripts());
|
|
653
|
-
const
|
|
654
|
-
if (
|
|
673
|
+
const redundantNames = Object.keys(scripts).filter((name) => builtInNames.includes(name));
|
|
674
|
+
if (redundantNames.length === 0) return true;
|
|
655
675
|
return {
|
|
656
676
|
ok: false,
|
|
657
|
-
detail: `redundant: ${
|
|
677
|
+
detail: `redundant: ${redundantNames.join(", ")}`
|
|
658
678
|
};
|
|
659
679
|
}
|
|
660
680
|
function noRetiredVitestConfigs(cwd = process.cwd()) {
|
|
@@ -665,14 +685,14 @@ function noRetiredVitestConfigs(cwd = process.cwd()) {
|
|
|
665
685
|
}
|
|
666
686
|
function noUnguardedLefthookInstall(cwd = process.cwd()) {
|
|
667
687
|
const scripts = readRootScripts(cwd);
|
|
668
|
-
const
|
|
688
|
+
const unguardedScripts = INSTALL_LIFECYCLE_SCRIPTS.flatMap((name) => {
|
|
669
689
|
const command = scripts[name];
|
|
670
690
|
if (typeof command !== "string") return [];
|
|
671
691
|
const isUnguarded = LEFTHOOK_INSTALL_PATTERN.test(command) && !LEFTHOOK_CHECK_INSTALL_PATTERN.test(command);
|
|
672
692
|
return isUnguarded ? [`${name}: ${command}`] : [];
|
|
673
693
|
});
|
|
674
|
-
if (
|
|
675
|
-
return { ok: false, detail: formatPaths(
|
|
694
|
+
if (unguardedScripts.length === 0) return true;
|
|
695
|
+
return { ok: false, detail: formatPaths(unguardedScripts) };
|
|
676
696
|
}
|
|
677
697
|
function noWorkspaceRunScriptReferences() {
|
|
678
698
|
const packagesDir = join(process.cwd(), "packages");
|
|
@@ -698,27 +718,27 @@ function readFileIn(cwd, relativePath) {
|
|
|
698
718
|
}
|
|
699
719
|
function readPnpmFieldKeys(content) {
|
|
700
720
|
if (content === void 0) return void 0;
|
|
701
|
-
let
|
|
721
|
+
let parsedManifest;
|
|
702
722
|
try {
|
|
703
|
-
|
|
723
|
+
parsedManifest = JSON.parse(content);
|
|
704
724
|
} catch {
|
|
705
725
|
return void 0;
|
|
706
726
|
}
|
|
707
|
-
if (!isRecord(
|
|
708
|
-
const pnpm =
|
|
727
|
+
if (!isRecord(parsedManifest)) return void 0;
|
|
728
|
+
const pnpm = parsedManifest["pnpm"];
|
|
709
729
|
return isRecord(pnpm) ? Object.keys(pnpm).toSorted() : void 0;
|
|
710
730
|
}
|
|
711
731
|
function readRootScripts(cwd) {
|
|
712
732
|
const content = readFileIn(cwd, "package.json");
|
|
713
733
|
if (content === void 0) return {};
|
|
714
|
-
let
|
|
734
|
+
let parsedManifest;
|
|
715
735
|
try {
|
|
716
|
-
|
|
736
|
+
parsedManifest = JSON.parse(content);
|
|
717
737
|
} catch {
|
|
718
738
|
return {};
|
|
719
739
|
}
|
|
720
|
-
if (!isRecord(
|
|
721
|
-
const scripts =
|
|
740
|
+
if (!isRecord(parsedManifest)) return {};
|
|
741
|
+
const scripts = parsedManifest["scripts"];
|
|
722
742
|
return isRecord(scripts) ? scripts : {};
|
|
723
743
|
}
|
|
724
744
|
function resolvesVersionViaWorkspace(range) {
|
|
@@ -729,11 +749,11 @@ function tazeConfigBuildsOnSharedConfig(cwd = process.cwd()) {
|
|
|
729
749
|
if (configs.length === 0) {
|
|
730
750
|
return { ok: false, detail: describeMissingTazeConfig(cwd) };
|
|
731
751
|
}
|
|
732
|
-
const
|
|
752
|
+
const staleConfigs = configs.filter(
|
|
733
753
|
(relativePath) => !importsSharedExport(readFileIn(cwd, relativePath), "defineConfig", SHARED_TAZE_MODULE)
|
|
734
754
|
);
|
|
735
|
-
if (
|
|
736
|
-
return { ok: false, detail: `does not import defineConfig from ${SHARED_TAZE_MODULE}: ${
|
|
755
|
+
if (staleConfigs.length === 0) return true;
|
|
756
|
+
return { ok: false, detail: `does not import defineConfig from ${SHARED_TAZE_MODULE}: ${staleConfigs.join(", ")}` };
|
|
737
757
|
}
|
|
738
758
|
function tazeConfigAvoidsClobberedOptions(cwd = process.cwd()) {
|
|
739
759
|
const configs = findFiles([`taze.config.${CONFIG_EXTENSIONS}`], cwd);
|
|
@@ -741,22 +761,22 @@ function tazeConfigAvoidsClobberedOptions(cwd = process.cwd()) {
|
|
|
741
761
|
for (const relativePath of configs) {
|
|
742
762
|
const content = readFileIn(cwd, relativePath);
|
|
743
763
|
if (content === void 0) continue;
|
|
744
|
-
const
|
|
745
|
-
if (
|
|
764
|
+
const discardedKeys = CLOBBERED_TAZE_OPTIONS.filter(({ pattern }) => pattern.test(content)).map(({ key }) => key);
|
|
765
|
+
if (discardedKeys.length > 0) findings.push(`${relativePath}: ${discardedKeys.join(", ")}`);
|
|
746
766
|
}
|
|
747
767
|
if (findings.length === 0) return true;
|
|
748
768
|
return { ok: false, detail: formatPaths(findings) };
|
|
749
769
|
}
|
|
750
770
|
function describeMissingTazeConfig(cwd) {
|
|
751
|
-
const
|
|
752
|
-
if (
|
|
771
|
+
const inertConfigs = findFiles(INERT_TAZE_CONFIGS, cwd);
|
|
772
|
+
if (inertConfigs.length > 0) return `does not contain code that calls the factory: ${inertConfigs.join(", ")}`;
|
|
753
773
|
return "taze.config.ts is missing";
|
|
754
774
|
}
|
|
755
775
|
function testSuiteGatesTestFileConventions(cwd = process.cwd()) {
|
|
756
776
|
if (findConventionsGuard(cwd) !== void 0) return true;
|
|
757
777
|
return {
|
|
758
778
|
ok: false,
|
|
759
|
-
detail: `
|
|
779
|
+
detail: `the test files under __tests__ do not import checkTestFileConventions from ${SHARED_TESTS_MODULE}`
|
|
760
780
|
};
|
|
761
781
|
}
|
|
762
782
|
function describeConventionsGuardSkip() {
|
|
@@ -777,9 +797,9 @@ function vitestConfigBuildsOnSharedConfig() {
|
|
|
777
797
|
const cwd = process.cwd();
|
|
778
798
|
const rootConfigs = findFiles([VITEST_CONFIG_PATTERN], cwd);
|
|
779
799
|
if (rootConfigs.length === 0) return { ok: false, detail: "vitest.config.ts is missing" };
|
|
780
|
-
const
|
|
781
|
-
if (!
|
|
782
|
-
const workspaceConfigs =
|
|
800
|
+
const workspaceDiscovery = discoverMemberWorkspaces();
|
|
801
|
+
if (!workspaceDiscovery.ok) return workspaceDiscovery;
|
|
802
|
+
const workspaceConfigs = workspaceDiscovery.workspaces.flatMap((workspace) => findWorkspaceConfigs(workspace, VITEST_CONFIG_PATTERN)).filter((relativePath) => !isOwnedByReExportCheck(cwd, relativePath));
|
|
783
803
|
const stale = [...rootConfigs, ...workspaceConfigs].filter(
|
|
784
804
|
(relativePath) => !importsSharedExport(readFileIn(cwd, relativePath), "defineVitestConfig", SHARED_VITEST_MODULE)
|
|
785
805
|
);
|
package/.readyup/manifest.json
CHANGED
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
"esbuildVersion": "0.28.2",
|
|
12
12
|
"inputs": [
|
|
13
13
|
{
|
|
14
|
-
"hash": "
|
|
14
|
+
"hash": "5f7bd316",
|
|
15
15
|
"kind": "module",
|
|
16
16
|
"path": "kits/default.ts"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
|
-
"hash": "
|
|
19
|
+
"hash": "9c1005ba",
|
|
20
20
|
"kind": "inline",
|
|
21
21
|
"path": "../package.json",
|
|
22
22
|
"paths": [
|
|
@@ -24,17 +24,22 @@
|
|
|
24
24
|
]
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
|
-
"hash": "
|
|
27
|
+
"hash": "9336f4c0",
|
|
28
28
|
"kind": "module",
|
|
29
29
|
"path": "../src/default-scripts.ts"
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
|
-
"hash": "
|
|
32
|
+
"hash": "d4828172",
|
|
33
|
+
"kind": "module",
|
|
34
|
+
"path": "../src/git-ignored-paths.ts"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"hash": "23646439",
|
|
33
38
|
"kind": "module",
|
|
34
39
|
"path": "../src/resolve-scripts.ts"
|
|
35
40
|
},
|
|
36
41
|
{
|
|
37
|
-
"hash": "
|
|
42
|
+
"hash": "19dec003",
|
|
38
43
|
"kind": "module",
|
|
39
44
|
"path": "../src/tiers.ts"
|
|
40
45
|
}
|
|
@@ -43,8 +48,8 @@
|
|
|
43
48
|
"path": "kits/default.js",
|
|
44
49
|
"readyupVersion": "0.38.0",
|
|
45
50
|
"source": "kits/default.ts",
|
|
46
|
-
"sourceHash": "
|
|
47
|
-
"targetHash": "
|
|
51
|
+
"sourceHash": "5f7bd316",
|
|
52
|
+
"targetHash": "da11ca1f"
|
|
48
53
|
}
|
|
49
54
|
]
|
|
50
55
|
}
|