@williamthorsen/nmr 0.16.0 → 0.17.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/AGENTS.md +1 -1
- package/README.md +41 -44
- package/dist/esm/.cache +1 -1
- 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 +2 -8
- package/dist/esm/commands/build.js +270 -118
- 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/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.
|
|
6
|
+
## Release notes — v0.17.0 (2026-07-01)
|
|
7
7
|
|
|
8
8
|
### 🎉 Features
|
|
9
9
|
|
|
10
|
-
- 🚨 **Breaking:**
|
|
10
|
+
- 🚨 **Breaking:** Rebuild nmr-compile on a unified tsc emit (#455)
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
### 🐛 Bug fixes
|
|
15
|
-
|
|
16
|
-
- Prevent CLI output truncation when piped before exit (#446)
|
|
17
|
-
|
|
18
|
-
Fixes an issue where large output from the `nmr` and `v11y` commands could be truncated when captured through a pipe — for example, by a CI job.
|
|
12
|
+
`nmr` now compiles a package's JavaScript and type declarations in one step, rewriting every import form — static, re-export, dynamic `import()`, bare side-effect, and tsconfig `paths` aliases — to runnable `.js` in both outputs. It now requires TypeScript 5.7 or newer as a peer dependency.
|
|
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 in `dist/esm/.cache`). 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/.cache
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
49f79592247d14646b1445473aa445a8bcbeafb478c6dc9949eb31cee8e2a627
|
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
5
|
cacheFile?: string;
|
|
8
|
-
format?: Format;
|
|
9
|
-
platform?: Platform;
|
|
10
|
-
target?: string;
|
|
11
6
|
}
|
|
12
7
|
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 rewriteTsImportExtensions(code: string): string;
|
|
8
|
+
export declare function computeBuildHash(packageDir: string, files: string[], emitConfig: object): Promise<string>;
|
|
9
|
+
export declare function resolveTsconfigChain(packageDir: string, configFileName?: string): string[];
|