@williamthorsen/nmr 0.41.0 → 0.43.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 +107 -0
- package/.readyup/kits/default.js +7447 -10
- package/.readyup/manifest.json +7 -6
- package/CHANGELOG.md +32 -0
- package/README.md +16 -6
- package/agents/guidance/rulebooks/nmr.md +1 -1
- package/dist/esm/cli-fmt.js +1 -1
- package/dist/esm/commands/build-output.js +1 -1
- package/dist/esm/commands/build.d.ts +15 -0
- package/dist/esm/commands/build.js +30 -8
- package/dist/esm/commands/fmt.d.ts +14 -2
- package/dist/esm/commands/fmt.js +89 -22
- package/dist/esm/help.d.ts +7 -3
- package/dist/esm/help.js +52 -9
- package/dist/esm/prettier-default-config.d.ts +6 -0
- package/dist/esm/prettier-default-config.js +2 -0
- package/dist/esm/vitest-source-resolution.js +5 -11
- package/docs/prettier.md +1 -1
- package/docs/scripts.md +1 -1
- package/docs/utilities.md +8 -6
- package/package.json +12 -12
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, realpathSync, statSync } from 'node:fs';
|
|
2
2
|
import { isBuiltin } from 'node:module';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { findDirectoryChainMatch } from '@williamthorsen/toolbelt.filesystem';
|
|
4
5
|
import { parsePackageJson } from "./helpers/package-json.js";
|
|
5
6
|
import { isObject } from "./helpers/type-guards.js";
|
|
6
7
|
import { UserError } from "./UserError.js";
|
|
@@ -57,17 +58,10 @@ function findPackageDir(name, fromDir, cache) {
|
|
|
57
58
|
if (cache?.has(key))
|
|
58
59
|
return cache.get(key);
|
|
59
60
|
let foundDir = findSelfReferenceDir(name, fromDir);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
foundDir = realpathSync(candidate);
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
const parent = path.dirname(dir);
|
|
68
|
-
if (parent === dir)
|
|
69
|
-
break;
|
|
70
|
-
dir = parent;
|
|
61
|
+
if (foundDir === undefined) {
|
|
62
|
+
const match = findDirectoryChainMatch(fromDir, [path.join(NODE_MODULES, name, 'package.json')]);
|
|
63
|
+
if (match !== undefined)
|
|
64
|
+
foundDir = realpathSync(path.join(match.dir, NODE_MODULES, name));
|
|
71
65
|
}
|
|
72
66
|
cache?.set(key, foundDir);
|
|
73
67
|
return foundDir;
|
package/docs/prettier.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The Prettier config that `@williamthorsen/nmr/prettier` publishes: what it formats, how it scopes the shell plugin, and how to customize and adopt it.
|
|
4
4
|
|
|
5
|
-
`prettier` is a
|
|
5
|
+
`prettier` is a dependency of nmr. When the repo installs its own copy, `nmr-fmt` runs that copy instead, so that the editor and the formatter agree. When Prettier does not find a config searching from the working directory, `nmr-fmt` passes this config with `--config`, which also applies it to files below that directory; `.editorconfig` still applies alongside it. The shell and Dockerfile plugin is a dependency of nmr rather than a peer, so its version is pinned centrally and two repos cannot format the same script differently.
|
|
6
6
|
|
|
7
7
|
## What it formats
|
|
8
8
|
|
package/docs/scripts.md
CHANGED
|
@@ -141,7 +141,7 @@ For example, if a workspace script resolves to `my-cli --verbose`, nmr rewrites
|
|
|
141
141
|
|
|
142
142
|
## Default commands
|
|
143
143
|
|
|
144
|
-
`nmr --help` lists every default command and the shell command to which it resolves. Repo-wide config (tier 2) and per-package overrides (tier 3) can add to or replace any of them. What the listing does not show is below.
|
|
144
|
+
`nmr --help` lists every default command and the shell command to which it resolves. Repo-wide config (tier 2) and per-package overrides (tier 3) can add to or replace any of them. Under `Package scripts:`, the listing also shows each script in the current scope's `package.json` whose name the registry does not contain, a hook such as `build:pre` included; it omits npm and pnpm lifecycle scripts, such as `prepare` and `postinstall`, which the package manager runs. What the listing does not show is below.
|
|
145
145
|
|
|
146
146
|
### Build and CI
|
|
147
147
|
|
package/docs/utilities.md
CHANGED
|
@@ -12,9 +12,11 @@ nmr-clean
|
|
|
12
12
|
|
|
13
13
|
## `nmr-compile`
|
|
14
14
|
|
|
15
|
-
Compile a single package's `src` tree to `dist/esm` with the TypeScript compiler API, emitting `.js` from one program and `.d.ts` from a second that reuses it. The package's own `removeComments` governs the `.js` emit; the declarations emit forces it off, so `.d.ts` files keep their doc comments either way. 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. An aliased import whose target resolves outside the package's `src/` and is not resolvable without the alias mapping fails the build with a diagnostic, rather than being emitted verbatim to produce output that fails at runtime. The build is skipped when every input is unchanged and the previous output is still on disk (the key is cached under `node_modules/.cache/nmr-compile/`, outside the published output). Deleting the output by any means (`nmr clean`, `rm -rf dist`, `git clean`) therefore forces a rebuild rather than a skip. This is the default `compile` script; run it from a package directory.
|
|
15
|
+
Compile a single package's `src` tree to `dist/esm` with the TypeScript compiler API, emitting `.js` from one program and `.d.ts` from a second that reuses it. The package's own `removeComments` governs the `.js` emit; the declarations emit forces it off, so `.d.ts` files keep their doc comments either way. Because the compiler parses each source file, every relative import form (static, re-export, dynamic `import()`, and bare side-effect) is rewritten from `.ts` or `.tsx` 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. An aliased import whose target resolves outside the package's `src/` and is not resolvable without the alias mapping fails the build with a diagnostic, rather than being emitted verbatim to produce output that fails at runtime. The build is skipped when every input is unchanged and the previous output is still on disk (the key is cached under `node_modules/.cache/nmr-compile/`, outside the published output). Deleting the output by any means (`nmr clean`, `rm -rf dist`, `git clean`) therefore forces a rebuild rather than a skip. This is the default `compile` script; run it from a package directory.
|
|
16
16
|
|
|
17
|
-
**What it compiles.** Entry points are `src/**/*.ts` less the directories that contain test scaffolding rather than published code: `__fixtures__/`, `__mocks__/`, `__tests__/`, and `test-utils/`. Add to that list per package with [`build.extraIgnorePatterns`](scripts.md#package-level-configuration). Ignoring a directory drops it as an _entry point_, not from the emit: The compiler still emits whatever the surviving entry points import, so a helper that production code uses is still compiled and the compiler never emits a dangling specifier in its importer. This list is deliberately not the Vitest [coverage exclusions](vitest.md#what-the-config-excludes): Helpers live in `test-utils/` precisely so that they stay covered.
|
|
17
|
+
**What it compiles.** Entry points are `src/**/*.{ts,tsx}` less the directories that contain test scaffolding rather than published code: `__fixtures__/`, `__mocks__/`, `__tests__/`, and `test-utils/`. Add to that list per package with [`build.extraIgnorePatterns`](scripts.md#package-level-configuration). Ignoring a directory drops it as an _entry point_, not from the emit: The compiler still emits whatever the surviving entry points import, so a helper that production code uses is still compiled and the compiler never emits a dangling specifier in its importer. This list is deliberately not the Vitest [coverage exclusions](vitest.md#what-the-config-excludes): Helpers live in `test-utils/` precisely so that they stay covered.
|
|
18
|
+
|
|
19
|
+
**JSX.** A `.tsx` source compiles to `.js` like any other. The package's own tsconfig `jsx` option decides the transform, such as `react-jsx`; the build does not set one.
|
|
18
20
|
|
|
19
21
|
**The build owns its output directory.** Every rebuild replaces `dist/esm` wholesale, so the directory contains exactly the current emit: A source that was deleted, or that a widened ignore set now covers, leaves nothing behind. Assets therefore belong outside the output directory, or in a `build:post` hook, which composes correctly because it runs after the output is published. Because a bare `nmr compile` does not run any hook, assets that a `build:post` step copies in are absent until a full `nmr build` restores them. An `outdir` that does not resolve inside the package is rejected rather than replaced.
|
|
20
22
|
|
|
@@ -22,7 +24,7 @@ Compile a single package's `src` tree to `dist/esm` with the TypeScript compiler
|
|
|
22
24
|
|
|
23
25
|
**What busts the cache.** The key folds each input's content and path, the emit options, the resolved TypeScript version, and a fingerprint of the nmr running the build -- its own build digest in a workspace checkout, its package version otherwise. Upgrading nmr in a consuming repo therefore rebuilds every package that nmr compiles, and an edit to nmr's own build sources rebuilds each sibling once nmr itself has been rebuilt. A build of nmr's own package folds the version rather than the digest, which is the cache entry that this build is about to write.
|
|
24
26
|
|
|
25
|
-
`typescript` is
|
|
27
|
+
`typescript` is an optional peer dependency (`>=5.7.0 <7`), needed only by `nmr-compile`; the consuming repo provides it, and `nmr-compile` run without it fails with an error naming the peer and the version that it requires. The floor is what `rewriteRelativeImportExtensions` requires; the ceiling exists because TypeScript 7 does not include a compiler API: Its root export is a version constant, so `nmr-compile` cannot run on it. Relative imports in source must use explicit `.ts` or `.tsx` extensions for them to be rewritten.
|
|
26
28
|
|
|
27
29
|
```bash
|
|
28
30
|
nmr-compile
|
|
@@ -32,9 +34,9 @@ nmr-compile
|
|
|
32
34
|
|
|
33
35
|
Format, or check the formatting of, the files that git reports for the working directory. Exactly one of `--check` and `--write` is required; a bare invocation prints usage and exits non-zero rather than defaulting to a mutation. `--write` also lists the files that it rewrote. This is what `fmt` and `fmt:check` resolve to, in both registries.
|
|
34
36
|
|
|
35
|
-
`prettier` is
|
|
37
|
+
`prettier` is a dependency of nmr, but a repo's own copy takes precedence: `nmr-fmt` resolves Prettier from the working directory first and from nmr's installation second. A repository's formatter has to be the one that its editor and pre-commit hook also run, and resolution goes through the module graph rather than `PATH`, so the copy that runs is the one that the repo declares and not whichever `prettier` happens to come first. A repo that does not install Prettier, or nmr, formats with nmr's copy; when it does not have a Prettier config either, the [house config](prettier.md) applies.
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
A repo's own copy must be Prettier 3.0.0 or later. The design requires `--ignore-path` to honour every flag rather than only the last, so that a repository-root ignore file passed alongside a package-level one is not silently dropped, and 2.x honoured only the final flag.
|
|
38
40
|
|
|
39
41
|
```bash
|
|
40
42
|
nmr-fmt --check
|
|
@@ -47,7 +49,7 @@ nmr-fmt --write packages/nmr
|
|
|
47
49
|
|
|
48
50
|
A file that git ignores is never formatted, even when named directly. Because trailing arguments are git pathspecs rather than Prettier flags, `nmr fmt:check packages/nmr` narrows the run, while an unrecognized option and a pathspec that does not match any formattable file both fail it. Running outside a git repository fails rather than reporting a clean run.
|
|
49
51
|
|
|
50
|
-
`nmr-fmt` drops paths that the index names but the filesystem does not have (a file deleted but not yet staged),
|
|
52
|
+
`nmr-fmt` drops paths that the index names but the filesystem does not have (a file deleted but not yet staged), submodule gitlinks, and symlinks whatever they point at, before calling Prettier, which rejects a symlink named explicitly. It also skips a path that it is denied access to, such as a `.envrc` that a sandbox shields, and names each one on stderr, so that a check that passes does not hide a file that it never read. Any other failure to stat a listed path fails the run, with an error that names the path.
|
|
51
53
|
|
|
52
54
|
## `ensure-prepublish-hooks`
|
|
53
55
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@williamthorsen/nmr",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Context-aware script runner for pnpm monorepos",
|
|
6
6
|
"keywords": [
|
|
@@ -77,32 +77,32 @@
|
|
|
77
77
|
"!docs/*.v*.md"
|
|
78
78
|
],
|
|
79
79
|
"dependencies": {
|
|
80
|
-
"@williamthorsen/nmr-core": "0.13.
|
|
81
|
-
"@williamthorsen/toolbelt.errors": "0.8.
|
|
80
|
+
"@williamthorsen/nmr-core": "0.13.2",
|
|
81
|
+
"@williamthorsen/toolbelt.errors": "0.8.2",
|
|
82
|
+
"@williamthorsen/toolbelt.filesystem": "0.9.2",
|
|
82
83
|
"glob": "13.0.6",
|
|
84
|
+
"prettier": "3.9.9",
|
|
83
85
|
"prettier-plugin-sh": "0.20.2",
|
|
84
|
-
"taze": "21.
|
|
86
|
+
"taze": "21.3.0"
|
|
85
87
|
},
|
|
86
88
|
"devDependencies": {
|
|
87
|
-
"@williamthorsen/toolbelt.
|
|
88
|
-
"@williamthorsen/toolbelt.
|
|
89
|
-
"@williamthorsen/toolbelt.vitest": "0.10.1",
|
|
89
|
+
"@williamthorsen/toolbelt.testing": "0.6.2",
|
|
90
|
+
"@williamthorsen/toolbelt.vitest": "0.10.2",
|
|
90
91
|
"esbuild": "0.28.2",
|
|
91
|
-
"
|
|
92
|
-
"readyup": "0.38.0",
|
|
92
|
+
"readyup": "0.40.0",
|
|
93
93
|
"tinyglobby": "0.2.17",
|
|
94
94
|
"typescript": "6.0.3",
|
|
95
95
|
"vite": "8.3.1",
|
|
96
|
-
"vitest": "4.1.11"
|
|
96
|
+
"vitest": "4.1.11",
|
|
97
|
+
"yaml": "2.9.1"
|
|
97
98
|
},
|
|
98
99
|
"peerDependencies": {
|
|
99
|
-
"prettier": ">=3.9.5 <4",
|
|
100
100
|
"typescript": ">=5.7.0 <7",
|
|
101
101
|
"vite": ">=8.0.0 <9",
|
|
102
102
|
"vitest": ">=4.0.0 <5"
|
|
103
103
|
},
|
|
104
104
|
"peerDependenciesMeta": {
|
|
105
|
-
"
|
|
105
|
+
"typescript": {
|
|
106
106
|
"optional": true
|
|
107
107
|
},
|
|
108
108
|
"vite": {
|