@jskit-ai/jskit-cli 0.2.39 → 0.2.41
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/package.json +3 -3
- package/src/server/cliRuntime/mutations/fileMutations.js +4 -3
- package/src/server/cliRuntime/packageInstallFlow.js +7 -2
- package/src/server/cliRuntime/packageOptions.js +160 -7
- package/src/server/cliRuntime/packageTemplateResolution.js +23 -2
- package/src/server/commandHandlers/packageCommands/add.js +7 -2
- package/src/server/commandHandlers/packageCommands/discoverabilityHelp.js +253 -99
- package/src/server/commandHandlers/packageCommands/generate.js +253 -49
- package/src/server/commandHandlers/shared.js +39 -0
- package/src/server/core/argParser.js +29 -10
- package/src/server/core/buildCommandDeps.js +2 -0
- package/src/server/core/commandCatalog.js +551 -32
- package/src/server/core/createCliRunner.js +11 -1
- package/src/server/core/dispatchCli.js +31 -69
- package/src/server/core/usageHelp.js +79 -335
- package/src/server/shared/cliError.js +2 -1
- package/src/server/shared/outputFormatting.js +164 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import {
|
|
2
3
|
isHelpToken,
|
|
3
4
|
renderGenerateCatalogHelp,
|
|
4
5
|
renderGeneratePackageHelp,
|
|
5
6
|
renderGenerateSubcommandHelp
|
|
6
7
|
} from "./discoverabilityHelp.js";
|
|
8
|
+
import { interpolateOptionValue } from "../../shared/optionInterpolation.js";
|
|
7
9
|
|
|
8
10
|
function resolveGeneratorSubcommandDefinitionMetadata(packageEntry = {}, subcommandName = "") {
|
|
9
11
|
const descriptor = packageEntry?.descriptor && typeof packageEntry.descriptor === "object"
|
|
@@ -25,6 +27,51 @@ function resolveGeneratorSubcommandDefinitionMetadata(packageEntry = {}, subcomm
|
|
|
25
27
|
return definition && typeof definition === "object" ? definition : {};
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
function mapDescriptorBackedSubcommandArgsToInlineOptions(
|
|
31
|
+
packageEntry = {},
|
|
32
|
+
subcommandName = "",
|
|
33
|
+
subcommandArgs = [],
|
|
34
|
+
inlineOptions = {},
|
|
35
|
+
createCliError
|
|
36
|
+
) {
|
|
37
|
+
const definition = resolveGeneratorSubcommandDefinitionMetadata(packageEntry, subcommandName);
|
|
38
|
+
const positionalArgs = Array.isArray(definition?.positionalArgs)
|
|
39
|
+
? definition.positionalArgs
|
|
40
|
+
: [];
|
|
41
|
+
const providedArgs = Array.isArray(subcommandArgs) ? subcommandArgs : [];
|
|
42
|
+
if (providedArgs.length > positionalArgs.length) {
|
|
43
|
+
throw createCliError(
|
|
44
|
+
`Generator command "${subcommandName}" for ${String(packageEntry?.packageId || "unknown-package")} accepts at most ${positionalArgs.length} positional argument${positionalArgs.length === 1 ? "" : "s"}.`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const mappedInlineOptions = {
|
|
49
|
+
...(inlineOptions && typeof inlineOptions === "object" ? inlineOptions : {})
|
|
50
|
+
};
|
|
51
|
+
for (const [index, rawValue] of providedArgs.entries()) {
|
|
52
|
+
const positionalArg = positionalArgs[index];
|
|
53
|
+
const optionName = String(positionalArg?.name || "").trim();
|
|
54
|
+
if (!optionName) {
|
|
55
|
+
throw createCliError(
|
|
56
|
+
`Generator command "${subcommandName}" for ${String(packageEntry?.packageId || "unknown-package")} defines positional arg ${index + 1} without a name.`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const value = String(rawValue || "").trim();
|
|
61
|
+
const existingValue = Object.prototype.hasOwnProperty.call(mappedInlineOptions, optionName)
|
|
62
|
+
? String(mappedInlineOptions[optionName] || "").trim()
|
|
63
|
+
: null;
|
|
64
|
+
if (existingValue != null && existingValue !== value) {
|
|
65
|
+
throw createCliError(
|
|
66
|
+
`Generator command "${subcommandName}" for ${String(packageEntry?.packageId || "unknown-package")} received both positional "${optionName}" and --${optionName} with different values.`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
mappedInlineOptions[optionName] = value;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return mappedInlineOptions;
|
|
73
|
+
}
|
|
74
|
+
|
|
28
75
|
function resolveSubcommandRequiresInput(packageEntry = {}, subcommandName = "") {
|
|
29
76
|
const descriptor = packageEntry?.descriptor && typeof packageEntry.descriptor === "object"
|
|
30
77
|
? packageEntry.descriptor
|
|
@@ -62,6 +109,108 @@ function resolveSubcommandRequiresInput(packageEntry = {}, subcommandName = "")
|
|
|
62
109
|
return false;
|
|
63
110
|
}
|
|
64
111
|
|
|
112
|
+
function collectUnexpectedGeneratorSubcommandOptionNames(packageEntry = {}, subcommandName = "", inlineOptions = {}) {
|
|
113
|
+
const subcommandDefinition = resolveGeneratorSubcommandDefinitionMetadata(packageEntry, subcommandName);
|
|
114
|
+
if (!Array.isArray(subcommandDefinition?.optionNames)) {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const allowedOptionNameSet = new Set(
|
|
119
|
+
subcommandDefinition.optionNames
|
|
120
|
+
.map((optionName) => String(optionName || "").trim())
|
|
121
|
+
.filter(Boolean)
|
|
122
|
+
);
|
|
123
|
+
return Object.keys(inlineOptions || {})
|
|
124
|
+
.map((optionName) => String(optionName || "").trim())
|
|
125
|
+
.filter(Boolean)
|
|
126
|
+
.filter((optionName) => !allowedOptionNameSet.has(optionName))
|
|
127
|
+
.sort((left, right) => left.localeCompare(right));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function resolveCreateTargetPolicy(packageEntry = {}, subcommandName = "") {
|
|
131
|
+
const definition = resolveGeneratorSubcommandDefinitionMetadata(packageEntry, subcommandName);
|
|
132
|
+
const createTarget = definition?.createTarget;
|
|
133
|
+
return createTarget && typeof createTarget === "object" ? createTarget : {};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function normalizeRelativePathWithinApp(appRoot = "", targetPath = "", createCliError) {
|
|
137
|
+
const normalizedTargetPath = String(targetPath || "").trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
|
|
138
|
+
if (!normalizedTargetPath) {
|
|
139
|
+
throw createCliError("Generator create target path cannot be empty.");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const absolutePath = path.resolve(appRoot, normalizedTargetPath);
|
|
143
|
+
const relativePath = path.relative(appRoot, absolutePath);
|
|
144
|
+
if (
|
|
145
|
+
!relativePath ||
|
|
146
|
+
relativePath === ".." ||
|
|
147
|
+
relativePath.startsWith(`..${path.sep}`) ||
|
|
148
|
+
path.isAbsolute(relativePath)
|
|
149
|
+
) {
|
|
150
|
+
throw createCliError(`Generator create target must stay within app root: ${normalizedTargetPath}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
absolutePath,
|
|
155
|
+
relativePath: relativePath.split(path.sep).join("/")
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function enforceDescriptorBackedCreateTargetPolicy({
|
|
160
|
+
packageEntry,
|
|
161
|
+
subcommandName,
|
|
162
|
+
inlineOptions = {},
|
|
163
|
+
appRoot = "",
|
|
164
|
+
packageIdInput = "",
|
|
165
|
+
createCliError,
|
|
166
|
+
readdir
|
|
167
|
+
} = {}) {
|
|
168
|
+
const policy = resolveCreateTargetPolicy(packageEntry, subcommandName);
|
|
169
|
+
const pathTemplate = String(policy.pathTemplate || "").trim();
|
|
170
|
+
if (!pathTemplate) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const forceOptionName = String(policy.forceOptionName || "force").trim() || "force";
|
|
175
|
+
const forceOverwrite = String(inlineOptions?.[forceOptionName] || "").trim().toLowerCase() === "true";
|
|
176
|
+
if (forceOverwrite) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const interpolatedTargetPath = interpolateOptionValue(
|
|
181
|
+
pathTemplate,
|
|
182
|
+
inlineOptions,
|
|
183
|
+
String(packageEntry?.packageId || "unknown-package"),
|
|
184
|
+
`${String(subcommandName || "generator")}.createTarget.pathTemplate`
|
|
185
|
+
);
|
|
186
|
+
const resolvedTargetPath = normalizeRelativePathWithinApp(appRoot, interpolatedTargetPath, createCliError);
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
const entries = await readdir(resolvedTargetPath.absolutePath);
|
|
190
|
+
if (policy.allowExistingEmptyDirectory === true && entries.length < 1) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const commandLabel = `${String(packageIdInput || packageEntry?.packageId || "generator").trim()} ${String(subcommandName || "").trim()}`.trim();
|
|
195
|
+
const targetLabel = String(policy.label || "target").trim() || "target";
|
|
196
|
+
throw createCliError(
|
|
197
|
+
`${commandLabel} will not overwrite existing ${targetLabel} ${resolvedTargetPath.relativePath}. Re-run with --force to overwrite it.`
|
|
198
|
+
);
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (error?.code === "ENOENT") {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (error?.code === "ENOTDIR") {
|
|
204
|
+
const commandLabel = `${String(packageIdInput || packageEntry?.packageId || "generator").trim()} ${String(subcommandName || "").trim()}`.trim();
|
|
205
|
+
const targetLabel = String(policy.label || "target").trim() || "target";
|
|
206
|
+
throw createCliError(
|
|
207
|
+
`${commandLabel} will not overwrite existing ${targetLabel} ${resolvedTargetPath.relativePath}. Re-run with --force to overwrite it.`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
65
214
|
async function runPackageGenerateCommand(
|
|
66
215
|
ctx = {},
|
|
67
216
|
{ positional, options, cwd, io },
|
|
@@ -79,7 +228,10 @@ async function runPackageGenerateCommand(
|
|
|
79
228
|
resolvePackageKind,
|
|
80
229
|
resolveGeneratorPrimarySubcommand,
|
|
81
230
|
hasGeneratorSubcommandDefinition,
|
|
82
|
-
|
|
231
|
+
readdir,
|
|
232
|
+
validateInlineOptionValuesForPackage,
|
|
233
|
+
runGeneratorSubcommand,
|
|
234
|
+
createCatalogFetchStatusReporter = () => () => {}
|
|
83
235
|
} = ctx;
|
|
84
236
|
|
|
85
237
|
const firstToken = String(positional[0] || "").trim();
|
|
@@ -93,6 +245,9 @@ async function runPackageGenerateCommand(
|
|
|
93
245
|
const targetId = firstToken === "package" ? secondToken : firstToken;
|
|
94
246
|
const subcommandName = firstToken === "package" ? thirdToken : secondToken;
|
|
95
247
|
const subcommandArgs = firstToken === "package" ? positional.slice(3) : positional.slice(2);
|
|
248
|
+
const reportTemplateFetchStatus = createCatalogFetchStatusReporter(io, {
|
|
249
|
+
enabled: options.json !== true
|
|
250
|
+
});
|
|
96
251
|
|
|
97
252
|
async function resolveGeneratorPackageEntry(packageIdInput = "") {
|
|
98
253
|
const appRoot = await resolveAppRootFromCwd(cwd);
|
|
@@ -145,27 +300,12 @@ async function runPackageGenerateCommand(
|
|
|
145
300
|
}
|
|
146
301
|
|
|
147
302
|
if (isHelpToken(subcommandName)) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
303
|
+
if (subcommandArgs.length > 0) {
|
|
304
|
+
throw createCliError(
|
|
305
|
+
`Unknown generator usage: jskit generate ${targetId} help ${subcommandArgs.join(" ")}. Use: jskit generate ${targetId} <subcommand> help`
|
|
306
|
+
);
|
|
151
307
|
}
|
|
152
308
|
const { packageEntry } = await resolveGeneratorPackageEntry(targetId);
|
|
153
|
-
if (helpSubcommandName) {
|
|
154
|
-
const rendered = renderGenerateSubcommandHelp({
|
|
155
|
-
io,
|
|
156
|
-
packageEntry,
|
|
157
|
-
packageIdInput: targetId,
|
|
158
|
-
subcommandName: helpSubcommandName,
|
|
159
|
-
json: options.json
|
|
160
|
-
});
|
|
161
|
-
if (!rendered) {
|
|
162
|
-
throw createCliError(
|
|
163
|
-
`Unknown generator subcommand "${helpSubcommandName}" for ${String(packageEntry?.packageId || targetId)}.`
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
return 0;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
309
|
renderGeneratePackageHelp({
|
|
170
310
|
io,
|
|
171
311
|
packageEntry,
|
|
@@ -175,56 +315,111 @@ async function runPackageGenerateCommand(
|
|
|
175
315
|
return 0;
|
|
176
316
|
}
|
|
177
317
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
318
|
+
async function runResolvedGeneratorSubcommand({
|
|
319
|
+
appRoot,
|
|
320
|
+
packageEntry,
|
|
321
|
+
resolvedPackageId,
|
|
322
|
+
subcommandName: rawSubcommandName = "",
|
|
323
|
+
subcommandArgs: rawSubcommandArgs = []
|
|
324
|
+
} = {}) {
|
|
325
|
+
const normalizedSubcommandName = String(rawSubcommandName || "").trim().toLowerCase();
|
|
326
|
+
const normalizedSubcommandArgs = Array.isArray(rawSubcommandArgs)
|
|
327
|
+
? rawSubcommandArgs
|
|
328
|
+
: [];
|
|
184
329
|
const hasInlineOptions = Object.keys(options?.inlineOptions || {}).length > 0;
|
|
185
|
-
const hasSubcommandArgs =
|
|
186
|
-
if (!hasInlineOptions && !hasSubcommandArgs && resolveSubcommandRequiresInput(packageEntry,
|
|
330
|
+
const hasSubcommandArgs = normalizedSubcommandArgs.length > 0;
|
|
331
|
+
if (!hasInlineOptions && !hasSubcommandArgs && resolveSubcommandRequiresInput(packageEntry, normalizedSubcommandName)) {
|
|
187
332
|
const rendered = renderGenerateSubcommandHelp({
|
|
188
333
|
io,
|
|
189
334
|
packageEntry,
|
|
190
335
|
packageIdInput: targetId,
|
|
191
|
-
subcommandName,
|
|
336
|
+
subcommandName: normalizedSubcommandName,
|
|
192
337
|
json: options.json
|
|
193
338
|
});
|
|
194
339
|
if (rendered) {
|
|
195
340
|
return 0;
|
|
196
341
|
}
|
|
197
342
|
}
|
|
198
|
-
if (
|
|
343
|
+
if (normalizedSubcommandArgs.length === 1 && isHelpToken(normalizedSubcommandArgs[0])) {
|
|
199
344
|
const rendered = renderGenerateSubcommandHelp({
|
|
200
345
|
io,
|
|
201
346
|
packageEntry,
|
|
202
347
|
packageIdInput: targetId,
|
|
203
|
-
subcommandName,
|
|
348
|
+
subcommandName: normalizedSubcommandName,
|
|
204
349
|
json: options.json
|
|
205
350
|
});
|
|
206
351
|
if (!rendered) {
|
|
207
|
-
throw createCliError(`Unknown generator subcommand "${
|
|
352
|
+
throw createCliError(`Unknown generator subcommand "${normalizedSubcommandName}" for ${resolvedPackageId}.`);
|
|
208
353
|
}
|
|
209
354
|
return 0;
|
|
210
355
|
}
|
|
211
356
|
|
|
212
|
-
const
|
|
357
|
+
const subcommandDefinition = resolveGeneratorSubcommandDefinitionMetadata(packageEntry, normalizedSubcommandName);
|
|
358
|
+
const unexpectedOptionNames = collectUnexpectedGeneratorSubcommandOptionNames(
|
|
359
|
+
packageEntry,
|
|
360
|
+
normalizedSubcommandName,
|
|
361
|
+
options.inlineOptions
|
|
362
|
+
);
|
|
363
|
+
if (unexpectedOptionNames.length > 0) {
|
|
364
|
+
const commandLabel = String(targetId || resolvedPackageId || "").trim() || resolvedPackageId;
|
|
365
|
+
throw createCliError(
|
|
366
|
+
`Unknown option${unexpectedOptionNames.length === 1 ? "" : "s"} for generator command ${commandLabel} ${normalizedSubcommandName}: ${unexpectedOptionNames.map((optionName) => `--${optionName}`).join(", ")}.`,
|
|
367
|
+
{
|
|
368
|
+
renderUsage: options.json
|
|
369
|
+
? null
|
|
370
|
+
: () => {
|
|
371
|
+
renderGenerateSubcommandHelp({
|
|
372
|
+
io: {
|
|
373
|
+
...io,
|
|
374
|
+
stdout: io.stderr || io.stdout
|
|
375
|
+
},
|
|
376
|
+
packageEntry,
|
|
377
|
+
packageIdInput: targetId,
|
|
378
|
+
subcommandName: normalizedSubcommandName,
|
|
379
|
+
json: false
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
const validatedOptionNames = Array.isArray(subcommandDefinition?.optionNames)
|
|
386
|
+
? subcommandDefinition.optionNames
|
|
387
|
+
: [];
|
|
388
|
+
await validateInlineOptionValuesForPackage(packageEntry, options.inlineOptions, {
|
|
389
|
+
appRoot,
|
|
390
|
+
optionNames: validatedOptionNames
|
|
391
|
+
});
|
|
392
|
+
|
|
213
393
|
const primarySubcommand = resolveGeneratorPrimarySubcommand(packageEntry);
|
|
214
394
|
if (
|
|
215
395
|
normalizedSubcommandName &&
|
|
216
396
|
normalizedSubcommandName === primarySubcommand &&
|
|
217
397
|
!hasGeneratorSubcommandDefinition(packageEntry, normalizedSubcommandName)
|
|
218
398
|
) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
399
|
+
const inlineOptionsForPrimarySubcommand = mapDescriptorBackedSubcommandArgsToInlineOptions(
|
|
400
|
+
packageEntry,
|
|
401
|
+
normalizedSubcommandName,
|
|
402
|
+
normalizedSubcommandArgs,
|
|
403
|
+
options.inlineOptions,
|
|
404
|
+
createCliError
|
|
405
|
+
);
|
|
406
|
+
await validateInlineOptionValuesForPackage(packageEntry, inlineOptionsForPrimarySubcommand, {
|
|
407
|
+
appRoot
|
|
408
|
+
});
|
|
409
|
+
await enforceDescriptorBackedCreateTargetPolicy({
|
|
410
|
+
packageEntry,
|
|
411
|
+
subcommandName: normalizedSubcommandName,
|
|
412
|
+
inlineOptions: inlineOptionsForPrimarySubcommand,
|
|
413
|
+
appRoot,
|
|
414
|
+
packageIdInput: targetId,
|
|
415
|
+
createCliError,
|
|
416
|
+
readdir
|
|
417
|
+
});
|
|
224
418
|
return runCommandAdd({
|
|
225
419
|
positional: ["package", resolvedPackageId],
|
|
226
420
|
options: {
|
|
227
421
|
...options,
|
|
422
|
+
inlineOptions: inlineOptionsForPrimarySubcommand,
|
|
228
423
|
commandMode: "generate"
|
|
229
424
|
},
|
|
230
425
|
cwd,
|
|
@@ -234,7 +429,8 @@ async function runPackageGenerateCommand(
|
|
|
234
429
|
|
|
235
430
|
const templateRoot = await resolvePackageTemplateRoot({
|
|
236
431
|
packageEntry,
|
|
237
|
-
appRoot
|
|
432
|
+
appRoot,
|
|
433
|
+
reportTemplateFetchStatus
|
|
238
434
|
});
|
|
239
435
|
const executablePackageEntry =
|
|
240
436
|
templateRoot === packageEntry.rootDir
|
|
@@ -246,8 +442,8 @@ async function runPackageGenerateCommand(
|
|
|
246
442
|
|
|
247
443
|
return runGeneratorSubcommand({
|
|
248
444
|
packageEntry: executablePackageEntry,
|
|
249
|
-
subcommandName,
|
|
250
|
-
subcommandArgs,
|
|
445
|
+
subcommandName: normalizedSubcommandName,
|
|
446
|
+
subcommandArgs: normalizedSubcommandArgs,
|
|
251
447
|
inlineOptions: options.inlineOptions,
|
|
252
448
|
appRoot,
|
|
253
449
|
io,
|
|
@@ -256,15 +452,23 @@ async function runPackageGenerateCommand(
|
|
|
256
452
|
});
|
|
257
453
|
}
|
|
258
454
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
...
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
455
|
+
if (subcommandName) {
|
|
456
|
+
const resolvedGeneratorPackage = await resolveGeneratorPackageEntry(targetId);
|
|
457
|
+
return runResolvedGeneratorSubcommand({
|
|
458
|
+
...resolvedGeneratorPackage,
|
|
459
|
+
subcommandName,
|
|
460
|
+
subcommandArgs
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const { packageEntry } = await resolveGeneratorPackageEntry(targetId);
|
|
465
|
+
renderGeneratePackageHelp({
|
|
466
|
+
io,
|
|
467
|
+
packageEntry,
|
|
468
|
+
packageIdInput: targetId,
|
|
469
|
+
json: options.json
|
|
267
470
|
});
|
|
471
|
+
return 0;
|
|
268
472
|
}
|
|
269
473
|
|
|
270
474
|
export { runPackageGenerateCommand };
|
|
@@ -39,6 +39,44 @@ function createCommandHandlerShared(ctx = {}) {
|
|
|
39
39
|
return lines.join("\n");
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
function createCatalogFetchStatusReporter(io = {}, { enabled = true } = {}) {
|
|
43
|
+
if (enabled !== true) {
|
|
44
|
+
return () => {};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const stdout = io?.stdout;
|
|
48
|
+
if (!stdout || typeof stdout.write !== "function") {
|
|
49
|
+
return () => {};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const activeFetchLabels = new Set();
|
|
53
|
+
return ({ packageEntry, state } = {}) => {
|
|
54
|
+
const packageId = String(packageEntry?.packageId || "").trim();
|
|
55
|
+
const version = String(packageEntry?.version || "").trim();
|
|
56
|
+
const packageLabel = version ? `${packageId}@${version}` : packageId;
|
|
57
|
+
if (!packageLabel) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (state === "start") {
|
|
62
|
+
if (activeFetchLabels.has(packageLabel)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
activeFetchLabels.add(packageLabel);
|
|
66
|
+
stdout.write(`Fetching ${packageLabel}...\n`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (state === "complete") {
|
|
71
|
+
if (!activeFetchLabels.has(packageLabel)) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
activeFetchLabels.delete(packageLabel);
|
|
75
|
+
stdout.write(`Fetching ${packageLabel}... done!\n`);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
42
80
|
async function runNpmInstall(appRoot, stderr) {
|
|
43
81
|
await new Promise((resolve, reject) => {
|
|
44
82
|
const child = spawn("npm", ["install"], {
|
|
@@ -298,6 +336,7 @@ function createCommandHandlerShared(ctx = {}) {
|
|
|
298
336
|
|
|
299
337
|
return {
|
|
300
338
|
renderResolvedSummary,
|
|
339
|
+
createCatalogFetchStatusReporter,
|
|
301
340
|
runNpmInstall,
|
|
302
341
|
getInstalledDependents,
|
|
303
342
|
resolvePackageKind,
|
|
@@ -51,6 +51,7 @@ function parseArgs(argv, { createCliError } = {}) {
|
|
|
51
51
|
inlineOptions: {}
|
|
52
52
|
};
|
|
53
53
|
const positional = [];
|
|
54
|
+
const allowLooseInlineOptionParsing = command === "generate";
|
|
54
55
|
|
|
55
56
|
while (args.length > 0) {
|
|
56
57
|
const token = String(args.shift() || "");
|
|
@@ -99,27 +100,45 @@ function parseArgs(argv, { createCliError } = {}) {
|
|
|
99
100
|
options.help = true;
|
|
100
101
|
continue;
|
|
101
102
|
}
|
|
103
|
+
if (token === "--force") {
|
|
104
|
+
options.inlineOptions.force = "true";
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
102
107
|
|
|
103
108
|
if (token.startsWith("--")) {
|
|
104
109
|
const withoutPrefix = token.slice(2);
|
|
105
110
|
const hasInlineValue = withoutPrefix.includes("=");
|
|
106
111
|
const optionName = hasInlineValue ? withoutPrefix.slice(0, withoutPrefix.indexOf("=")) : withoutPrefix;
|
|
107
|
-
const
|
|
108
|
-
?
|
|
109
|
-
:
|
|
110
|
-
|
|
111
|
-
if (!/^[a-z][a-z0-9-]*$/.test(optionName)) {
|
|
112
|
+
const optionNamePattern = allowLooseInlineOptionParsing
|
|
113
|
+
? /^[A-Za-z][A-Za-z0-9_-]*$/
|
|
114
|
+
: /^[a-z][a-z0-9-]*$/;
|
|
115
|
+
if (!optionNamePattern.test(optionName)) {
|
|
112
116
|
throw createCliError(`Unknown option: ${token}`, { showUsage: true });
|
|
113
117
|
}
|
|
114
|
-
|
|
115
|
-
|
|
118
|
+
|
|
119
|
+
let optionValueRaw;
|
|
120
|
+
if (hasInlineValue) {
|
|
121
|
+
optionValueRaw = withoutPrefix.slice(withoutPrefix.indexOf("=") + 1);
|
|
122
|
+
} else {
|
|
123
|
+
const nextToken = typeof args[0] === "string" ? String(args[0]) : "";
|
|
124
|
+
if (nextToken && !nextToken.startsWith("-")) {
|
|
125
|
+
optionValueRaw = args.shift();
|
|
126
|
+
}
|
|
116
127
|
}
|
|
117
|
-
|
|
118
|
-
if (!
|
|
128
|
+
|
|
129
|
+
if (!allowLooseInlineOptionParsing && typeof optionValueRaw !== "string") {
|
|
119
130
|
throw createCliError(`--${optionName} requires a value.`, { showUsage: true });
|
|
120
131
|
}
|
|
132
|
+
if (typeof optionValueRaw === "string") {
|
|
133
|
+
const optionValue = optionValueRaw.trim();
|
|
134
|
+
if (!hasInlineValue && optionValue.startsWith("-")) {
|
|
135
|
+
throw createCliError(`--${optionName} requires a value.`, { showUsage: true });
|
|
136
|
+
}
|
|
137
|
+
options.inlineOptions[optionName] = optionValue;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
121
140
|
|
|
122
|
-
options.inlineOptions[optionName] =
|
|
141
|
+
options.inlineOptions[optionName] = undefined;
|
|
123
142
|
continue;
|
|
124
143
|
}
|
|
125
144
|
|
|
@@ -18,6 +18,7 @@ function createCommandHandlerDeps(deps = {}) {
|
|
|
18
18
|
hydratePackageRegistryFromInstalledNodeModules: deps.hydratePackageRegistryFromInstalledNodeModules,
|
|
19
19
|
resolvePackageTemplateRoot: deps.resolvePackageTemplateRoot,
|
|
20
20
|
validateInlineOptionsForPackage: deps.validateInlineOptionsForPackage,
|
|
21
|
+
validateInlineOptionValuesForPackage: deps.validateInlineOptionValuesForPackage,
|
|
21
22
|
resolveLocalDependencyOrder: deps.resolveLocalDependencyOrder,
|
|
22
23
|
validatePlannedCapabilityClosure: deps.validatePlannedCapabilityClosure,
|
|
23
24
|
resolvePackageOptions: deps.resolvePackageOptions,
|
|
@@ -34,6 +35,7 @@ function createCommandHandlerDeps(deps = {}) {
|
|
|
34
35
|
writeJsonFile: deps.writeJsonFile,
|
|
35
36
|
writeFile: deps.writeFile,
|
|
36
37
|
mkdir: deps.mkdir,
|
|
38
|
+
readdir: deps.readdir,
|
|
37
39
|
path: deps.path,
|
|
38
40
|
inspectPackageOfferings: deps.inspectPackageOfferings,
|
|
39
41
|
buildFileWriteGroups: deps.buildFileWriteGroups,
|