@navbook/cli 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/args.d.ts +28 -0
- package/dist/args.js +48 -0
- package/dist/args.js.map +1 -0
- package/dist/commands/complete.d.ts +10 -0
- package/dist/commands/complete.js +59 -51
- package/dist/commands/complete.js.map +1 -1
- package/dist/commands/compose.d.ts +35 -0
- package/dist/commands/doctor.d.ts +15 -0
- package/dist/commands/entity.d.ts +73 -0
- package/dist/commands/entity.js +59 -44
- package/dist/commands/entity.js.map +1 -1
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/install.d.ts +17 -0
- package/dist/commands/install.js +6 -3
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/issue.d.ts +35 -0
- package/dist/commands/issue.js +4 -5
- package/dist/commands/issue.js.map +1 -1
- package/dist/commands/plugin.d.ts +36 -0
- package/dist/commands/plugin.js +386 -0
- package/dist/commands/plugin.js.map +1 -0
- package/dist/commands/policy.d.ts +58 -0
- package/dist/commands/policy.js +13 -0
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/pr-elsewhere.d.ts +40 -0
- package/dist/commands/pr-elsewhere.js +209 -0
- package/dist/commands/pr-elsewhere.js.map +1 -0
- package/dist/commands/pr.d.ts +69 -0
- package/dist/commands/pr.js +86 -60
- package/dist/commands/pr.js.map +1 -1
- package/dist/context.d.ts +42 -0
- package/dist/context.js +2 -0
- package/dist/context.js.map +1 -1
- package/dist/editor.d.ts +23 -0
- package/dist/editor.js +4 -0
- package/dist/editor.js.map +1 -1
- package/dist/errors.d.ts +17 -0
- package/dist/install/completions.d.ts +19 -0
- package/dist/install/hook.d.ts +32 -0
- package/dist/install/hook.js +48 -4
- package/dist/install/hook.js.map +1 -1
- package/dist/main.d.ts +21 -0
- package/dist/main.js +112 -8
- package/dist/main.js.map +1 -1
- package/dist/plugins/commands.d.ts +69 -0
- package/dist/plugins/commands.js +148 -0
- package/dist/plugins/commands.js.map +1 -0
- package/dist/plugins/hint.d.ts +23 -0
- package/dist/plugins/hint.js +65 -0
- package/dist/plugins/hint.js.map +1 -0
- package/dist/plugins/host.d.ts +125 -0
- package/dist/plugins/host.js +18 -0
- package/dist/plugins/host.js.map +1 -0
- package/dist/plugins/loader.d.ts +54 -0
- package/dist/plugins/loader.js +165 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/resolve.d.ts +50 -0
- package/dist/plugins/resolve.js +134 -0
- package/dist/plugins/resolve.js.map +1 -0
- package/dist/plugins/runtime.d.ts +69 -0
- package/dist/plugins/runtime.js +256 -0
- package/dist/plugins/runtime.js.map +1 -0
- package/dist/plugins/store.d.ts +96 -0
- package/dist/plugins/store.js +186 -0
- package/dist/plugins/store.js.map +1 -0
- package/dist/program.d.ts +49 -0
- package/dist/program.js +351 -113
- package/dist/program.js.map +1 -1
- package/dist/prompt.d.ts +31 -0
- package/dist/render/colors.d.ts +13 -0
- package/dist/render/detail.d.ts +22 -0
- package/dist/render/table.d.ts +32 -0
- package/dist/render/table.js +123 -13
- package/dist/render/table.js.map +1 -1
- package/dist/sort.d.ts +27 -0
- package/package.json +9 -2
- package/dist/commands/feature.js +0 -202
- package/dist/commands/feature.js.map +0 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which plugins this invocation has, and which it was told about but lacks.
|
|
3
|
+
*
|
|
4
|
+
* Two sources, in order: `NAVBOOK_PLUGIN_PATH`, for a plugin being written and
|
|
5
|
+
* not installed anywhere, and the store, for everything else. A path entry
|
|
6
|
+
* shadows an installed copy of the same package — otherwise developing a
|
|
7
|
+
* plugin you have also installed would silently test the installed one.
|
|
8
|
+
*
|
|
9
|
+
* The declaration (spec 02 §2.12) is read here, and read only to *report*:
|
|
10
|
+
* nothing in this file fetches anything. A plugin the repository names and the
|
|
11
|
+
* machine does not have is one line on stderr and a run that carries on, which
|
|
12
|
+
* is the rule that makes cloning a repository safe.
|
|
13
|
+
*/
|
|
14
|
+
import { type PluginDeclarationReading, type WsCtx } from "@navbook/core";
|
|
15
|
+
import { type IndexedPlugin } from "./store.ts";
|
|
16
|
+
export declare const PLUGIN_PATH_ENV = "NAVBOOK_PLUGIN_PATH";
|
|
17
|
+
/** An installed plugin, with the settings the repository declares for it. */
|
|
18
|
+
export interface LoadedPlugin extends IndexedPlugin {
|
|
19
|
+
/** What `navbook.json` declares under this name; empty when undeclared. */
|
|
20
|
+
settings: Record<string, unknown>;
|
|
21
|
+
/** True when the repository's marker names it (spec 02 §2.12). */
|
|
22
|
+
declared: boolean;
|
|
23
|
+
/** True when it came from `NAVBOOK_PLUGIN_PATH` rather than the store. */
|
|
24
|
+
fromPath: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface ResolvedPlugins {
|
|
27
|
+
/** Usable plugins, in the order their contributions should be applied. */
|
|
28
|
+
active: LoadedPlugin[];
|
|
29
|
+
/** Declared by the repository, absent from this machine. */
|
|
30
|
+
missing: string[];
|
|
31
|
+
/** Found but not used, each with the reason, for one line on stderr. */
|
|
32
|
+
skipped: {
|
|
33
|
+
name: string;
|
|
34
|
+
reason: string;
|
|
35
|
+
}[];
|
|
36
|
+
/** What the marker declared, for a caller that wants the settings or faults. */
|
|
37
|
+
declaration: PluginDeclarationReading;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve the plugins for this invocation.
|
|
41
|
+
*
|
|
42
|
+
* Costs one `plugins.json` read in the common case, and one `package.json`
|
|
43
|
+
* read per `NAVBOOK_PLUGIN_PATH` entry, which is a development path. The
|
|
44
|
+
* marker is read too, but the workspace reads it anyway for the review policy.
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolvePlugins(ws: WsCtx, env: NodeJS.ProcessEnv): ResolvedPlugins;
|
|
47
|
+
/** The line printed for a plugin the repository declares and the machine lacks. */
|
|
48
|
+
export declare function missingLine(name: string, navDir: string): string;
|
|
49
|
+
/** The line printed for a plugin that was found but cannot be used. */
|
|
50
|
+
export declare function skippedLine(name: string, reason: string): string;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which plugins this invocation has, and which it was told about but lacks.
|
|
3
|
+
*
|
|
4
|
+
* Two sources, in order: `NAVBOOK_PLUGIN_PATH`, for a plugin being written and
|
|
5
|
+
* not installed anywhere, and the store, for everything else. A path entry
|
|
6
|
+
* shadows an installed copy of the same package — otherwise developing a
|
|
7
|
+
* plugin you have also installed would silently test the installed one.
|
|
8
|
+
*
|
|
9
|
+
* The declaration (spec 02 §2.12) is read here, and read only to *report*:
|
|
10
|
+
* nothing in this file fetches anything. A plugin the repository names and the
|
|
11
|
+
* machine does not have is one line on stderr and a run that carries on, which
|
|
12
|
+
* is the rule that makes cloning a repository safe.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync } from "node:fs";
|
|
15
|
+
import { isAbsolute, resolve as resolvePath } from "node:path";
|
|
16
|
+
import { PLUGIN_API_VERSION, readPluginDeclaration, satisfiesRange, } from "@navbook/core";
|
|
17
|
+
import { readIndex, readPluginAt } from "./store.js";
|
|
18
|
+
export const PLUGIN_PATH_ENV = "NAVBOOK_PLUGIN_PATH";
|
|
19
|
+
const NOTHING = {
|
|
20
|
+
active: [],
|
|
21
|
+
missing: [],
|
|
22
|
+
skipped: [],
|
|
23
|
+
declaration: { plugins: new Map(), declared: false, problems: [] },
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the plugins for this invocation.
|
|
27
|
+
*
|
|
28
|
+
* Costs one `plugins.json` read in the common case, and one `package.json`
|
|
29
|
+
* read per `NAVBOOK_PLUGIN_PATH` entry, which is a development path. The
|
|
30
|
+
* marker is read too, but the workspace reads it anyway for the review policy.
|
|
31
|
+
*/
|
|
32
|
+
export function resolvePlugins(ws, env) {
|
|
33
|
+
const fromPath = pathPlugins(env);
|
|
34
|
+
const index = readIndex(env);
|
|
35
|
+
if (fromPath.active.length === 0 && fromPath.skipped.length === 0 && index === null) {
|
|
36
|
+
// Nothing installed and nothing on the path: the overwhelmingly common
|
|
37
|
+
// case, and it must not cost a marker read of its own.
|
|
38
|
+
return ws.hasNavbook ? undeclaredOnly(ws) : NOTHING;
|
|
39
|
+
}
|
|
40
|
+
const declaration = ws.hasNavbook
|
|
41
|
+
? readPluginDeclaration(ws)
|
|
42
|
+
: { plugins: new Map(), declared: false, problems: [] };
|
|
43
|
+
const active = [];
|
|
44
|
+
const skipped = [...fromPath.skipped];
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
const admit = (plugin, viaPath) => {
|
|
47
|
+
if (seen.has(plugin.name))
|
|
48
|
+
return;
|
|
49
|
+
const problem = incompatible(plugin);
|
|
50
|
+
if (problem !== null) {
|
|
51
|
+
skipped.push({ name: plugin.name, reason: problem });
|
|
52
|
+
seen.add(plugin.name);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
seen.add(plugin.name);
|
|
56
|
+
active.push({
|
|
57
|
+
...plugin,
|
|
58
|
+
settings: declaration.plugins.get(plugin.name) ?? {},
|
|
59
|
+
declared: declaration.plugins.has(plugin.name),
|
|
60
|
+
fromPath: viaPath,
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
// Path first, so a plugin being developed shadows an installed copy.
|
|
64
|
+
for (const plugin of fromPath.active)
|
|
65
|
+
admit(plugin, true);
|
|
66
|
+
for (const plugin of Object.values(index?.plugins ?? {}))
|
|
67
|
+
admit(plugin, false);
|
|
68
|
+
const missing = [...declaration.plugins.keys()].filter((name) => !seen.has(name));
|
|
69
|
+
return { active, missing, skipped, declaration };
|
|
70
|
+
}
|
|
71
|
+
/** The store is empty, so everything declared is missing. */
|
|
72
|
+
function undeclaredOnly(ws) {
|
|
73
|
+
const declaration = readPluginDeclaration(ws);
|
|
74
|
+
return {
|
|
75
|
+
active: [],
|
|
76
|
+
missing: [...declaration.plugins.keys()],
|
|
77
|
+
skipped: [],
|
|
78
|
+
declaration,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** Read every directory `NAVBOOK_PLUGIN_PATH` names. */
|
|
82
|
+
function pathPlugins(env) {
|
|
83
|
+
const raw = env[PLUGIN_PATH_ENV];
|
|
84
|
+
if (raw === undefined || raw.trim() === "")
|
|
85
|
+
return { active: [], skipped: [] };
|
|
86
|
+
const active = [];
|
|
87
|
+
const skipped = [];
|
|
88
|
+
for (const entry of raw.split(":")) {
|
|
89
|
+
const dir = entry.trim();
|
|
90
|
+
if (dir === "")
|
|
91
|
+
continue;
|
|
92
|
+
const path = isAbsolute(dir) ? dir : resolvePath(dir);
|
|
93
|
+
if (!existsSync(path)) {
|
|
94
|
+
skipped.push({
|
|
95
|
+
name: path,
|
|
96
|
+
reason: `${PLUGIN_PATH_ENV} names a directory that is not there`,
|
|
97
|
+
});
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const read = readPluginAt(path);
|
|
101
|
+
if ("error" in read) {
|
|
102
|
+
skipped.push({ name: path, reason: read.error });
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
active.push(read);
|
|
106
|
+
}
|
|
107
|
+
return { active, skipped };
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Why a plugin cannot be used, or null when it can.
|
|
111
|
+
*
|
|
112
|
+
* A version mismatch is reported rather than risked. The alternative is
|
|
113
|
+
* loading it anyway and discovering the mismatch as a missing function in the
|
|
114
|
+
* middle of an operation, by which point the user is reading a stack trace
|
|
115
|
+
* about a tree that is perfectly sound.
|
|
116
|
+
*/
|
|
117
|
+
function incompatible(plugin) {
|
|
118
|
+
if (plugin.engines === null) {
|
|
119
|
+
return `it declares no 'engines.navbook', so there is nothing to check it against`;
|
|
120
|
+
}
|
|
121
|
+
if (!satisfiesRange(plugin.engines, PLUGIN_API_VERSION)) {
|
|
122
|
+
return `it needs a Navbook plugin API of ${plugin.engines}; this is ${PLUGIN_API_VERSION}`;
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
/** The line printed for a plugin the repository declares and the machine lacks. */
|
|
127
|
+
export function missingLine(name, navDir) {
|
|
128
|
+
return `nav: ${name} is declared in ${navDir}/navbook.json but is not installed; run 'nav plugin install'`;
|
|
129
|
+
}
|
|
130
|
+
/** The line printed for a plugin that was found but cannot be used. */
|
|
131
|
+
export function skippedLine(name, reason) {
|
|
132
|
+
return `nav: plugin ${name} skipped: ${reason}`;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/plugins/resolve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CLI's plugin runtime: what is installed, what it contributes, and when
|
|
3
|
+
* its code is actually loaded.
|
|
4
|
+
*
|
|
5
|
+
* One of these is built per invocation and threaded through the program. It
|
|
6
|
+
* holds the resolution (cheap, always done) and memoises the loading
|
|
7
|
+
* (expensive, done only when a declaration says it is needed).
|
|
8
|
+
*
|
|
9
|
+
* The division of labour with `program.ts` is worth stating: the program asks
|
|
10
|
+
* this for *declarations* while building the command tree, and for
|
|
11
|
+
* *implementations* inside an action. Nothing in the build phase can trigger a
|
|
12
|
+
* load, which is how spec 04 §4.3's rule is kept structurally rather than by
|
|
13
|
+
* remembering to.
|
|
14
|
+
*/
|
|
15
|
+
import type { CoreExtensions, EntityKind, QueryKeySpec } from "@navbook/core";
|
|
16
|
+
import type { Ctx } from "../context.ts";
|
|
17
|
+
import { type PluginCommands } from "./commands.ts";
|
|
18
|
+
import type { VerbHandlers } from "./host.ts";
|
|
19
|
+
import type { LoadedPlugin, ResolvedPlugins } from "./resolve.ts";
|
|
20
|
+
export declare class PluginRuntime {
|
|
21
|
+
#private;
|
|
22
|
+
readonly resolved: ResolvedPlugins;
|
|
23
|
+
readonly commands: PluginCommands;
|
|
24
|
+
/** Everything worth saying on stderr once, collected while resolving. */
|
|
25
|
+
readonly notices: string[];
|
|
26
|
+
constructor(resolved: ResolvedPlugins, builtinNouns: readonly string[]);
|
|
27
|
+
/** True when no plugin is installed, which is the common case. */
|
|
28
|
+
get empty(): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Whether this command line needs the format extensions loaded.
|
|
31
|
+
*
|
|
32
|
+
* Decided from the manifests and the words typed, before anything parses,
|
|
33
|
+
* because loading them means importing plugin code — and spec 04 §4.3 says
|
|
34
|
+
* no plugin code is loaded for a command whose declaration does not name it.
|
|
35
|
+
* `nav issue list` with five plugins installed answers false here and
|
|
36
|
+
* imports nothing.
|
|
37
|
+
*
|
|
38
|
+
* The four cases that answer true:
|
|
39
|
+
*
|
|
40
|
+
* - a plugin noun is the first word, and its declaration asks for core;
|
|
41
|
+
* - a plugin contributes to this exact `noun verb`;
|
|
42
|
+
* - a term on the line names a query key a plugin declared;
|
|
43
|
+
* - the command is `doctor`, which runs every registered check.
|
|
44
|
+
*/
|
|
45
|
+
needsCoreFor(argv: readonly string[]): boolean;
|
|
46
|
+
/** Plugins whose commands are still usable after collision checking. */
|
|
47
|
+
get usable(): LoadedPlugin[];
|
|
48
|
+
/**
|
|
49
|
+
* The query terms usable plugins declared, for one noun or for either.
|
|
50
|
+
*
|
|
51
|
+
* From the manifests, so help and completion can offer them without loading
|
|
52
|
+
* a line of plugin code (spec 04 §4.3).
|
|
53
|
+
*/
|
|
54
|
+
queryKeys(kind?: EntityKind): QueryKeySpec[];
|
|
55
|
+
/**
|
|
56
|
+
* The registered format extensions, loading the `./core` entries once.
|
|
57
|
+
*
|
|
58
|
+
* Everything that reads or validates a tree needs these; nothing else does.
|
|
59
|
+
* The caller decides whether this invocation is one of those, which is where
|
|
60
|
+
* the startup budget is actually spent or saved.
|
|
61
|
+
*/
|
|
62
|
+
coreExtensions(ctx: Ctx): Promise<CoreExtensions>;
|
|
63
|
+
/** What a plugin registered for a verb, loading its `./cli` entry if needed. */
|
|
64
|
+
handlersFor(ctx: Ctx, verb: string): Promise<VerbHandlers[]>;
|
|
65
|
+
/** Run a plugin command, loading its `./cli` entry. */
|
|
66
|
+
run(ctx: Ctx, plugin: LoadedPlugin, path: string, args: string[], opts: Record<string, unknown>): Promise<void>;
|
|
67
|
+
/** Completion candidates a plugin offers for a declared completer. */
|
|
68
|
+
completions(ctx: Ctx, plugin: LoadedPlugin, id: string, words: string[]): Promise<string[]>;
|
|
69
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CLI's plugin runtime: what is installed, what it contributes, and when
|
|
3
|
+
* its code is actually loaded.
|
|
4
|
+
*
|
|
5
|
+
* One of these is built per invocation and threaded through the program. It
|
|
6
|
+
* holds the resolution (cheap, always done) and memoises the loading
|
|
7
|
+
* (expensive, done only when a declaration says it is needed).
|
|
8
|
+
*
|
|
9
|
+
* The division of labour with `program.ts` is worth stating: the program asks
|
|
10
|
+
* this for *declarations* while building the command tree, and for
|
|
11
|
+
* *implementations* inside an action. Nothing in the build phase can trigger a
|
|
12
|
+
* load, which is how spec 04 §4.3's rule is kept structurally rather than by
|
|
13
|
+
* remembering to.
|
|
14
|
+
*/
|
|
15
|
+
import { commitReport } from "@navbook/core";
|
|
16
|
+
import { composeFile } from "../commands/compose.js";
|
|
17
|
+
import { openInEditor } from "../editor.js";
|
|
18
|
+
import { fail, failFormat } from "../errors.js";
|
|
19
|
+
import { askYesNo, confirmAndPerform } from "../prompt.js";
|
|
20
|
+
import { pad, renderTable, terminalWidth } from "../render/table.js";
|
|
21
|
+
import { collectCommands } from "./commands.js";
|
|
22
|
+
import { importEntry, loadCoreExtensions } from "./loader.js";
|
|
23
|
+
export class PluginRuntime {
|
|
24
|
+
resolved;
|
|
25
|
+
commands;
|
|
26
|
+
/** Everything worth saying on stderr once, collected while resolving. */
|
|
27
|
+
notices;
|
|
28
|
+
#ext = null;
|
|
29
|
+
#activated = new Map();
|
|
30
|
+
constructor(resolved, builtinNouns) {
|
|
31
|
+
this.resolved = resolved;
|
|
32
|
+
this.commands = collectCommands(resolved.active, builtinNouns);
|
|
33
|
+
this.notices = [...this.commands.problems];
|
|
34
|
+
}
|
|
35
|
+
/** True when no plugin is installed, which is the common case. */
|
|
36
|
+
get empty() {
|
|
37
|
+
return this.resolved.active.length === 0;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Whether this command line needs the format extensions loaded.
|
|
41
|
+
*
|
|
42
|
+
* Decided from the manifests and the words typed, before anything parses,
|
|
43
|
+
* because loading them means importing plugin code — and spec 04 §4.3 says
|
|
44
|
+
* no plugin code is loaded for a command whose declaration does not name it.
|
|
45
|
+
* `nav issue list` with five plugins installed answers false here and
|
|
46
|
+
* imports nothing.
|
|
47
|
+
*
|
|
48
|
+
* The four cases that answer true:
|
|
49
|
+
*
|
|
50
|
+
* - a plugin noun is the first word, and its declaration asks for core;
|
|
51
|
+
* - a plugin contributes to this exact `noun verb`;
|
|
52
|
+
* - a term on the line names a query key a plugin declared;
|
|
53
|
+
* - the command is `doctor`, which runs every registered check.
|
|
54
|
+
*/
|
|
55
|
+
needsCoreFor(argv) {
|
|
56
|
+
if (this.empty)
|
|
57
|
+
return false;
|
|
58
|
+
// `--help` runs no command: Commander prints what the manifests already
|
|
59
|
+
// built and exits, so there is nothing for an extension to be needed by.
|
|
60
|
+
if (argv.some((word) => word === "--help" || word === "-h"))
|
|
61
|
+
return false;
|
|
62
|
+
const words = argv.filter((word) => !word.startsWith("-"));
|
|
63
|
+
// `__complete` is a stand-in for whatever is being typed, so the decision
|
|
64
|
+
// is made about *that*: completing `nav issue list` must load whatever
|
|
65
|
+
// completing `nav issue list` would.
|
|
66
|
+
//
|
|
67
|
+
// With one exception. A plugin noun on its own — `nav probe <TAB>` — is
|
|
68
|
+
// answered with its verb names, which come from the manifest, so it costs
|
|
69
|
+
// nothing. Only once a verb is on the line can completion reach an
|
|
70
|
+
// argument completer or a query term, and only then is the tree needed.
|
|
71
|
+
const completing = words[0] === "__complete";
|
|
72
|
+
if (completing) {
|
|
73
|
+
words.shift();
|
|
74
|
+
if (words.length <= 1)
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
const [noun, verb] = words;
|
|
78
|
+
if (noun === undefined)
|
|
79
|
+
return false;
|
|
80
|
+
if (noun === "doctor")
|
|
81
|
+
return true;
|
|
82
|
+
const declared = this.commands.nouns.get(noun);
|
|
83
|
+
if (declared !== undefined) {
|
|
84
|
+
// A noun's own commands default to needing core: nearly all of them read
|
|
85
|
+
// the tree, and a plugin that does not can say so.
|
|
86
|
+
const leaf = (declared.spec.commands ?? []).find((child) => child.name === verb);
|
|
87
|
+
return (leaf ?? declared.spec).needsCore !== false;
|
|
88
|
+
}
|
|
89
|
+
if (verb !== undefined && this.commands.contributions.has(`${noun} ${verb}`))
|
|
90
|
+
return true;
|
|
91
|
+
const keys = new Set(this.queryKeys().map((key) => `${key.key}:`));
|
|
92
|
+
return argv.some((word) => {
|
|
93
|
+
const colon = word.indexOf(":");
|
|
94
|
+
return colon > 0 && keys.has(word.slice(0, colon + 1));
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/** Plugins whose commands are still usable after collision checking. */
|
|
98
|
+
get usable() {
|
|
99
|
+
return this.resolved.active.filter((plugin) => !this.commands.skipped.has(plugin.name));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The query terms usable plugins declared, for one noun or for either.
|
|
103
|
+
*
|
|
104
|
+
* From the manifests, so help and completion can offer them without loading
|
|
105
|
+
* a line of plugin code (spec 04 §4.3).
|
|
106
|
+
*/
|
|
107
|
+
queryKeys(kind) {
|
|
108
|
+
return this.usable.flatMap((plugin) => (plugin.manifest.core?.queryKeys ?? []).filter((key) => kind === undefined || key.kinds.includes(kind)));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The registered format extensions, loading the `./core` entries once.
|
|
112
|
+
*
|
|
113
|
+
* Everything that reads or validates a tree needs these; nothing else does.
|
|
114
|
+
* The caller decides whether this invocation is one of those, which is where
|
|
115
|
+
* the startup budget is actually spent or saved.
|
|
116
|
+
*/
|
|
117
|
+
async coreExtensions(ctx) {
|
|
118
|
+
if (this.#ext !== null)
|
|
119
|
+
return this.#ext;
|
|
120
|
+
const { ext, problems } = await loadCoreExtensions(this.usable);
|
|
121
|
+
for (const line of problems)
|
|
122
|
+
ctx.stderr.write(`${line}\n`);
|
|
123
|
+
this.#ext = ext;
|
|
124
|
+
return ext;
|
|
125
|
+
}
|
|
126
|
+
/** What a plugin registered for a verb, loading its `./cli` entry if needed. */
|
|
127
|
+
async handlersFor(ctx, verb) {
|
|
128
|
+
const declared = this.commands.contributions.get(verb) ?? [];
|
|
129
|
+
const out = [];
|
|
130
|
+
for (const { plugin } of declared) {
|
|
131
|
+
const activated = await this.#activate(ctx, plugin);
|
|
132
|
+
const handlers = activated?.handlers.get(verb);
|
|
133
|
+
if (handlers !== undefined)
|
|
134
|
+
out.push(handlers);
|
|
135
|
+
}
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
/** Run a plugin command, loading its `./cli` entry. */
|
|
139
|
+
async run(ctx, plugin, path, args, opts) {
|
|
140
|
+
const activated = await this.#activate(ctx, plugin);
|
|
141
|
+
// Why it did not load is already on stderr; saying it does not implement
|
|
142
|
+
// the command would blame the plugin for what may be a missing package.
|
|
143
|
+
if (activated === null)
|
|
144
|
+
fail(`cannot run '${path}': plugin ${plugin.name} did not load`);
|
|
145
|
+
const run = activated.commands.get(path);
|
|
146
|
+
if (run === undefined) {
|
|
147
|
+
fail(`plugin ${plugin.name} declares '${path}' but does not implement it`);
|
|
148
|
+
}
|
|
149
|
+
await run(args, opts);
|
|
150
|
+
}
|
|
151
|
+
/** Completion candidates a plugin offers for a declared completer. */
|
|
152
|
+
async completions(ctx, plugin, id, words) {
|
|
153
|
+
const activated = await this.#activate(ctx, plugin);
|
|
154
|
+
return activated?.completers.get(id)?.(words) ?? [];
|
|
155
|
+
}
|
|
156
|
+
/** Activate a plugin's CLI entry once per process. */
|
|
157
|
+
async #activate(ctx, plugin) {
|
|
158
|
+
const existing = this.#activated.get(plugin.name);
|
|
159
|
+
if (existing !== undefined)
|
|
160
|
+
return existing;
|
|
161
|
+
const entry = await importEntry(plugin, "cli").catch((error) => {
|
|
162
|
+
ctx.stderr.write(`nav: plugin ${plugin.name} could not be loaded: ${message(error)}\n`);
|
|
163
|
+
return null;
|
|
164
|
+
});
|
|
165
|
+
if (entry === null) {
|
|
166
|
+
ctx.stderr.write(`nav: plugin ${plugin.name} has no './cli' entry\n`);
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
if (typeof entry.activate !== "function") {
|
|
170
|
+
ctx.stderr.write(`nav: plugin ${plugin.name} exports no activate() from './cli'\n`);
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
const activated = {
|
|
174
|
+
commands: new Map(),
|
|
175
|
+
handlers: new Map(),
|
|
176
|
+
completers: new Map(),
|
|
177
|
+
};
|
|
178
|
+
const declaredPaths = declaredCommandPaths(plugin);
|
|
179
|
+
const declaredVerbs = new Set((plugin.manifest.cli?.contributions ?? []).map((contribution) => contribution.on));
|
|
180
|
+
const host = {
|
|
181
|
+
core: await coreModule(),
|
|
182
|
+
manifest: plugin.manifest,
|
|
183
|
+
settings: plugin.settings,
|
|
184
|
+
// The core registrations happen through `coreExtensions`; a CLI entry
|
|
185
|
+
// calling `register` is registering for nothing, so it is refused rather
|
|
186
|
+
// than silently ignored.
|
|
187
|
+
register: () => {
|
|
188
|
+
fail(`plugin ${plugin.name}: register() belongs in './core', not './cli'`);
|
|
189
|
+
},
|
|
190
|
+
ctx,
|
|
191
|
+
command: (path, run) => {
|
|
192
|
+
if (!declaredPaths.has(path)) {
|
|
193
|
+
ctx.stderr.write(`nav: plugin ${plugin.name} implements '${path}', which its manifest does not declare\n`);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
activated.commands.set(path, run);
|
|
197
|
+
},
|
|
198
|
+
contribute: (on, handlers) => {
|
|
199
|
+
if (!declaredVerbs.has(on)) {
|
|
200
|
+
ctx.stderr.write(`nav: plugin ${plugin.name} contributes to '${on}', which its manifest does not declare\n`);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
activated.handlers.set(on, handlers);
|
|
204
|
+
},
|
|
205
|
+
completer: (id, complete) => void activated.completers.set(id, complete),
|
|
206
|
+
ui: makeUi(ctx),
|
|
207
|
+
};
|
|
208
|
+
try {
|
|
209
|
+
await entry.activate(host);
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
ctx.stderr.write(`nav: plugin ${plugin.name} failed to activate: ${message(error)}\n`);
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
this.#activated.set(plugin.name, activated);
|
|
216
|
+
return activated;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/** Every command path a plugin's manifest declares, `noun verb` style. */
|
|
220
|
+
function declaredCommandPaths(plugin) {
|
|
221
|
+
const paths = new Set();
|
|
222
|
+
const walk = (specs, prefix) => {
|
|
223
|
+
for (const spec of specs) {
|
|
224
|
+
const path = prefix === "" ? spec.name : `${prefix} ${spec.name}`;
|
|
225
|
+
const children = spec.commands;
|
|
226
|
+
if (children?.length)
|
|
227
|
+
walk(children, path);
|
|
228
|
+
else
|
|
229
|
+
paths.add(path);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
walk(plugin.manifest.cli?.commands ?? [], "");
|
|
233
|
+
return paths;
|
|
234
|
+
}
|
|
235
|
+
/** The running core, imported once and shared with every plugin. */
|
|
236
|
+
async function coreModule() {
|
|
237
|
+
return await import("@navbook/core");
|
|
238
|
+
}
|
|
239
|
+
function makeUi(ctx) {
|
|
240
|
+
return {
|
|
241
|
+
fail: (message, details) => fail(message, details ?? []),
|
|
242
|
+
failFormat: (message, details) => failFormat(message, details ?? []),
|
|
243
|
+
askYesNo: (question) => askYesNo(ctx, question),
|
|
244
|
+
confirmAndPerform: (opts) => confirmAndPerform(ctx, opts),
|
|
245
|
+
composeFile: (opts) => composeFile(ctx, opts),
|
|
246
|
+
editFile: (path) => openInEditor(ctx, path),
|
|
247
|
+
renderTable: (columns, rows) => renderTable([...columns], rows, { colors: ctx.colors, width: terminalWidth(ctx.stdout) }),
|
|
248
|
+
pad,
|
|
249
|
+
commitReport,
|
|
250
|
+
collect: (value, previous = []) => [...previous, value],
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function message(error) {
|
|
254
|
+
return error instanceof Error ? error.message : String(error);
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/plugins/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-user plugin store — spec 04 §4.3.
|
|
3
|
+
*
|
|
4
|
+
* Plugins live in a directory `nav` owns rather than beside `nav` itself,
|
|
5
|
+
* because where `nav` itself lives is not knowable: it may have arrived by
|
|
6
|
+
* `npm -g`, by pnpm, through a version manager, or not be installed at all
|
|
7
|
+
* under `npx`. A directory of our own works the same way in every one of those.
|
|
8
|
+
*
|
|
9
|
+
* The store is an ordinary npm project — a `package.json` and a `node_modules`
|
|
10
|
+
* — so resolution, transitive dependencies and updates are npm's problem and
|
|
11
|
+
* not ours. What we add on top is `plugins.json`: an index of what is
|
|
12
|
+
* installed, with each plugin's manifest copied into it. That file is why
|
|
13
|
+
* startup costs one read. Scanning `node_modules` to find out what is there
|
|
14
|
+
* would cost a directory walk and a `package.json` parse per plugin on every
|
|
15
|
+
* single `nav` invocation, which is the cost spec 05 §5.2 says we cannot pay —
|
|
16
|
+
* and is exactly the mistake Prettier removed its plugin search for.
|
|
17
|
+
*/
|
|
18
|
+
import { type PluginManifest } from "@navbook/core";
|
|
19
|
+
/** Where the store lives, honouring `$XDG_DATA_HOME` (spec 04 §4.3). */
|
|
20
|
+
export declare function storeDir(env: NodeJS.ProcessEnv): string;
|
|
21
|
+
/** One installed plugin, as the index records it. */
|
|
22
|
+
export interface IndexedPlugin {
|
|
23
|
+
name: string;
|
|
24
|
+
version: string;
|
|
25
|
+
/** Absolute path to the package directory inside the store. */
|
|
26
|
+
dir: string;
|
|
27
|
+
/** The `engines.navbook` range, or null when the plugin names none. */
|
|
28
|
+
engines: string | null;
|
|
29
|
+
/**
|
|
30
|
+
* The manifest, copied at install time.
|
|
31
|
+
*
|
|
32
|
+
* A copy rather than a reference to the package's own `package.json`,
|
|
33
|
+
* because reading that file is precisely the work the index exists to avoid.
|
|
34
|
+
* It goes stale only when the package is replaced, and replacing it goes
|
|
35
|
+
* through `nav plugin`, which rewrites the index.
|
|
36
|
+
*/
|
|
37
|
+
manifest: PluginManifest;
|
|
38
|
+
}
|
|
39
|
+
export interface PluginIndex {
|
|
40
|
+
version: 1;
|
|
41
|
+
plugins: Record<string, IndexedPlugin>;
|
|
42
|
+
}
|
|
43
|
+
export declare function indexPath(env: NodeJS.ProcessEnv): string;
|
|
44
|
+
/**
|
|
45
|
+
* Read the index, or null when there is none.
|
|
46
|
+
*
|
|
47
|
+
* Null for every fault — no store, no file, unreadable, not JSON, a version
|
|
48
|
+
* from some future `nav`. A store this `nav` cannot read is a store with no
|
|
49
|
+
* plugins in it as far as this run is concerned, and refusing to list issues
|
|
50
|
+
* because of a file in the user's data directory would be absurd. The repair
|
|
51
|
+
* is `nav plugin install`, which rewrites it.
|
|
52
|
+
*/
|
|
53
|
+
export declare function readIndex(env: NodeJS.ProcessEnv): PluginIndex | null;
|
|
54
|
+
export declare function writeIndex(env: NodeJS.ProcessEnv, index: PluginIndex): void;
|
|
55
|
+
/** Create the store's own `package.json` if it is not there yet. */
|
|
56
|
+
export declare function ensureStore(env: NodeJS.ProcessEnv): string;
|
|
57
|
+
export interface NpmResult {
|
|
58
|
+
ok: boolean;
|
|
59
|
+
/** Everything npm said, for a failure worth showing in full. */
|
|
60
|
+
output: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The npm command a given install would run, as a string to print.
|
|
64
|
+
*
|
|
65
|
+
* Printed before it runs (spec 04 §4.3), so that what the user agrees to is
|
|
66
|
+
* the command rather than a summary of it.
|
|
67
|
+
*/
|
|
68
|
+
export declare function installCommand(specs: readonly string[]): string;
|
|
69
|
+
/** Run npm in the store. Its output is captured rather than streamed. */
|
|
70
|
+
export declare function npmInstall(env: NodeJS.ProcessEnv, specs: readonly string[]): NpmResult;
|
|
71
|
+
export declare function npmRemove(env: NodeJS.ProcessEnv, names: readonly string[]): NpmResult;
|
|
72
|
+
/**
|
|
73
|
+
* Read an installed package directory as a plugin.
|
|
74
|
+
*
|
|
75
|
+
* Used at install time to check what npm actually fetched, and by
|
|
76
|
+
* `NAVBOOK_PLUGIN_PATH` to read a plugin nobody installed. Faults come back as
|
|
77
|
+
* a message so the caller can name the directory alongside them.
|
|
78
|
+
*/
|
|
79
|
+
export declare function readPluginAt(dir: string): IndexedPlugin | {
|
|
80
|
+
error: string;
|
|
81
|
+
};
|
|
82
|
+
/** Where npm put a package inside the store. */
|
|
83
|
+
export declare function packageDir(env: NodeJS.ProcessEnv, name: string): string;
|
|
84
|
+
/**
|
|
85
|
+
* The packages the store depends on, as npm recorded them: each real package
|
|
86
|
+
* name, and the specifier npm saved for it.
|
|
87
|
+
*
|
|
88
|
+
* What was *asked* for and what was *installed* are not the same string: a
|
|
89
|
+
* tarball path, a git URL and a short name all resolve to a package whose real
|
|
90
|
+
* name is inside it, and looking in `node_modules/<what-was-typed>` finds
|
|
91
|
+
* nothing. npm writes the real name into the store's own `package.json`, so
|
|
92
|
+
* reading it back — before an install and again after — is how an install
|
|
93
|
+
* learns what it actually got. The specifier is kept too, because a new
|
|
94
|
+
* version of a plugin already installed changes it without adding a name.
|
|
95
|
+
*/
|
|
96
|
+
export declare function storeDependencies(env: NodeJS.ProcessEnv): Record<string, string>;
|