@williamthorsen/nmr-core 0.3.0 → 0.3.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.
Files changed (43) hide show
  1. package/README.md +1 -9
  2. package/dist/esm/.cache +1 -1
  3. package/dist/esm/index.d.ts +7 -6
  4. package/dist/esm/index.js +2 -0
  5. package/dist/esm/readPackageVersion.d.ts +1 -0
  6. package/dist/esm/readPackageVersion.js +15 -0
  7. package/package.json +3 -3
  8. package/dist/esm/cli-report-overrides.d.ts +0 -1
  9. package/dist/esm/cli-report-overrides.js +0 -9
  10. package/dist/esm/cli-sync-pnpm-version.d.ts +0 -1
  11. package/dist/esm/cli-sync-pnpm-version.js +0 -9
  12. package/dist/esm/cli.d.ts +0 -1
  13. package/dist/esm/cli.js +0 -110
  14. package/dist/esm/commands/report-overrides.d.ts +0 -1
  15. package/dist/esm/commands/report-overrides.js +0 -15
  16. package/dist/esm/commands/sync-pnpm-version.d.ts +0 -3
  17. package/dist/esm/commands/sync-pnpm-version.js +0 -42
  18. package/dist/esm/config.d.ts +0 -6
  19. package/dist/esm/config.js +0 -21
  20. package/dist/esm/context.d.ts +0 -11
  21. package/dist/esm/context.js +0 -84
  22. package/dist/esm/help.d.ts +0 -2
  23. package/dist/esm/help.js +0 -31
  24. package/dist/esm/helpers/code-quality-pnpm-action.d.ts +0 -12
  25. package/dist/esm/helpers/code-quality-pnpm-action.js +0 -17
  26. package/dist/esm/helpers/package-json.d.ts +0 -11
  27. package/dist/esm/helpers/package-json.js +0 -45
  28. package/dist/esm/helpers/type-guards.d.ts +0 -1
  29. package/dist/esm/helpers/type-guards.js +0 -6
  30. package/dist/esm/helpers/yaml-utils.d.ts +0 -1
  31. package/dist/esm/helpers/yaml-utils.js +0 -14
  32. package/dist/esm/registries.d.ts +0 -5
  33. package/dist/esm/registries.js +0 -67
  34. package/dist/esm/resolver.d.ts +0 -11
  35. package/dist/esm/resolver.js +0 -64
  36. package/dist/esm/runner.d.ts +0 -4
  37. package/dist/esm/runner.js +0 -38
  38. package/dist/esm/tests/consistency.d.ts +0 -1
  39. package/dist/esm/tests/consistency.js +0 -70
  40. package/dist/esm/tests/helpers/get-runtime-version-from-asdf.d.ts +0 -1
  41. package/dist/esm/tests/helpers/get-runtime-version-from-asdf.js +0 -15
  42. package/dist/esm/tests/helpers/get-value-at-path.d.ts +0 -1
  43. package/dist/esm/tests/helpers/get-value-at-path.js +0 -34
package/README.md CHANGED
@@ -4,15 +4,7 @@ Shared utilities for node-monorepo-tools packages.
4
4
 
5
5
  This package serves as the shared library foundation for the monorepo. For the nmr CLI tool, see [`@williamthorsen/nmr`](../nmr).
6
6
 
