@williamthorsen/nmr 0.14.0 → 0.14.2
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/README.md +2 -2
- package/dist/esm/.cache +1 -1
- package/dist/esm/cli.js +11 -157
- package/dist/esm/context.d.ts +1 -1
- package/dist/esm/context.js +2 -2
- package/dist/esm/help.d.ts +1 -1
- package/dist/esm/helpers/yaml-utils.js +2 -2
- package/dist/esm/resolve-scripts.d.ts +2 -2
- package/dist/esm/resolver.d.ts +2 -2
- package/dist/esm/runCli.d.ts +12 -0
- package/dist/esm/runCli.js +169 -0
- package/dist/esm/runner.d.ts +4 -0
- package/dist/esm/runner.js +29 -27
- package/dist/esm/scripts.d.ts +2 -2
- package/dist/esm/tests/helpers/get-string-from-yaml-file.js +2 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ nmr's key feature is that the same command runs different scripts depending on w
|
|
|
45
45
|
Use `-w` to escape package context:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
# From inside packages/core, run the root check suite
|
|
48
|
+
# From inside packages/nmr-core, run the root check suite
|
|
49
49
|
nmr -w check
|
|
50
50
|
```
|
|
51
51
|
|
|
@@ -321,7 +321,7 @@ The default root `check:strict` composite includes `check:agent-files`, which ru
|
|
|
321
321
|
To expose the synced guidance to Claude Code sessions, add this include to the consuming repo's `.agents/PROJECT.md`:
|
|
322
322
|
|
|
323
323
|
```markdown
|
|
324
|
-
|
|
324
|
+
@nmr/AGENTS.md
|
|
325
325
|
```
|
|
326
326
|
|
|
327
327
|
### `sync-pnpm-version`
|
package/dist/esm/.cache
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
762ecbce6ca8e70fc4e9734eb2fab7d61a05d38cda5a77fe8d1ce2c2ad45aa85
|
package/dist/esm/cli.js
CHANGED
|
@@ -1,162 +1,16 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
1
|
import process from "node:process";
|
|
3
|
-
import {
|
|
4
|
-
import { resolveContext } from "./context.js";
|
|
5
|
-
import { generateHelp } from "./help.js";
|
|
6
|
-
import { isHookName } from "./helpers/hook-name.js";
|
|
7
|
-
import { applyDevBin, buildRootRegistry, buildWorkspaceRegistry, resolveScript } from "./resolver.js";
|
|
8
|
-
import { runCommand } from "./runner.js";
|
|
9
|
-
const VERSION = readPackageVersion(import.meta.url);
|
|
10
|
-
function shellQuote(arg) {
|
|
11
|
-
return "'" + arg.replace(/'/g, String.raw`'\''`) + "'";
|
|
12
|
-
}
|
|
13
|
-
function parseArgs(argv) {
|
|
14
|
-
const args = argv.slice(2);
|
|
15
|
-
const result = {
|
|
16
|
-
quiet: false,
|
|
17
|
-
recursive: false,
|
|
18
|
-
workspaceRoot: false,
|
|
19
|
-
help: false,
|
|
20
|
-
version: false,
|
|
21
|
-
intTest: false,
|
|
22
|
-
passthrough: []
|
|
23
|
-
};
|
|
24
|
-
let i = 0;
|
|
25
|
-
while (i < args.length) {
|
|
26
|
-
const arg = args[i];
|
|
27
|
-
if (arg === void 0) break;
|
|
28
|
-
if (arg === "-F" || arg === "--filter") {
|
|
29
|
-
i++;
|
|
30
|
-
const filterValue = args[i];
|
|
31
|
-
if (filterValue === void 0) {
|
|
32
|
-
console.error("Error: -F/--filter requires a pattern argument");
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
result.filter = filterValue;
|
|
36
|
-
i++;
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
if (arg === "-R" || arg === "--recursive") {
|
|
40
|
-
result.recursive = true;
|
|
41
|
-
i++;
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
if (arg === "-w" || arg === "--workspace-root") {
|
|
45
|
-
result.workspaceRoot = true;
|
|
46
|
-
i++;
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (arg === "-?" || arg === "--help") {
|
|
50
|
-
result.help = true;
|
|
51
|
-
i++;
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
if (arg === "-V" || arg === "--version") {
|
|
55
|
-
result.version = true;
|
|
56
|
-
i++;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (arg === "-q" || arg === "--quiet") {
|
|
60
|
-
result.quiet = true;
|
|
61
|
-
i++;
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
if (arg === "--int-test") {
|
|
65
|
-
result.intTest = true;
|
|
66
|
-
i++;
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
result.command = arg;
|
|
70
|
-
result.passthrough = args.slice(i + 1);
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
return result;
|
|
74
|
-
}
|
|
75
|
-
async function main() {
|
|
76
|
-
const parsed = parseArgs(process.argv);
|
|
77
|
-
if (parsed.version) {
|
|
78
|
-
console.info(VERSION);
|
|
79
|
-
process.exit(0);
|
|
80
|
-
}
|
|
81
|
-
const context = await resolveContext();
|
|
82
|
-
const useRoot = parsed.workspaceRoot || context.isRoot;
|
|
83
|
-
const packageDir = useRoot ? context.monorepoRoot : context.packageDir ?? context.monorepoRoot;
|
|
84
|
-
if (parsed.help || !parsed.command) {
|
|
85
|
-
console.info(generateHelp(context.config, packageDir, useRoot));
|
|
86
|
-
process.exit(0);
|
|
87
|
-
}
|
|
88
|
-
const { command } = parsed;
|
|
89
|
-
const passthrough = parsed.passthrough.length > 0 ? " " + parsed.passthrough.map(shellQuote).join(" ") : "";
|
|
90
|
-
const runOptions = { quiet: parsed.quiet };
|
|
91
|
-
if (parsed.filter) {
|
|
92
|
-
const delegateCmd = `pnpm --filter ${shellQuote(parsed.filter)} exec nmr ${command}${passthrough}`;
|
|
93
|
-
const code2 = runCommand(delegateCmd, context.monorepoRoot, runOptions);
|
|
94
|
-
process.exit(code2);
|
|
95
|
-
}
|
|
96
|
-
if (parsed.recursive) {
|
|
97
|
-
process.env.NMR_RUN_IF_PRESENT = "1";
|
|
98
|
-
const delegateCmd = `pnpm --recursive exec nmr ${command}${passthrough}`;
|
|
99
|
-
const code2 = runCommand(delegateCmd, context.monorepoRoot, runOptions);
|
|
100
|
-
process.exit(code2);
|
|
101
|
-
}
|
|
102
|
-
const registry = useRoot ? buildRootRegistry(context.config) : buildWorkspaceRegistry(context.config, parsed.intTest);
|
|
103
|
-
const resolved = resolveScript(command, registry, packageDir, parsed.workspaceRoot);
|
|
104
|
-
if (!resolved) {
|
|
105
|
-
if (process.env.NMR_RUN_IF_PRESENT === "1") {
|
|
106
|
-
process.exit(0);
|
|
107
|
-
}
|
|
108
|
-
console.error(`Unknown command: ${command}`);
|
|
109
|
-
process.exit(1);
|
|
110
|
-
}
|
|
111
|
-
const skipExitCode = handleSkipMessage(resolved.command, packageDir, parsed.quiet);
|
|
112
|
-
if (skipExitCode !== void 0) {
|
|
113
|
-
process.exit(skipExitCode);
|
|
114
|
-
}
|
|
115
|
-
if (resolved.source === "package" && !parsed.quiet && registry[command] !== void 0) {
|
|
116
|
-
console.info(`\u{1F4E6} ${path.basename(packageDir)}: Using override script: ${resolved.command}`);
|
|
117
|
-
}
|
|
118
|
-
const substitutedCommand = applyDevBin(resolved.command, context.config.devBin, context.monorepoRoot);
|
|
119
|
-
const mainCommand = substitutedCommand + passthrough;
|
|
120
|
-
const isHookInvocation = isHookName(command);
|
|
121
|
-
const fullCommand = isHookInvocation ? mainCommand : wrapWithHooks(command, mainCommand, registry, packageDir, parsed.workspaceRoot);
|
|
122
|
-
const code = runCommand(fullCommand, void 0, runOptions);
|
|
123
|
-
process.exit(code);
|
|
124
|
-
}
|
|
125
|
-
function handleSkipMessage(resolvedCommand, packageDir, quiet) {
|
|
126
|
-
if (resolvedCommand === "") {
|
|
127
|
-
if (!quiet) {
|
|
128
|
-
console.info(`\u26D4 ${path.basename(packageDir)}: Override script is defined but empty. Skipping.`);
|
|
129
|
-
}
|
|
130
|
-
return 0;
|
|
131
|
-
}
|
|
132
|
-
if (resolvedCommand === ":") {
|
|
133
|
-
if (!quiet) {
|
|
134
|
-
console.info(`\u26D4 ${path.basename(packageDir)}: Override script is a no-op. Skipping.`);
|
|
135
|
-
}
|
|
136
|
-
return 0;
|
|
137
|
-
}
|
|
138
|
-
return void 0;
|
|
139
|
-
}
|
|
140
|
-
function wrapWithHooks(command, mainCommand, registry, packageDir, workspaceRoot) {
|
|
141
|
-
const segments = [];
|
|
142
|
-
const flag = workspaceRoot ? "-w " : "";
|
|
143
|
-
if (hasRunnableHook(`${command}:pre`, registry, packageDir, workspaceRoot)) {
|
|
144
|
-
segments.push(`nmr ${flag}${command}:pre`);
|
|
145
|
-
}
|
|
146
|
-
segments.push(mainCommand);
|
|
147
|
-
if (hasRunnableHook(`${command}:post`, registry, packageDir, workspaceRoot)) {
|
|
148
|
-
segments.push(`nmr ${flag}${command}:post`);
|
|
149
|
-
}
|
|
150
|
-
return segments.join(" && ");
|
|
151
|
-
}
|
|
152
|
-
function hasRunnableHook(hookName, registry, packageDir, workspaceRoot) {
|
|
153
|
-
const resolved = resolveScript(hookName, registry, packageDir, workspaceRoot);
|
|
154
|
-
if (!resolved) return false;
|
|
155
|
-
return resolved.command !== "" && resolved.command !== ":";
|
|
156
|
-
}
|
|
2
|
+
import { runCli } from "./runCli.js";
|
|
157
3
|
try {
|
|
158
|
-
await
|
|
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.exit(exitCode);
|
|
159
12
|
} catch (error) {
|
|
160
|
-
|
|
13
|
+
process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}
|
|
14
|
+
`);
|
|
161
15
|
process.exit(1);
|
|
162
16
|
}
|
package/dist/esm/context.d.ts
CHANGED
package/dist/esm/context.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import
|
|
3
|
+
import { parse } from "yaml";
|
|
4
4
|
import { loadConfig } from "./config.js";
|
|
5
5
|
import { isObject } from "./helpers/type-guards.js";
|
|
6
6
|
function findMonorepoRoot(startDir) {
|
|
@@ -19,7 +19,7 @@ function findMonorepoRoot(startDir) {
|
|
|
19
19
|
function getWorkspacePackageDirs(monorepoRoot) {
|
|
20
20
|
const workspaceFile = path.join(monorepoRoot, "pnpm-workspace.yaml");
|
|
21
21
|
const content = readFileSync(workspaceFile, "utf8");
|
|
22
|
-
const parsed =
|
|
22
|
+
const parsed = parse(content);
|
|
23
23
|
const packages = getPackagesFromParsedYaml(parsed);
|
|
24
24
|
if (!packages) {
|
|
25
25
|
return [];
|
package/dist/esm/help.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { NmrConfig } from './config.
|
|
1
|
+
import type { NmrConfig } from './config.ts';
|
|
2
2
|
export declare function generateHelp(config: NmrConfig, packageDir: string | undefined, useRoot: boolean): string;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import
|
|
2
|
+
import { parse } from "yaml";
|
|
3
3
|
function readYamlFile(filepath) {
|
|
4
4
|
try {
|
|
5
5
|
const content = readFileSync(filepath, "utf8");
|
|
6
|
-
return
|
|
6
|
+
return parse(content);
|
|
7
7
|
} catch (error) {
|
|
8
8
|
throw new Error(`Failed to read YAML file: ${filepath}
|
|
9
9
|
${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ScriptRegistry } from './default-scripts.
|
|
2
|
-
export type { ScriptRegistry, ScriptValue } from './default-scripts.
|
|
1
|
+
import type { ScriptRegistry } from './default-scripts.ts';
|
|
2
|
+
export type { ScriptRegistry, ScriptValue } from './default-scripts.ts';
|
|
3
3
|
export declare function getDefaultWorkspaceScripts(useIntTests: boolean): ScriptRegistry;
|
|
4
4
|
export declare function getDefaultRootScripts(): ScriptRegistry;
|
package/dist/esm/resolver.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { NmrConfig } from './config.
|
|
2
|
-
import type { ScriptRegistry, ScriptValue } from './resolve-scripts.
|
|
1
|
+
import type { NmrConfig } from './config.ts';
|
|
2
|
+
import type { ScriptRegistry, ScriptValue } from './resolve-scripts.ts';
|
|
3
3
|
export declare function applyDevBin(command: string, devBin: Record<string, string> | undefined, monorepoRoot: string): string;
|
|
4
4
|
export interface ResolvedScript {
|
|
5
5
|
command: string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Writable } from 'node:stream';
|
|
2
|
+
export interface RunCliOptions {
|
|
3
|
+
args: string[];
|
|
4
|
+
cwd: string;
|
|
5
|
+
env: NodeJS.ProcessEnv;
|
|
6
|
+
stdout: Writable;
|
|
7
|
+
stderr: Writable;
|
|
8
|
+
}
|
|
9
|
+
export interface RunCliResult {
|
|
10
|
+
exitCode: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function runCli(options: RunCliOptions): Promise<RunCliResult>;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { readPackageVersion } from "@williamthorsen/nmr-core";
|
|
3
|
+
import { resolveContext } from "./context.js";
|
|
4
|
+
import { generateHelp } from "./help.js";
|
|
5
|
+
import { isHookName } from "./helpers/hook-name.js";
|
|
6
|
+
import { applyDevBin, buildRootRegistry, buildWorkspaceRegistry, resolveScript } from "./resolver.js";
|
|
7
|
+
import { runCommand } from "./runner.js";
|
|
8
|
+
const VERSION = readPackageVersion(import.meta.url);
|
|
9
|
+
async function runCli(options) {
|
|
10
|
+
const { args, cwd, env, stdout, stderr } = options;
|
|
11
|
+
const parseResult = parseArgs(args);
|
|
12
|
+
if (!parseResult.ok) {
|
|
13
|
+
stderr.write(`${parseResult.error}
|
|
14
|
+
`);
|
|
15
|
+
return { exitCode: 1 };
|
|
16
|
+
}
|
|
17
|
+
const { parsed } = parseResult;
|
|
18
|
+
if (parsed.version) {
|
|
19
|
+
stdout.write(`${VERSION}
|
|
20
|
+
`);
|
|
21
|
+
return { exitCode: 0 };
|
|
22
|
+
}
|
|
23
|
+
const context = await resolveContext(cwd);
|
|
24
|
+
const useRoot = parsed.workspaceRoot || context.isRoot;
|
|
25
|
+
const packageDir = useRoot ? context.monorepoRoot : context.packageDir ?? context.monorepoRoot;
|
|
26
|
+
if (parsed.help || !parsed.command) {
|
|
27
|
+
stdout.write(`${generateHelp(context.config, packageDir, useRoot)}
|
|
28
|
+
`);
|
|
29
|
+
return { exitCode: 0 };
|
|
30
|
+
}
|
|
31
|
+
const { command } = parsed;
|
|
32
|
+
const passthrough = parsed.passthrough.length > 0 ? " " + parsed.passthrough.map(shellQuote).join(" ") : "";
|
|
33
|
+
const runOptions = { quiet: parsed.quiet, stdout, stderr, env };
|
|
34
|
+
if (parsed.filter) {
|
|
35
|
+
const delegateCmd = `pnpm --filter ${shellQuote(parsed.filter)} exec nmr ${command}${passthrough}`;
|
|
36
|
+
const exitCode2 = runCommand(delegateCmd, context.monorepoRoot, runOptions);
|
|
37
|
+
return { exitCode: exitCode2 };
|
|
38
|
+
}
|
|
39
|
+
if (parsed.recursive) {
|
|
40
|
+
const childEnv = { ...env, NMR_RUN_IF_PRESENT: "1" };
|
|
41
|
+
const delegateCmd = `pnpm --recursive exec nmr ${command}${passthrough}`;
|
|
42
|
+
const exitCode2 = runCommand(delegateCmd, context.monorepoRoot, { ...runOptions, env: childEnv });
|
|
43
|
+
return { exitCode: exitCode2 };
|
|
44
|
+
}
|
|
45
|
+
const registry = useRoot ? buildRootRegistry(context.config) : buildWorkspaceRegistry(context.config, parsed.intTest);
|
|
46
|
+
const resolved = resolveScript(command, registry, packageDir, parsed.workspaceRoot);
|
|
47
|
+
if (!resolved) {
|
|
48
|
+
if (env.NMR_RUN_IF_PRESENT === "1") {
|
|
49
|
+
return { exitCode: 0 };
|
|
50
|
+
}
|
|
51
|
+
stderr.write(`Unknown command: ${command}
|
|
52
|
+
`);
|
|
53
|
+
return { exitCode: 1 };
|
|
54
|
+
}
|
|
55
|
+
const skipExitCode = handleSkipMessage(resolved.command, packageDir, parsed.quiet, stdout);
|
|
56
|
+
if (skipExitCode !== void 0) {
|
|
57
|
+
return { exitCode: skipExitCode };
|
|
58
|
+
}
|
|
59
|
+
if (resolved.source === "package" && !parsed.quiet && registry[command] !== void 0) {
|
|
60
|
+
stdout.write(`\u{1F4E6} ${path.basename(packageDir)}: Using override script: ${resolved.command}
|
|
61
|
+
`);
|
|
62
|
+
}
|
|
63
|
+
const substitutedCommand = applyDevBin(resolved.command, context.config.devBin, context.monorepoRoot);
|
|
64
|
+
const mainCommand = substitutedCommand + passthrough;
|
|
65
|
+
const isHookInvocation = isHookName(command);
|
|
66
|
+
const fullCommand = isHookInvocation ? mainCommand : wrapWithHooks(command, mainCommand, registry, packageDir, parsed.workspaceRoot);
|
|
67
|
+
const exitCode = runCommand(fullCommand, cwd, runOptions);
|
|
68
|
+
return { exitCode };
|
|
69
|
+
}
|
|
70
|
+
function parseArgs(args) {
|
|
71
|
+
const parsed = {
|
|
72
|
+
quiet: false,
|
|
73
|
+
recursive: false,
|
|
74
|
+
workspaceRoot: false,
|
|
75
|
+
help: false,
|
|
76
|
+
version: false,
|
|
77
|
+
intTest: false,
|
|
78
|
+
passthrough: []
|
|
79
|
+
};
|
|
80
|
+
let i = 0;
|
|
81
|
+
while (i < args.length) {
|
|
82
|
+
const arg = args[i];
|
|
83
|
+
if (arg === void 0) break;
|
|
84
|
+
if (arg === "-F" || arg === "--filter") {
|
|
85
|
+
i++;
|
|
86
|
+
const filterValue = args[i];
|
|
87
|
+
if (filterValue === void 0) {
|
|
88
|
+
return { ok: false, error: "Error: -F/--filter requires a pattern argument" };
|
|
89
|
+
}
|
|
90
|
+
parsed.filter = filterValue;
|
|
91
|
+
i++;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (arg === "-R" || arg === "--recursive") {
|
|
95
|
+
parsed.recursive = true;
|
|
96
|
+
i++;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (arg === "-w" || arg === "--workspace-root") {
|
|
100
|
+
parsed.workspaceRoot = true;
|
|
101
|
+
i++;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (arg === "-?" || arg === "--help") {
|
|
105
|
+
parsed.help = true;
|
|
106
|
+
i++;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (arg === "-V" || arg === "--version") {
|
|
110
|
+
parsed.version = true;
|
|
111
|
+
i++;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (arg === "-q" || arg === "--quiet") {
|
|
115
|
+
parsed.quiet = true;
|
|
116
|
+
i++;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (arg === "--int-test") {
|
|
120
|
+
parsed.intTest = true;
|
|
121
|
+
i++;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
parsed.command = arg;
|
|
125
|
+
parsed.passthrough = args.slice(i + 1);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
return { ok: true, parsed };
|
|
129
|
+
}
|
|
130
|
+
function handleSkipMessage(resolvedCommand, packageDir, quiet, stdout) {
|
|
131
|
+
if (resolvedCommand === "") {
|
|
132
|
+
if (!quiet) {
|
|
133
|
+
stdout.write(`\u26D4 ${path.basename(packageDir)}: Override script is defined but empty. Skipping.
|
|
134
|
+
`);
|
|
135
|
+
}
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
if (resolvedCommand === ":") {
|
|
139
|
+
if (!quiet) {
|
|
140
|
+
stdout.write(`\u26D4 ${path.basename(packageDir)}: Override script is a no-op. Skipping.
|
|
141
|
+
`);
|
|
142
|
+
}
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
return void 0;
|
|
146
|
+
}
|
|
147
|
+
function wrapWithHooks(command, mainCommand, registry, packageDir, workspaceRoot) {
|
|
148
|
+
const segments = [];
|
|
149
|
+
const flag = workspaceRoot ? "-w " : "";
|
|
150
|
+
if (hasRunnableHook(`${command}:pre`, registry, packageDir, workspaceRoot)) {
|
|
151
|
+
segments.push(`nmr ${flag}${command}:pre`);
|
|
152
|
+
}
|
|
153
|
+
segments.push(mainCommand);
|
|
154
|
+
if (hasRunnableHook(`${command}:post`, registry, packageDir, workspaceRoot)) {
|
|
155
|
+
segments.push(`nmr ${flag}${command}:post`);
|
|
156
|
+
}
|
|
157
|
+
return segments.join(" && ");
|
|
158
|
+
}
|
|
159
|
+
function hasRunnableHook(hookName, registry, packageDir, workspaceRoot) {
|
|
160
|
+
const resolved = resolveScript(hookName, registry, packageDir, workspaceRoot);
|
|
161
|
+
if (!resolved) return false;
|
|
162
|
+
return resolved.command !== "" && resolved.command !== ":";
|
|
163
|
+
}
|
|
164
|
+
function shellQuote(arg) {
|
|
165
|
+
return "'" + arg.replace(/'/g, String.raw`'\''`) + "'";
|
|
166
|
+
}
|
|
167
|
+
export {
|
|
168
|
+
runCli
|
|
169
|
+
};
|
package/dist/esm/runner.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import type { Writable } from 'node:stream';
|
|
1
2
|
export interface RunCommandOptions {
|
|
2
3
|
quiet?: boolean;
|
|
4
|
+
stdout?: Writable;
|
|
5
|
+
stderr?: Writable;
|
|
6
|
+
env?: NodeJS.ProcessEnv;
|
|
3
7
|
}
|
|
4
8
|
export declare function runCommand(command: string, cwd?: string, options?: RunCommandOptions): number;
|
package/dist/esm/runner.js
CHANGED
|
@@ -1,37 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
2
|
import process from "node:process";
|
|
3
3
|
function runCommand(command, cwd, options) {
|
|
4
4
|
const quiet = options?.quiet === true;
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
5
|
+
const stdout = options?.stdout ?? process.stdout;
|
|
6
|
+
const stderr = options?.stderr ?? process.stderr;
|
|
7
|
+
const env = options?.env ?? process.env;
|
|
8
|
+
const stdoutChannel = quiet ? "pipe" : streamFdOrPipe(stdout);
|
|
9
|
+
const stderrChannel = quiet ? "pipe" : streamFdOrPipe(stderr);
|
|
10
|
+
const result = spawnSync(command, [], {
|
|
11
|
+
shell: true,
|
|
12
|
+
stdio: ["inherit", stdoutChannel, stderrChannel],
|
|
13
|
+
cwd,
|
|
14
|
+
env
|
|
15
|
+
});
|
|
16
|
+
if (result.error) {
|
|
17
|
+
stderr.write(`${result.error.message}
|
|
18
|
+
`);
|
|
19
19
|
return 1;
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (Buffer.isBuffer(stdout) && stdout.length > 0) {
|
|
26
|
-
process.stderr.write(stdout);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
if ("stderr" in error) {
|
|
30
|
-
const { stderr } = error;
|
|
31
|
-
if (Buffer.isBuffer(stderr) && stderr.length > 0) {
|
|
32
|
-
process.stderr.write(stderr);
|
|
21
|
+
if (quiet) {
|
|
22
|
+
if (result.status !== 0) {
|
|
23
|
+
writeBuffer(result.stdout, stderr);
|
|
24
|
+
writeBuffer(result.stderr, stderr);
|
|
33
25
|
}
|
|
26
|
+
} else {
|
|
27
|
+
if (stdoutChannel === "pipe") writeBuffer(result.stdout, stdout);
|
|
28
|
+
if (stderrChannel === "pipe") writeBuffer(result.stderr, stderr);
|
|
34
29
|
}
|
|
30
|
+
return result.status ?? 1;
|
|
31
|
+
}
|
|
32
|
+
function streamFdOrPipe(stream) {
|
|
33
|
+
return "fd" in stream && typeof stream.fd === "number" ? stream.fd : "pipe";
|
|
34
|
+
}
|
|
35
|
+
function writeBuffer(buffer, dest) {
|
|
36
|
+
if (buffer.length > 0) dest.write(buffer);
|
|
35
37
|
}
|
|
36
38
|
export {
|
|
37
39
|
runCommand
|
package/dist/esm/scripts.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { ScriptRegistry, ScriptValue } from './default-scripts.
|
|
2
|
-
export { getDefaultRootScripts, getDefaultWorkspaceScripts } from './resolve-scripts.
|
|
1
|
+
export type { ScriptRegistry, ScriptValue } from './default-scripts.ts';
|
|
2
|
+
export { getDefaultRootScripts, getDefaultWorkspaceScripts } from './resolve-scripts.ts';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
-
import
|
|
3
|
+
import { parse } from "yaml";
|
|
4
4
|
import { getValueAtPathOrThrow } from "./get-value-at-path.js";
|
|
5
5
|
async function getStringFromYamlFile(filePath, keyPath, label) {
|
|
6
6
|
const raw = await fs.promises.readFile(filePath, { encoding: "utf8" });
|
|
7
|
-
const parsed =
|
|
7
|
+
const parsed = parse(raw);
|
|
8
8
|
assert.ok(parsed, `YAML content not found in ${filePath}`);
|
|
9
9
|
const value = getValueAtPathOrThrow(parsed, keyPath);
|
|
10
10
|
assert.ok(typeof value === "string" && value.length > 0, `${label} not found at ${keyPath}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@williamthorsen/nmr",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Context-aware script runner for PNPM monorepos",
|
|
6
6
|
"keywords": [
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"jiti": "2.7.0",
|
|
43
|
-
"
|
|
43
|
+
"yaml": "2.9.0",
|
|
44
44
|
"zod": "4.4.3",
|
|
45
|
-
"@williamthorsen/nmr-core": "0.3.
|
|
45
|
+
"@williamthorsen/nmr-core": "0.3.2"
|
|
46
46
|
},
|
|
47
47
|
"publishConfig": {
|
|
48
48
|
"access": "public"
|