@pagepocket/cli 0.11.0 → 0.12.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/commands/archive.js +80 -118
- package/dist/commands/plugin/add.js +19 -25
- package/dist/commands/plugin/doctor.js +22 -28
- package/dist/commands/plugin/ls.js +16 -22
- package/dist/commands/plugin/prune.js +13 -19
- package/dist/commands/plugin/remove.js +16 -22
- package/dist/commands/plugin/set.js +14 -22
- package/dist/commands/plugin/uninstall.js +29 -35
- package/dist/commands/plugin/update.js +22 -28
- package/dist/commands/strategy/add.js +14 -20
- package/dist/commands/strategy/doctor.js +15 -21
- package/dist/commands/strategy/ls.js +31 -17
- package/dist/commands/strategy/pin.js +10 -16
- package/dist/commands/strategy/remove.js +10 -16
- package/dist/commands/strategy/update.js +21 -27
- package/dist/commands/view.js +36 -42
- package/dist/index.js +9 -7
- package/dist/lib/filename.js +1 -5
- package/dist/services/config-service.js +30 -36
- package/dist/services/load-configured-plugins.js +8 -12
- package/dist/services/plugin-installer.js +14 -20
- package/dist/services/plugin-store.js +36 -79
- package/dist/services/strategy/builtin-strategy-registry.js +14 -32
- package/dist/services/strategy/strategy-analyze.js +14 -22
- package/dist/services/strategy/strategy-config.js +10 -17
- package/dist/services/strategy/strategy-fetch.js +12 -21
- package/dist/services/strategy/strategy-io.js +14 -30
- package/dist/services/strategy/strategy-normalize.js +9 -15
- package/dist/services/strategy/strategy-pack-read.js +12 -21
- package/dist/services/strategy/strategy-pack-store.js +12 -18
- package/dist/services/strategy/strategy-service.js +122 -117
- package/dist/services/strategy/types.js +1 -2
- package/dist/services/units/unit-store.js +16 -20
- package/dist/services/units/unit-validate.js +23 -11
- package/dist/services/user-packages/parse-pinned-spec.js +7 -17
- package/dist/services/user-packages/user-package-installer.js +8 -13
- package/dist/services/user-packages/user-package-store.js +27 -65
- package/dist/stages/prepare-output.js +6 -13
- package/dist/units/network-observer-unit.js +7 -10
- package/dist/utils/array.js +1 -7
- package/dist/utils/normalize-argv.js +3 -8
- package/dist/utils/parse-json.js +1 -5
- package/dist/utils/parse-plugin-options.js +1 -5
- package/dist/utils/parse-plugin-spec.js +5 -11
- package/dist/utils/validate-plugin-default-export.js +1 -5
- package/dist/utils/with-spinner.js +3 -10
- package/dist/view.js +14 -21
- package/package.json +16 -12
package/dist/commands/archive.js
CHANGED
|
@@ -1,61 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
const node_module_1 = require("node:module");
|
|
40
|
-
const node_url_1 = require("node:url");
|
|
41
|
-
const core_1 = require("@oclif/core");
|
|
42
|
-
const lib_1 = require("@pagepocket/lib");
|
|
43
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
44
|
-
const load_configured_plugins_1 = require("../services/load-configured-plugins");
|
|
45
|
-
const config_service_1 = require("../services/config-service");
|
|
46
|
-
const builtin_strategy_registry_1 = require("../services/strategy/builtin-strategy-registry");
|
|
47
|
-
const strategy_service_1 = require("../services/strategy/strategy-service");
|
|
48
|
-
const strategy_normalize_1 = require("../services/strategy/strategy-normalize");
|
|
49
|
-
const parse_pinned_spec_1 = require("../services/user-packages/parse-pinned-spec");
|
|
50
|
-
const unit_validate_1 = require("../services/units/unit-validate");
|
|
51
|
-
const unit_store_1 = require("../services/units/unit-store");
|
|
52
|
-
const array_1 = require("../utils/array");
|
|
53
|
-
const with_spinner_1 = require("../utils/with-spinner");
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import { Args, Command, Flags } from "@oclif/core";
|
|
4
|
+
import { ga, ns, PagePocket } from "@pagepocket/lib";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { ConfigService } from "../services/config-service.js";
|
|
7
|
+
import { loadConfiguredPlugins } from "../services/load-configured-plugins.js";
|
|
8
|
+
import { readBuiltinStrategy, listBuiltinStrategyNames } from "../services/strategy/builtin-strategy-registry.js";
|
|
9
|
+
import { normalizeStrategyUnits } from "../services/strategy/strategy-normalize.js";
|
|
10
|
+
import { StrategyService } from "../services/strategy/strategy-service.js";
|
|
11
|
+
import { UnitStore } from "../services/units/unit-store.js";
|
|
12
|
+
import { isUnitLike, resolveUnitConstructor } from "../services/units/unit-validate.js";
|
|
13
|
+
import { parsePinnedSpec } from "../services/user-packages/parse-pinned-spec.js";
|
|
14
|
+
import { uniq } from "../utils/array.js";
|
|
15
|
+
import { withSpinner } from "../utils/with-spinner.js";
|
|
54
16
|
const buildMissingStrategyError = (input) => {
|
|
55
|
-
const available =
|
|
17
|
+
const available = uniq([...input.installedNames, ...listBuiltinStrategyNames()])
|
|
56
18
|
.filter((name) => name.trim().length > 0)
|
|
57
19
|
.sort((left, right) => left.localeCompare(right));
|
|
58
|
-
const suffix = available.length > 0
|
|
20
|
+
const suffix = available.length > 0
|
|
21
|
+
? ` Available strategies: ${available.join(", ")}`
|
|
22
|
+
: " No strategies found.";
|
|
59
23
|
return new Error(`Strategy not found: ${input.strategyName}.${suffix}`);
|
|
60
24
|
};
|
|
61
25
|
const resolveStrategy = (input) => {
|
|
@@ -67,7 +31,7 @@ const resolveStrategy = (input) => {
|
|
|
67
31
|
source: "installed"
|
|
68
32
|
};
|
|
69
33
|
}
|
|
70
|
-
const builtin =
|
|
34
|
+
const builtin = readBuiltinStrategy(input.strategyName);
|
|
71
35
|
if (builtin) {
|
|
72
36
|
return {
|
|
73
37
|
name: input.strategyName,
|
|
@@ -82,26 +46,26 @@ const resolveStrategy = (input) => {
|
|
|
82
46
|
};
|
|
83
47
|
const assertNoDuplicateUnitIds = (input) => {
|
|
84
48
|
const seen = new Set();
|
|
85
|
-
const
|
|
86
|
-
if (seen.has(
|
|
49
|
+
const duplicateUnit = input.units.find((unit) => {
|
|
50
|
+
if (seen.has(unit.id)) {
|
|
87
51
|
return true;
|
|
88
52
|
}
|
|
89
|
-
seen.add(
|
|
53
|
+
seen.add(unit.id);
|
|
90
54
|
return false;
|
|
91
55
|
});
|
|
92
|
-
if (
|
|
93
|
-
throw new Error(`Duplicate unit id detected in strategy ${input.strategyName}: ${
|
|
56
|
+
if (duplicateUnit) {
|
|
57
|
+
throw new Error(`Duplicate unit id detected in strategy ${input.strategyName}: ${duplicateUnit.id}`);
|
|
94
58
|
}
|
|
95
59
|
};
|
|
96
60
|
const loadInstalledStrategyUnits = async (input) => {
|
|
97
|
-
const strategyService = new
|
|
98
|
-
const unitStore = new
|
|
61
|
+
const strategyService = new StrategyService(input.configService);
|
|
62
|
+
const unitStore = new UnitStore(input.configService);
|
|
99
63
|
strategyService.ensureConfigFileExists();
|
|
100
64
|
const strategyFile = strategyService.readStrategy(input.strategyName);
|
|
101
|
-
const normalized =
|
|
65
|
+
const normalized = normalizeStrategyUnits(strategyFile);
|
|
102
66
|
const installed = unitStore.readInstalledDependencyVersions();
|
|
103
|
-
const drift = normalized.flatMap((
|
|
104
|
-
const pinned =
|
|
67
|
+
const drift = normalized.flatMap((unit) => {
|
|
68
|
+
const pinned = parsePinnedSpec(unit.ref);
|
|
105
69
|
const installedVersion = installed[pinned.name];
|
|
106
70
|
if (!installedVersion || installedVersion !== pinned.version) {
|
|
107
71
|
return [
|
|
@@ -116,15 +80,15 @@ const loadInstalledStrategyUnits = async (input) => {
|
|
|
116
80
|
});
|
|
117
81
|
if (drift.length > 0) {
|
|
118
82
|
const details = drift
|
|
119
|
-
.map((
|
|
120
|
-
const installedText = typeof
|
|
121
|
-
return `${
|
|
83
|
+
.map((driftItem) => {
|
|
84
|
+
const installedText = typeof driftItem.installed === "string" ? driftItem.installed : "(not installed)";
|
|
85
|
+
return `${driftItem.name}: pinned ${driftItem.pinned}, installed ${installedText}`;
|
|
122
86
|
})
|
|
123
87
|
.join("; ");
|
|
124
88
|
throw new Error(`Strategy drift detected (${input.strategyName}). ${details}. ` +
|
|
125
89
|
`Fix: pp strategy update ${input.strategyName} OR pp strategy pin ${input.strategyName}`);
|
|
126
90
|
}
|
|
127
|
-
const units = await Promise.all(normalized.map(async (
|
|
91
|
+
const units = await Promise.all(normalized.map(async (unit) => unitStore.instantiateFromRef(unit.ref, unit.args)));
|
|
128
92
|
assertNoDuplicateUnitIds({
|
|
129
93
|
strategyName: input.strategyName,
|
|
130
94
|
units
|
|
@@ -132,26 +96,25 @@ const loadInstalledStrategyUnits = async (input) => {
|
|
|
132
96
|
return units;
|
|
133
97
|
};
|
|
134
98
|
const instantiateBuiltinUnit = async (input) => {
|
|
135
|
-
const req = (
|
|
136
|
-
const pinned =
|
|
99
|
+
const req = createRequire(import.meta.url);
|
|
100
|
+
const pinned = parsePinnedSpec(input.ref);
|
|
137
101
|
const resolved = req.resolve(pinned.name);
|
|
138
|
-
const mod = (await
|
|
139
|
-
const
|
|
140
|
-
if (
|
|
141
|
-
throw new Error(`Unit ${pinned.name}
|
|
102
|
+
const mod = (await import(pathToFileURL(resolved).href));
|
|
103
|
+
const ctor = resolveUnitConstructor(mod);
|
|
104
|
+
if (!ctor) {
|
|
105
|
+
throw new Error(`Unit ${pinned.name} does not export a Unit constructor.`);
|
|
142
106
|
}
|
|
143
|
-
const ctor = def;
|
|
144
107
|
const instance = new ctor(...input.args);
|
|
145
|
-
if (!
|
|
146
|
-
throw new Error(`Unit ${pinned.name}
|
|
108
|
+
if (!isUnitLike(instance)) {
|
|
109
|
+
throw new Error(`Unit ${pinned.name} exported constructor did not produce a Unit.`);
|
|
147
110
|
}
|
|
148
111
|
return instance;
|
|
149
112
|
};
|
|
150
113
|
const loadBuiltinStrategyUnits = async (input) => {
|
|
151
|
-
const normalized =
|
|
152
|
-
const units = await Promise.all(normalized.map(async (
|
|
153
|
-
ref:
|
|
154
|
-
args:
|
|
114
|
+
const normalized = normalizeStrategyUnits(input.strategyFile);
|
|
115
|
+
const units = await Promise.all(normalized.map(async (unit) => instantiateBuiltinUnit({
|
|
116
|
+
ref: unit.ref,
|
|
117
|
+
args: unit.args
|
|
155
118
|
})));
|
|
156
119
|
assertNoDuplicateUnitIds({
|
|
157
120
|
strategyName: input.strategyName,
|
|
@@ -171,19 +134,43 @@ const loadStrategyUnits = async (input) => {
|
|
|
171
134
|
strategyFile: input.strategy.strategyFile
|
|
172
135
|
});
|
|
173
136
|
};
|
|
174
|
-
class ArchiveCommand extends
|
|
137
|
+
export default class ArchiveCommand extends Command {
|
|
138
|
+
static description = "Archive a web page as an offline snapshot.";
|
|
139
|
+
static args = {
|
|
140
|
+
url: Args.string({
|
|
141
|
+
description: "URL to archive",
|
|
142
|
+
required: true
|
|
143
|
+
})
|
|
144
|
+
};
|
|
145
|
+
static flags = {
|
|
146
|
+
help: Flags.help({
|
|
147
|
+
char: "h"
|
|
148
|
+
}),
|
|
149
|
+
timeout: Flags.integer({
|
|
150
|
+
char: "t",
|
|
151
|
+
description: "Network idle duration in milliseconds before capture stops",
|
|
152
|
+
required: false
|
|
153
|
+
}),
|
|
154
|
+
maxDuration: Flags.integer({
|
|
155
|
+
description: "Hard max capture duration in milliseconds",
|
|
156
|
+
required: false
|
|
157
|
+
}),
|
|
158
|
+
strategy: Flags.string({
|
|
159
|
+
description: "Run an installed or built-in strategy (unit pipeline) by name"
|
|
160
|
+
})
|
|
161
|
+
};
|
|
175
162
|
async run() {
|
|
176
163
|
const { args, flags } = await this.parse(ArchiveCommand);
|
|
177
164
|
const targetUrl = args.url;
|
|
178
165
|
const timeoutMs = typeof flags.timeout === "number" ? flags.timeout : undefined;
|
|
179
166
|
const maxDurationMs = typeof flags.maxDuration === "number" ? flags.maxDuration : undefined;
|
|
180
167
|
const strategyName = typeof flags.strategy === "string" ? flags.strategy.trim() : undefined;
|
|
181
|
-
const pagepocket =
|
|
182
|
-
const result = await
|
|
168
|
+
const pagepocket = PagePocket.fromURL(targetUrl);
|
|
169
|
+
const result = await withSpinner(async (spinner) => {
|
|
183
170
|
{
|
|
184
|
-
const configService = new
|
|
171
|
+
const configService = new ConfigService();
|
|
185
172
|
const name = strategyName && strategyName.length > 0 ? strategyName : "default";
|
|
186
|
-
const strategyService = new
|
|
173
|
+
const strategyService = new StrategyService(configService);
|
|
187
174
|
strategyService.ensureConfigFileExists();
|
|
188
175
|
const strategy = resolveStrategy({
|
|
189
176
|
strategyName: name,
|
|
@@ -203,45 +190,20 @@ class ArchiveCommand extends core_1.Command {
|
|
|
203
190
|
...(typeof effectiveMaxDurationMs === "number"
|
|
204
191
|
? { maxDurationMs: effectiveMaxDurationMs }
|
|
205
192
|
: {}),
|
|
206
|
-
blacklist: [...
|
|
193
|
+
blacklist: [...ga, ...ns],
|
|
207
194
|
units,
|
|
208
|
-
plugins: await
|
|
195
|
+
plugins: await loadConfiguredPlugins().catch(() => [])
|
|
209
196
|
});
|
|
210
197
|
return result;
|
|
211
198
|
}
|
|
212
199
|
}, "Freezing page");
|
|
213
|
-
this.log(
|
|
200
|
+
this.log(chalk.green("All done! Snapshot created."));
|
|
214
201
|
if (result.kind === "zip") {
|
|
215
|
-
this.log(`Snapshot saved to ${
|
|
202
|
+
this.log(`Snapshot saved to ${chalk.cyan(result.zip.outputPath)}`);
|
|
216
203
|
}
|
|
217
204
|
else if (result.kind === "raw") {
|
|
218
|
-
this.log(`Snapshot saved to ${
|
|
205
|
+
this.log(`Snapshot saved to ${chalk.cyan(result.outputDir)}`);
|
|
219
206
|
}
|
|
220
207
|
process.exit();
|
|
221
208
|
}
|
|
222
209
|
}
|
|
223
|
-
ArchiveCommand.description = "Archive a web page as an offline snapshot.";
|
|
224
|
-
ArchiveCommand.args = {
|
|
225
|
-
url: core_1.Args.string({
|
|
226
|
-
description: "URL to archive",
|
|
227
|
-
required: true
|
|
228
|
-
})
|
|
229
|
-
};
|
|
230
|
-
ArchiveCommand.flags = {
|
|
231
|
-
help: core_1.Flags.help({
|
|
232
|
-
char: "h"
|
|
233
|
-
}),
|
|
234
|
-
timeout: core_1.Flags.integer({
|
|
235
|
-
char: "t",
|
|
236
|
-
description: "Network idle duration in milliseconds before capture stops",
|
|
237
|
-
required: false
|
|
238
|
-
}),
|
|
239
|
-
maxDuration: core_1.Flags.integer({
|
|
240
|
-
description: "Hard max capture duration in milliseconds",
|
|
241
|
-
required: false
|
|
242
|
-
}),
|
|
243
|
-
strategy: core_1.Flags.string({
|
|
244
|
-
description: "Run an installed or built-in strategy (unit pipeline) by name"
|
|
245
|
-
})
|
|
246
|
-
};
|
|
247
|
-
exports.default = ArchiveCommand;
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const parse_plugin_spec_1 = require("../../utils/parse-plugin-spec");
|
|
13
|
-
const validate_plugin_default_export_1 = require("../../utils/validate-plugin-default-export");
|
|
14
|
-
class PluginAddCommand extends core_1.Command {
|
|
1
|
+
import { Command } from "@oclif/core";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { ConfigService } from "../../services/config-service.js";
|
|
4
|
+
import { installPluginPackage } from "../../services/plugin-installer.js";
|
|
5
|
+
import { PluginStore } from "../../services/plugin-store.js";
|
|
6
|
+
import { parsePluginOptions } from "../../utils/parse-plugin-options.js";
|
|
7
|
+
import { parsePluginSpec } from "../../utils/parse-plugin-spec.js";
|
|
8
|
+
import { validatePluginDefaultExport } from "../../utils/validate-plugin-default-export.js";
|
|
9
|
+
export default class PluginAddCommand extends Command {
|
|
10
|
+
static description = "Install a plugin package, add it to config, and run its optional setup().";
|
|
11
|
+
static strict = false;
|
|
15
12
|
async run() {
|
|
16
13
|
const [name, ...rest] = this.argv;
|
|
17
14
|
if (!name) {
|
|
@@ -21,24 +18,24 @@ class PluginAddCommand extends core_1.Command {
|
|
|
21
18
|
this.log("Usage: pp plugin add <plugin-name> [--option-* <value> ...] [--option <value> ...]");
|
|
22
19
|
return;
|
|
23
20
|
}
|
|
24
|
-
const parsedSpec =
|
|
25
|
-
const configService = new
|
|
26
|
-
const store = new
|
|
21
|
+
const parsedSpec = parsePluginSpec(name);
|
|
22
|
+
const configService = new ConfigService();
|
|
23
|
+
const store = new PluginStore(configService);
|
|
27
24
|
configService.ensureConfigFileExists();
|
|
28
25
|
const config = configService.readConfigOrDefault();
|
|
29
26
|
if (store.hasPlugin(config, parsedSpec.name)) {
|
|
30
|
-
this.log(
|
|
27
|
+
this.log(chalk.gray(`Plugin already exists in config: ${parsedSpec.name}`));
|
|
31
28
|
return;
|
|
32
29
|
}
|
|
33
|
-
const parsed =
|
|
30
|
+
const parsed = parsePluginOptions(rest);
|
|
34
31
|
const entry = parsed.kind === "none"
|
|
35
32
|
? parsedSpec.name
|
|
36
33
|
: parsed.kind === "array"
|
|
37
34
|
? { name: parsedSpec.name, spec: parsedSpec.spec, options: parsed.value }
|
|
38
35
|
: { name: parsedSpec.name, spec: parsedSpec.spec, options: parsed.value };
|
|
39
|
-
|
|
36
|
+
installPluginPackage(store, { packageName: parsedSpec.spec });
|
|
40
37
|
const mod = await store.importPluginModule(parsedSpec.name);
|
|
41
|
-
const validation =
|
|
38
|
+
const validation = validatePluginDefaultExport({
|
|
42
39
|
pluginName: parsedSpec.name,
|
|
43
40
|
moduleExports: mod,
|
|
44
41
|
options: parsed.kind === "none" ? undefined : parsed.value
|
|
@@ -49,9 +46,6 @@ class PluginAddCommand extends core_1.Command {
|
|
|
49
46
|
const nextConfig = store.addPluginToConfig(config, entry);
|
|
50
47
|
configService.writeConfig(nextConfig);
|
|
51
48
|
await store.runOptionalSetup(parsedSpec.name);
|
|
52
|
-
this.log(
|
|
49
|
+
this.log(chalk.green(`Added plugin: ${parsedSpec.name}`));
|
|
53
50
|
}
|
|
54
51
|
}
|
|
55
|
-
PluginAddCommand.description = "Install a plugin package, add it to config, and run its optional setup().";
|
|
56
|
-
PluginAddCommand.strict = false;
|
|
57
|
-
exports.default = PluginAddCommand;
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
const core_1 = require("@oclif/core");
|
|
7
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
-
const config_service_1 = require("../../services/config-service");
|
|
9
|
-
const plugin_store_1 = require("../../services/plugin-store");
|
|
10
|
-
const parse_plugin_spec_1 = require("../../utils/parse-plugin-spec");
|
|
1
|
+
import { Args, Command } from "@oclif/core";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { ConfigService } from "../../services/config-service.js";
|
|
4
|
+
import { PluginStore, normalizePluginConfigEntry } from "../../services/plugin-store.js";
|
|
5
|
+
import { parsePluginSpec } from "../../utils/parse-plugin-spec.js";
|
|
11
6
|
/**
|
|
12
7
|
* Validate one configured plugin can be loaded and instantiated.
|
|
13
8
|
*
|
|
@@ -41,16 +36,23 @@ const runDoctorCheck = async (store, spec) => {
|
|
|
41
36
|
};
|
|
42
37
|
}
|
|
43
38
|
};
|
|
44
|
-
class PluginDoctorCommand extends
|
|
39
|
+
export default class PluginDoctorCommand extends Command {
|
|
40
|
+
static description = "Validate configured plugins are installed and loadable.";
|
|
41
|
+
static args = {
|
|
42
|
+
name: Args.string({
|
|
43
|
+
description: "npm package name (optional; if omitted checks all configured plugins)",
|
|
44
|
+
required: false
|
|
45
|
+
})
|
|
46
|
+
};
|
|
45
47
|
async run() {
|
|
46
48
|
const { args } = await this.parse(PluginDoctorCommand);
|
|
47
|
-
const configService = new
|
|
48
|
-
const store = new
|
|
49
|
+
const configService = new ConfigService();
|
|
50
|
+
const store = new PluginStore(configService);
|
|
49
51
|
configService.ensureConfigFileExists();
|
|
50
52
|
const config = store.readConfig();
|
|
51
|
-
const targetName = args.name ?
|
|
53
|
+
const targetName = args.name ? parsePluginSpec(args.name).name : undefined;
|
|
52
54
|
const configured = config.plugins
|
|
53
|
-
.map((entry) =>
|
|
55
|
+
.map((entry) => normalizePluginConfigEntry(entry))
|
|
54
56
|
.filter((entry) => {
|
|
55
57
|
if (!targetName) {
|
|
56
58
|
return true;
|
|
@@ -59,33 +61,25 @@ class PluginDoctorCommand extends core_1.Command {
|
|
|
59
61
|
});
|
|
60
62
|
if (configured.length === 0) {
|
|
61
63
|
if (targetName) {
|
|
62
|
-
this.log(
|
|
64
|
+
this.log(chalk.gray(`Plugin not found in config: ${targetName}`));
|
|
63
65
|
return;
|
|
64
66
|
}
|
|
65
|
-
this.log(
|
|
67
|
+
this.log(chalk.gray("No plugins configured."));
|
|
66
68
|
return;
|
|
67
69
|
}
|
|
68
70
|
const checks = await Promise.all(configured.map((entry) => runDoctorCheck(store, entry)));
|
|
69
71
|
const failed = checks.filter((item) => !item.installed || !item.loadable);
|
|
70
72
|
checks.forEach((item) => {
|
|
71
73
|
if (item.installed && item.loadable) {
|
|
72
|
-
this.log(
|
|
74
|
+
this.log(chalk.green(`[OK] ${item.plugin}`));
|
|
73
75
|
return;
|
|
74
76
|
}
|
|
75
|
-
this.log(
|
|
77
|
+
this.log(chalk.red(`[FAIL] ${item.plugin}: ${item.error ?? "unknown error"}`));
|
|
76
78
|
});
|
|
77
79
|
if (failed.length === 0) {
|
|
78
|
-
this.log(
|
|
80
|
+
this.log(chalk.green("Plugin doctor checks passed."));
|
|
79
81
|
return;
|
|
80
82
|
}
|
|
81
83
|
throw new Error(`${failed.length} plugin(s) failed doctor checks.`);
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
|
-
PluginDoctorCommand.description = "Validate configured plugins are installed and loadable.";
|
|
85
|
-
PluginDoctorCommand.args = {
|
|
86
|
-
name: core_1.Args.string({
|
|
87
|
-
description: "npm package name (optional; if omitted checks all configured plugins)",
|
|
88
|
-
required: false
|
|
89
|
-
})
|
|
90
|
-
};
|
|
91
|
-
exports.default = PluginDoctorCommand;
|
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
-
const config_service_1 = require("../../services/config-service");
|
|
9
|
-
const plugin_store_1 = require("../../services/plugin-store");
|
|
10
|
-
class PluginLsCommand extends core_1.Command {
|
|
1
|
+
import { Command } from "@oclif/core";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { ConfigService } from "../../services/config-service.js";
|
|
4
|
+
import { PluginStore } from "../../services/plugin-store.js";
|
|
5
|
+
export default class PluginLsCommand extends Command {
|
|
6
|
+
static description = "List configured plugins.";
|
|
11
7
|
async run() {
|
|
12
|
-
const configService = new
|
|
13
|
-
const store = new
|
|
8
|
+
const configService = new ConfigService();
|
|
9
|
+
const store = new PluginStore(configService);
|
|
14
10
|
configService.ensureConfigFileExists();
|
|
15
11
|
const config = configService.readConfigOrDefault();
|
|
16
12
|
const entries = config.plugins;
|
|
17
13
|
if (entries.length === 0) {
|
|
18
|
-
this.log(
|
|
14
|
+
this.log(chalk.gray("No plugins configured."));
|
|
19
15
|
return;
|
|
20
16
|
}
|
|
21
17
|
entries
|
|
@@ -28,18 +24,16 @@ class PluginLsCommand extends core_1.Command {
|
|
|
28
24
|
: undefined;
|
|
29
25
|
const meta = store.readInstalledPackageMeta(name);
|
|
30
26
|
if (!meta) {
|
|
31
|
-
const specText = configuredSpec ?
|
|
32
|
-
return `${
|
|
27
|
+
const specText = configuredSpec ? chalk.gray(`(configured: ${configuredSpec})`) : "";
|
|
28
|
+
return `${chalk.yellow(name)} ${chalk.gray("(not installed)")} ${specText}`.trim();
|
|
33
29
|
}
|
|
34
|
-
const specText = configuredSpec ?
|
|
30
|
+
const specText = configuredSpec ? chalk.gray(`(configured: ${configuredSpec})`) : "";
|
|
35
31
|
const desc = meta.description
|
|
36
|
-
?
|
|
37
|
-
:
|
|
38
|
-
const version = meta.version ?
|
|
39
|
-
return `${
|
|
32
|
+
? chalk.gray(meta.description)
|
|
33
|
+
: chalk.gray("(no description)");
|
|
34
|
+
const version = meta.version ? chalk.cyan(`v${meta.version}`) : chalk.gray("(no version)");
|
|
35
|
+
return `${chalk.green(meta.name)} ${version} ${specText} ${desc}`.replace(/\s{2,}/g, " ");
|
|
40
36
|
})
|
|
41
37
|
.forEach((line) => this.log(line));
|
|
42
38
|
}
|
|
43
39
|
}
|
|
44
|
-
PluginLsCommand.description = "List configured plugins.";
|
|
45
|
-
exports.default = PluginLsCommand;
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
const core_1 = require("@oclif/core");
|
|
7
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
-
const config_service_1 = require("../../services/config-service");
|
|
9
|
-
const plugin_installer_1 = require("../../services/plugin-installer");
|
|
10
|
-
const plugin_store_1 = require("../../services/plugin-store");
|
|
1
|
+
import { Command } from "@oclif/core";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { ConfigService } from "../../services/config-service.js";
|
|
4
|
+
import { uninstallPluginPackage } from "../../services/plugin-installer.js";
|
|
5
|
+
import { PluginStore, normalizePluginConfigEntry } from "../../services/plugin-store.js";
|
|
11
6
|
/**
|
|
12
7
|
* Get configured plugin names from config.
|
|
13
8
|
*
|
|
@@ -17,27 +12,26 @@ const plugin_store_1 = require("../../services/plugin-store");
|
|
|
17
12
|
const getConfiguredPluginNames = (store) => {
|
|
18
13
|
const config = store.readConfig();
|
|
19
14
|
const names = config.plugins
|
|
20
|
-
.map((entry) =>
|
|
15
|
+
.map((entry) => normalizePluginConfigEntry(entry).name)
|
|
21
16
|
.filter((name) => name.trim().length > 0);
|
|
22
17
|
return new Set(names);
|
|
23
18
|
};
|
|
24
|
-
class PluginPruneCommand extends
|
|
19
|
+
export default class PluginPruneCommand extends Command {
|
|
20
|
+
static description = "Uninstall plugin packages that are not referenced by config.";
|
|
25
21
|
async run() {
|
|
26
|
-
const configService = new
|
|
27
|
-
const store = new
|
|
22
|
+
const configService = new ConfigService();
|
|
23
|
+
const store = new PluginStore(configService);
|
|
28
24
|
configService.ensureConfigFileExists();
|
|
29
25
|
const configured = getConfiguredPluginNames(store);
|
|
30
26
|
const installed = store.readInstalledDependencyNames();
|
|
31
27
|
const orphans = installed.filter((name) => !configured.has(name));
|
|
32
28
|
if (orphans.length === 0) {
|
|
33
|
-
this.log(
|
|
29
|
+
this.log(chalk.gray("No orphan plugin packages found."));
|
|
34
30
|
return;
|
|
35
31
|
}
|
|
36
32
|
orphans.forEach((name) => {
|
|
37
|
-
|
|
38
|
-
this.log(
|
|
33
|
+
uninstallPluginPackage(store, { packageName: name });
|
|
34
|
+
this.log(chalk.green(`Pruned plugin package: ${name}`));
|
|
39
35
|
});
|
|
40
36
|
}
|
|
41
37
|
}
|
|
42
|
-
PluginPruneCommand.description = "Uninstall plugin packages that are not referenced by config.";
|
|
43
|
-
exports.default = PluginPruneCommand;
|
|
@@ -1,34 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { Args, Command } from "@oclif/core";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { ConfigService } from "../../services/config-service.js";
|
|
4
|
+
import { PluginStore } from "../../services/plugin-store.js";
|
|
5
|
+
export default class PluginRemoveCommand extends Command {
|
|
6
|
+
static description = "Remove a plugin from config (does not uninstall).";
|
|
7
|
+
static args = {
|
|
8
|
+
name: Args.string({
|
|
9
|
+
description: "npm package name",
|
|
10
|
+
required: true
|
|
11
|
+
})
|
|
12
|
+
};
|
|
11
13
|
async run() {
|
|
12
14
|
const { args } = await this.parse(PluginRemoveCommand);
|
|
13
15
|
const name = args.name;
|
|
14
|
-
const configService = new
|
|
15
|
-
const store = new
|
|
16
|
+
const configService = new ConfigService();
|
|
17
|
+
const store = new PluginStore(configService);
|
|
16
18
|
configService.ensureConfigFileExists();
|
|
17
19
|
const config = configService.readConfigOrDefault();
|
|
18
20
|
const out = store.removePluginFromConfig(config, name);
|
|
19
21
|
if (!out.removed) {
|
|
20
|
-
this.log(
|
|
22
|
+
this.log(chalk.gray(`Plugin not found in config: ${name}`));
|
|
21
23
|
return;
|
|
22
24
|
}
|
|
23
25
|
configService.writeConfig(out.config);
|
|
24
|
-
this.log(
|
|
26
|
+
this.log(chalk.green(`Removed from config: ${name}`));
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
|
-
PluginRemoveCommand.description = "Remove a plugin from config (does not uninstall).";
|
|
28
|
-
PluginRemoveCommand.args = {
|
|
29
|
-
name: core_1.Args.string({
|
|
30
|
-
description: "npm package name",
|
|
31
|
-
required: true
|
|
32
|
-
})
|
|
33
|
-
};
|
|
34
|
-
exports.default = PluginRemoveCommand;
|