7
- <!-- section:release-notes -->
8
- ## Release notes — v0.3.0 (2026-04-23)
9
-
10
- ### Features
11
-
12
- - Scaffold audit.yaml workflow from audit-deps init (#277)
13
-
14
- Adds GitHub Actions workflow scaffolding to `audit-deps init`. Running the command now writes both `.config/audit-deps.config.json` and `.github/workflows/audit.yaml` in the target repo, so that consumers no longer have to copy the canonical caller workflow by hand from this repo. The workflow content is shipped as a bundled template that ships to npm, and the repo's own workflow is kept byte-identical to that template via a consistency test — the canonical workflow cannot silently drift from what is published.
15
- <!-- /section:release-notes -->
7
+ <!-- section:release-notes --><!-- /section:release-notes -->
16
8
 
17
9
  ## Installation
18
10
 
package/dist/esm/.cache CHANGED
@@ -1 +1 @@
1
- 45293b659d2a5fd6eb962de2df0c6baec80dcb02ed10f63893831df600a7a39b
1
+ 1dc24071a091c2b879e7ff50fa75a058fc98715554757f75dc49184e781ea464
@@ -1,7 +1,8 @@
1
1
  export declare const PACKAGE_NAME = "@williamthorsen/nmr-core";
2
- export { findPackageRoot } from './findPackageRoot.js';
3
- export type { FlagDefinition, FlagSchema, ParsedArgs, ParsedFlags } from './parseArgs.js';
4
- export { parseArgs, translateParseError } from './parseArgs.js';
5
- export { printError, printSkip, printStep, printSuccess, reportWriteResult } from './terminal.js';
6
- export type { WriteOutcome, WriteResult } from './writeFileWithCheck.js';
7
- export { writeFileWithCheck } from './writeFileWithCheck.js';
2
+ export { findPackageRoot } from './findPackageRoot.ts';
3
+ export type { FlagDefinition, FlagSchema, ParsedArgs, ParsedFlags } from './parseArgs.ts';
4
+ export { parseArgs, translateParseError } from './parseArgs.ts';
5
+ export { readPackageVersion } from './readPackageVersion.ts';
6
+ export { printError, printSkip, printStep, printSuccess, reportWriteResult } from './terminal.ts';
7
+ export type { WriteOutcome, WriteResult } from './writeFileWithCheck.ts';
8
+ export { writeFileWithCheck } from './writeFileWithCheck.ts';
package/dist/esm/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const PACKAGE_NAME = "@williamthorsen/nmr-core";
2
2
  import { findPackageRoot } from "./findPackageRoot.js";
3
3
  import { parseArgs, translateParseError } from "./parseArgs.js";
4
+ import { readPackageVersion } from "./readPackageVersion.js";
4
5
  import { printError, printSkip, printStep, printSuccess, reportWriteResult } from "./terminal.js";
5
6
  import { writeFileWithCheck } from "./writeFileWithCheck.js";
6
7
  export {
@@ -11,6 +12,7 @@ export {
11
12
  printSkip,
12
13
  printStep,
13
14
  printSuccess,
15
+ readPackageVersion,
14
16
  reportWriteResult,
15
17
  translateParseError,
16
18
  writeFileWithCheck
@@ -0,0 +1 @@
1
+ export declare function readPackageVersion(fromUrl: string): string;
@@ -0,0 +1,15 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { findPackageRoot } from "./findPackageRoot.js";
4
+ function readPackageVersion(fromUrl) {
5
+ const root = findPackageRoot(fromUrl);
6
+ const packageJsonPath = resolve(root, "package.json");
7
+ const raw = JSON.parse(readFileSync(packageJsonPath, "utf8"));
8
+ if (typeof raw !== "object" || raw === null || !("version" in raw) || typeof raw.version !== "string") {
9
+ throw new Error(`No string "version" field in ${packageJsonPath}`);
10
+ }
11
+ return raw.version;
12
+ }
13
+ export {
14
+ readPackageVersion
15
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@williamthorsen/nmr-core",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "private": false,
5
5
  "description": "Shared utilities for node-monorepo-tools packages",
6
6
  "keywords": [
@@ -8,14 +8,14 @@
8
8
  "pnpm",
9
9
  "utilities"
10
10
  ],
11
- "homepage": "https://github.com/williamthorsen/node-monorepo-tools/tree/main/packages/core#readme",
11
+ "homepage": "https://github.com/williamthorsen/node-monorepo-tools/tree/main/packages/nmr-core#readme",
12
12
  "bugs": {
13
13
  "url": "https://github.com/williamthorsen/node-monorepo-tools/issues"
14
14
  },
15
15
  "repository": {
16
16
  "type": "git",
17
17
  "url": "https://github.com/williamthorsen/node-monorepo-tools.git",
18
- "directory": "packages/core"
18
+ "directory": "packages/nmr-core"
19
19
  },
20
20
  "license": "ISC",
21
21
  "author": "William Thorsen <william@thorsen.dev> (https://github.com/williamthorsen)",
@@ -1 +0,0 @@
1
- export {};
@@ -1,9 +0,0 @@
1
- import { reportOverrides } from "./commands/report-overrides.js";
2
- import { findMonorepoRoot } from "./context.js";
3
- try {
4
- const monorepoRoot = findMonorepoRoot();
5
- reportOverrides(monorepoRoot);
6
- } catch (error) {
7
- console.error(error instanceof Error ? error.message : String(error));
8
- process.exit(1);
9
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,9 +0,0 @@
1
- import { syncPnpmVersion } from "./commands/sync-pnpm-version.js";
2
- import { findMonorepoRoot } from "./context.js";
3
- try {
4
- const monorepoRoot = findMonorepoRoot();
5
- syncPnpmVersion(monorepoRoot);
6
- } catch (error) {
7
- console.error(error instanceof Error ? error.message : String(error));
8
- process.exit(1);
9
- }
package/dist/esm/cli.d.ts DELETED
@@ -1 +0,0 @@
1
- export {};
package/dist/esm/cli.js DELETED
@@ -1,110 +0,0 @@
1
- import process from "node:process";
2
- import { resolveContext } from "./context.js";
3
- import { generateHelp } from "./help.js";
4
- import { buildRootRegistry, buildWorkspaceRegistry, resolveScript } from "./resolver.js";
5
- import { runCommand } from "./runner.js";
6
- function shellQuote(arg) {
7
- return "'" + arg.replace(/'/g, String.raw`'\''`) + "'";
8
- }
9
- function parseArgs(argv) {
10
- const args = argv.slice(2);
11
- const result = {
12
- quiet: false,
13
- recursive: false,
14
- workspaceRoot: false,
15
- help: false,
16
- intTest: false,
17
- passthrough: []
18
- };
19
- let i = 0;
20
- while (i < args.length) {
21
- const arg = args[i];
22
- if (arg === void 0) break;
23
- if (arg === "-F" || arg === "--filter") {
24
- i++;
25
- const filterValue = args[i];
26
- if (filterValue === void 0) {
27
- console.error("Error: -F/--filter requires a pattern argument");
28
- process.exit(1);
29
- }
30
- result.filter = filterValue;
31
- i++;
32
- continue;
33
- }
34
- if (arg === "-R" || arg === "--recursive") {
35
- result.recursive = true;
36
- i++;
37
- continue;
38
- }
39
- if (arg === "-w" || arg === "--workspace-root") {
40
- result.workspaceRoot = true;
41
- i++;
42
- continue;
43
- }
44
- if (arg === "-?" || arg === "--help") {
45
- result.help = true;
46
- i++;
47
- continue;
48
- }
49
- if (arg === "-q" || arg === "--quiet") {
50
- result.quiet = true;
51
- i++;
52
- continue;
53
- }
54
- if (arg === "--int-test") {
55
- result.intTest = true;
56
- i++;
57
- continue;
58
- }
59
- result.command = arg;
60
- result.passthrough = args.slice(i + 1);
61
- break;
62
- }
63
- return result;
64
- }
65
- async function main() {
66
- const parsed = parseArgs(process.argv);
67
- const context = await resolveContext();
68
- if (parsed.help || !parsed.command) {
69
- console.info(generateHelp(context.config));
70
- process.exit(0);
71
- }
72
- const { command } = parsed;
73
- const passthrough = parsed.passthrough.length > 0 ? " " + parsed.passthrough.map(shellQuote).join(" ") : "";
74
- const runOptions = { quiet: parsed.quiet };
75
- if (parsed.filter) {
76
- const delegateCmd = `pnpm --filter ${shellQuote(parsed.filter)} exec nmr ${command}${passthrough}`;
77
- const code2 = runCommand(delegateCmd, context.monorepoRoot, runOptions);
78
- process.exit(code2);
79
- }
80
- if (parsed.recursive) {
81
- const delegateCmd = `pnpm --recursive exec nmr ${command}${passthrough}`;
82
- const code2 = runCommand(delegateCmd, context.monorepoRoot, runOptions);
83
- process.exit(code2);
84
- }
85
- const useRoot = parsed.workspaceRoot || context.isRoot;
86
- const registry = useRoot ? buildRootRegistry(context.config) : buildWorkspaceRegistry(context.config, parsed.intTest);
87
- const resolved = resolveScript(command, registry, context.packageDir);
88
- if (!resolved) {
89
- console.error(`Unknown command: ${command}`);
90
- process.exit(1);
91
- }
92
- if (resolved.command === "") {
93
- if (!parsed.quiet) {
94
- console.info("Override script is defined but empty. Skipping.");
95
- }
96
- process.exit(0);
97
- }
98
- if (resolved.source === "package" && !parsed.quiet) {
99
- console.info(`Using override script: ${resolved.command}`);
100
- }
101
- const fullCommand = resolved.command + passthrough;
102
- const code = runCommand(fullCommand, void 0, runOptions);
103
- process.exit(code);
104
- }
105
- try {
106
- await main();
107
- } catch (error) {
108
- console.error(error);
109
- process.exit(1);
110
- }
@@ -1 +0,0 @@
1
- export declare function reportOverrides(monorepoRoot: string): void;
@@ -1,15 +0,0 @@
1
- import { getPnpmOverrides, readPackageJson } from "../helpers/package-json.js";
2
- function reportOverrides(monorepoRoot) {
3
- const pkg = readPackageJson(monorepoRoot);
4
- const overrides = getPnpmOverrides(pkg);
5
- if (!overrides || Object.keys(overrides).length === 0) {
6
- return;
7
- }
8
- console.warn("\u{1F512} WARN: pnpm overrides are active! Check whether these are still needed:");
9
- for (const [name, version] of Object.entries(overrides)) {
10
- console.warn(`- ${name} \u2192 ${version}`);
11
- }
12
- }
13
- export {
14
- reportOverrides
15
- };
@@ -1,3 +0,0 @@
1
- declare function extractPnpmVersion(packageManager: string | undefined): string | null;
2
- export declare function syncPnpmVersion(monorepoRoot: string): void;
3
- export { extractPnpmVersion };
@@ -1,42 +0,0 @@
1
- import { readFileSync, writeFileSync } from "node:fs";
2
- import path from "node:path";
3
- import { CodeQualityPnpmWorkflowSchema, getPnpmVersion } from "../helpers/code-quality-pnpm-action.js";
4
- import { readPackageJson } from "../helpers/package-json.js";
5
- import { readYamlFile } from "../helpers/yaml-utils.js";
6
- const WORKFLOW_RELATIVE_PATH = ".github/workflows/code-quality.yaml";
7
- function extractPnpmVersion(packageManager) {
8
- if (!packageManager) {
9
- return null;
10
- }
11
- const match = /^pnpm@(.+)$/.exec(packageManager);
12
- return match?.[1] ?? null;
13
- }
14
- function syncPnpmVersion(monorepoRoot) {
15
- console.info("Synchronizing pnpm version in GitHub workflow...");
16
- const pkg = readPackageJson(monorepoRoot);
17
- const pnpmVersion = extractPnpmVersion(pkg.packageManager);
18
- if (!pnpmVersion) {
19
- throw new Error(
20
- `Could not extract pnpm version from package.json packageManager field
21
- packageManager field: ${pkg.packageManager ?? "(not set)"}`
22
- );
23
- }
24
- console.info(`Package.json pnpm version: ${pnpmVersion}`);
25
- const workflowPath = path.join(monorepoRoot, WORKFLOW_RELATIVE_PATH);
26
- const workflowData = readYamlFile(workflowPath);
27
- const workflow = CodeQualityPnpmWorkflowSchema.parse(workflowData);
28
- const currentWorkflowVersion = getPnpmVersion(workflow);
29
- console.info(`Current workflow pnpm version: ${currentWorkflowVersion}`);
30
- if (currentWorkflowVersion === pnpmVersion) {
31
- console.info("Workflow pnpm version is already up to date");
32
- return;
33
- }
34
- const originalContent = readFileSync(workflowPath, "utf8");
35
- const updatedContent = originalContent.replace(/(\s+pnpm-version:\s+)(['"]?)[\d.]+\2/, `$1$2${pnpmVersion}$2`);
36
- writeFileSync(workflowPath, updatedContent, "utf8");
37
- console.info(`\u2713 Updated workflow pnpm version: ${currentWorkflowVersion} \u2192 ${pnpmVersion}`);
38
- }
39
- export {
40
- extractPnpmVersion,
41
- syncPnpmVersion
42
- };
@@ -1,6 +0,0 @@
1
- export interface NmrConfig {
2
- workspaceScripts?: Record<string, string | string[]>;
3
- rootScripts?: Record<string, string | string[]>;
4
- }
5
- export declare function defineConfig(config: NmrConfig): NmrConfig;
6
- export declare function loadConfig(monorepoRoot: string): Promise<NmrConfig>;
@@ -1,21 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import path from "node:path";
3
- import { createJiti } from "jiti";
4
- const CONFIG_FILENAME = "nmr.config.ts";
5
- const CONFIG_DIR = ".config";
6
- function defineConfig(config) {
7
- return config;
8
- }
9
- async function loadConfig(monorepoRoot) {
10
- const configPath = path.join(monorepoRoot, CONFIG_DIR, CONFIG_FILENAME);
11
- if (!existsSync(configPath)) {
12
- return {};
13
- }
14
- const jiti = createJiti(path.join(monorepoRoot, "package.json"));
15
- const loaded = await jiti.import(configPath, { default: true });
16
- return loaded;
17
- }
18
- export {
19
- defineConfig,
20
- loadConfig
21
- };
@@ -1,11 +0,0 @@
1
- import type { NmrConfig } from './config.js';
2
- export interface ResolvedContext {
3
- monorepoRoot: string;
4
- isRoot: boolean;
5
- packageDir?: string;
6
- config: NmrConfig;
7
- }
8
- export declare function findMonorepoRoot(startDir?: string): string;
9
- export declare function getWorkspacePackageDirs(monorepoRoot: string): string[];
10
- export declare function findContainingPackageDir(dir: string, workspacePackageDirs: string[]): string | undefined;
11
- export declare function resolveContext(cwd?: string): Promise<ResolvedContext>;
@@ -1,84 +0,0 @@
1
- import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
- import path from "node:path";
3
- import yaml from "js-yaml";
4
- import { loadConfig } from "./config.js";
5
- import { isObject } from "./helpers/type-guards.js";
6
- function findMonorepoRoot(startDir) {
7
- let dir = path.resolve(startDir ?? process.cwd());
8
- for (; ; ) {
9
- if (existsSync(path.join(dir, "pnpm-workspace.yaml"))) {
10
- return dir;
11
- }
12
- const parent = path.dirname(dir);
13
- if (parent === dir) {
14
- throw new Error("Could not find monorepo root: no pnpm-workspace.yaml found in any parent directory");
15
- }
16
- dir = parent;
17
- }
18
- }
19
- function getWorkspacePackageDirs(monorepoRoot) {
20
- const workspaceFile = path.join(monorepoRoot, "pnpm-workspace.yaml");
21
- const content = readFileSync(workspaceFile, "utf8");
22
- const parsed = yaml.load(content);
23
- const packages = getPackagesFromParsedYaml(parsed);
24
- if (!packages) {
25
- return [];
26
- }
27
- const dirs = [];
28
- for (const pattern of packages) {
29
- if (pattern.endsWith("/*")) {
30
- const prefix = pattern.slice(0, -2);
31
- const prefixDir = path.resolve(monorepoRoot, prefix);
32
- if (existsSync(prefixDir)) {
33
- for (const entry of readdirSync(prefixDir)) {
34
- const fullPath = path.join(prefixDir, entry);
35
- if (statSync(fullPath).isDirectory() && existsSync(path.join(fullPath, "package.json"))) {
36
- dirs.push(fullPath);
37
- }
38
- }
39
- }
40
- } else if (!pattern.includes("*")) {
41
- const fullPath = path.resolve(monorepoRoot, pattern);
42
- if (existsSync(fullPath) && existsSync(path.join(fullPath, "package.json"))) {
43
- dirs.push(fullPath);
44
- }
45
- }
46
- }
47
- return dirs;
48
- }
49
- function findContainingPackageDir(dir, workspacePackageDirs) {
50
- const resolved = path.resolve(dir);
51
- for (const pkgDir of workspacePackageDirs) {
52
- const resolvedPkgDir = path.resolve(pkgDir);
53
- if (resolved === resolvedPkgDir || resolved.startsWith(resolvedPkgDir + path.sep)) {
54
- return resolvedPkgDir;
55
- }
56
- }
57
- return void 0;
58
- }
59
- function getPackagesFromParsedYaml(parsed) {
60
- if (!isObject(parsed)) return void 0;
61
- const packages = parsed.packages;
62
- if (!Array.isArray(packages)) return void 0;
63
- if (!packages.every((p) => typeof p === "string")) return void 0;
64
- return packages;
65
- }
66
- async function resolveContext(cwd) {
67
- const resolvedCwd = path.resolve(cwd ?? process.cwd());
68
- const monorepoRoot = findMonorepoRoot(resolvedCwd);
69
- const config = await loadConfig(monorepoRoot);
70
- const workspaceDirs = getWorkspacePackageDirs(monorepoRoot);
71
- const packageDir = findContainingPackageDir(resolvedCwd, workspaceDirs);
72
- return {
73
- monorepoRoot,
74
- isRoot: packageDir === void 0,
75
- ...packageDir === void 0 ? {} : { packageDir },
76
- config
77
- };
78
- }
79
- export {
80
- findContainingPackageDir,
81
- findMonorepoRoot,
82
- getWorkspacePackageDirs,
83
- resolveContext
84
- };
@@ -1,2 +0,0 @@
1
- import type { NmrConfig } from './config.js';
2
- export declare function generateHelp(config: NmrConfig): string;
package/dist/esm/help.js DELETED
@@ -1,31 +0,0 @@
1
- import { buildRootRegistry, buildWorkspaceRegistry } from "./resolver.js";
2
- import { describeScript } from "./resolver.js";
3
- function generateHelp(config) {
4
- const lines = [
5
- "Usage: nmr [flags] <command> [args...]",
6
- "",
7
- "Flags:",
8
- " -F, --filter <pattern> Run command in matching packages",
9
- " -R, --recursive Run command in all packages",
10
- " -w, --workspace-root Run root command regardless of cwd",
11
- " -q, --quiet Suppress output on success; show full output on failure",
12
- " -?, --help Show this help",
13
- " --int-test Use integration test scripts",
14
- "",
15
- "Workspace commands:"
16
- ];
17
- formatRegistry(buildWorkspaceRegistry(config, false), lines);
18
- lines.push("", "Root commands:");
19
- formatRegistry(buildRootRegistry(config), lines);
20
- return lines.join("\n");
21
- }
22
- function formatRegistry(registry, lines) {
23
- const maxKeyLen = Math.max(...Object.keys(registry).map((k) => k.length));
24
- const pad = Math.max(maxKeyLen + 2, 20);
25
- for (const [key, value] of Object.entries(registry)) {
26
- lines.push(` ${key.padEnd(pad)} ${describeScript(value)}`);
27
- }
28
- }
29
- export {
30
- generateHelp
31
- };
@@ -1,12 +0,0 @@
1
- import { z } from 'zod';
2
- export declare const CodeQualityPnpmWorkflowSchema: z.ZodObject<{
3
- jobs: z.ZodObject<{
4
- 'code-quality': z.ZodObject<{
5
- with: z.ZodObject<{
6
- 'pnpm-version': z.ZodString;
7
- }, z.core.$loose>;
8
- }, z.core.$loose>;
9
- }, z.core.$loose>;
10
- }, z.core.$loose>;
11
- export type CodeQualityPnpmWorkflow = z.infer<typeof CodeQualityPnpmWorkflowSchema>;
12
- export declare function getPnpmVersion(workflow: CodeQualityPnpmWorkflow): string;
@@ -1,17 +0,0 @@
1
- import { z } from "zod";
2
- const CodeQualityPnpmWorkflowSchema = z.looseObject({
3
- jobs: z.looseObject({
4
- "code-quality": z.looseObject({
5
- with: z.looseObject({
6
- "pnpm-version": z.string()
7
- })
8
- })
9
- })
10
- });
11
- function getPnpmVersion(workflow) {
12
- return workflow.jobs["code-quality"].with["pnpm-version"];
13
- }
14
- export {
15
- CodeQualityPnpmWorkflowSchema,
16
- getPnpmVersion
17
- };
@@ -1,11 +0,0 @@
1
- export interface PackageJson {
2
- name?: string;
3
- version?: string;
4
- packageManager?: string;
5
- scripts?: Record<string, string>;
6
- pnpm?: {
7
- overrides?: Record<string, string>;
8
- };
9
- }
10
- export declare function readPackageJson(dir: string): PackageJson;
11
- export declare function getPnpmOverrides(pkg: PackageJson): Record<string, string> | undefined;
@@ -1,45 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import path from "node:path";
3
- import { isObject } from "./type-guards.js";
4
- function readPackageJson(dir) {
5
- const content = readFileSync(path.join(dir, "package.json"), "utf8");
6
- const parsed = JSON.parse(content);
7
- if (!isObject(parsed)) {
8
- throw new TypeError(`Invalid package.json in ${dir}: expected an object`);
9
- }
10
- const pkg = {};
11
- if (typeof parsed.name === "string") pkg.name = parsed.name;
12
- if (typeof parsed.version === "string") pkg.version = parsed.version;
13
- if (typeof parsed.packageManager === "string") pkg.packageManager = parsed.packageManager;
14
- if (isObject(parsed.scripts)) {
15
- const scripts = {};
16
- for (const [key, val] of Object.entries(parsed.scripts)) {
17
- if (typeof val === "string") scripts[key] = val;
18
- }
19
- pkg.scripts = scripts;
20
- }
21
- if (isObject(parsed.pnpm)) {
22
- const pnpm = parsed.pnpm;
23
- if (isObject(pnpm.overrides)) {
24
- const overrides = {};
25
- for (const [key, val] of Object.entries(pnpm.overrides)) {
26
- if (typeof val === "string") overrides[key] = val;
27
- }
28
- pkg.pnpm = { overrides };
29
- }
30
- }
31
- return pkg;
32
- }
33
- function getPnpmOverrides(pkg) {
34
- if (!isObject(pkg.pnpm)) return void 0;
35
- const overrides = pkg.pnpm.overrides;
36
- if (!isObject(overrides)) return void 0;
37
- for (const value of Object.values(overrides)) {
38
- if (typeof value !== "string") return void 0;
39
- }
40
- return overrides;
41
- }
42
- export {
43
- getPnpmOverrides,
44
- readPackageJson
45
- };
@@ -1 +0,0 @@
1
- export declare function isObject(value: unknown): value is Record<string, unknown>;
@@ -1,6 +0,0 @@
1
- function isObject(value) {
2
- return typeof value === "object" && value !== null;
3
- }
4
- export {
5
- isObject
6
- };
@@ -1 +0,0 @@
1
- export declare function readYamlFile(filepath: string): unknown;
@@ -1,14 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import yaml from "js-yaml";
3
- function readYamlFile(filepath) {
4
- try {
5
- const content = readFileSync(filepath, "utf8");
6
- return yaml.load(content);
7
- } catch (error) {
8
- throw new Error(`Failed to read YAML file: ${filepath}
9
- ${error instanceof Error ? error.message : String(error)}`);
10
- }
11
- }
12
- export {
13
- readYamlFile
14
- };
@@ -1,5 +0,0 @@
1
- export type ScriptValue = string | string[];
2
- export type ScriptRegistry = Record<string, ScriptValue>;
3
- declare function getDefaultWorkspaceScripts(useIntTests: boolean): ScriptRegistry;
4
- declare function getDefaultRootScripts(): ScriptRegistry;
5
- export { getDefaultRootScripts, getDefaultWorkspaceScripts };
@@ -1,67 +0,0 @@
1
- function getDefaultWorkspaceScripts(useIntTests) {
2
- const commonScripts = {
3
- build: ["compile", "generate-typings"],
4
- check: ["typecheck", "fmt:check", "lint:check", "test"],
5
- "check:strict": ["typecheck", "fmt:check", "lint:strict", "test:coverage"],
6
- clean: "pnpm exec rimraf dist/*",
7
- compile: "tsx ../../config/build.ts",
8
- fmt: "prettier --list-different --write .",
9
- "fmt:check": "prettier --check .",
10
- "generate-typings": "tsc --project tsconfig.generate-typings.json",
11
- lint: "eslint --fix .",
12
- "lint:check": "eslint .",
13
- "lint:strict": "strict-lint",
14
- typecheck: "tsgo --noEmit",
15
- "view-coverage": "open coverage/index.html"
16
- };
17
- const integrationTestOverrides = {
18
- test: "pnpm exec vitest --config=vitest.standalone.config.ts",
19
- "test:coverage": "pnpm exec vitest --config=vitest.standalone.config.ts --coverage",
20
- "test:integration": "pnpm exec vitest --config=vitest.integration.config.ts",
21
- "test:watch": "pnpm exec vitest --config=vitest.standalone.config.ts --watch"
22
- };
23
- const standardTestOverrides = {
24
- test: "pnpm exec vitest",
25
- "test:coverage": "pnpm exec vitest --coverage",
26
- "test:watch": "pnpm exec vitest --watch"
27
- };
28
- return {
29
- ...commonScripts,
30
- ...useIntTests ? integrationTestOverrides : standardTestOverrides
31
- };
32
- }
33
- function getDefaultRootScripts() {
34
- return {
35
- audit: ["audit:prod", "audit:dev"],
36
- "audit:dev": "pnpm dlx audit-ci@^6 --config .audit-ci/config.dev.json5",
37
- "audit:prod": "pnpm dlx audit-ci@^6 --config .audit-ci/config.prod.json5",
38
- build: "pnpm --recursive exec nmr build",
39
- check: ["typecheck", "fmt:check", "lint:check", "test"],
40
- "check:strict": ["typecheck", "fmt:check", "lint:strict", "test:coverage", "audit"],
41
- ci: ["check:strict", "build"],
42
- fmt: `sh -c 'prettier --list-different --write "\${@:-.}"' --`,
43
- "fmt:check": `sh -c 'prettier --check "\${@:-.}"' --`,
44
- lint: "nmr root:lint && pnpm --recursive exec nmr lint",
45
- "lint:check": "nmr root:lint:check && pnpm --recursive exec nmr lint:check",
46
- "lint:strict": "nmr root:lint:strict && pnpm --recursive exec nmr lint:strict",
47
- outdated: "pnpm outdated --compatible --recursive",
48
- "outdated:latest": "pnpm outdated --recursive",
49
- "report-overrides": "nmr-report-overrides",
50
- "root:lint": "eslint --fix --ignore-pattern 'packages/**' .",
51
- "root:lint:check": "eslint --ignore-pattern 'packages/**' .",
52
- "root:lint:strict": "strict-lint --ignore-pattern 'packages/**' .",
53
- "root:test": "vitest --config ./vitest.root.config.ts",
54
- "root:typecheck": "tsgo --noEmit",
55
- "sync-pnpm-version": "nmr-sync-pnpm-version",
56
- test: "nmr root:test && pnpm --recursive exec nmr test",
57
- "test:coverage": "nmr root:test && pnpm --recursive exec nmr test:coverage",
58
- "test:watch": "vitest --watch",
59
- typecheck: "nmr root:typecheck && pnpm --recursive exec nmr typecheck",
60
- update: "pnpm update --recursive",
61
- "update:latest": "pnpm update --latest --recursive"
62
- };
63
- }
64
- export {
65
- getDefaultRootScripts,
66
- getDefaultWorkspaceScripts
67
- };
@@ -1,11 +0,0 @@
1
- import type { NmrConfig } from './config.js';
2
- import type { ScriptRegistry, ScriptValue } from './registries.js';
3
- export interface ResolvedScript {
4
- command: string;
5
- source: 'default' | 'package';
6
- }
7
- export declare function expandScript(script: ScriptValue): string;
8
- export declare function describeScript(script: ScriptValue): string;
9
- export declare function buildWorkspaceRegistry(config: NmrConfig, useIntTests: boolean): ScriptRegistry;
10
- export declare function buildRootRegistry(config: NmrConfig): ScriptRegistry;
11
- export declare function resolveScript(commandName: string, registry: ScriptRegistry, packageDir?: string): ResolvedScript | undefined;
@@ -1,64 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import path from "node:path";
3
- import { isObject } from "./helpers/type-guards.js";
4
- import { getDefaultRootScripts, getDefaultWorkspaceScripts } from "./registries.js";
5
- function expandScript(script) {
6
- if (typeof script === "string") {
7
- return script;
8
- }
9
- return script.map((s) => `nmr ${s}`).join(" && ");
10
- }
11
- function describeScript(script) {
12
- return typeof script === "string" ? script : `[${script.join(", ")}]`;
13
- }
14
- function readPackageJsonScripts(packageDir) {
15
- try {
16
- const raw = readFileSync(path.join(packageDir, "package.json"), "utf8");
17
- const parsed = JSON.parse(raw);
18
- if (!isObject(parsed)) return void 0;
19
- const scripts = parsed.scripts;
20
- if (!isObject(scripts)) return void 0;
21
- const result = {};
22
- for (const [key, val] of Object.entries(scripts)) {
23
- if (typeof val === "string") result[key] = val;
24
- }
25
- return result;
26
- } catch {
27
- return void 0;
28
- }
29
- }
30
- function buildWorkspaceRegistry(config, useIntTests) {
31
- return {
32
- ...getDefaultWorkspaceScripts(useIntTests),
33
- ...config.workspaceScripts
34
- };
35
- }
36
- function buildRootRegistry(config) {
37
- return {
38
- ...getDefaultRootScripts(),
39
- ...config.rootScripts
40
- };
41
- }
42
- function resolveScript(commandName, registry, packageDir) {
43
- if (packageDir) {
44
- const pkgScripts = readPackageJsonScripts(packageDir);
45
- if (pkgScripts && commandName in pkgScripts) {
46
- const override = pkgScripts[commandName];
47
- if (override !== void 0) {
48
- return { command: override, source: "package" };
49
- }
50
- }
51
- }
52
- const registryEntry = registry[commandName];
53
- if (registryEntry === void 0) {
54
- return void 0;
55
- }
56
- return { command: expandScript(registryEntry), source: "default" };
57
- }
58
- export {
59
- buildRootRegistry,
60
- buildWorkspaceRegistry,
61
- describeScript,
62
- expandScript,
63
- resolveScript
64
- };
@@ -1,4 +0,0 @@
1
- export interface RunCommandOptions {
2
- quiet?: boolean;
3
- }
4
- export declare function runCommand(command: string, cwd?: string, options?: RunCommandOptions): number;
@@ -1,38 +0,0 @@
1
- import { execSync } from "node:child_process";
2
- import process from "node:process";
3
- function runCommand(command, cwd, options) {
4
- const quiet = options?.quiet === true;
5
- const stdio = quiet ? "pipe" : "inherit";
6
- try {
7
- execSync(command, { stdio, cwd });
8
- return 0;
9
- } catch (error) {
10
- if (error !== null && typeof error === "object") {
11
- if (quiet) {
12
- writeErrorOutput(error);
13
- }
14
- if ("status" in error) {
15
- const { status } = error;
16
- return typeof status === "number" ? status : 1;
17
- }
18
- }
19
- return 1;
20
- }
21
- }
22
- function writeErrorOutput(error) {
23
- if ("stdout" in error) {
24
- const { stdout } = error;
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);
33
- }
34
- }
35
- }
36
- export {
37
- runCommand
38
- };
@@ -1 +0,0 @@
1
- export declare function runConsistencyChecks(): void;
@@ -1,70 +0,0 @@
1
- import assert from "node:assert";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import yaml from "js-yaml";
5
- import { describe, expect, it } from "vitest";
6
- import { findMonorepoRoot } from "../context.js";
7
- import { getRuntimeVersionFromAsdf } from "./helpers/get-runtime-version-from-asdf.js";
8
- import { getValueAtPathOrThrow } from "./helpers/get-value-at-path.js";
9
- const GITHUB_ACTION_FILE_PATH = ".github/workflows/code-quality.yaml";
10
- function checkPnpmVersionConsistency(monorepoRoot) {
11
- describe("pnpm version consistency", () => {
12
- it("pnpm version is the same in GitHub action and package.json", async () => {
13
- const actionVersion = await getPnpmVersionFromAction(monorepoRoot);
14
- const packageJsonVersion = getPnpmVersionFromPackageJson(monorepoRoot);
15
- expect(actionVersion).toBe(packageJsonVersion);
16
- });
17
- });
18
- }
19
- function checkNodeVersionConsistency(monorepoRoot) {
20
- describe("Node.js version consistency", () => {
21
- it("version is the same in GitHub action and .tool-versions", async () => {
22
- const toolVersion = await getRuntimeVersionFromAsdf("nodejs", monorepoRoot);
23
- const actionVersion = await getNodeVersionFromAction(monorepoRoot);
24
- expect(toolVersion).toBe(actionVersion);
25
- });
26
- });
27
- }
28
- async function getPnpmVersionFromAction(monorepoRoot) {
29
- const actionPath = path.join(monorepoRoot, GITHUB_ACTION_FILE_PATH);
30
- const actionYaml = await fs.promises.readFile(actionPath, { encoding: "utf8" });
31
- const action = yaml.load(actionYaml);
32
- assert.ok(action, "Action YAML not found");
33
- const version = getValueAtPathOrThrow(action, "jobs.code-quality.with.pnpm-version");
34
- assert.ok(typeof version === "string" && version.length > 0, "pnpm version not found in action");
35
- return version;
36
- }
37
- function getPnpmVersionFromPackageJson(monorepoRoot) {
38
- const pkgPath = path.join(monorepoRoot, "package.json");
39
- const raw = fs.readFileSync(pkgPath, "utf8");
40
- const pkg = JSON.parse(raw);
41
- const pm = getValueAtPathOrThrow(pkg, "packageManager");
42
- if (typeof pm !== "string") {
43
- throw new TypeError('"packageManager" field missing or not a string in package.json.');
44
- }
45
- const [name, version] = pm.split("@");
46
- if (name !== "pnpm") {
47
- throw new Error("packageManager is not pnpm.");
48
- }
49
- if (!version) {
50
- throw new Error("pnpm version missing in package.json.");
51
- }
52
- return version;
53
- }
54
- async function getNodeVersionFromAction(monorepoRoot) {
55
- const actionPath = path.join(monorepoRoot, GITHUB_ACTION_FILE_PATH);
56
- const actionYaml = await fs.promises.readFile(actionPath, { encoding: "utf8" });
57
- const action = yaml.load(actionYaml);
58
- assert.ok(action, "Action YAML not found");
59
- const version = getValueAtPathOrThrow(action, "jobs.code-quality.with.node-version");
60
- assert.ok(typeof version === "string" && version.length > 0, "Node.js version not found in action");
61
- return version;
62
- }
63
- function runConsistencyChecks() {
64
- const monorepoRoot = findMonorepoRoot();
65
- checkPnpmVersionConsistency(monorepoRoot);
66
- checkNodeVersionConsistency(monorepoRoot);
67
- }
68
- export {
69
- runConsistencyChecks
70
- };
@@ -1 +0,0 @@
1
- export declare function getRuntimeVersionFromAsdf(runtime: string, monorepoRoot: string): Promise<string>;
@@ -1,15 +0,0 @@
1
- import assert from "node:assert";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- async function getRuntimeVersionFromAsdf(runtime, monorepoRoot) {
5
- const toolVersionsPath = path.join(monorepoRoot, ".tool-versions");
6
- const toolVersions = await fs.promises.readFile(toolVersionsPath, { encoding: "utf8" });
7
- const versionLine = toolVersions.split("\n").find((line) => line.trim().startsWith(runtime));
8
- assert.ok(versionLine, `${runtime} not found in .tool-versions`);
9
- const [, version] = versionLine.trim().split(/\s+/);
10
- assert.ok(version, `${runtime} version missing in .tool-versions`);
11
- return version;
12
- }
13
- export {
14
- getRuntimeVersionFromAsdf
15
- };
@@ -1 +0,0 @@
1
- export declare function getValueAtPathOrThrow(obj: unknown, objPath: string): unknown;
@@ -1,34 +0,0 @@
1
- function isObject(value) {
2
- return typeof value === "object" && value !== null && !Array.isArray(value);
3
- }
4
- function getValueAtPathOrThrow(obj, objPath) {
5
- if (!isObject(obj)) {
6
- throw new Error("Expected an object as root value.");
7
- }
8
- const keys = objPath.split(".");
9
- let current = obj;
10
- for (const key of keys) {
11
- if (Array.isArray(current)) {
12
- const index = Number(key);
13
- if (Number.isNaN(index)) {
14
- throw new TypeError(`Expected array index at segment "${key}" in path "${objPath}"`);
15
- }
16
- if (index >= 0 && index < current.length) {
17
- current = current[index];
18
- } else {
19
- throw new Error(`Array index out of bounds: "${key}" in path "${objPath}"`);
20
- }
21
- } else if (isObject(current)) {
22
- if (!(key in current)) {
23
- throw new Error(`Missing key "${key}" in path "${objPath}"`);
24
- }
25
- current = current[key];
26
- } else {
27
- throw new Error(`Unexpected non-object/non-array at segment "${key}" in path "${objPath}"`);
28
- }
29
- }
30
- return current;
31
- }
32
- export {
33
- getValueAtPathOrThrow
34
- };