@procedures/mark 0.46.0 → 0.47.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.
@@ -1 +1 @@
1
- {"version":3,"file":"print.d.ts","sourceRoot":"","sources":["../src/print.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,eAAO,MAAM,KAAK;IAEhB,IAAI,QAAQ,MAAM,KAAG,IAAI;IAIzB,OAAO,QAAQ,MAAM,KAAG,IAAI;IAI5B,KAAK,QAAQ,MAAM,KAAG,IAAI;IAI1B,OAAO,QAAQ,MAAM,KAAG,IAAI;IAI5B,KAAK,QAAQ,MAAM,KAAG,IAAI;IAI1B,SAAS,QAAQ,MAAM,KAAG,IAAI;IAK9B,IAAI,QAAQ,MAAM,KAAG,OAAO;IAgB5B,KAAK,SAAS,MAAM,EAAE,EAAE,aAAa;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,KAAG,IAAI;IAsB/D,OAAO,QAAM,IAAI;IAKjB,OAAO,QAAM,IAAI;CAGlB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC;AAEjC;;GAEG;AACH,OAAO,EAAE,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"print.d.ts","sourceRoot":"","sources":["../src/print.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAa1B;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,eAAO,MAAM,KAAK;IAEhB,IAAI,QAAQ,MAAM,KAAG,IAAI;IAIzB,OAAO,QAAQ,MAAM,KAAG,IAAI;IAI5B,KAAK,QAAQ,MAAM,KAAG,IAAI;IAI1B,OAAO,QAAQ,MAAM,KAAG,IAAI;IAI5B,KAAK,QAAQ,MAAM,KAAG,IAAI;IAI1B,SAAS,QAAQ,MAAM,KAAG,IAAI;IAK9B,IAAI,QAAQ,MAAM,KAAG,OAAO;IAgB5B,KAAK,SAAS,MAAM,EAAE,EAAE,aAAa;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,KAAG,IAAI;IAuB/D,OAAO,QAAM,IAAI;IAKjB,OAAO,QAAM,IAAI;CAGlB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC;AAEjC;;GAEG;AACH,OAAO,EAAE,KAAK,EAAE,CAAC"}
package/dist/print.js CHANGED
@@ -6,7 +6,14 @@
6
6
  */
7
7
  import chalk from "chalk";
8
8
  import ora from "ora";
9
- import Table from "cli-table3";
9
+ import { createRequire } from "node:module";
10
+ /**
11
+ * This function gives the table class of `cli-table3`. The first table loads the package, not the
12
+ * start of `mark`: a call that prints no table does not load it (roadmap O93).
13
+ */
14
+ function tableClass() {
15
+ return createRequire(import.meta.url)("cli-table3");
16
+ }
10
17
  /**
11
18
  * Print utilities
12
19
  */
@@ -52,6 +59,7 @@ export const print = {
52
59
  const [headers, ...rows] = data;
53
60
  if (!headers)
54
61
  return;
62
+ const Table = tableClass();
55
63
  const table = new Table({
56
64
  head: headers.map((h) => chalk.bold(h)),
57
65
  style: {
package/dist/print.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"print.js","sourceRoot":"","sources":["../src/print.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,MAAM,YAAY,CAAC;AAY/B;;GAEG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,eAAe;IACf,IAAI,EAAE,CAAC,GAAW,EAAQ,EAAE;QAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,EAAE,CAAC,GAAW,EAAQ,EAAE;QAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,EAAE,CAAC,GAAW,EAAQ,EAAE;QAC3B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,EAAE,CAAC,GAAW,EAAQ,EAAE;QAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,EAAE,CAAC,GAAW,EAAQ,EAAE;QAC3B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,CAAC;IAED,SAAS,EAAE,CAAC,GAAW,EAAQ,EAAE;QAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IACrC,CAAC;IAED,wEAAwE;IACxE,IAAI,EAAE,CAAC,GAAW,EAAW,EAAE;QAC7B,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC;QACjC,OAAO;YACL,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE;YAC1B,OAAO,EAAE,CAAC,OAAgB,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;YACvD,IAAI,EAAE,CAAC,OAAgB,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YACjD,IAAI,IAAI;gBACN,OAAO,OAAO,CAAC,IAAI,CAAC;YACtB,CAAC;YACD,IAAI,IAAI,CAAC,KAAa;gBACpB,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC;YACvB,CAAC;SACF,CAAC;IACJ,CAAC;IAED,yDAAyD;IACzD,KAAK,EAAE,CAAC,IAAgB,EAAE,QAA8B,EAAQ,EAAE;QAChE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAE9B,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;QAChC,IAAI,CAAC,OAAO;YAAE,OAAO;QAErB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;YACtB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACvC,KAAK,EAAE;gBACL,IAAI,EAAE,EAAE;gBACR,MAAM,EAAE,EAAE;aACX;SACF,CAAC,CAAC;QAEH,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IAChC,CAAC;IAED,eAAe;IACf,OAAO,EAAE,GAAS,EAAE;QAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED,UAAU;IACV,OAAO,EAAE,GAAS,EAAE;QAClB,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;CACF,CAAC;AAOF;;GAEG;AACH,OAAO,EAAE,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"print.js","sourceRoot":"","sources":["../src/print.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@procedures/mark",
4
- "version": "0.46.0",
4
+ "version": "0.47.2",
5
5
  "description": "mark: one CLI for every procedure package. It finds the procedures, parses flags by their Zod types, runs local or through a fast warm server, and asks for missing arguments.",
6
6
  "keywords": [
7
7
  "cli",
@@ -52,18 +52,18 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@mark1russell7/service": "^0.6.0",
55
- "@procedures/cli-kit": "0.46.0",
56
- "@procedures/cli-run": "0.46.0",
57
- "@procedures/client": "0.46.0",
58
- "@procedures/fs": "0.46.0",
59
- "@procedures/lib": "0.46.0",
60
- "@procedures/mcp-dev-tools": "0.46.0",
61
- "@procedures/peer": "0.46.0",
62
- "@procedures/pnpm": "0.46.0",
63
- "@procedures/procedure": "0.46.0",
64
- "@procedures/server": "0.46.0",
65
- "@procedures/shell": "0.46.0",
66
- "@procedures/vitest": "0.46.0",
55
+ "@procedures/cli-kit": "0.47.2",
56
+ "@procedures/cli-run": "0.47.2",
57
+ "@procedures/client": "0.47.2",
58
+ "@procedures/fs": "0.47.2",
59
+ "@procedures/lib": "0.47.2",
60
+ "@procedures/mcp-dev-tools": "0.47.2",
61
+ "@procedures/peer": "0.47.2",
62
+ "@procedures/pnpm": "0.47.2",
63
+ "@procedures/procedure": "0.47.2",
64
+ "@procedures/server": "0.47.2",
65
+ "@procedures/shell": "0.47.2",
66
+ "@procedures/vitest": "0.47.2",
67
67
  "chalk": "^5.3.0",
68
68
  "cli-table3": "^0.6.5",
69
69
  "ora": "^8.0.0",
@@ -71,44 +71,44 @@
71
71
  "zod": "^4.6.5"
72
72
  },
73
73
  "devDependencies": {
74
- "@mark1russell7/cue": "^1.0.0",
75
- "@procedures/archive": "0.46.0",
76
- "@procedures/bundle-assistant": "0.46.0",
77
- "@procedures/bundle-dev": "0.46.0",
78
- "@procedures/bundle-mcp": "0.46.0",
79
- "@procedures/claude-cli": "0.46.0",
80
- "@procedures/compiler": "0.46.0",
81
- "@procedures/cue": "0.46.0",
82
- "@procedures/dag": "0.46.0",
83
- "@procedures/docker": "0.46.0",
84
- "@procedures/env": "0.46.0",
85
- "@procedures/exif": "0.46.0",
86
- "@procedures/geo": "0.46.0",
87
- "@procedures/gh": "0.46.0",
88
- "@procedures/git": "0.46.0",
89
- "@procedures/http": "0.46.0",
90
- "@procedures/ics": "0.46.0",
91
- "@procedures/ios-backup": "0.46.0",
92
- "@procedures/json": "0.46.0",
93
- "@procedures/llm": "0.46.0",
94
- "@procedures/mail": "0.46.0",
95
- "@procedures/mongo": "0.46.0",
96
- "@procedures/node": "0.46.0",
97
- "@procedures/notify": "0.46.0",
98
- "@procedures/npm": "0.46.0",
99
- "@procedures/pdf": "0.46.0",
100
- "@procedures/privacy-scan": "0.46.0",
101
- "@procedures/restic": "0.46.0",
102
- "@procedures/s3": "0.46.0",
103
- "@procedures/schedule": "0.46.0",
104
- "@procedures/snapshot": "0.46.0",
105
- "@procedures/splay": "0.46.0",
106
- "@procedures/sqlite": "0.46.0",
107
- "@procedures/store": "0.46.0",
108
- "@procedures/systems": "0.46.0",
109
- "@procedures/tailscale": "0.46.0",
110
- "@procedures/vite": "0.46.0",
111
- "@procedures/weather": "0.46.0",
74
+ "@mark1russell7/cue": "^1.1.0",
75
+ "@procedures/archive": "0.47.2",
76
+ "@procedures/bundle-assistant": "0.47.2",
77
+ "@procedures/bundle-dev": "0.47.2",
78
+ "@procedures/bundle-mcp": "0.47.2",
79
+ "@procedures/claude-cli": "0.47.2",
80
+ "@procedures/compiler": "0.47.2",
81
+ "@procedures/cue": "0.47.2",
82
+ "@procedures/dag": "0.47.2",
83
+ "@procedures/docker": "0.47.2",
84
+ "@procedures/env": "0.47.2",
85
+ "@procedures/exif": "0.47.2",
86
+ "@procedures/geo": "0.47.2",
87
+ "@procedures/gh": "0.47.2",
88
+ "@procedures/git": "0.47.2",
89
+ "@procedures/http": "0.47.2",
90
+ "@procedures/ics": "0.47.2",
91
+ "@procedures/ios-backup": "0.47.2",
92
+ "@procedures/json": "0.47.2",
93
+ "@procedures/llm": "0.47.2",
94
+ "@procedures/mail": "0.47.2",
95
+ "@procedures/mongo": "0.47.2",
96
+ "@procedures/node": "0.47.2",
97
+ "@procedures/notify": "0.47.2",
98
+ "@procedures/npm": "0.47.2",
99
+ "@procedures/pdf": "0.47.2",
100
+ "@procedures/privacy-scan": "0.47.2",
101
+ "@procedures/restic": "0.47.2",
102
+ "@procedures/s3": "0.47.2",
103
+ "@procedures/schedule": "0.47.2",
104
+ "@procedures/snapshot": "0.47.2",
105
+ "@procedures/splay": "0.47.2",
106
+ "@procedures/sqlite": "0.47.2",
107
+ "@procedures/store": "0.47.2",
108
+ "@procedures/systems": "0.47.2",
109
+ "@procedures/tailscale": "0.47.2",
110
+ "@procedures/vite": "0.47.2",
111
+ "@procedures/weather": "0.47.2",
112
112
  "@types/node": "^22.0.0",
113
113
  "fast-check": "^4.10.2",
114
114
  "typescript": "^7.0.2",
@@ -116,7 +116,7 @@
116
116
  },
117
117
  "scripts": {
118
118
  "build": "tsc -b && node dist/manifest-build.js",
119
- "typecheck": "tsc --noEmit",
119
+ "typecheck": "tsc --noEmit && tsc -p tsconfig.test.json",
120
120
  "clean": "rm -rf dist .tsbuildinfo",
121
121
  "test": "vitest run",
122
122
  "test:watch": "vitest",
package/src/bin.ts CHANGED
@@ -7,8 +7,14 @@
7
7
  *
8
8
  * The bin owns its process: the runner of each command owns the signals, and it ends the process
9
9
  * with the exit code of the command (CLI-26).
10
+ *
11
+ * This file enables the compile cache first (roadmap O90). Node.js loads each static import before
12
+ * the first statement, so the CLI loads with a dynamic import after the cache starts.
10
13
  */
11
14
 
12
- import { main } from "./cli.js";
15
+ import { enableCompileCache } from "./compile-cache.js";
16
+
17
+ enableCompileCache();
13
18
 
19
+ const { main } = await import("./cli.js");
14
20
  await main(process.argv.slice(2), { ownsProcess: true });
package/src/cli.ts CHANGED
@@ -16,7 +16,7 @@ import { fieldsOf, generateHelp, resultFieldsOf, type CLIMeta, type CommandLike
16
16
  import { readFreshManifest, writeManifest, type ManifestCommand } from "./manifest.js";
17
17
  import { askMissing, canAsk, missingFields, processTerminal, terminalAsker, type Asker, type Terminal } from "./ask.js";
18
18
  import { formatOutput, type OutputFormat, type Print } from "./format.js";
19
- import { loadEcosystemRegistry } from "./ecosystem.js";
19
+ import { createLazyEcosystemRegistry, indexOfCommands, loadEcosystemRegistry, packageIndexOf } from "./ecosystem.js";
20
20
  import { startServerMode, parsePort, parseTransport } from "./server-mode.js";
21
21
  import { connectCliServer } from "./client-mode.js";
22
22
  import type { ColdCommand, CommandRun } from "./command.js";
@@ -202,6 +202,37 @@ export async function initCli(verbose = false, fsRoot?: string): Promise<CliCont
202
202
  return { client, procedures, registry, verbose, ...(ecosystem.fsRoot !== undefined ? { fsRoot: ecosystem.fsRoot } : {}) };
203
203
  }
204
204
 
205
+ /**
206
+ * This function makes the context of a run from a fresh manifest (roadmap O93). Its registry is
207
+ * lazy: it has the core procedures at once, and a call loads only the packages that the manifest
208
+ * names for its path. A list, the help of the registry and `procedure.list` load each package
209
+ * first (`loadAll()`). `procedures` gives the procedures that have loaded at the time of the read.
210
+ * A root of the files (`--fs-root`) needs each procedure, so `run()` then uses `initCli()`.
211
+ */
212
+ export async function initLazyCli(verbose: boolean, commands: readonly ManifestCommand[]): Promise<CliContext> {
213
+ const { Client, LocalTransport } = await import("@procedures/client");
214
+ const registry = createLazyEcosystemRegistry(indexOfCommands(commands), verbose, true);
215
+ const transport = new LocalTransport({ registry });
216
+ const client = new Client({ transport }).useRegistry(registry);
217
+ return {
218
+ client,
219
+ get procedures(): AnyProcedure[] {
220
+ return registry.getAll();
221
+ },
222
+ registry,
223
+ verbose,
224
+ };
225
+ }
226
+
227
+ /**
228
+ * The procedure of a command in the context of a run. A lazy registry loads the packages of the
229
+ * path first (roadmap O93).
230
+ */
231
+ async function procedureOfCommand(runtime: CliContext, path: readonly string[]): Promise<AnyProcedure | undefined> {
232
+ if (runtime.registry?.lazy) return runtime.registry.resolve([...path]);
233
+ return findProcedure(runtime.procedures, path);
234
+ }
235
+
205
236
  /**
206
237
  * Execute a single command given argv tokens.
207
238
  * Reusable by both CLI entry point and REPL.
@@ -434,7 +465,14 @@ export async function executeArgs(
434
465
  fail(`Error: ${messageOf(error)}`);
435
466
  return;
436
467
  }
437
- const target = findProcedure(runtime.procedures, path);
468
+ let target: AnyProcedure | undefined;
469
+ try {
470
+ target = await procedureOfCommand(runtime, path);
471
+ } catch (error) {
472
+ // A package of the path did not load: the error has the load error
473
+ fail(`Error: ${messageOf(error)}`);
474
+ return;
475
+ }
438
476
  if (!target) {
439
477
  fail(`Unknown command: mark ${path.join(" ")}`, "Run 'mark --help' for available commands.");
440
478
  return;
@@ -755,7 +793,9 @@ async function run(argv: string[], options: RunCliOptions = {}): Promise<void> {
755
793
  // server do not load them (roadmap 1.3)
756
794
  const manifest = readFreshManifest();
757
795
  if (manifest) {
758
- await executeArgs(argv, { commands: manifest.commands, verbose, fsRoot, load: () => initCli(verbose, fsRoot) }, execute);
796
+ // A call loads only the packages of its path (roadmap O93). A root of the files needs each procedure.
797
+ const load = fsRoot === undefined ? () => initLazyCli(verbose, manifest.commands) : () => initCli(verbose, fsRoot);
798
+ await executeArgs(argv, { commands: manifest.commands, verbose, fsRoot, load }, execute);
759
799
  return;
760
800
  }
761
801
 
@@ -763,7 +803,7 @@ async function run(argv: string[], options: RunCliOptions = {}): Promise<void> {
763
803
  // write the manifest for the next run. A failure to write costs only time.
764
804
  const ctx = await initCli(verbose, fsRoot);
765
805
  try {
766
- await writeManifest(ctx.procedures);
806
+ await writeManifest(ctx.procedures, packageIndexOf(ctx.registry));
767
807
  } catch (error) {
768
808
  if (verbose) {
769
809
  print.warning(`mark: the manifest was not written: ${error instanceof Error ? error.message : String(error)}`);
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The compile cache of Node.js for the `mark` executable (roadmap O90).
3
+ *
4
+ * With the cache, a run of `mark` reads the compiled code of its modules from a folder and does not
5
+ * compile them again. In CI, one `mark` call took 1019 ms with no cache and 754 ms with a warm cache.
6
+ * The first run after an install or a change of a file writes the cache, and costs 174 ms more.
7
+ *
8
+ * The folder is in the cache folder of the user, so a reboot keeps it and other users do not share it.
9
+ * `NODE_COMPILE_CACHE` replaces the folder, and `NODE_DISABLE_COMPILE_CACHE=1` turns the cache off.
10
+ */
11
+
12
+ import module from "node:module";
13
+ import { homedir } from "node:os";
14
+ import * as path from "node:path";
15
+
16
+ /** The part of `node:module` that this module uses. A test gives its own. */
17
+ export interface CompileCacheApi {
18
+ readonly enableCompileCache?: ((dir?: string) => { status: number; message?: string; directory?: string }) | undefined;
19
+ }
20
+
21
+ /**
22
+ * This function gives the folder of the compile cache of `mark` for a platform: the cache folder of
23
+ * the user, then `mark/compile-cache`. Linux uses `XDG_CACHE_HOME` or `~/.cache`, macOS uses
24
+ * `~/Library/Caches`, and Windows uses `LOCALAPPDATA`.
25
+ */
26
+ export function compileCacheDir(platform: NodeJS.Platform = process.platform, env: NodeJS.ProcessEnv = process.env, home: string = homedir()): string {
27
+ const join = platform === "win32" ? path.win32.join : path.posix.join;
28
+ const base =
29
+ platform === "win32"
30
+ ? (env["LOCALAPPDATA"] ?? path.win32.join(home, "AppData", "Local"))
31
+ : platform === "darwin"
32
+ ? path.posix.join(home, "Library", "Caches")
33
+ : env["XDG_CACHE_HOME"] !== undefined && path.posix.isAbsolute(env["XDG_CACHE_HOME"])
34
+ ? env["XDG_CACHE_HOME"]
35
+ : path.posix.join(home, ".cache");
36
+ return join(base, "mark", "compile-cache");
37
+ }
38
+
39
+ /**
40
+ * This function enables the compile cache in the folder of `compileCacheDir()`. It gives the result
41
+ * of Node.js, or `undefined` when this Node.js has no compile cache (before 22.1). It never throws:
42
+ * a folder that cannot be written gives the status `FAILED`, and `mark` runs with no cache.
43
+ */
44
+ export function enableCompileCache(api: CompileCacheApi = module, dir: () => string = compileCacheDir): ReturnType<NonNullable<CompileCacheApi["enableCompileCache"]>> | undefined {
45
+ if (typeof api.enableCompileCache !== "function") return undefined;
46
+ try {
47
+ return api.enableCompileCache(dir());
48
+ } catch {
49
+ return undefined;
50
+ }
51
+ }
package/src/ecosystem.ts CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  PROCEDURE_REGISTRY,
20
20
  allCoreProcedures,
21
21
  clientModule,
22
+ createLazyRegistry,
22
23
  createRegistry,
23
24
  defineModule,
24
25
  pathToKey,
@@ -40,7 +41,8 @@ interface PackageJson {
40
41
  exports?: Record<string, unknown>;
41
42
  }
42
43
 
43
- interface DiscoveredPackage {
44
+ /** A workspace package that declares `client.procedures`. */
45
+ export interface DiscoveredPackage {
44
46
  name: string;
45
47
  path: string;
46
48
  proceduresPath: string;
@@ -205,10 +207,18 @@ export function listEcosystemProcedurePackages(): Array<{ name: string; path: st
205
207
  /** The core procedures that `mark` serves: all of them, also the ones that the core import does not register. */
206
208
  const allCoreModule: ProcedureModule = defineModule("@procedures/client (all core procedures)", allCoreProcedures);
207
209
 
210
+ /**
211
+ * The index of the packages of a registry (roadmap O93): the key of each path and the names of
212
+ * the packages that give it, in the order of the eager load. A core procedure has no package.
213
+ */
214
+ export type PackageIndex = Record<string, string[]>;
215
+
208
216
  /** The registry of `mark` and the names of the packages that it holds. */
209
217
  export interface EcosystemRegistry {
210
218
  registry: ProcedureRegistry;
211
219
  loaded: string[];
220
+ /** The packages of each path that a package gives. */
221
+ index: PackageIndex;
212
222
  /** The absolute path of the root of the files (`--fs-root`), or undefined. */
213
223
  fsRoot?: string | undefined;
214
224
  /** The keys of the procedures that the root refuses, or undefined without a root. */
@@ -248,6 +258,12 @@ async function confineRoot(
248
258
  return { root: folder, refused };
249
259
  }
250
260
 
261
+ /**
262
+ * The end of the last diff of the global registry. The lazy registry can load two packages at
263
+ * the same time: the diffs of the packages of the old kind go one after the other.
264
+ */
265
+ let globalDiffs: Promise<unknown> = Promise.resolve();
266
+
251
267
  /**
252
268
  * The module of a package: its `./module` entry (it registers nothing), or else the procedures
253
269
  * that importing its procedures file adds to the global registry (a package of the old kind).
@@ -262,10 +278,90 @@ async function moduleOfPackage(pkg: DiscoveredPackage): Promise<ProcedureModule
262
278
  }
263
279
  const proceduresFile = path.join(pkg.path, pkg.proceduresPath);
264
280
  if (!fs.existsSync(proceduresFile)) return undefined;
265
- const before = new Set(PROCEDURE_REGISTRY.getAll().map((procedure) => pathToKey(procedure.path)));
266
- await import(pathToFileURL(proceduresFile).href);
267
- const added: AnyProcedure[] = PROCEDURE_REGISTRY.getAll().filter((procedure) => !before.has(pathToKey(procedure.path)));
268
- return { name: pkg.name, procedures: added };
281
+ const diff = globalDiffs.then(async (): Promise<ProcedureModule> => {
282
+ const before = new Set(PROCEDURE_REGISTRY.getAll().map((procedure) => pathToKey(procedure.path)));
283
+ await import(pathToFileURL(proceduresFile).href);
284
+ const added: AnyProcedure[] = PROCEDURE_REGISTRY.getAll().filter((procedure) => !before.has(pathToKey(procedure.path)));
285
+ return { name: pkg.name, procedures: added };
286
+ });
287
+ globalDiffs = diff.catch(() => undefined);
288
+ return diff;
289
+ }
290
+
291
+ /** The keys of the procedures of a registry. */
292
+ function keysOf(registry: ProcedureRegistry): Set<string> {
293
+ return new Set(registry.getAll().map((procedure) => pathToKey(procedure.path)));
294
+ }
295
+
296
+ /**
297
+ * This function adds the paths of a package to the index. The paths of its own procedures name it.
298
+ * A path that only a module that it requires gives names the first package that registered it.
299
+ */
300
+ function indexPackage(index: PackageIndex, fallback: Map<string, string>, name: string, module: ProcedureModule, before: ReadonlySet<string>, after: ReadonlySet<string>): void {
301
+ for (const procedure of module.procedures) {
302
+ const key = pathToKey(procedure.path);
303
+ // A procedure that did not register (a conflict stopped the package) is not in the index
304
+ if (!after.has(key)) continue;
305
+ const names = (index[key] ??= []);
306
+ if (!names.includes(name)) names.push(name);
307
+ }
308
+ for (const key of after) if (!before.has(key) && !fallback.has(key)) fallback.set(key, name);
309
+ }
310
+
311
+ /** The index of a registry that `loadEcosystemRegistry()` made, by registry. */
312
+ const INDEXES = new WeakMap<ProcedureRegistry, PackageIndex>();
313
+
314
+ /** This function gives the index of the packages of an eager registry of `mark`, or undefined. */
315
+ export function packageIndexOf(registry: ProcedureRegistry | undefined): PackageIndex | undefined {
316
+ return registry === undefined ? undefined : INDEXES.get(registry);
317
+ }
318
+
319
+ /**
320
+ * This function gives the index of the commands of a manifest: the key of each path that a
321
+ * package gives, and the names of the packages.
322
+ */
323
+ export function indexOfCommands(commands: ReadonlyArray<{ path: readonly string[]; packages?: readonly string[] | undefined }>): PackageIndex {
324
+ const index: PackageIndex = {};
325
+ for (const command of commands) {
326
+ if (command.packages !== undefined && command.packages.length > 0) index[pathToKey(command.path)] = [...command.packages];
327
+ }
328
+ return index;
329
+ }
330
+
331
+ /**
332
+ * The lazy registry of `mark` (roadmap O93). It has all the core procedures at once. A call of a
333
+ * path loads only the packages that the index names for it. `loadAll()` loads each package in the
334
+ * order of `discoverFromEcosystem()`, as `loadEcosystemRegistry()` does. A package that fails to
335
+ * load gives the warning of the eager load.
336
+ */
337
+ export function createLazyEcosystemRegistry(
338
+ index: Readonly<Record<string, readonly string[]>>,
339
+ verbose = false,
340
+ warn = true,
341
+ discovered: readonly DiscoveredPackage[] = discoverFromEcosystem(),
342
+ ): ProcedureRegistry {
343
+ const byName = new Map(discovered.map((pkg) => [pkg.name, pkg]));
344
+ return createLazyRegistry({
345
+ modules: [allCoreModule],
346
+ index,
347
+ packages: discovered.map((pkg) => pkg.name),
348
+ load: async (name) => {
349
+ const pkg = byName.get(name);
350
+ if (!pkg) return undefined;
351
+ try {
352
+ const module = await moduleOfPackage(pkg);
353
+ if (!module && verbose) console.warn(`Procedures file not found (build the package): ${path.join(pkg.path, pkg.proceduresPath)}`);
354
+ if (module && verbose) console.log(`Loaded procedures from: ${pkg.name}`);
355
+ return module;
356
+ } catch (error) {
357
+ if (verbose && error instanceof Error && error.stack) console.warn(error.stack);
358
+ throw error;
359
+ }
360
+ },
361
+ warn: (message) => {
362
+ if (warn) console.warn(`mark: ${message}`);
363
+ },
364
+ });
269
365
  }
270
366
 
271
367
  /**
@@ -278,6 +374,8 @@ async function moduleOfPackage(pkg: DiscoveredPackage): Promise<ProcedureModule
278
374
  export async function loadEcosystemRegistry(verbose = false, warn = true, options: EcosystemOptions = {}): Promise<EcosystemRegistry> {
279
375
  const registry = createRegistry(allCoreModule);
280
376
  const loaded: string[] = [];
377
+ const index: PackageIndex = {};
378
+ const fallback = new Map<string, string>();
281
379
  const discovered = discoverFromEcosystem();
282
380
  for (const pkg of discovered) {
283
381
  try {
@@ -286,7 +384,13 @@ export async function loadEcosystemRegistry(verbose = false, warn = true, option
286
384
  if (verbose) console.warn(`Procedures file not found (build the package): ${path.join(pkg.path, pkg.proceduresPath)}`);
287
385
  continue;
288
386
  }
289
- registerModules(registry, [module]);
387
+ const before = keysOf(registry);
388
+ try {
389
+ registerModules(registry, [module]);
390
+ } finally {
391
+ // The paths that the package registered before an error count too: the lazy load registers them
392
+ indexPackage(index, fallback, pkg.name, module, before, keysOf(registry));
393
+ }
290
394
  loaded.push(pkg.name);
291
395
  if (verbose) console.log(`Loaded procedures from: ${pkg.name}`);
292
396
  } catch (error) {
@@ -295,8 +399,11 @@ export async function loadEcosystemRegistry(verbose = false, warn = true, option
295
399
  if (verbose && error instanceof Error && error.stack) console.warn(error.stack);
296
400
  }
297
401
  }
298
- if (options.fsRoot === undefined) return { registry, loaded };
402
+ // A path that only a required module gives names the first package that registered it
403
+ for (const [key, name] of fallback) index[key] ??= [name];
404
+ INDEXES.set(registry, index);
405
+ if (options.fsRoot === undefined) return { registry, loaded, index };
299
406
  // After the last package: each procedure of the registry gets the rules of the root
300
407
  const { root: fsRoot, refused } = await confineRoot(registry, options.fsRoot, discovered);
301
- return { registry, loaded, fsRoot, refusedUnderRoot: refused };
408
+ return { registry, loaded, index, fsRoot, refusedUnderRoot: refused };
302
409
  }
@@ -7,6 +7,7 @@
7
7
  import { loadEcosystemRegistry } from "./ecosystem.js";
8
8
  import { manifestPath, writeManifest } from "./manifest.js";
9
9
 
10
- const { registry, loaded } = await loadEcosystemRegistry(false, false);
11
- const manifest = await writeManifest(registry.getAll());
10
+ const { registry, loaded, index } = await loadEcosystemRegistry(false, false);
11
+ // Each command records the packages of its path: the lazy registry of a run loads only those (roadmap O93)
12
+ const manifest = await writeManifest(registry.getAll(), index);
12
13
  console.log(`mark: ${manifestPath()} has ${manifest.commands.length} commands from ${loaded.length} packages`);
package/src/manifest.ts CHANGED
@@ -17,16 +17,16 @@ import { createHash } from "node:crypto";
17
17
  import * as fs from "node:fs";
18
18
  import * as path from "node:path";
19
19
  import { fileURLToPath } from "node:url";
20
- import { describeProcedureEntry, jsonSchemaOf, type AnyProcedure, type ProcedureEntry } from "@procedures/client";
20
+ import { describeProcedureEntry, jsonSchemaOf, pathToKey, type AnyProcedure, type ProcedureEntry } from "@procedures/client";
21
21
  import { writeFileAtomicSync } from "@procedures/fs/files";
22
22
  import { discoverFromEcosystem, findWorkspaceRoot } from "./ecosystem.js";
23
23
  import { extractSchemaFields, resultFieldsOfSchema, type CLIMeta, type SchemaFieldInfo } from "./parse.js";
24
24
 
25
25
  /**
26
26
  * The format of the manifest. A change of the format makes old manifests stale.
27
- * Version 2 adds `resultFields`.
27
+ * Version 2 adds `resultFields`. Version 3 adds `packages` (roadmap O93).
28
28
  */
29
- export const MANIFEST_VERSION = 2;
29
+ export const MANIFEST_VERSION = 3;
30
30
 
31
31
  /** One command of the manifest */
32
32
  export interface ManifestCommand {
@@ -39,6 +39,11 @@ export interface ManifestCommand {
39
39
  inputSchema: unknown;
40
40
  /** The fields of the result, for the help (none when the result is not an object) */
41
41
  resultFields: SchemaFieldInfo[];
42
+ /**
43
+ * The packages that give the procedure, in the order of the eager load (roadmap O93). A core
44
+ * procedure has none. The lazy registry loads only these packages for a call of the path.
45
+ */
46
+ packages: string[];
42
47
  }
43
48
 
44
49
  export interface Manifest {
@@ -111,8 +116,14 @@ function cliMetadata(entry: ProcedureEntry): CLIMeta {
111
116
  return result;
112
117
  }
113
118
 
114
- /** The commands of the manifest, from the procedures of the registry */
115
- export async function commandsOf(procedures: readonly AnyProcedure[]): Promise<ManifestCommand[]> {
119
+ /**
120
+ * The commands of the manifest, from the procedures of the registry. The index gives the packages
121
+ * of each path (`packageIndexOf()`). Without it, each command has no package.
122
+ */
123
+ export async function commandsOf(
124
+ procedures: readonly AnyProcedure[],
125
+ index: Readonly<Record<string, readonly string[]>> = {},
126
+ ): Promise<ManifestCommand[]> {
116
127
  return procedures.map((procedure) => {
117
128
  // One JSON Schema for each command: the fields come from it too (roadmap 2.1). The schema and
118
129
  // the CLI metadata come from the one manifest type of the core (roadmap R17).
@@ -124,6 +135,7 @@ export async function commandsOf(procedures: readonly AnyProcedure[]): Promise<M
124
135
  fields: extractSchemaFields(inputSchema),
125
136
  inputSchema,
126
137
  resultFields: resultFieldsOfSchema(procedure.output),
138
+ packages: [...(index[pathToKey(procedure.path)] ?? [])],
127
139
  };
128
140
  });
129
141
  }
@@ -133,13 +145,20 @@ function toJson(manifest: Manifest): string {
133
145
  return JSON.stringify(manifest, (_key, value: unknown) => (typeof value === "bigint" ? value.toString() : value), 1);
134
146
  }
135
147
 
136
- /** Write the manifest of these procedures for the current fingerprint (a temporary file, then a rename) */
137
- export async function writeManifest(procedures: readonly AnyProcedure[], file: string = manifestPath()): Promise<Manifest> {
148
+ /**
149
+ * Write the manifest of these procedures for the current fingerprint (a temporary file, then a
150
+ * rename). The index gives the packages of each path.
151
+ */
152
+ export async function writeManifest(
153
+ procedures: readonly AnyProcedure[],
154
+ index: Readonly<Record<string, readonly string[]>> = {},
155
+ file: string = manifestPath(),
156
+ ): Promise<Manifest> {
138
157
  const manifest: Manifest = {
139
158
  version: MANIFEST_VERSION,
140
159
  fingerprint: computeFingerprint(),
141
160
  generatedAt: new Date().toISOString(),
142
- commands: await commandsOf(procedures),
161
+ commands: await commandsOf(procedures, index),
143
162
  };
144
163
  // writeFileAtomicSync flushes the file, tries the rename again on Windows and removes the temporary file after a failure
145
164
  writeFileAtomicSync(file, toJson(manifest));
package/src/print.ts CHANGED
@@ -7,7 +7,16 @@
7
7
 
8
8
  import chalk from "chalk";
9
9
  import ora from "ora";
10
- import Table from "cli-table3";
10
+ import { createRequire } from "node:module";
11
+ import type TableClass from "cli-table3";
12
+
13
+ /**
14
+ * This function gives the table class of `cli-table3`. The first table loads the package, not the
15
+ * start of `mark`: a call that prints no table does not load it (roadmap O93).
16
+ */
17
+ function tableClass(): typeof TableClass {
18
+ return createRequire(import.meta.url)("cli-table3") as typeof TableClass;
19
+ }
11
20
 
12
21
  /**
13
22
  * Spinner interface (compatible with existing code)
@@ -71,6 +80,7 @@ export const print = {
71
80
  const [headers, ...rows] = data;
72
81
  if (!headers) return;
73
82
 
83
+ const Table = tableClass();
74
84
  const table = new Table({
75
85
  head: headers.map((h) => chalk.bold(h)),
76
86
  style: {