@williamthorsen/nmr 0.46.0 → 0.46.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.
@@ -1,4 +1,21 @@
1
1
  [
2
+ {
3
+ "version": "0.46.1",
4
+ "date": "2026-10-13",
5
+ "sections": [
6
+ {
7
+ "title": "🐛 Bug fixes",
8
+ "audience": "all",
9
+ "items": [
10
+ {
11
+ "description": "Fixes the issue that a script run by bare `nmr` inside a package failed with `command not found` on a binary installed only in that package, and found the root's binaries, such as `eslint` and `tsgo`, only when the shell's `PATH` already contained the root's `node_modules/.bin`. (#1039)",
12
+ "hash": "64cde10b969329630577e2f9011d0a6cd480e009",
13
+ "entry": 1
14
+ }
15
+ ]
16
+ }
17
+ ]
18
+ },
2
19
  {
3
20
  "version": "0.46.0",
4
21
  "date": "2026-10-13",
@@ -8939,7 +8939,7 @@ function formatPaths(paths) {
8939
8939
  ${paths.map((path7) => ` ${path7}`).join("\n")}`;
8940
8940
  }
8941
8941
  function getMinVersion() {
8942
- const pickedFields = { "version": "0.46.0" };
8942
+ const pickedFields = { "version": "0.46.1" };
8943
8943
  if (typeof pickedFields["version"] !== "string") {
8944
8944
  throw new TypeError("nmr/package.json: 'version' must be a string");
8945
8945
  }
@@ -86,7 +86,7 @@
86
86
  "path": "kits/default.ts"
87
87
  },
88
88
  {
89
- "hash": "106526b9",
89
+ "hash": "e03b7529",
90
90
  "kind": "inline",
91
91
  "path": "../package.json",
92
92
  "paths": [
@@ -189,7 +189,7 @@
189
189
  "readyupVersion": "0.40.0",
190
190
  "source": "kits/default.ts",
191
191
  "sourceHash": "aaeb1a84",
192
- "targetHash": "92d23b08"
192
+ "targetHash": "dbfec446"
193
193
  }
194
194
  ]
195
195
  }
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 0.46.1 — 2026-10-13
6
+
7
+ ### 🐛 Bug fixes
8
+
9
+ - Fixes the issue that a script run by bare `nmr` inside a package failed with `command not found` on a binary installed only in that package, and found the root's binaries, such as `eslint` and `tsgo`, only when the shell's `PATH` already contained the root's `node_modules/.bin`. (#1039)
10
+
5
11
  ## 0.46.0 — 2026-10-13
6
12
 
7
13
  ### 🎉 Features
package/README.md CHANGED
@@ -5,13 +5,11 @@
5
5
  Context-aware script runner for pnpm monorepos. Includes an `nmr` (node-monorepo run) binary that provides centralized, consistent script execution across workspace packages and the monorepo root.
6
6
 
7
7
  <!-- section:release-notes -->
8
- ## Release notes — v0.46.0 (2026-10-13)
8
+ ## Release notes — v0.46.1 (2026-10-13)
9
9
 
10
- ### 🎉 Features
10
+ ### 🐛 Bug fixes
11
11
 
12
- - Adds a `packageMaturityPeriod` option to the config built with `defineConfig` from `@williamthorsen/nmr/taze`, which sets, for packages whose names match a `*` pattern, how many days after a release `nmr upgrade` waits before proposing it, in place of the shared `maturityPeriod`. (#1036)
13
- - Defines an embedded test, which runs an engine installed by the package, such as PGlite (Postgres compiled to WebAssembly), inside the test process, and places it in the `tool` tier rather than `unit` in `docs/vitest.md` and the `nmr` rulebook. (#1037)
14
- - Redefines a package's `peerDependencies` in `docs/vitest.md` as candidate tools rather than as the `tool` boundary, so a test is `tool` only when it makes a peer do work, and a test that imports a peer only for its constants, such as `AST_NODE_TYPES`, stays `unit`. (#1037)
12
+ - Fixes the issue that a script run by bare `nmr` inside a package failed with `command not found` on a binary installed only in that package, and found the root's binaries, such as `eslint` and `tsgo`, only when the shell's `PATH` already contained the root's `node_modules/.bin`. (#1039)
15
13
  <!-- /section:release-notes -->
16
14
 
17
15
  ## Installation
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Returns the environment in which a scope's commands run: Its `PATH` leads with the anchor's `node_modules/.bin`
3
+ * and then the monorepo root's, as under `pnpm run`, followed by the inherited `PATH`.
4
+ *
5
+ * A directory that the inherited `PATH` already contains is left where it is, so that an nmr run nested in another
6
+ * does not lengthen `PATH`.
7
+ */
8
+ export declare function prependBinDirs(options: {
9
+ anchorDir: string;
10
+ env: NodeJS.ProcessEnv;
11
+ monorepoRoot: string;
12
+ }): NodeJS.ProcessEnv;
@@ -0,0 +1,21 @@
1
+ import path from 'node:path';
2
+ export function prependBinDirs(options) {
3
+ const { anchorDir, env, monorepoRoot } = options;
4
+ const inherited = env['PATH'] ?? '';
5
+ const seen = new Set(inherited
6
+ .split(path.delimiter)
7
+ .filter((entry) => entry !== '')
8
+ .map((entry) => path.resolve(entry)));
9
+ const additions = [];
10
+ for (const dir of [anchorDir, monorepoRoot]) {
11
+ const binDir = path.resolve(dir, 'node_modules', '.bin');
12
+ if (!seen.has(binDir)) {
13
+ seen.add(binDir);
14
+ additions.push(binDir);
15
+ }
16
+ }
17
+ if (additions.length === 0) {
18
+ return env;
19
+ }
20
+ return { ...env, PATH: [...additions, ...(inherited === '' ? [] : [inherited])].join(path.delimiter) };
21
+ }
@@ -1,6 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { describeInvalidOutputStyle, formatGlyphLine, reportError, resolveSelfVersion, STATUS_GLYPHS, } from '@williamthorsen/nmr-core';
3
3
  import { describeError } from '@williamthorsen/toolbelt.errors';
4
+ import { prependBinDirs } from "./bin-path.js";
4
5
  import { certifyRetention, computeCacheKey, computeRetentionKey, CURRENT_RUNTIME, encodeTreeSnapshot, findStaleBuildOutput, formatMisplacedNoCacheWarning, isCacheableCommand, NO_CACHE_ENV_VAR, readBuildOutputState, readCheckCacheEntry, recordTranscript, resolveRunId, resolveTreeSnapshot, RUN_ID_ENV_VAR, TREE_SNAPSHOT_ENV_VAR, writeCheckCacheEntry, writeDebugNote, } from "./check-cache.js";
5
6
  import { resolveContext } from "./context.js";
6
7
  import { enterChain } from "./entry-chain.js";
@@ -163,7 +164,12 @@ async function planRun(invocation) {
163
164
  ? runOptions
164
165
  : {
165
166
  ...runOptions,
166
- env: enterChain({ anchorDir, command, env: childEnv, monorepoRoot: context.monorepoRoot }),
167
+ env: enterChain({
168
+ anchorDir,
169
+ command,
170
+ env: prependBinDirs({ anchorDir, env: childEnv, monorepoRoot: context.monorepoRoot }),
171
+ monorepoRoot: context.monorepoRoot,
172
+ }),
167
173
  };
168
174
  const chain = assembleChain({ invocation, registry, resolvedScript, snapshot });
169
175
  if (chain.settled) {
package/docs/scripts.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Scripts and configuration
2
2
 
3
- How nmr resolves a command through its three tiers, how script values compose, how hooks wrap a command, how to configure scripts and builds, and what the default commands do beyond what `nmr --help` prints.
3
+ How nmr resolves a command through its three tiers, how script values compose, which binaries a command finds on `PATH`, how hooks wrap a command, how to configure scripts and builds, and what the default commands do beyond what `nmr --help` prints.
4
4
 
5
5
  ## Three-tier override system
6
6
 
@@ -83,6 +83,10 @@ nmr recognizes `nmr` in command position: at the start of a step, after an unquo
83
83
 
84
84
  A [self-referential entry](#three-tier-override-system), in `package.json` or in `.config/nmr.config.ts`, is the shape that nmr cannot run as written, and it is rejected rather than reported. The same recognition finds it, so the same residuals apply: What goes unrecognized there re-enters nmr once, and the run then fails with an error naming the command and the directory that it re-entered.
85
85
 
86
+ ### Binaries on `PATH`
87
+
88
+ Every command that nmr runs, whatever its tier, finds the binaries that `pnpm run` would find. Inside a package, its `PATH` leads with the package's `node_modules/.bin` and then the root's, followed by the `PATH` from which nmr was started; at the root, it leads with the root's `node_modules/.bin`. A script can therefore invoke a binary installed only in its own package by bare name, however nmr was started: bare `nmr` through direnv, `pnpm exec nmr`, or `nmr -F <pkg>` from the root. A directory that `PATH` already contains is not added again, so a nested nmr run does not lengthen it.
89
+
86
90
  ## Pre and post hooks
87
91
 
88
92
  The wrap rule and a repo-wide example are in the [README](../README.md#pre-and-post-hooks).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@williamthorsen/nmr",
3
- "version": "0.46.0",
3
+ "version": "0.46.1",
4
4
  "private": false,
5
5
  "description": "Context-aware script runner for pnpm monorepos",
6
6
  "keywords": [