@jskit-ai/jskit-cli 0.2.25 → 0.2.27
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 +2 -2
- package/src/server/argParser.js +21 -4
- package/src/server/cliRuntime.js +438 -121
- package/src/server/commandHandlers.js +329 -14
- package/src/server/pathResolution.js +0 -64
- package/src/server/runCli.js +8 -0
|
@@ -124,6 +124,18 @@ function createCommandHandlers(deps) {
|
|
|
124
124
|
return sortStrings(dependents);
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
function resolvePackageKind(packageEntry) {
|
|
128
|
+
const descriptor = ensureObject(packageEntry?.descriptor);
|
|
129
|
+
const normalizedKind = String(descriptor.kind || "").trim().toLowerCase();
|
|
130
|
+
if (normalizedKind === "runtime" || normalizedKind === "generator") {
|
|
131
|
+
return normalizedKind;
|
|
132
|
+
}
|
|
133
|
+
const packageId = String(packageEntry?.packageId || descriptor.packageId || "unknown-package").trim();
|
|
134
|
+
throw createCliError(
|
|
135
|
+
`Invalid package descriptor for ${packageId}: missing/invalid kind (expected runtime or generator).`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
127
139
|
function resolvePackageOptionNames(packageEntry) {
|
|
128
140
|
const optionSchemas = ensureObject(packageEntry?.descriptor?.options);
|
|
129
141
|
return Object.keys(optionSchemas);
|
|
@@ -177,6 +189,155 @@ function createCommandHandlers(deps) {
|
|
|
177
189
|
: " This bundle does not accept inline options.";
|
|
178
190
|
throw createCliError(`Unknown option(s) for bundle ${bundleId}: ${sortedUnknown.join(", ")}.${suffix}`);
|
|
179
191
|
}
|
|
192
|
+
|
|
193
|
+
function collectDescriptorContainerTokens({ packageId, side, values, issues }) {
|
|
194
|
+
const declaredTokens = new Set();
|
|
195
|
+
const duplicateTokens = new Set();
|
|
196
|
+
let invalidCount = 0;
|
|
197
|
+
|
|
198
|
+
for (const rawValue of ensureArray(values)) {
|
|
199
|
+
if (typeof rawValue !== "string") {
|
|
200
|
+
invalidCount += 1;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const token = rawValue.trim();
|
|
204
|
+
if (!token) {
|
|
205
|
+
invalidCount += 1;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (declaredTokens.has(token)) {
|
|
209
|
+
duplicateTokens.add(token);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
declaredTokens.add(token);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (invalidCount > 0) {
|
|
216
|
+
issues.push({
|
|
217
|
+
packageId,
|
|
218
|
+
side,
|
|
219
|
+
code: "descriptor-token-invalid",
|
|
220
|
+
message: `${packageId} (${side}): metadata.apiSummary.containerTokens includes ${invalidCount} non-string or empty token value(s).`
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
for (const token of sortStrings([...duplicateTokens])) {
|
|
224
|
+
issues.push({
|
|
225
|
+
packageId,
|
|
226
|
+
side,
|
|
227
|
+
code: "descriptor-token-duplicate",
|
|
228
|
+
token,
|
|
229
|
+
message: `${packageId} (${side}): descriptor token is declared more than once: ${token}.`
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return declaredTokens;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function collectUsedContainerTokens({ packageId, side, bindings, issues }) {
|
|
237
|
+
const usedTokens = new Set();
|
|
238
|
+
for (const rawBinding of ensureArray(bindings)) {
|
|
239
|
+
const binding = ensureObject(rawBinding);
|
|
240
|
+
const tokenExpression = String(binding.tokenExpression || "").trim();
|
|
241
|
+
const token = String(binding.token || "").trim();
|
|
242
|
+
const location = String(binding.location || "").trim();
|
|
243
|
+
if (binding.tokenResolved !== true || !token) {
|
|
244
|
+
const expressionLabel = tokenExpression || "<empty>";
|
|
245
|
+
const locationSuffix = location ? ` at ${location}` : "";
|
|
246
|
+
issues.push({
|
|
247
|
+
packageId,
|
|
248
|
+
side,
|
|
249
|
+
code: "binding-token-unresolved",
|
|
250
|
+
tokenExpression: expressionLabel,
|
|
251
|
+
location,
|
|
252
|
+
message: `${packageId} (${side}): unresolved DI token expression "${expressionLabel}"${locationSuffix}.`
|
|
253
|
+
});
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
usedTokens.add(token);
|
|
257
|
+
}
|
|
258
|
+
return usedTokens;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function collectProviderIntrospectionIssues({ packageId, packageInsights, issues }) {
|
|
262
|
+
const introspection = ensureObject(packageInsights);
|
|
263
|
+
if (!introspection.available) {
|
|
264
|
+
issues.push({
|
|
265
|
+
packageId,
|
|
266
|
+
side: "",
|
|
267
|
+
code: "provider-introspection-unavailable",
|
|
268
|
+
message: `${packageId}: provider source introspection is unavailable, so DI token parity cannot be verified.`
|
|
269
|
+
});
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const notes = ensureArray(introspection.notes).map((value) => String(value || "").trim()).filter(Boolean);
|
|
274
|
+
for (const note of notes) {
|
|
275
|
+
if (
|
|
276
|
+
note.startsWith("Skipped wildcard provider entrypoint during introspection:") ||
|
|
277
|
+
note.startsWith("Provider file missing during introspection:") ||
|
|
278
|
+
note.startsWith("Failed reading provider ")
|
|
279
|
+
) {
|
|
280
|
+
issues.push({
|
|
281
|
+
packageId,
|
|
282
|
+
side: "",
|
|
283
|
+
code: "provider-introspection-incomplete",
|
|
284
|
+
message: `${packageId}: ${note}`
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function collectDiLabelParityIssuesForPackage({ packageEntry, packageInsights }) {
|
|
291
|
+
const packageId = String(packageEntry?.packageId || "").trim();
|
|
292
|
+
const descriptor = ensureObject(packageEntry?.descriptor);
|
|
293
|
+
const metadataApiSummary = ensureObject(ensureObject(descriptor.metadata).apiSummary);
|
|
294
|
+
const descriptorTokenSummary = ensureObject(metadataApiSummary.containerTokens);
|
|
295
|
+
const bindingSections = ensureObject(ensureObject(packageInsights).containerBindings);
|
|
296
|
+
const issues = [];
|
|
297
|
+
const sides = ["server", "client"];
|
|
298
|
+
|
|
299
|
+
collectProviderIntrospectionIssues({ packageId, packageInsights, issues });
|
|
300
|
+
|
|
301
|
+
for (const side of sides) {
|
|
302
|
+
const declaredTokens = collectDescriptorContainerTokens({
|
|
303
|
+
packageId,
|
|
304
|
+
side,
|
|
305
|
+
values: descriptorTokenSummary[side],
|
|
306
|
+
issues
|
|
307
|
+
});
|
|
308
|
+
const usedTokens = collectUsedContainerTokens({
|
|
309
|
+
packageId,
|
|
310
|
+
side,
|
|
311
|
+
bindings: bindingSections[side],
|
|
312
|
+
issues
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
for (const token of sortStrings([...usedTokens])) {
|
|
316
|
+
if (!declaredTokens.has(token)) {
|
|
317
|
+
issues.push({
|
|
318
|
+
packageId,
|
|
319
|
+
side,
|
|
320
|
+
code: "binding-token-undeclared",
|
|
321
|
+
token,
|
|
322
|
+
message: `${packageId} (${side}): token is used by providers but missing from metadata.apiSummary.containerTokens.${side}: ${token}.`
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
for (const token of sortStrings([...declaredTokens])) {
|
|
327
|
+
if (!usedTokens.has(token)) {
|
|
328
|
+
issues.push({
|
|
329
|
+
packageId,
|
|
330
|
+
side,
|
|
331
|
+
code: "descriptor-token-unused",
|
|
332
|
+
token,
|
|
333
|
+
message: `${packageId} (${side}): token is declared in metadata.apiSummary.containerTokens.${side} but never bound by providers: ${token}.`
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return issues;
|
|
340
|
+
}
|
|
180
341
|
|
|
181
342
|
async function commandList({ positional, options, cwd, stdout }) {
|
|
182
343
|
const packageRegistry = await loadPackageRegistry();
|
|
@@ -207,8 +368,9 @@ function createCommandHandlers(deps) {
|
|
|
207
368
|
const mode = String(positional[0] || "").trim();
|
|
208
369
|
const shouldListBundles = !mode || mode === "bundles";
|
|
209
370
|
const shouldListPackages = !mode || mode === "packages";
|
|
210
|
-
|
|
211
|
-
|
|
371
|
+
const shouldListGenerators = !mode || mode === "generators";
|
|
372
|
+
|
|
373
|
+
if (!shouldListBundles && !shouldListPackages && !shouldListGenerators) {
|
|
212
374
|
throw createCliError(`Unknown list mode: ${mode}`, { showUsage: true });
|
|
213
375
|
}
|
|
214
376
|
|
|
@@ -238,8 +400,11 @@ function createCommandHandlers(deps) {
|
|
|
238
400
|
if (lines.length > 0) {
|
|
239
401
|
lines.push("");
|
|
240
402
|
}
|
|
241
|
-
lines.push(color.heading("Available packages:"));
|
|
242
|
-
const packageIds = sortStrings([...packageRegistry.keys()])
|
|
403
|
+
lines.push(color.heading("Available runtime packages:"));
|
|
404
|
+
const packageIds = sortStrings([...packageRegistry.keys()].filter((packageId) => {
|
|
405
|
+
const packageEntry = packageRegistry.get(packageId);
|
|
406
|
+
return resolvePackageKind(packageEntry) === "runtime";
|
|
407
|
+
}));
|
|
243
408
|
for (const packageId of packageIds) {
|
|
244
409
|
const packageEntry = packageRegistry.get(packageId);
|
|
245
410
|
const installedLabel = installedPackages.has(packageId) ? " (installed)" : "";
|
|
@@ -281,6 +446,24 @@ function createCommandHandlers(deps) {
|
|
|
281
446
|
}
|
|
282
447
|
}
|
|
283
448
|
}
|
|
449
|
+
|
|
450
|
+
if (shouldListGenerators) {
|
|
451
|
+
if (lines.length > 0) {
|
|
452
|
+
lines.push("");
|
|
453
|
+
}
|
|
454
|
+
lines.push(color.heading("Available generators:"));
|
|
455
|
+
const packageIds = sortStrings([...packageRegistry.keys()].filter((packageId) => {
|
|
456
|
+
const packageEntry = packageRegistry.get(packageId);
|
|
457
|
+
return resolvePackageKind(packageEntry) === "generator";
|
|
458
|
+
}));
|
|
459
|
+
for (const packageId of packageIds) {
|
|
460
|
+
const packageEntry = packageRegistry.get(packageId);
|
|
461
|
+
const installedLabel = installedPackages.has(packageId) ? " (installed)" : "";
|
|
462
|
+
lines.push(
|
|
463
|
+
`- ${color.item(packageId)} ${color.version(`(${packageEntry.version})`)}${installedLabel ? color.installed(installedLabel) : ""}`
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
284
467
|
|
|
285
468
|
if (options.json) {
|
|
286
469
|
const payload = {
|
|
@@ -299,14 +482,42 @@ function createCommandHandlers(deps) {
|
|
|
299
482
|
})
|
|
300
483
|
: [],
|
|
301
484
|
packages: shouldListPackages
|
|
485
|
+
? sortStrings([...packageRegistry.keys()])
|
|
486
|
+
.filter((packageId) => resolvePackageKind(packageRegistry.get(packageId)) === "runtime")
|
|
487
|
+
.map((packageId) => {
|
|
488
|
+
const packageEntry = packageRegistry.get(packageId);
|
|
489
|
+
return {
|
|
490
|
+
packageId,
|
|
491
|
+
version: packageEntry.version,
|
|
492
|
+
installed: installedPackages.has(packageId)
|
|
493
|
+
};
|
|
494
|
+
})
|
|
495
|
+
: [],
|
|
496
|
+
runtimePackages: shouldListPackages
|
|
302
497
|
? sortStrings([...packageRegistry.keys()]).map((packageId) => {
|
|
303
498
|
const packageEntry = packageRegistry.get(packageId);
|
|
499
|
+
if (resolvePackageKind(packageEntry) !== "runtime") {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
304
502
|
return {
|
|
305
503
|
packageId,
|
|
306
504
|
version: packageEntry.version,
|
|
307
505
|
installed: installedPackages.has(packageId)
|
|
308
506
|
};
|
|
309
|
-
})
|
|
507
|
+
}).filter(Boolean)
|
|
508
|
+
: [],
|
|
509
|
+
generators: shouldListGenerators
|
|
510
|
+
? sortStrings([...packageRegistry.keys()]).map((packageId) => {
|
|
511
|
+
const packageEntry = packageRegistry.get(packageId);
|
|
512
|
+
if (resolvePackageKind(packageEntry) !== "generator") {
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
return {
|
|
516
|
+
packageId,
|
|
517
|
+
version: packageEntry.version,
|
|
518
|
+
installed: installedPackages.has(packageId)
|
|
519
|
+
};
|
|
520
|
+
}).filter(Boolean)
|
|
310
521
|
: [],
|
|
311
522
|
installedLocalPackages: shouldListPackages
|
|
312
523
|
? installedLocalPackageIds.map((packageId) => {
|
|
@@ -1103,10 +1314,16 @@ function createCommandHandlers(deps) {
|
|
|
1103
1314
|
}
|
|
1104
1315
|
|
|
1105
1316
|
async function commandAdd({ positional, options, cwd, io }) {
|
|
1317
|
+
const invocationMode = options?.commandMode === "generate" ? "generate" : "add";
|
|
1106
1318
|
const targetType = String(positional[0] || "").trim();
|
|
1107
1319
|
const targetId = String(positional[1] || "").trim();
|
|
1108
1320
|
|
|
1109
1321
|
if (!targetType || !targetId) {
|
|
1322
|
+
if (invocationMode === "generate") {
|
|
1323
|
+
throw createCliError("generate requires a package id (generate <packageId>).", {
|
|
1324
|
+
showUsage: true
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1110
1327
|
throw createCliError("add requires target type and id (add bundle <id> | add package <id>).", {
|
|
1111
1328
|
showUsage: true
|
|
1112
1329
|
});
|
|
@@ -1114,6 +1331,11 @@ function createCommandHandlers(deps) {
|
|
|
1114
1331
|
if (targetType !== "bundle" && targetType !== "package") {
|
|
1115
1332
|
throw createCliError(`Unsupported add target type: ${targetType}`, { showUsage: true });
|
|
1116
1333
|
}
|
|
1334
|
+
if (invocationMode === "generate" && targetType !== "package") {
|
|
1335
|
+
throw createCliError("generate requires a package id (generate <packageId>).", {
|
|
1336
|
+
showUsage: true
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1117
1339
|
|
|
1118
1340
|
const appRoot = await resolveAppRootFromCwd(cwd);
|
|
1119
1341
|
const packageRegistry = await loadPackageRegistry();
|
|
@@ -1123,10 +1345,11 @@ function createCommandHandlers(deps) {
|
|
|
1123
1345
|
const { packageJsonPath, packageJson } = await loadAppPackageJson(appRoot);
|
|
1124
1346
|
const { lockPath, lock } = await loadLockFile(appRoot);
|
|
1125
1347
|
let resolvedTargetPackageId = targetType === "package" ? resolvePackageIdInput(targetId, combinedPackageRegistry) : "";
|
|
1126
|
-
if (targetType === "package"
|
|
1348
|
+
if (targetType === "package") {
|
|
1349
|
+
const packageIdForNodeModulesLookup = resolvedTargetPackageId || targetId;
|
|
1127
1350
|
const installedNodeModuleEntry = await resolveInstalledNodeModulePackageEntry({
|
|
1128
1351
|
appRoot,
|
|
1129
|
-
packageId:
|
|
1352
|
+
packageId: packageIdForNodeModulesLookup
|
|
1130
1353
|
});
|
|
1131
1354
|
if (installedNodeModuleEntry) {
|
|
1132
1355
|
combinedPackageRegistry.set(installedNodeModuleEntry.packageId, installedNodeModuleEntry);
|
|
@@ -1157,6 +1380,17 @@ function createCommandHandlers(deps) {
|
|
|
1157
1380
|
if (!targetPackageEntry) {
|
|
1158
1381
|
throw createCliError(`Unknown package: ${targetId}`);
|
|
1159
1382
|
}
|
|
1383
|
+
const packageKind = resolvePackageKind(targetPackageEntry);
|
|
1384
|
+
if (invocationMode === "add" && packageKind === "generator") {
|
|
1385
|
+
throw createCliError(
|
|
1386
|
+
`Package ${resolvedTargetPackageId} is a generator. Use: jskit generate ${resolvedTargetPackageId}`
|
|
1387
|
+
);
|
|
1388
|
+
}
|
|
1389
|
+
if (invocationMode === "generate" && packageKind !== "generator") {
|
|
1390
|
+
throw createCliError(
|
|
1391
|
+
`Package ${resolvedTargetPackageId} is a runtime package. Use: jskit add package ${resolvedTargetPackageId}`
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1160
1394
|
validateInlineOptionsForPackage(targetPackageEntry, options.inlineOptions);
|
|
1161
1395
|
}
|
|
1162
1396
|
|
|
@@ -1164,6 +1398,17 @@ function createCommandHandlers(deps) {
|
|
|
1164
1398
|
targetPackageIds,
|
|
1165
1399
|
combinedPackageRegistry
|
|
1166
1400
|
);
|
|
1401
|
+
if (invocationMode === "add" && targetType === "bundle") {
|
|
1402
|
+
const bundledGenerators = resolvedPackageIds.filter((packageId) => {
|
|
1403
|
+
const packageEntry = combinedPackageRegistry.get(packageId);
|
|
1404
|
+
return resolvePackageKind(packageEntry) === "generator";
|
|
1405
|
+
});
|
|
1406
|
+
if (bundledGenerators.length > 0) {
|
|
1407
|
+
throw createCliError(
|
|
1408
|
+
`Bundle ${targetId} includes generator package(s): ${bundledGenerators.join(", ")}. Use: jskit generate <packageId>`
|
|
1409
|
+
);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1167
1412
|
const plannedInstalledPackageIds = sortStrings([
|
|
1168
1413
|
...new Set([
|
|
1169
1414
|
...Object.keys(ensureObject(lock.installedPackages)).map((value) => String(value || "").trim()).filter(Boolean),
|
|
@@ -1173,7 +1418,7 @@ function createCommandHandlers(deps) {
|
|
|
1173
1418
|
validatePlannedCapabilityClosure(
|
|
1174
1419
|
plannedInstalledPackageIds,
|
|
1175
1420
|
combinedPackageRegistry,
|
|
1176
|
-
|
|
1421
|
+
`${invocationMode} ${targetType} ${targetId}`
|
|
1177
1422
|
);
|
|
1178
1423
|
|
|
1179
1424
|
if (targetType === "bundle") {
|
|
@@ -1252,14 +1497,18 @@ function createCommandHandlers(deps) {
|
|
|
1252
1497
|
validatePlannedCapabilityClosure(
|
|
1253
1498
|
postInstallPackageIds,
|
|
1254
1499
|
combinedPackageRegistry,
|
|
1255
|
-
|
|
1500
|
+
`${invocationMode} ${targetType} ${targetId}`
|
|
1256
1501
|
);
|
|
1257
1502
|
}
|
|
1258
1503
|
|
|
1259
1504
|
const finalResolvedPackageIds = sortStrings([...resolvedPackageIds, ...adoptedPackageIds]);
|
|
1260
1505
|
|
|
1261
1506
|
const touchedFileList = sortStrings([...touchedFiles]);
|
|
1262
|
-
const successLabel =
|
|
1507
|
+
const successLabel = invocationMode === "generate"
|
|
1508
|
+
? "Generated with"
|
|
1509
|
+
: targetType === "bundle"
|
|
1510
|
+
? "Added bundle"
|
|
1511
|
+
: "Added package";
|
|
1263
1512
|
const installWarnings = installedPackageRecords
|
|
1264
1513
|
.flatMap((record) => ensureArray(ensureObject(record).warnings))
|
|
1265
1514
|
.map((value) => String(value || "").trim())
|
|
@@ -1275,7 +1524,7 @@ function createCommandHandlers(deps) {
|
|
|
1275
1524
|
|
|
1276
1525
|
if (options.json) {
|
|
1277
1526
|
io.stdout.write(`${JSON.stringify({
|
|
1278
|
-
targetType,
|
|
1527
|
+
targetType: invocationMode === "generate" ? "generator" : targetType,
|
|
1279
1528
|
targetId,
|
|
1280
1529
|
resolvedPackages: finalResolvedPackageIds,
|
|
1281
1530
|
touchedFiles: touchedFileList,
|
|
@@ -1310,6 +1559,32 @@ function createCommandHandlers(deps) {
|
|
|
1310
1559
|
|
|
1311
1560
|
return 0;
|
|
1312
1561
|
}
|
|
1562
|
+
|
|
1563
|
+
async function commandGenerate({ positional, options, cwd, io }) {
|
|
1564
|
+
const firstToken = String(positional[0] || "").trim();
|
|
1565
|
+
const secondToken = String(positional[1] || "").trim();
|
|
1566
|
+
if (firstToken === "bundle") {
|
|
1567
|
+
throw createCliError("generate supports packages only (generate <packageId>).", {
|
|
1568
|
+
showUsage: true
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
const targetId = firstToken === "package" ? secondToken : firstToken;
|
|
1572
|
+
if (!targetId) {
|
|
1573
|
+
throw createCliError("generate requires a package id (generate <packageId>).", {
|
|
1574
|
+
showUsage: true
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
return commandAdd({
|
|
1579
|
+
positional: ["package", targetId],
|
|
1580
|
+
options: {
|
|
1581
|
+
...options,
|
|
1582
|
+
commandMode: "generate"
|
|
1583
|
+
},
|
|
1584
|
+
cwd,
|
|
1585
|
+
io
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1313
1588
|
|
|
1314
1589
|
async function commandUpdate({ positional, options, cwd, io }) {
|
|
1315
1590
|
const targetType = String(positional[0] || "").trim();
|
|
@@ -1455,7 +1730,7 @@ function createCommandHandlers(deps) {
|
|
|
1455
1730
|
io.stdout.write(`- ${touchedFile}\n`);
|
|
1456
1731
|
}
|
|
1457
1732
|
io.stdout.write(`Lock file: ${normalizeRelativePath(appRoot, lockPath)}\n`);
|
|
1458
|
-
if (migrationWarnings.length > 0) {
|
|
1733
|
+
if (options.verbose && migrationWarnings.length > 0) {
|
|
1459
1734
|
io.stdout.write(`Warnings (${migrationWarnings.length}):\n`);
|
|
1460
1735
|
for (const warning of migrationWarnings) {
|
|
1461
1736
|
io.stdout.write(`- ${warning}\n`);
|
|
@@ -1760,19 +2035,58 @@ function createCommandHandlers(deps) {
|
|
|
1760
2035
|
async function commandLintDescriptors({ options, stdout }) {
|
|
1761
2036
|
const packageRegistry = await loadPackageRegistry();
|
|
1762
2037
|
const bundleRegistry = await loadBundleRegistry();
|
|
2038
|
+
const shouldCheckDiLabels = options.checkDiLabels === true;
|
|
2039
|
+
let diLabelIssues = [];
|
|
2040
|
+
if (shouldCheckDiLabels) {
|
|
2041
|
+
const issues = [];
|
|
2042
|
+
for (const packageId of sortStrings([...packageRegistry.keys()])) {
|
|
2043
|
+
const packageEntry = packageRegistry.get(packageId);
|
|
2044
|
+
if (!packageEntry) {
|
|
2045
|
+
continue;
|
|
2046
|
+
}
|
|
2047
|
+
const packageInsights = await inspectPackageOfferings({ packageEntry });
|
|
2048
|
+
issues.push(...collectDiLabelParityIssuesForPackage({ packageEntry, packageInsights }));
|
|
2049
|
+
}
|
|
2050
|
+
diLabelIssues = issues;
|
|
2051
|
+
}
|
|
1763
2052
|
const payload = {
|
|
1764
2053
|
packageCount: packageRegistry.size,
|
|
1765
2054
|
bundleCount: bundleRegistry.size,
|
|
1766
2055
|
packages: sortStrings([...packageRegistry.keys()]),
|
|
1767
|
-
bundles: sortStrings([...bundleRegistry.keys()])
|
|
2056
|
+
bundles: sortStrings([...bundleRegistry.keys()]),
|
|
2057
|
+
diLabelCheck: shouldCheckDiLabels
|
|
2058
|
+
? {
|
|
2059
|
+
enabled: true,
|
|
2060
|
+
issueCount: diLabelIssues.length,
|
|
2061
|
+
issues: diLabelIssues
|
|
2062
|
+
}
|
|
2063
|
+
: {
|
|
2064
|
+
enabled: false
|
|
2065
|
+
}
|
|
1768
2066
|
};
|
|
1769
2067
|
|
|
1770
2068
|
if (options.json) {
|
|
1771
2069
|
stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
1772
2070
|
} else {
|
|
1773
|
-
|
|
2071
|
+
const descriptorStatus = shouldCheckDiLabels && diLabelIssues.length > 0 ? "failed" : "passed";
|
|
2072
|
+
stdout.write(`Descriptor lint ${descriptorStatus}.\n`);
|
|
1774
2073
|
stdout.write(`Packages: ${payload.packageCount}\n`);
|
|
1775
2074
|
stdout.write(`Bundles: ${payload.bundleCount}\n`);
|
|
2075
|
+
if (shouldCheckDiLabels) {
|
|
2076
|
+
if (diLabelIssues.length === 0) {
|
|
2077
|
+
stdout.write("DI label parity check passed.\n");
|
|
2078
|
+
} else {
|
|
2079
|
+
stdout.write(`DI label parity check failed (${diLabelIssues.length} issue(s)).\n`);
|
|
2080
|
+
for (const issue of diLabelIssues) {
|
|
2081
|
+
const code = String(issue?.code || "").trim();
|
|
2082
|
+
const codeLabel = code ? `[${code}] ` : "";
|
|
2083
|
+
stdout.write(`- ${codeLabel}${String(issue?.message || "").trim()}\n`);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
if (shouldCheckDiLabels && diLabelIssues.length > 0) {
|
|
2089
|
+
return 1;
|
|
1776
2090
|
}
|
|
1777
2091
|
return 0;
|
|
1778
2092
|
}
|
|
@@ -1782,6 +2096,7 @@ function createCommandHandlers(deps) {
|
|
|
1782
2096
|
commandShow,
|
|
1783
2097
|
commandCreate,
|
|
1784
2098
|
commandAdd,
|
|
2099
|
+
commandGenerate,
|
|
1785
2100
|
commandMigrations,
|
|
1786
2101
|
commandPosition,
|
|
1787
2102
|
commandUpdate,
|
|
@@ -8,66 +8,6 @@ import { createCliError } from "./cliError.js";
|
|
|
8
8
|
const CLI_PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
9
9
|
const require = createRequire(import.meta.url);
|
|
10
10
|
|
|
11
|
-
function isWorkspaceRoot(candidateRoot) {
|
|
12
|
-
if (!candidateRoot) {
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
return (
|
|
16
|
-
existsSync(path.join(candidateRoot, "packages")) &&
|
|
17
|
-
existsSync(path.join(candidateRoot, "packages", "kernel")) &&
|
|
18
|
-
existsSync(path.join(candidateRoot, "tooling", "jskit-cli"))
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function collectAncestorDirectories(startDirectory) {
|
|
23
|
-
const ancestors = [];
|
|
24
|
-
let current = path.resolve(startDirectory);
|
|
25
|
-
while (true) {
|
|
26
|
-
ancestors.push(current);
|
|
27
|
-
const parent = path.dirname(current);
|
|
28
|
-
if (parent === current) {
|
|
29
|
-
break;
|
|
30
|
-
}
|
|
31
|
-
current = parent;
|
|
32
|
-
}
|
|
33
|
-
return ancestors;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function resolveWorkspaceRoot() {
|
|
37
|
-
const candidates = [];
|
|
38
|
-
const seen = new Set();
|
|
39
|
-
const appendCandidate = (candidatePath) => {
|
|
40
|
-
const raw = String(candidatePath || "").trim();
|
|
41
|
-
if (!raw) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const absolute = path.resolve(raw);
|
|
45
|
-
if (seen.has(absolute)) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
seen.add(absolute);
|
|
49
|
-
candidates.push(absolute);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
appendCandidate(process.env.JSKIT_REPO_ROOT);
|
|
53
|
-
appendCandidate(path.resolve(CLI_PACKAGE_ROOT, "../.."));
|
|
54
|
-
appendCandidate(CLI_PACKAGE_ROOT);
|
|
55
|
-
|
|
56
|
-
const cwdAncestors = collectAncestorDirectories(process.cwd());
|
|
57
|
-
for (const ancestor of cwdAncestors) {
|
|
58
|
-
appendCandidate(ancestor);
|
|
59
|
-
appendCandidate(path.join(ancestor, "jskit-ai"));
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
for (const candidate of candidates) {
|
|
63
|
-
if (isWorkspaceRoot(candidate)) {
|
|
64
|
-
return candidate;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return "";
|
|
69
|
-
}
|
|
70
|
-
|
|
71
11
|
function resolveCatalogPackagesPath() {
|
|
72
12
|
const explicitPath = String(process.env.JSKIT_CATALOG_PACKAGES_PATH || "").trim();
|
|
73
13
|
if (explicitPath) {
|
|
@@ -92,15 +32,11 @@ function resolveCatalogPackagesPath() {
|
|
|
92
32
|
);
|
|
93
33
|
}
|
|
94
34
|
|
|
95
|
-
const WORKSPACE_ROOT = resolveWorkspaceRoot();
|
|
96
|
-
const MODULES_ROOT = WORKSPACE_ROOT ? path.join(WORKSPACE_ROOT, "packages") : "";
|
|
97
35
|
const BUNDLES_ROOT = path.join(CLI_PACKAGE_ROOT, "bundles");
|
|
98
36
|
const CATALOG_PACKAGES_PATH = resolveCatalogPackagesPath();
|
|
99
37
|
|
|
100
38
|
export {
|
|
101
39
|
CLI_PACKAGE_ROOT,
|
|
102
|
-
WORKSPACE_ROOT,
|
|
103
|
-
MODULES_ROOT,
|
|
104
40
|
BUNDLES_ROOT,
|
|
105
41
|
CATALOG_PACKAGES_PATH
|
|
106
42
|
};
|
package/src/server/runCli.js
CHANGED
|
@@ -64,6 +64,14 @@ function createRunCli({
|
|
|
64
64
|
io: { stdin, stdout, stderr }
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
|
+
if (command === "generate") {
|
|
68
|
+
return await commandHandlers.commandGenerate({
|
|
69
|
+
positional,
|
|
70
|
+
options,
|
|
71
|
+
cwd,
|
|
72
|
+
io: { stdin, stdout, stderr }
|
|
73
|
+
});
|
|
74
|
+
}
|
|
67
75
|
if (command === "position") {
|
|
68
76
|
return await commandHandlers.commandPosition({
|
|
69
77
|
positional,
|