@holo-js/cli 0.2.6 → 0.3.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/bin/holo.mjs +409 -104
- package/dist/{broadcast-JBQWQZSO.mjs → broadcast-5VZM5SGQ.mjs} +13 -11
- package/dist/{broadcast-YGJFCEPP.mjs → broadcast-AABT52BK.mjs} +10 -8
- package/dist/cache-A42JSVYA.mjs +138 -0
- package/dist/cache-DUVBBOZU.mjs +138 -0
- package/dist/{cache-migrations-77TLXIVG.mjs → cache-migrations-4WKSEN2Q.mjs} +12 -10
- package/dist/{cache-migrations-FKAHRP24.mjs → cache-migrations-PD2T5AV4.mjs} +14 -12
- package/dist/chunk-3RFRE7XG.mjs +491 -0
- package/dist/{chunk-7B2RVYLL.mjs → chunk-6BHJLCAO.mjs} +256 -392
- package/dist/{chunk-KS5TWO75.mjs → chunk-EQUFFLRG.mjs} +7 -7
- package/dist/chunk-HX6MOYBO.mjs +2 -0
- package/dist/{chunk-YEFJBN56.mjs → chunk-JVLZ3SOW.mjs} +11 -8
- package/dist/{chunk-4K4CWMMP.mjs → chunk-KRADH7PK.mjs} +53 -49
- package/dist/chunk-LALDNNPH.mjs +428 -0
- package/dist/chunk-LHK5YUR4.mjs +156 -0
- package/dist/{chunk-LBJAJLKU.mjs → chunk-M3IPUCTM.mjs} +1 -1
- package/dist/{chunk-C624G3R2.mjs → chunk-OI54DOHK.mjs} +256 -392
- package/dist/{chunk-LXGQCG56.mjs → chunk-OLZEMIDQ.mjs} +1 -1
- package/dist/{chunk-VP2E62DF.mjs → chunk-QSVMXMTQ.mjs} +49 -349
- package/dist/{chunk-J76GH2DR.mjs → chunk-R4ZYMEDU.mjs} +129 -86
- package/dist/{chunk-AJLRAC5M.mjs → chunk-TH5YCOZB.mjs} +55 -51
- package/dist/chunk-TRYHX5S4.mjs +360 -0
- package/dist/chunk-XBOVM6LO.mjs +41 -0
- package/dist/{chunk-ILU426CF.mjs → chunk-YACY2572.mjs} +482 -2
- package/dist/{config-MD27U4FM.mjs → config-BZHQK532.mjs} +3 -3
- package/dist/{dev-L4Y2GU2E.mjs → dev-7KGRV56X.mjs} +10 -8
- package/dist/{dev-YPBQBEOE.mjs → dev-GQT37XJU.mjs} +10 -8
- package/dist/{discovery-GWTBF5RZ.mjs → discovery-HNH3BLDL.mjs} +8 -6
- package/dist/{generators-VCYISHWO.mjs → generators-3FVW4AEJ.mjs} +18 -15
- package/dist/{generators-H4NTV4DB.mjs → generators-JWVNGUUE.mjs} +14 -11
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +407 -102
- package/dist/{media-migrations-BNVAPYEM.mjs → media-migrations-EG34NYAG.mjs} +13 -11
- package/dist/{media-migrations-NNB3DAQR.mjs → media-migrations-LGI7R7AR.mjs} +11 -9
- package/dist/plugins-ZGLB6JO6.mjs +24 -0
- package/dist/{queue-5BXSDFCO.mjs → queue-BEZDCZCY.mjs} +54 -16
- package/dist/{queue-D4LSWNS2.mjs → queue-R54DUUYA.mjs} +54 -16
- package/dist/{queue-migrations-EF4AMMSY.mjs → queue-migrations-AM3AKT76.mjs} +16 -11
- package/dist/{queue-migrations-LXEWXJYT.mjs → queue-migrations-U477YSFD.mjs} +18 -13
- package/dist/{runtime-RPMZMTD4.mjs → runtime-4TJYZLQV.mjs} +10 -8
- package/dist/{runtime-UKBJQFXM.mjs → runtime-MSRQD5W5.mjs} +10 -8
- package/dist/{runtime-GSXF4NB3.mjs → runtime-VR5BDPYQ.mjs} +1 -1
- package/dist/runtime-worker.mjs +7 -1
- package/dist/{scaffold-NJ5VH73M.mjs → scaffold-ASEX5LEX.mjs} +8 -4
- package/dist/{scaffold-VV3KTYGO.mjs → scaffold-ZAD3XUSD.mjs} +8 -4
- package/dist/{security-K5PZS3E3.mjs → security-4QLV7QCH.mjs} +8 -6
- package/dist/{security-JR4P7L2C.mjs → security-KYTLP4DF.mjs} +8 -6
- package/package.json +35 -8
- package/dist/cache-HUGRWOHQ.mjs +0 -66
- package/dist/cache-OSJ4PD52.mjs +0 -66
- package/dist/chunk-D7O4SU6N.mjs +0 -2
- package/dist/chunk-FGQ2I2YH.mjs +0 -22
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeCommandAliases
|
|
3
|
+
} from "./chunk-TRYHX5S4.mjs";
|
|
4
|
+
import {
|
|
5
|
+
APP_CONFIG_FILE_NAMES,
|
|
6
|
+
pathExists,
|
|
7
|
+
readTextFile,
|
|
8
|
+
resolveFirstExistingPath,
|
|
9
|
+
writeTextFile
|
|
10
|
+
} from "./chunk-YACY2572.mjs";
|
|
11
|
+
|
|
12
|
+
// src/project/plugins.ts
|
|
13
|
+
import { createRequire } from "module";
|
|
14
|
+
import { isAbsolute, join, relative, resolve } from "path";
|
|
15
|
+
import { pathToFileURL } from "url";
|
|
16
|
+
import { loadConfigDirectory } from "@holo-js/config";
|
|
17
|
+
import {
|
|
18
|
+
loadHoloPluginDefinitions,
|
|
19
|
+
normalizeHoloPluginDefinition as normalizeKernelPluginDefinition
|
|
20
|
+
} from "@holo-js/kernel";
|
|
21
|
+
function isRecord(value) {
|
|
22
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
23
|
+
}
|
|
24
|
+
function normalizeString(value) {
|
|
25
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
26
|
+
}
|
|
27
|
+
function isValidPackageName(packageName) {
|
|
28
|
+
return /^@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*$/i.test(packageName) || /^[a-z0-9][a-z0-9._-]*$/i.test(packageName);
|
|
29
|
+
}
|
|
30
|
+
function assertValidPackageName(packageName) {
|
|
31
|
+
if (!isValidPackageName(packageName)) {
|
|
32
|
+
throw new Error(`Invalid plugin package name: ${packageName || "(empty)"}.`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function isHoloAppCommand(value) {
|
|
36
|
+
return isRecord(value) && typeof value.description === "string" && typeof value.run === "function" && (typeof value.name === "undefined" || typeof value.name === "string") && (typeof value.usage === "undefined" || typeof value.usage === "string") && (typeof value.aliases === "undefined" || Array.isArray(value.aliases) && value.aliases.every((alias) => typeof alias === "string"));
|
|
37
|
+
}
|
|
38
|
+
function normalizeStringArray(value) {
|
|
39
|
+
if (!Array.isArray(value)) {
|
|
40
|
+
return void 0;
|
|
41
|
+
}
|
|
42
|
+
const values = [...new Set(value.map(normalizeString).filter((entry) => Boolean(entry)))];
|
|
43
|
+
return values.length > 0 ? Object.freeze(values) : void 0;
|
|
44
|
+
}
|
|
45
|
+
function normalizeRequiredStringArray(value) {
|
|
46
|
+
return Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "string" && entry.trim()) ? Object.freeze(value.map((entry) => entry.trim())) : void 0;
|
|
47
|
+
}
|
|
48
|
+
function normalizeRequiredStringMap(value) {
|
|
49
|
+
if (!isRecord(value)) {
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
const entries = Object.entries(value).flatMap(([key, entry]) => {
|
|
53
|
+
return key.trim() && typeof entry === "string" && entry.trim() ? [[key.trim(), entry.trim()]] : [];
|
|
54
|
+
});
|
|
55
|
+
if (entries.length !== Object.keys(value).length) {
|
|
56
|
+
return void 0;
|
|
57
|
+
}
|
|
58
|
+
return Object.freeze(Object.fromEntries(entries));
|
|
59
|
+
}
|
|
60
|
+
function normalizeFrameworkTsconfigKind(value) {
|
|
61
|
+
return value === "nuxt" || value === "next" || value === "sveltekit" ? value : void 0;
|
|
62
|
+
}
|
|
63
|
+
function normalizeHoloPackageArray(value) {
|
|
64
|
+
const values = normalizeStringArray(value);
|
|
65
|
+
if (!values) {
|
|
66
|
+
return void 0;
|
|
67
|
+
}
|
|
68
|
+
const holoPackages = values.filter((entry) => entry.startsWith("@holo-js/"));
|
|
69
|
+
return holoPackages.length > 0 ? Object.freeze(holoPackages) : void 0;
|
|
70
|
+
}
|
|
71
|
+
function normalizeDependencyContributions(value) {
|
|
72
|
+
if (!isRecord(value)) {
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
const runtime = normalizeStringArray(value.runtime);
|
|
76
|
+
const holo = normalizeHoloPackageArray(value.holo);
|
|
77
|
+
if (!runtime && !holo) {
|
|
78
|
+
return void 0;
|
|
79
|
+
}
|
|
80
|
+
return Object.freeze({
|
|
81
|
+
...runtime ? { runtime } : {},
|
|
82
|
+
...holo ? { holo } : {}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function normalizeConfigContributions(value) {
|
|
86
|
+
if (!isRecord(value)) {
|
|
87
|
+
return void 0;
|
|
88
|
+
}
|
|
89
|
+
const files = normalizeStringArray(value.files);
|
|
90
|
+
const env = normalizeStringArray(value.env);
|
|
91
|
+
if (!files && !env) {
|
|
92
|
+
return void 0;
|
|
93
|
+
}
|
|
94
|
+
return Object.freeze({
|
|
95
|
+
...files ? { files } : {},
|
|
96
|
+
...env ? { env } : {}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function normalizeRuntimeMapContributions(value, key) {
|
|
100
|
+
if (!isRecord(value) || !isRecord(value[key])) {
|
|
101
|
+
return void 0;
|
|
102
|
+
}
|
|
103
|
+
const entries = [];
|
|
104
|
+
for (const [name, contribution] of Object.entries(value[key])) {
|
|
105
|
+
if (!isRecord(contribution)) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const runtime = normalizeString(contribution.runtime);
|
|
109
|
+
if (runtime) {
|
|
110
|
+
entries.push([name, { runtime }]);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const contributions = Object.fromEntries(
|
|
114
|
+
entries.sort(([left], [right]) => left.localeCompare(right))
|
|
115
|
+
);
|
|
116
|
+
return Object.keys(contributions).length > 0 ? Object.freeze({ [key]: contributions }) : void 0;
|
|
117
|
+
}
|
|
118
|
+
function normalizeBroadcastContributions(value) {
|
|
119
|
+
return normalizeRuntimeMapContributions(value, "drivers");
|
|
120
|
+
}
|
|
121
|
+
function normalizeFrameworkContribution(value) {
|
|
122
|
+
if (!isRecord(value)) {
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
const id = normalizeString(value.id);
|
|
126
|
+
const displayName = normalizeString(value.displayName);
|
|
127
|
+
const detectPackages = normalizeRequiredStringArray(value.detectPackages);
|
|
128
|
+
const adapterPackage = normalizeString(value.adapterPackage);
|
|
129
|
+
if (!id || !displayName || !detectPackages || !adapterPackage?.startsWith("@holo-js/")) {
|
|
130
|
+
return void 0;
|
|
131
|
+
}
|
|
132
|
+
const fluxPackage = normalizeString(value.fluxPackage);
|
|
133
|
+
if (fluxPackage && !fluxPackage.startsWith("@holo-js/")) {
|
|
134
|
+
return void 0;
|
|
135
|
+
}
|
|
136
|
+
if (!isRecord(value.scaffold) || !isRecord(value.runner) || !isRecord(value.capabilities)) {
|
|
137
|
+
return void 0;
|
|
138
|
+
}
|
|
139
|
+
const scaffoldDependencies = normalizeRequiredStringMap(value.scaffold.dependencies);
|
|
140
|
+
const scaffoldDevDependencies = normalizeRequiredStringMap(value.scaffold.devDependencies);
|
|
141
|
+
const scaffoldScripts = normalizeRequiredStringMap(value.scaffold.scripts);
|
|
142
|
+
const lintScript = normalizeString(value.scaffold.lintScript);
|
|
143
|
+
const typecheckScript = normalizeString(value.scaffold.typecheckScript);
|
|
144
|
+
const defaultUrl = normalizeString(value.scaffold.defaultUrl);
|
|
145
|
+
const tsconfig = normalizeFrameworkTsconfigKind(value.scaffold.tsconfig);
|
|
146
|
+
if (!scaffoldDependencies || !scaffoldDevDependencies || !scaffoldScripts || !lintScript || !typecheckScript || !defaultUrl || !tsconfig) {
|
|
147
|
+
return void 0;
|
|
148
|
+
}
|
|
149
|
+
const commandName = normalizeString(value.runner.commandName);
|
|
150
|
+
const buildArgs = normalizeRequiredStringArray(value.runner.buildArgs);
|
|
151
|
+
const start = normalizeRequiredStringArray(value.runner.start);
|
|
152
|
+
if (!commandName || !buildArgs || !start || typeof value.runner.startUsesFrameworkBinary !== "boolean" || typeof value.runner.preloadNextRuntime !== "boolean" || typeof value.runner.suppressSvelteKitOutput !== "boolean" || typeof value.runner.nextDevServerConflictHandling !== "boolean" || typeof value.capabilities.managedBroadcastAuthRoute !== "boolean") {
|
|
153
|
+
return void 0;
|
|
154
|
+
}
|
|
155
|
+
const sync = normalizeFrameworkSync(value.sync);
|
|
156
|
+
const descriptor = {
|
|
157
|
+
id,
|
|
158
|
+
displayName,
|
|
159
|
+
detectPackages,
|
|
160
|
+
adapterPackage,
|
|
161
|
+
...fluxPackage ? { fluxPackage } : {},
|
|
162
|
+
scaffold: {
|
|
163
|
+
dependencies: scaffoldDependencies,
|
|
164
|
+
devDependencies: scaffoldDevDependencies,
|
|
165
|
+
scripts: scaffoldScripts,
|
|
166
|
+
lintScript,
|
|
167
|
+
typecheckScript,
|
|
168
|
+
defaultUrl,
|
|
169
|
+
tsconfig,
|
|
170
|
+
...typeof value.scaffold.vscodeVueHybridMode === "boolean" ? { vscodeVueHybridMode: value.scaffold.vscodeVueHybridMode } : {}
|
|
171
|
+
},
|
|
172
|
+
runner: {
|
|
173
|
+
commandName,
|
|
174
|
+
buildArgs,
|
|
175
|
+
start,
|
|
176
|
+
startUsesFrameworkBinary: value.runner.startUsesFrameworkBinary,
|
|
177
|
+
preloadNextRuntime: value.runner.preloadNextRuntime,
|
|
178
|
+
suppressSvelteKitOutput: value.runner.suppressSvelteKitOutput,
|
|
179
|
+
nextDevServerConflictHandling: value.runner.nextDevServerConflictHandling
|
|
180
|
+
},
|
|
181
|
+
capabilities: {
|
|
182
|
+
managedBroadcastAuthRoute: value.capabilities.managedBroadcastAuthRoute
|
|
183
|
+
},
|
|
184
|
+
...sync ? { sync } : {}
|
|
185
|
+
};
|
|
186
|
+
return Object.freeze(descriptor);
|
|
187
|
+
}
|
|
188
|
+
function normalizeFrameworkSync(value) {
|
|
189
|
+
if (typeof value === "undefined") {
|
|
190
|
+
return void 0;
|
|
191
|
+
}
|
|
192
|
+
if (!isRecord(value) || !isRecord(value.commands)) {
|
|
193
|
+
return void 0;
|
|
194
|
+
}
|
|
195
|
+
const commandMap = value.commands;
|
|
196
|
+
const errorLabel = normalizeString(value.errorLabel);
|
|
197
|
+
if (!errorLabel) {
|
|
198
|
+
return void 0;
|
|
199
|
+
}
|
|
200
|
+
const managers = ["npm", "pnpm", "yarn", "bun"];
|
|
201
|
+
const commands = Object.fromEntries(managers.flatMap((manager) => {
|
|
202
|
+
const command = commandMap[manager];
|
|
203
|
+
return Array.isArray(command) && command.length > 0 && command.every((entry) => typeof entry === "string" && entry.trim()) ? [[manager, Object.freeze(command.map((entry) => entry.trim()))]] : [];
|
|
204
|
+
}));
|
|
205
|
+
return managers.every((manager) => manager in commands) ? Object.freeze({
|
|
206
|
+
commands,
|
|
207
|
+
errorLabel
|
|
208
|
+
}) : void 0;
|
|
209
|
+
}
|
|
210
|
+
function normalizeSinglePathContribution(value, key) {
|
|
211
|
+
if (!isRecord(value)) {
|
|
212
|
+
return void 0;
|
|
213
|
+
}
|
|
214
|
+
const entry = normalizeString(value[key]);
|
|
215
|
+
return entry ? Object.freeze({ [key]: entry }) : void 0;
|
|
216
|
+
}
|
|
217
|
+
function normalizePluginContributions(value) {
|
|
218
|
+
if (!isRecord(value)) {
|
|
219
|
+
return void 0;
|
|
220
|
+
}
|
|
221
|
+
const dependencies = normalizeDependencyContributions(value.dependencies);
|
|
222
|
+
const config = normalizeConfigContributions(value.config);
|
|
223
|
+
const broadcast = normalizeBroadcastContributions(value.broadcast);
|
|
224
|
+
const cache = normalizeRuntimeMapContributions(value.cache, "drivers");
|
|
225
|
+
const queue = normalizeRuntimeMapContributions(value.queue, "drivers");
|
|
226
|
+
const mail = normalizeRuntimeMapContributions(value.mail, "drivers");
|
|
227
|
+
const notifications = normalizeRuntimeMapContributions(value.notifications, "channels");
|
|
228
|
+
const runtime = normalizeSinglePathContribution(value.runtime, "boot");
|
|
229
|
+
const cli = normalizeSinglePathContribution(value.cli, "commands");
|
|
230
|
+
const migrations = normalizeSinglePathContribution(value.migrations, "publish");
|
|
231
|
+
const framework = normalizeFrameworkContribution(value.framework);
|
|
232
|
+
if (!dependencies && !config && !broadcast && !cache && !queue && !mail && !notifications && !runtime && !cli && !migrations && !framework) {
|
|
233
|
+
return void 0;
|
|
234
|
+
}
|
|
235
|
+
return Object.freeze({
|
|
236
|
+
...dependencies ? { dependencies } : {},
|
|
237
|
+
...config ? { config } : {},
|
|
238
|
+
...broadcast ? { broadcast } : {},
|
|
239
|
+
...cache ? { cache } : {},
|
|
240
|
+
...queue ? { queue } : {},
|
|
241
|
+
...mail ? { mail } : {},
|
|
242
|
+
...notifications ? { notifications } : {},
|
|
243
|
+
...runtime ? { runtime } : {},
|
|
244
|
+
...cli ? { cli } : {},
|
|
245
|
+
...migrations ? { migrations } : {},
|
|
246
|
+
...framework ? { framework } : {}
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
function normalizeHoloPluginDefinition(value) {
|
|
250
|
+
const candidate = isRecord(value) && isRecord(value.default) ? value.default : isRecord(value) && isRecord(value.plugin) ? value.plugin : value;
|
|
251
|
+
const definition = normalizeKernelPluginDefinition(value);
|
|
252
|
+
const contributes = normalizePluginContributions(isRecord(candidate) ? candidate.contributes : void 0);
|
|
253
|
+
return Object.freeze({
|
|
254
|
+
id: definition.id,
|
|
255
|
+
...definition.name ? { name: definition.name } : {},
|
|
256
|
+
...definition.description ? { description: definition.description } : {},
|
|
257
|
+
...contributes ? { contributes } : {}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
async function loadHoloPluginFromPackage(projectRoot, packageName) {
|
|
261
|
+
assertValidPackageName(packageName);
|
|
262
|
+
const [loaded] = await loadHoloPluginDefinitions(projectRoot, [packageName], {
|
|
263
|
+
moduleVersion: String(Date.now())
|
|
264
|
+
});
|
|
265
|
+
if (!loaded) throw new Error(`Plugin package failed to load: ${packageName}.`);
|
|
266
|
+
return {
|
|
267
|
+
packageName,
|
|
268
|
+
packageRoot: loaded.packageRoot,
|
|
269
|
+
entryPath: loaded.entryPath,
|
|
270
|
+
definition: normalizeHoloPluginDefinition(loaded.definition)
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
function resolvePluginModulePath(projectRoot, plugin, specifier) {
|
|
274
|
+
const normalizedSpecifier = specifier.trim();
|
|
275
|
+
if (!normalizedSpecifier) {
|
|
276
|
+
throw new Error(`Plugin ${plugin.packageName} declared an empty module specifier.`);
|
|
277
|
+
}
|
|
278
|
+
if (isAbsolute(normalizedSpecifier)) {
|
|
279
|
+
throw new Error(`Plugin ${plugin.packageName} module specifier must not be absolute: ${specifier}.`);
|
|
280
|
+
}
|
|
281
|
+
if (normalizedSpecifier.startsWith(".")) {
|
|
282
|
+
const candidate = resolve(plugin.packageRoot, normalizedSpecifier);
|
|
283
|
+
const relativePath = relative(plugin.packageRoot, candidate);
|
|
284
|
+
if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
|
|
285
|
+
throw new Error(`Plugin ${plugin.packageName} module specifier must stay inside the package root: ${specifier}.`);
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
specifier: normalizedSpecifier,
|
|
289
|
+
path: candidate
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const projectRequire = createRequire(join(projectRoot, "package.json"));
|
|
293
|
+
return {
|
|
294
|
+
specifier: normalizedSpecifier,
|
|
295
|
+
path: projectRequire.resolve(normalizedSpecifier)
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
async function importPluginModule(modulePath) {
|
|
299
|
+
if (!await pathExists(modulePath.path)) {
|
|
300
|
+
throw new Error(`Plugin module does not exist: ${modulePath.specifier}.`);
|
|
301
|
+
}
|
|
302
|
+
return await import(`${pathToFileURL(modulePath.path).href}?t=${Date.now()}`);
|
|
303
|
+
}
|
|
304
|
+
function normalizePluginCommandExports(moduleValue, plugin, modulePath) {
|
|
305
|
+
const candidate = isRecord(moduleValue) && Array.isArray(moduleValue.commands) ? moduleValue.commands : isRecord(moduleValue) && typeof moduleValue.default !== "undefined" ? moduleValue.default : moduleValue;
|
|
306
|
+
const commands = Array.isArray(candidate) ? candidate : [candidate];
|
|
307
|
+
return Object.freeze(commands.map((command, index) => {
|
|
308
|
+
if (!isHoloAppCommand(command) || !command.name?.trim()) {
|
|
309
|
+
throw new Error(`Plugin ${plugin.packageName} command ${modulePath.specifier}#${index + 1} must define name, description, and run().`);
|
|
310
|
+
}
|
|
311
|
+
return Object.freeze({
|
|
312
|
+
...command,
|
|
313
|
+
name: command.name.trim()
|
|
314
|
+
});
|
|
315
|
+
}));
|
|
316
|
+
}
|
|
317
|
+
async function loadProjectPluginCommands(projectRoot) {
|
|
318
|
+
const plugins = await resolveProjectPlugins(projectRoot);
|
|
319
|
+
const commands = [];
|
|
320
|
+
for (const resolvedPlugin of plugins) {
|
|
321
|
+
const loadedPlugin = resolvedPlugin.loaded;
|
|
322
|
+
const commandSpecifier = loadedPlugin?.definition.contributes?.cli?.commands;
|
|
323
|
+
if (!loadedPlugin || !commandSpecifier) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const modulePath = resolvePluginModulePath(projectRoot, loadedPlugin, commandSpecifier);
|
|
327
|
+
const moduleValue = await importPluginModule(modulePath);
|
|
328
|
+
const pluginCommands = normalizePluginCommandExports(moduleValue, loadedPlugin, modulePath);
|
|
329
|
+
for (const command of pluginCommands) {
|
|
330
|
+
commands.push({
|
|
331
|
+
sourcePath: `${loadedPlugin.packageName}:${modulePath.specifier}`,
|
|
332
|
+
name: command.name ?? "",
|
|
333
|
+
aliases: normalizeCommandAliases(command.aliases),
|
|
334
|
+
description: command.description,
|
|
335
|
+
usage: command.usage,
|
|
336
|
+
async load() {
|
|
337
|
+
return command;
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return Object.freeze(commands);
|
|
343
|
+
}
|
|
344
|
+
async function loadProjectPluginFrameworkDescriptors(projectRoot) {
|
|
345
|
+
const plugins = await resolveProjectPlugins(projectRoot);
|
|
346
|
+
const descriptors = [];
|
|
347
|
+
const ids = /* @__PURE__ */ new Set();
|
|
348
|
+
for (const resolvedPlugin of plugins) {
|
|
349
|
+
const descriptor = resolvedPlugin.loaded?.definition.contributes?.framework;
|
|
350
|
+
if (!descriptor) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (ids.has(descriptor.id)) {
|
|
354
|
+
throw new Error(`Duplicate plugin framework descriptor: ${descriptor.id}.`);
|
|
355
|
+
}
|
|
356
|
+
ids.add(descriptor.id);
|
|
357
|
+
descriptors.push(descriptor);
|
|
358
|
+
}
|
|
359
|
+
return Object.freeze(descriptors);
|
|
360
|
+
}
|
|
361
|
+
async function readProjectPluginNames(projectRoot) {
|
|
362
|
+
try {
|
|
363
|
+
const config = await loadConfigDirectory(projectRoot, {
|
|
364
|
+
preferCache: false,
|
|
365
|
+
processEnv: process.env
|
|
366
|
+
});
|
|
367
|
+
return Object.freeze([...new Set(config.app.plugins)]);
|
|
368
|
+
} catch {
|
|
369
|
+
return await readProjectPluginNamesFromSource(projectRoot);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
async function readProjectPluginNamesFromSource(projectRoot) {
|
|
373
|
+
const manifestPath = await resolveFirstExistingPath(projectRoot, APP_CONFIG_FILE_NAMES);
|
|
374
|
+
if (!manifestPath) {
|
|
375
|
+
return Object.freeze([]);
|
|
376
|
+
}
|
|
377
|
+
const contents = await readTextFile(manifestPath);
|
|
378
|
+
if (!contents) {
|
|
379
|
+
return Object.freeze([]);
|
|
380
|
+
}
|
|
381
|
+
const pluginsMatch = contents.match(/^\s*plugins:\s*\[([\s\S]*?)\]/m) ?? contents.match(/^\s*"plugins":\s*\[([\s\S]*?)\]/m);
|
|
382
|
+
if (!pluginsMatch) {
|
|
383
|
+
return Object.freeze([]);
|
|
384
|
+
}
|
|
385
|
+
const pluginBlock = pluginsMatch[1] ?? "";
|
|
386
|
+
const pluginNames = [...pluginBlock.matchAll(/(['"])(.*?)\1/g)].map((match) => match[2]?.trim() ?? "").filter(Boolean);
|
|
387
|
+
return Object.freeze([...new Set(pluginNames)]);
|
|
388
|
+
}
|
|
389
|
+
function renderPluginConfigProperty(packageNames) {
|
|
390
|
+
if (packageNames.length === 0) {
|
|
391
|
+
return " plugins: [],\n";
|
|
392
|
+
}
|
|
393
|
+
return [
|
|
394
|
+
" plugins: [",
|
|
395
|
+
...packageNames.map((packageName) => ` ${JSON.stringify(packageName)},`),
|
|
396
|
+
" ],",
|
|
397
|
+
""
|
|
398
|
+
].join("\n");
|
|
399
|
+
}
|
|
400
|
+
function replaceExistingPluginsProperty(contents, packageNames) {
|
|
401
|
+
const nextProperty = renderPluginConfigProperty(packageNames);
|
|
402
|
+
const nextContents = contents.replace(/^\s*(?:"plugins"|plugins):\s*\[[\s\S]*?\],?\n/m, nextProperty);
|
|
403
|
+
return nextContents === contents ? void 0 : nextContents;
|
|
404
|
+
}
|
|
405
|
+
function hasNonLiteralPluginsProperty(contents) {
|
|
406
|
+
return /^\s*(?:"plugins"|plugins)\s*[:,]/m.test(contents);
|
|
407
|
+
}
|
|
408
|
+
function insertPluginsProperty(contents, packageNames) {
|
|
409
|
+
if (hasNonLiteralPluginsProperty(contents)) {
|
|
410
|
+
return void 0;
|
|
411
|
+
}
|
|
412
|
+
const nextProperty = renderPluginConfigProperty(packageNames);
|
|
413
|
+
const pathsMatch = contents.match(/^\s*(?:"paths"|paths):\s*\{/m);
|
|
414
|
+
if (pathsMatch?.index === void 0) {
|
|
415
|
+
const openConfigMatch = contents.match(/defineAppConfig\(\{\s*\n/);
|
|
416
|
+
if (openConfigMatch?.index !== void 0) {
|
|
417
|
+
const insertIndex = openConfigMatch.index + openConfigMatch[0].length;
|
|
418
|
+
return `${contents.slice(0, insertIndex)}${nextProperty}${contents.slice(insertIndex)}`;
|
|
419
|
+
}
|
|
420
|
+
const emptyConfigMatch = contents.match(/defineAppConfig\(\{\s*\}\)/);
|
|
421
|
+
return emptyConfigMatch ? contents.replace(emptyConfigMatch[0], `defineAppConfig({
|
|
422
|
+
${nextProperty}})`) : void 0;
|
|
423
|
+
}
|
|
424
|
+
return `${contents.slice(0, pathsMatch.index)}${nextProperty}${contents.slice(pathsMatch.index)}`;
|
|
425
|
+
}
|
|
426
|
+
async function writeProjectPluginNames(projectRoot, packageNames) {
|
|
427
|
+
const manifestPath = await resolveFirstExistingPath(projectRoot, APP_CONFIG_FILE_NAMES);
|
|
428
|
+
if (!manifestPath) {
|
|
429
|
+
throw new Error(`Missing config/app.(ts|mts|js|mjs) in ${projectRoot}.`);
|
|
430
|
+
}
|
|
431
|
+
const contents = await readTextFile(manifestPath);
|
|
432
|
+
if (!contents) {
|
|
433
|
+
throw new Error(`Missing app config: ${manifestPath}.`);
|
|
434
|
+
}
|
|
435
|
+
const normalized = [...new Set(packageNames.map((packageName) => packageName.trim()).filter(Boolean))];
|
|
436
|
+
const nextContents = replaceExistingPluginsProperty(contents, normalized) ?? insertPluginsProperty(contents, normalized);
|
|
437
|
+
if (!nextContents) {
|
|
438
|
+
throw new Error(`Unable to update ${manifestPath} automatically. Add plugins: ${JSON.stringify(normalized)} to defineAppConfig().`);
|
|
439
|
+
}
|
|
440
|
+
if (nextContents === contents) {
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
await writeTextFile(manifestPath, nextContents);
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
async function activateProjectPlugin(projectRoot, packageName) {
|
|
447
|
+
assertValidPackageName(packageName);
|
|
448
|
+
const plugins = await readProjectPluginNames(projectRoot);
|
|
449
|
+
if (plugins.includes(packageName)) {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
return await writeProjectPluginNames(projectRoot, [...plugins, packageName]);
|
|
453
|
+
}
|
|
454
|
+
async function deactivateProjectPlugin(projectRoot, packageName) {
|
|
455
|
+
assertValidPackageName(packageName);
|
|
456
|
+
const plugins = await readProjectPluginNames(projectRoot);
|
|
457
|
+
if (!plugins.includes(packageName)) {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
return await writeProjectPluginNames(projectRoot, plugins.filter((plugin) => plugin !== packageName));
|
|
461
|
+
}
|
|
462
|
+
async function resolveProjectPlugins(projectRoot) {
|
|
463
|
+
const packageNames = await readProjectPluginNames(projectRoot);
|
|
464
|
+
const resolved = [];
|
|
465
|
+
for (const packageName of packageNames) {
|
|
466
|
+
try {
|
|
467
|
+
resolved.push({
|
|
468
|
+
packageName,
|
|
469
|
+
loaded: await loadHoloPluginFromPackage(projectRoot, packageName)
|
|
470
|
+
});
|
|
471
|
+
} catch (error) {
|
|
472
|
+
resolved.push({
|
|
473
|
+
packageName,
|
|
474
|
+
error: error instanceof Error && error.message ? error.message : `Failed to load ${packageName}.`
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return Object.freeze(resolved);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
export {
|
|
482
|
+
normalizeHoloPluginDefinition,
|
|
483
|
+
loadHoloPluginFromPackage,
|
|
484
|
+
loadProjectPluginCommands,
|
|
485
|
+
loadProjectPluginFrameworkDescriptors,
|
|
486
|
+
readProjectPluginNames,
|
|
487
|
+
writeProjectPluginNames,
|
|
488
|
+
activateProjectPlugin,
|
|
489
|
+
deactivateProjectPlugin,
|
|
490
|
+
resolveProjectPlugins
|
|
491
|
+
};
|