@williamthorsen/nmr 0.16.0 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/README.md +41 -44
- package/dist/esm/cli-build.js +5 -5
- package/dist/esm/cli-ensure-prepublish-hooks.js +33 -34
- package/dist/esm/cli-report-overrides.js +7 -6
- package/dist/esm/cli-sync-agent-files.js +20 -22
- package/dist/esm/cli-sync-pnpm-version.js +7 -6
- package/dist/esm/cli.js +13 -13
- package/dist/esm/commands/build.d.ts +3 -9
- package/dist/esm/commands/build.js +288 -120
- package/dist/esm/commands/ensure-prepublish-hooks.js +66 -69
- package/dist/esm/commands/report-overrides.js +10 -13
- package/dist/esm/commands/sync-agent-files.js +46 -52
- package/dist/esm/commands/sync-pnpm-version.js +35 -42
- package/dist/esm/config.js +59 -57
- package/dist/esm/context.d.ts +1 -1
- package/dist/esm/context.js +70 -72
- package/dist/esm/default-scripts.js +63 -68
- package/dist/esm/help.d.ts +1 -1
- package/dist/esm/help.js +65 -67
- package/dist/esm/helpers/code-quality-pnpm-action.js +20 -24
- package/dist/esm/helpers/hook-name.js +2 -5
- package/dist/esm/helpers/package-json.js +46 -41
- package/dist/esm/helpers/type-guards.js +2 -5
- package/dist/esm/index.d.ts +3 -3
- package/dist/esm/index.js +1 -4
- package/dist/esm/resolve-scripts.d.ts +2 -2
- package/dist/esm/resolve-scripts.js +7 -11
- package/dist/esm/resolver.d.ts +2 -2
- package/dist/esm/resolver.js +84 -86
- package/dist/esm/runCli.js +136 -144
- package/dist/esm/runner.js +35 -34
- package/dist/esm/scripts.d.ts +2 -2
- package/dist/esm/scripts.js +1 -5
- package/dist/esm/tests/consistency.d.ts +1 -1
- package/dist/esm/tests/consistency.js +45 -54
- package/dist/esm/tests/helpers/get-runtime-version-from-asdf.js +11 -14
- package/dist/esm/tests/helpers/get-string-from-yaml-file.js +10 -13
- package/dist/esm/tests/helpers/get-value-at-path.js +30 -30
- package/package.json +5 -3
- package/dist/esm/.cache +0 -1
package/AGENTS.md
CHANGED
|
@@ -38,7 +38,7 @@ Some root scripts (e.g. `lint`, `typecheck`, `test`) expand to `nmr root:X && pn
|
|
|
38
38
|
|
|
39
39
|
## Managed build
|
|
40
40
|
|
|
41
|
-
The default `compile` script runs `nmr-compile`, a standalone bin that
|
|
41
|
+
The default `compile` script runs `nmr-compile`, a standalone bin that uses the TypeScript compiler API to emit a package's `src` to `dist/esm` as `.js` and `.d.ts` in one pass, rewriting relative `.ts`→`.js` specifiers and tsconfig `paths` aliases in both outputs, and skipping work when inputs are unchanged. `typescript` is a peer dependency (`>=5.7.0`). There is no repo-local build script to maintain, and no separate typings step; `nmr build` runs `compile` alone. To find or debug the build, look to `nmr-compile`, not a `config/build.ts` in the consuming repo.
|
|
42
42
|
|
|
43
43
|
## Override behaviors
|
|
44
44
|
|
package/README.md
CHANGED
|
@@ -3,19 +3,13 @@
|
|
|
3
3
|
Context-aware script runner for PNPM monorepos. Ships an `nmr` (node-monorepo run) binary that provides centralized, consistent script execution across workspace packages and the monorepo root.
|
|
4
4
|
|
|
5
5
|
<!-- section:release-notes -->
|
|
6
|
-
## Release notes — v0.
|
|
7
|
-
|
|
8
|
-
### 🎉 Features
|
|
9
|
-
|
|
10
|
-
- 🚨 **Breaking:** Auto-activate integration test variant from config presence (#448)
|
|
11
|
-
|
|
12
|
-
A package can now separate its integration tests from its standalone suite simply by including a `vitest.integration.config.ts` (alongside a `vitest.standalone.config.ts`). The `--int-test` flag that previously enabled this is removed — that config-file pairing is now the only way to activate the separation. In such a package, `test` and `test:coverage` run only the standalone suite and skip integration tests, while a new `test:all` runs both suites together. The separation now holds even when tests run across every package at once, so a full-workspace `test` run still keeps integration tests out of the default suite. Packages that previously hand-copied these test scripts no longer need to.
|
|
6
|
+
## Release notes — v0.17.1 (2026-07-01)
|
|
13
7
|
|
|
14
8
|
### 🐛 Bug fixes
|
|
15
9
|
|
|
16
|
-
-
|
|
10
|
+
- Relocate nmr-compile's build cache out of the published dist (#457)
|
|
17
11
|
|
|
18
|
-
Fixes an issue where
|
|
12
|
+
Fixes an issue where packages built with nmr-compile shipped an internal build-cache file inside their published npm tarball.
|
|
19
13
|
<!-- /section:release-notes -->
|
|
20
14
|
|
|
21
15
|
## Installation
|
|
@@ -32,7 +26,7 @@ From a package directory:
|
|
|
32
26
|
|
|
33
27
|
```bash
|
|
34
28
|
nmr test # Run tests for the current package
|
|
35
|
-
nmr build # Compile
|
|
29
|
+
nmr build # Compile to .js and .d.ts in one pass
|
|
36
30
|
nmr check # Typecheck, format check, lint check, and tests
|
|
37
31
|
```
|
|
38
32
|
|
|
@@ -75,11 +69,11 @@ Scripts resolve through three tiers. Higher tiers override lower ones:
|
|
|
75
69
|
|
|
76
70
|
Given the `build` command for a package that defines its own build script:
|
|
77
71
|
|
|
78
|
-
| Tier | Source | Value
|
|
79
|
-
| ---- | ----------------------- |
|
|
80
|
-
| 1 | Built-in default | `['compile'
|
|
81
|
-
| 2 | `.config/nmr.config.ts` | _(not set)_
|
|
82
|
-
| 3 | `package.json` scripts | `"tsx custom-build.ts"`
|
|
72
|
+
| Tier | Source | Value | Wins? |
|
|
73
|
+
| ---- | ----------------------- | ----------------------- | ----- |
|
|
74
|
+
| 1 | Built-in default | `['compile']` | — |
|
|
75
|
+
| 2 | `.config/nmr.config.ts` | _(not set)_ | — |
|
|
76
|
+
| 3 | `package.json` scripts | `"tsx custom-build.ts"` | ✓ |
|
|
83
77
|
|
|
84
78
|
If no per-package override exists, the highest-tier value that is set wins. Set a script to `""` in `package.json` to skip it for that package.
|
|
85
79
|
|
|
@@ -90,8 +84,8 @@ If no per-package override exists, the highest-tier value that is set wins. Set
|
|
|
90
84
|
Script values can be `string` or `string[]`. Arrays expand to chained `nmr` sub-invocations:
|
|
91
85
|
|
|
92
86
|
```ts
|
|
93
|
-
// "
|
|
94
|
-
// expands to: nmr
|
|
87
|
+
// "fix": ["lint", "fmt"]
|
|
88
|
+
// expands to: nmr lint && nmr fmt
|
|
95
89
|
```
|
|
96
90
|
|
|
97
91
|
## Configuration
|
|
@@ -145,26 +139,26 @@ These scripts are available out of the box. Repo-wide config (tier 2) and per-pa
|
|
|
145
139
|
|
|
146
140
|
### Workspace scripts
|
|
147
141
|
|
|
148
|
-
| Command
|
|
149
|
-
|
|
|
150
|
-
| `
|
|
151
|
-
| `
|
|
152
|
-
| `check
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
| `fix
|
|
157
|
-
| `
|
|
158
|
-
| `fmt
|
|
159
|
-
| `
|
|
160
|
-
| `lint`
|
|
161
|
-
| `lint:check`
|
|
162
|
-
| `lint:strict`
|
|
163
|
-
| `test`
|
|
164
|
-
| `test:coverage`
|
|
165
|
-
| `test:watch`
|
|
166
|
-
| `typecheck`
|
|
167
|
-
| `view-coverage`
|
|
142
|
+
| Command | Runs |
|
|
143
|
+
| --------------- | ---------------------------------------------------------------- |
|
|
144
|
+
| `attw` | `attw --pack --profile esm-only` |
|
|
145
|
+
| `build` | `compile` |
|
|
146
|
+
| `check` | `typecheck`, `fmt:check`, `lint:check`, `test` |
|
|
147
|
+
| `check:strict` | `typecheck`, `fmt:check`, `lint:strict`, `test:coverage`, `attw` |
|
|
148
|
+
| `clean` | `pnpm exec rimraf dist/*` |
|
|
149
|
+
| `compile` | `nmr-compile` |
|
|
150
|
+
| `fix` | `lint`, `fmt` |
|
|
151
|
+
| `fix:check` | `fmt:check`, `lint:check` |
|
|
152
|
+
| `fmt` | `prettier --list-different --write .` |
|
|
153
|
+
| `fmt:check` | `prettier --check .` |
|
|
154
|
+
| `lint` | `eslint --fix .` |
|
|
155
|
+
| `lint:check` | `eslint .` |
|
|
156
|
+
| `lint:strict` | `strict-lint` |
|
|
157
|
+
| `test` | `pnpm exec vitest` |
|
|
158
|
+
| `test:coverage` | `pnpm exec vitest --coverage` |
|
|
159
|
+
| `test:watch` | `pnpm exec vitest --watch` |
|
|
160
|
+
| `typecheck` | `tsgo --noEmit` |
|
|
161
|
+
| `view-coverage` | `open coverage/index.html` |
|
|
168
162
|
|
|
169
163
|
#### Integration test variant
|
|
170
164
|
|
|
@@ -190,11 +184,12 @@ A package gets this variant automatically when it contains a `vitest.integration
|
|
|
190
184
|
|
|
191
185
|
#### Check and quality
|
|
192
186
|
|
|
193
|
-
| Command | Runs
|
|
194
|
-
| ------------------- |
|
|
195
|
-
| `
|
|
196
|
-
| `check
|
|
197
|
-
| `check:
|
|
187
|
+
| Command | Runs |
|
|
188
|
+
| ------------------- | ------------------------------------------------------------------------------------- |
|
|
189
|
+
| `attw` | `pnpm --recursive exec nmr attw` |
|
|
190
|
+
| `check` | `typecheck`, `fmt:check`, `lint:check`, `test` |
|
|
191
|
+
| `check:agent-files` | `nmr-sync-agent-files --check` |
|
|
192
|
+
| `check:strict` | `typecheck`, `fmt:check`, `lint:strict`, `test:coverage`, `attw`, `check:agent-files` |
|
|
198
193
|
|
|
199
194
|
#### Fix
|
|
200
195
|
|
|
@@ -293,7 +288,7 @@ nmr [flags] <command> [args...]
|
|
|
293
288
|
```bash
|
|
294
289
|
# From a package directory
|
|
295
290
|
nmr test # Run workspace test script
|
|
296
|
-
nmr build # Compile
|
|
291
|
+
nmr build # Compile to .js and .d.ts in one pass
|
|
297
292
|
|
|
298
293
|
# From the monorepo root
|
|
299
294
|
nmr test # Root tests + recursive workspace tests
|
|
@@ -350,7 +345,9 @@ nmr sync-pnpm-version
|
|
|
350
345
|
|
|
351
346
|
### `nmr-compile`
|
|
352
347
|
|
|
353
|
-
Compile a single package's `src` tree to `dist/esm` with
|
|
348
|
+
Compile a single package's `src` tree to `dist/esm` with the TypeScript compiler API, emitting `.js` and `.d.ts` in one pass. Because the compiler parses each source file, every relative import form — static, re-export, dynamic `import()`, and bare side-effect — is rewritten from `.ts` to `.js` in both outputs, and `.ts` occurrences inside strings and comments are left intact. tsconfig `paths` aliases are resolved to runnable relative `.js` specifiers in both outputs, sourced from the package's tsconfig. The build is skipped when no input has changed (a content-and-path hash is cached under `node_modules/.cache/nmr-compile/`, outside the published output). This is the default `compile` script — run it from a package directory.
|
|
349
|
+
|
|
350
|
+
`typescript` is a peer dependency (`>=5.7.0`, required for `rewriteRelativeImportExtensions`); the consuming repo provides it. Relative imports in source must carry explicit `.ts` extensions for them to be rewritten.
|
|
354
351
|
|
|
355
352
|
```bash
|
|
356
353
|
nmr-compile
|
package/dist/esm/cli-build.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { buildPackage } from "./commands/build.js";
|
|
2
2
|
try {
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
`);
|
|
7
|
-
|
|
3
|
+
await buildPackage(process.cwd());
|
|
4
|
+
}
|
|
5
|
+
catch (error) {
|
|
6
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
7
|
+
process.exitCode = 1;
|
|
8
8
|
}
|
|
@@ -1,44 +1,43 @@
|
|
|
1
|
-
import { parseArgsOrExit, reportError } from
|
|
1
|
+
import { parseArgsOrExit, reportError } from '@williamthorsen/nmr-core';
|
|
2
2
|
import { DEFAULT_HOOK, ensurePrepublishHooks } from "./commands/ensure-prepublish-hooks.js";
|
|
3
3
|
import { findMonorepoRoot } from "./context.js";
|
|
4
4
|
const flagSchema = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
fix: { long: '--fix', type: 'boolean' },
|
|
6
|
+
dryRun: { long: '--dry-run', type: 'boolean' },
|
|
7
|
+
command: { long: '--command', type: 'string' },
|
|
8
8
|
};
|
|
9
9
|
const { fix, dryRun, command } = parseArgsOrExit(process.argv.slice(2), flagSchema).flags;
|
|
10
10
|
try {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
const monorepoRoot = findMonorepoRoot();
|
|
12
|
+
const hookCommand = command ?? DEFAULT_HOOK;
|
|
13
|
+
const options = command ? { fix, dryRun, command } : { fix, dryRun };
|
|
14
|
+
const result = ensurePrepublishHooks(monorepoRoot, options);
|
|
15
|
+
for (const pkg of result.packages) {
|
|
16
|
+
if (pkg.isPrivate) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
switch (pkg.action) {
|
|
20
|
+
case 'ok':
|
|
21
|
+
console.info(`✓ ${pkg.packageName}: prepublishOnly = "${pkg.prepublishOnly}"`);
|
|
22
|
+
break;
|
|
23
|
+
case 'missing':
|
|
24
|
+
console.warn(`✗ ${pkg.packageName}: missing prepublishOnly`);
|
|
25
|
+
break;
|
|
26
|
+
case 'fixed':
|
|
27
|
+
console.info(`✓ ${pkg.packageName}: added prepublishOnly = "${hookCommand}"`);
|
|
28
|
+
break;
|
|
29
|
+
case 'would-fix':
|
|
30
|
+
console.info(`~ ${pkg.packageName}: would add prepublishOnly = "${hookCommand}"`);
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
18
33
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
case "missing":
|
|
24
|
-
console.warn(`\u2717 ${pkg.packageName}: missing prepublishOnly`);
|
|
25
|
-
break;
|
|
26
|
-
case "fixed":
|
|
27
|
-
console.info(`\u2713 ${pkg.packageName}: added prepublishOnly = "${hookCommand}"`);
|
|
28
|
-
break;
|
|
29
|
-
case "would-fix":
|
|
30
|
-
console.info(`~ ${pkg.packageName}: would add prepublishOnly = "${hookCommand}"`);
|
|
31
|
-
break;
|
|
34
|
+
if (result.hasFailures) {
|
|
35
|
+
const missing = result.packages.filter((p) => p.action === 'missing').length;
|
|
36
|
+
process.stderr.write(`\n${missing} package(s) missing prepublishOnly. Use --fix to add it.\n`);
|
|
37
|
+
process.exitCode = 1;
|
|
32
38
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
process.stderr.write(`
|
|
37
|
-
${missing} package(s) missing prepublishOnly. Use --fix to add it.
|
|
38
|
-
`);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
reportError(error instanceof Error ? error.message : String(error));
|
|
39
42
|
process.exitCode = 1;
|
|
40
|
-
}
|
|
41
|
-
} catch (error) {
|
|
42
|
-
reportError(error instanceof Error ? error.message : String(error));
|
|
43
|
-
process.exitCode = 1;
|
|
44
43
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { reportError } from
|
|
1
|
+
import { reportError } from '@williamthorsen/nmr-core';
|
|
2
2
|
import { reportOverrides } from "./commands/report-overrides.js";
|
|
3
3
|
import { findMonorepoRoot } from "./context.js";
|
|
4
4
|
try {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const monorepoRoot = findMonorepoRoot();
|
|
6
|
+
reportOverrides(monorepoRoot);
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
reportError(error instanceof Error ? error.message : String(error));
|
|
10
|
+
process.exitCode = 1;
|
|
10
11
|
}
|
|
@@ -1,31 +1,29 @@
|
|
|
1
|
-
import { parseArgsOrExit, reportError } from
|
|
1
|
+
import { parseArgsOrExit, reportError } from '@williamthorsen/nmr-core';
|
|
2
2
|
import { check, sync } from "./commands/sync-agent-files.js";
|
|
3
3
|
import { findMonorepoRoot } from "./context.js";
|
|
4
4
|
const flagSchema = {
|
|
5
|
-
|
|
5
|
+
check: { long: '--check', type: 'boolean' },
|
|
6
6
|
};
|
|
7
7
|
const { flags } = parseArgsOrExit(process.argv.slice(2), flagSchema);
|
|
8
8
|
try {
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
runCommand(flags.check);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
reportError(error instanceof Error ? error.message : String(error));
|
|
13
|
+
process.exit(1);
|
|
13
14
|
}
|
|
14
15
|
function runCommand(checkOnly) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
process.
|
|
27
|
-
|
|
28
|
-
process.stderr.write(`${result.reason}
|
|
29
|
-
`);
|
|
30
|
-
process.exit(1);
|
|
16
|
+
const monorepoRoot = findMonorepoRoot();
|
|
17
|
+
if (!checkOnly) {
|
|
18
|
+
const { path: destinationPath, packageSpecifier, changed } = sync(monorepoRoot);
|
|
19
|
+
console.info(changed ? `✓ Wrote ${destinationPath} (${packageSpecifier})` : `✓ ${destinationPath} already up to date`);
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
const result = check(monorepoRoot);
|
|
23
|
+
if (result.ok) {
|
|
24
|
+
console.info('✓ .agents/nmr/AGENTS.md is in sync');
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
process.stderr.write(`${result.reason}\n`);
|
|
28
|
+
process.exit(1);
|
|
31
29
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { reportError } from
|
|
1
|
+
import { reportError } from '@williamthorsen/nmr-core';
|
|
2
2
|
import { syncPnpmVersion } from "./commands/sync-pnpm-version.js";
|
|
3
3
|
import { findMonorepoRoot } from "./context.js";
|
|
4
4
|
try {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const monorepoRoot = findMonorepoRoot();
|
|
6
|
+
syncPnpmVersion(monorepoRoot);
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
reportError(error instanceof Error ? error.message : String(error));
|
|
10
|
+
process.exitCode = 1;
|
|
10
11
|
}
|
package/dist/esm/cli.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import process from
|
|
1
|
+
import process from 'node:process';
|
|
2
2
|
import { runCli } from "./runCli.js";
|
|
3
3
|
try {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
`);
|
|
15
|
-
|
|
4
|
+
const { exitCode } = await runCli({
|
|
5
|
+
args: process.argv.slice(2),
|
|
6
|
+
cwd: process.cwd(),
|
|
7
|
+
env: process.env,
|
|
8
|
+
stdout: process.stdout,
|
|
9
|
+
stderr: process.stderr,
|
|
10
|
+
});
|
|
11
|
+
process.exitCode = exitCode;
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
process.stderr.write(`${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`);
|
|
15
|
+
process.exitCode = 1;
|
|
16
16
|
}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import { type Format, type Platform } from 'esbuild';
|
|
2
1
|
export interface BuildOptions {
|
|
3
|
-
aliases?: Record<string, string>;
|
|
4
2
|
entryGlobs?: string[];
|
|
5
3
|
ignore?: string[];
|
|
6
4
|
outdir?: string;
|
|
7
|
-
cacheFile?: string;
|
|
8
|
-
format?: Format;
|
|
9
|
-
platform?: Platform;
|
|
10
|
-
target?: string;
|
|
11
5
|
}
|
|
12
6
|
export declare function buildPackage(packageDir: string, options?: BuildOptions): Promise<void>;
|
|
13
|
-
export declare function computeBuildHash(packageDir: string, files: string[],
|
|
14
|
-
export declare function
|
|
15
|
-
export declare function
|
|
7
|
+
export declare function computeBuildHash(packageDir: string, files: string[], emitConfig: object): Promise<string>;
|
|
8
|
+
export declare function resolveTsconfigChain(packageDir: string, configFileName?: string): string[];
|
|
9
|
+
export declare function resolveBuildCachePath(packageDir: string): string;
|