@jskit-ai/jskit-cli 0.2.27 → 0.2.28
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 -2
- package/src/server/cliRuntime/appState.js +226 -0
- package/src/server/cliRuntime/capabilitySupport.js +194 -0
- package/src/server/cliRuntime/descriptorValidation.js +150 -0
- package/src/server/cliRuntime/ioAndMigrations.js +381 -0
- package/src/server/cliRuntime/localPackageSupport.js +390 -0
- package/src/server/cliRuntime/mutationApplication.js +9 -0
- package/src/server/cliRuntime/mutationWhen.js +285 -0
- package/src/server/cliRuntime/mutations/fileMutations.js +247 -0
- package/src/server/cliRuntime/mutations/installMigrationMutation.js +213 -0
- package/src/server/cliRuntime/mutations/mutationPathUtils.js +12 -0
- package/src/server/cliRuntime/mutations/surfaceTargets.js +155 -0
- package/src/server/cliRuntime/mutations/templateContext.js +171 -0
- package/src/server/cliRuntime/mutations/textMutations.js +250 -0
- package/src/server/cliRuntime/packageInstallFlow.js +489 -0
- package/src/server/cliRuntime/packageIntrospection/exportEntries.js +259 -0
- package/src/server/cliRuntime/packageIntrospection/exportedSymbols.js +216 -0
- package/src/server/cliRuntime/packageIntrospection/placementNormalization.js +98 -0
- package/src/server/cliRuntime/packageIntrospection/providerBindingIntrospection.js +377 -0
- package/src/server/cliRuntime/packageIntrospection.js +137 -0
- package/src/server/cliRuntime/packageOptions.js +299 -0
- package/src/server/cliRuntime/packageRegistries.js +343 -0
- package/src/server/cliRuntime/packageTemplateResolution.js +131 -0
- package/src/server/cliRuntime/viteProxy.js +356 -0
- package/src/server/commandHandlers/health.js +292 -0
- package/src/server/commandHandlers/list.js +292 -0
- package/src/server/commandHandlers/package.js +23 -0
- package/src/server/commandHandlers/packageCommands/add.js +282 -0
- package/src/server/commandHandlers/packageCommands/create.js +155 -0
- package/src/server/commandHandlers/packageCommands/generate.js +116 -0
- package/src/server/commandHandlers/packageCommands/migrations.js +155 -0
- package/src/server/commandHandlers/packageCommands/position.js +103 -0
- package/src/server/commandHandlers/packageCommands/remove.js +181 -0
- package/src/server/commandHandlers/packageCommands/update.js +40 -0
- package/src/server/commandHandlers/shared.js +314 -0
- package/src/server/commandHandlers/show/payloads.js +92 -0
- package/src/server/commandHandlers/show/renderBundleText.js +16 -0
- package/src/server/commandHandlers/show/renderHelpers.js +82 -0
- package/src/server/commandHandlers/show/renderPackageCapabilities.js +124 -0
- package/src/server/commandHandlers/show/renderPackageExports.js +203 -0
- package/src/server/commandHandlers/show/renderPackageText.js +332 -0
- package/src/server/commandHandlers/show.js +114 -0
- package/src/server/core/argParser.js +144 -0
- package/src/server/{runtimeDeps.js → core/buildCommandDeps.js} +2 -1
- package/src/server/core/commandCatalog.js +47 -0
- package/src/server/core/createCliRunner.js +150 -0
- package/src/server/core/createCommandHandlers.js +43 -0
- package/src/server/{runCli.js → core/dispatchCli.js} +14 -1
- package/src/server/core/usageHelp.js +344 -0
- package/src/server/index.js +1 -1
- package/src/server/{optionInterpolation.js → shared/optionInterpolation.js} +12 -1
- package/src/server/{pathResolution.js → shared/pathResolution.js} +1 -1
- package/src/server/argParser.js +0 -206
- package/src/server/cliRuntime.js +0 -4956
- package/src/server/commandHandlers.js +0 -2109
- /package/src/server/{cliError.js → shared/cliError.js} +0 -0
- /package/src/server/{collectionUtils.js → shared/collectionUtils.js} +0 -0
- /package/src/server/{outputFormatting.js → shared/outputFormatting.js} +0 -0
- /package/src/server/{packageIdHelpers.js → shared/packageIdHelpers.js} +0 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ensureArray,
|
|
3
|
+
ensureObject,
|
|
4
|
+
sortStrings
|
|
5
|
+
} from "../../shared/collectionUtils.js";
|
|
6
|
+
|
|
7
|
+
async function runPackageAddCommand(ctx = {}, { positional, options, cwd, io }) {
|
|
8
|
+
const {
|
|
9
|
+
createCliError,
|
|
10
|
+
normalizeRelativePath,
|
|
11
|
+
resolveAppRootFromCwd,
|
|
12
|
+
loadPackageRegistry,
|
|
13
|
+
loadAppLocalPackageRegistry,
|
|
14
|
+
loadBundleRegistry,
|
|
15
|
+
mergePackageRegistries,
|
|
16
|
+
loadAppPackageJson,
|
|
17
|
+
loadLockFile,
|
|
18
|
+
resolvePackageIdFromRegistryOrNodeModules,
|
|
19
|
+
hydratePackageRegistryFromInstalledNodeModules,
|
|
20
|
+
resolvePackageKind,
|
|
21
|
+
validateInlineOptionsForPackage,
|
|
22
|
+
resolveLocalDependencyOrder,
|
|
23
|
+
validatePlannedCapabilityClosure,
|
|
24
|
+
validateInlineOptionsForBundle,
|
|
25
|
+
resolveBundleInlineOptionsForPackage,
|
|
26
|
+
resolvePackageOptions,
|
|
27
|
+
applyPackageInstall,
|
|
28
|
+
adoptAppLocalPackageDependencies,
|
|
29
|
+
writeJsonFile,
|
|
30
|
+
runNpmInstall,
|
|
31
|
+
renderResolvedSummary
|
|
32
|
+
} = ctx;
|
|
33
|
+
|
|
34
|
+
const invocationMode = options?.commandMode === "generate" ? "generate" : "add";
|
|
35
|
+
const targetType = String(positional[0] || "").trim();
|
|
36
|
+
const targetId = String(positional[1] || "").trim();
|
|
37
|
+
|
|
38
|
+
if (!targetType || !targetId) {
|
|
39
|
+
if (invocationMode === "generate") {
|
|
40
|
+
throw createCliError("generate requires a package id (generate <packageId>).", {
|
|
41
|
+
showUsage: true
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
throw createCliError("add requires target type and id (add bundle <id> | add package <id>).", {
|
|
45
|
+
showUsage: true
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (targetType !== "bundle" && targetType !== "package") {
|
|
49
|
+
throw createCliError(`Unsupported add target type: ${targetType}`, { showUsage: true });
|
|
50
|
+
}
|
|
51
|
+
if (invocationMode === "generate" && targetType !== "package") {
|
|
52
|
+
throw createCliError("generate requires a package id (generate <packageId>).", {
|
|
53
|
+
showUsage: true
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const appRoot = await resolveAppRootFromCwd(cwd);
|
|
58
|
+
const packageRegistry = await loadPackageRegistry();
|
|
59
|
+
const appLocalRegistry = await loadAppLocalPackageRegistry(appRoot);
|
|
60
|
+
const bundleRegistry = await loadBundleRegistry();
|
|
61
|
+
const combinedPackageRegistry = mergePackageRegistries(packageRegistry, appLocalRegistry);
|
|
62
|
+
const { packageJsonPath, packageJson } = await loadAppPackageJson(appRoot);
|
|
63
|
+
const { lockPath, lock } = await loadLockFile(appRoot);
|
|
64
|
+
const resolvedTargetPackageId = targetType === "package"
|
|
65
|
+
? await resolvePackageIdFromRegistryOrNodeModules({
|
|
66
|
+
appRoot,
|
|
67
|
+
packageRegistry: combinedPackageRegistry,
|
|
68
|
+
packageIdInput: targetId
|
|
69
|
+
})
|
|
70
|
+
: "";
|
|
71
|
+
|
|
72
|
+
const targetPackageIds = targetType === "bundle"
|
|
73
|
+
? ensureArray(bundleRegistry.get(targetId)?.packages).map((value) => String(value))
|
|
74
|
+
: [resolvedTargetPackageId];
|
|
75
|
+
if (targetType === "bundle" && targetPackageIds.length === 0) {
|
|
76
|
+
throw createCliError(`Unknown bundle: ${targetId}`);
|
|
77
|
+
}
|
|
78
|
+
if (targetType === "package" && !resolvedTargetPackageId) {
|
|
79
|
+
throw createCliError(
|
|
80
|
+
`Unknown package: ${targetId}. Install an external module first (npm install ${targetId}) if you want to adopt it into lock.`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
await hydratePackageRegistryFromInstalledNodeModules({
|
|
85
|
+
appRoot,
|
|
86
|
+
packageRegistry: combinedPackageRegistry,
|
|
87
|
+
seedPackageIds: targetPackageIds
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (targetType === "package") {
|
|
91
|
+
const targetPackageEntry = combinedPackageRegistry.get(resolvedTargetPackageId);
|
|
92
|
+
if (!targetPackageEntry) {
|
|
93
|
+
throw createCliError(`Unknown package: ${targetId}`);
|
|
94
|
+
}
|
|
95
|
+
const packageKind = resolvePackageKind(targetPackageEntry);
|
|
96
|
+
if (invocationMode === "add" && packageKind === "generator") {
|
|
97
|
+
throw createCliError(
|
|
98
|
+
`Package ${resolvedTargetPackageId} is a generator. Use: jskit generate ${resolvedTargetPackageId}`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
if (invocationMode === "generate" && packageKind !== "generator") {
|
|
102
|
+
throw createCliError(
|
|
103
|
+
`Package ${resolvedTargetPackageId} is a runtime package. Use: jskit add package ${resolvedTargetPackageId}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
validateInlineOptionsForPackage(targetPackageEntry, options.inlineOptions);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const { ordered: resolvedPackageIds, externalDependencies } = resolveLocalDependencyOrder(
|
|
110
|
+
targetPackageIds,
|
|
111
|
+
combinedPackageRegistry
|
|
112
|
+
);
|
|
113
|
+
if (invocationMode === "add" && targetType === "bundle") {
|
|
114
|
+
const bundledGenerators = resolvedPackageIds.filter((packageId) => {
|
|
115
|
+
const packageEntry = combinedPackageRegistry.get(packageId);
|
|
116
|
+
return resolvePackageKind(packageEntry) === "generator";
|
|
117
|
+
});
|
|
118
|
+
if (bundledGenerators.length > 0) {
|
|
119
|
+
throw createCliError(
|
|
120
|
+
`Bundle ${targetId} includes generator package(s): ${bundledGenerators.join(", ")}. Use: jskit generate <packageId>`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const plannedInstalledPackageIds = sortStrings([
|
|
125
|
+
...new Set([
|
|
126
|
+
...Object.keys(ensureObject(lock.installedPackages)).map((value) => String(value || "").trim()).filter(Boolean),
|
|
127
|
+
...resolvedPackageIds
|
|
128
|
+
])
|
|
129
|
+
]);
|
|
130
|
+
validatePlannedCapabilityClosure(
|
|
131
|
+
plannedInstalledPackageIds,
|
|
132
|
+
combinedPackageRegistry,
|
|
133
|
+
`${invocationMode} ${targetType} ${targetId}`
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
if (targetType === "bundle") {
|
|
137
|
+
validateInlineOptionsForBundle({
|
|
138
|
+
bundleId: targetId,
|
|
139
|
+
inlineOptions: options.inlineOptions,
|
|
140
|
+
packageIds: resolvedPackageIds,
|
|
141
|
+
packageRegistry: combinedPackageRegistry
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const packagesToInstall = [];
|
|
146
|
+
const resolvedOptionsByPackage = {};
|
|
147
|
+
const forceReapplyTarget = options?.forceReapplyTarget === true;
|
|
148
|
+
const hasInlineOptions = Object.keys(ensureObject(options.inlineOptions)).length > 0;
|
|
149
|
+
for (const packageId of resolvedPackageIds) {
|
|
150
|
+
const packageEntry = combinedPackageRegistry.get(packageId);
|
|
151
|
+
const existingInstall = ensureObject(lock.installedPackages[packageId]);
|
|
152
|
+
const existingVersion = String(existingInstall.version || "").trim();
|
|
153
|
+
const isDirectTargetPackage = targetType === "package" && packageId === resolvedTargetPackageId;
|
|
154
|
+
const packageInlineOptions = targetType === "bundle"
|
|
155
|
+
? resolveBundleInlineOptionsForPackage(packageEntry, options.inlineOptions)
|
|
156
|
+
: isDirectTargetPackage
|
|
157
|
+
? ensureObject(options.inlineOptions)
|
|
158
|
+
: {};
|
|
159
|
+
const hasPackageInlineOptions = Object.keys(packageInlineOptions).length > 0;
|
|
160
|
+
const shouldReapplyInstalledPackage =
|
|
161
|
+
(isDirectTargetPackage && (forceReapplyTarget || hasInlineOptions)) ||
|
|
162
|
+
(targetType === "bundle" && hasPackageInlineOptions);
|
|
163
|
+
const shouldSkipGenerateDependencyReinstall =
|
|
164
|
+
invocationMode === "generate" &&
|
|
165
|
+
!isDirectTargetPackage &&
|
|
166
|
+
Boolean(existingVersion);
|
|
167
|
+
if (shouldSkipGenerateDependencyReinstall && !shouldReapplyInstalledPackage) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (existingVersion && existingVersion === packageEntry.version && !shouldReapplyInstalledPackage) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
packagesToInstall.push(packageId);
|
|
174
|
+
const lockEntryOptions = ensureObject(existingInstall.options);
|
|
175
|
+
resolvedOptionsByPackage[packageId] = await resolvePackageOptions(
|
|
176
|
+
packageEntry,
|
|
177
|
+
{
|
|
178
|
+
...lockEntryOptions,
|
|
179
|
+
...packageInlineOptions
|
|
180
|
+
},
|
|
181
|
+
io,
|
|
182
|
+
{ appRoot }
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const touchedFiles = new Set();
|
|
187
|
+
const installedPackageRecords = [];
|
|
188
|
+
|
|
189
|
+
for (const packageId of packagesToInstall) {
|
|
190
|
+
const packageEntry = combinedPackageRegistry.get(packageId);
|
|
191
|
+
const managedRecord = await applyPackageInstall({
|
|
192
|
+
packageEntry,
|
|
193
|
+
packageOptions: resolvedOptionsByPackage[packageId],
|
|
194
|
+
appRoot,
|
|
195
|
+
appPackageJson: packageJson,
|
|
196
|
+
lock,
|
|
197
|
+
packageRegistry: combinedPackageRegistry,
|
|
198
|
+
touchedFiles
|
|
199
|
+
});
|
|
200
|
+
installedPackageRecords.push(managedRecord);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const {
|
|
204
|
+
appLocalRegistry: refreshedAppLocalRegistry,
|
|
205
|
+
adoptedPackageIds
|
|
206
|
+
} = await adoptAppLocalPackageDependencies({
|
|
207
|
+
appRoot,
|
|
208
|
+
appPackageJson: packageJson,
|
|
209
|
+
lock
|
|
210
|
+
});
|
|
211
|
+
for (const [packageId, packageEntry] of refreshedAppLocalRegistry.entries()) {
|
|
212
|
+
combinedPackageRegistry.set(packageId, packageEntry);
|
|
213
|
+
}
|
|
214
|
+
if (adoptedPackageIds.length > 0) {
|
|
215
|
+
const postInstallPackageIds = sortStrings(Object.keys(ensureObject(lock.installedPackages)));
|
|
216
|
+
validatePlannedCapabilityClosure(
|
|
217
|
+
postInstallPackageIds,
|
|
218
|
+
combinedPackageRegistry,
|
|
219
|
+
`${invocationMode} ${targetType} ${targetId}`
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const finalResolvedPackageIds = sortStrings([...resolvedPackageIds, ...adoptedPackageIds]);
|
|
224
|
+
|
|
225
|
+
const touchedFileList = sortStrings([...touchedFiles]);
|
|
226
|
+
const successLabel = invocationMode === "generate"
|
|
227
|
+
? "Generated with"
|
|
228
|
+
: targetType === "bundle"
|
|
229
|
+
? "Added bundle"
|
|
230
|
+
: "Added package";
|
|
231
|
+
const installWarnings = installedPackageRecords
|
|
232
|
+
.flatMap((record) => ensureArray(ensureObject(record).warnings))
|
|
233
|
+
.map((value) => String(value || "").trim())
|
|
234
|
+
.filter(Boolean);
|
|
235
|
+
|
|
236
|
+
if (!options.dryRun) {
|
|
237
|
+
await writeJsonFile(packageJsonPath, packageJson);
|
|
238
|
+
await writeJsonFile(lockPath, lock);
|
|
239
|
+
if (options.runNpmInstall) {
|
|
240
|
+
await runNpmInstall(appRoot, io.stderr);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (options.json) {
|
|
245
|
+
io.stdout.write(`${JSON.stringify({
|
|
246
|
+
targetType: invocationMode === "generate" ? "generator" : targetType,
|
|
247
|
+
targetId,
|
|
248
|
+
resolvedPackages: finalResolvedPackageIds,
|
|
249
|
+
touchedFiles: touchedFileList,
|
|
250
|
+
lockPath: normalizeRelativePath(appRoot, lockPath),
|
|
251
|
+
externalDependencies,
|
|
252
|
+
dryRun: options.dryRun,
|
|
253
|
+
installed: installedPackageRecords,
|
|
254
|
+
warnings: installWarnings
|
|
255
|
+
}, null, 2)}\n`);
|
|
256
|
+
} else {
|
|
257
|
+
io.stdout.write(
|
|
258
|
+
`${renderResolvedSummary(
|
|
259
|
+
`${successLabel}`,
|
|
260
|
+
targetId,
|
|
261
|
+
finalResolvedPackageIds,
|
|
262
|
+
touchedFileList,
|
|
263
|
+
appRoot,
|
|
264
|
+
lockPath,
|
|
265
|
+
externalDependencies
|
|
266
|
+
)}\n`
|
|
267
|
+
);
|
|
268
|
+
if (installWarnings.length > 0) {
|
|
269
|
+
io.stdout.write(`Warnings (${installWarnings.length}):\n`);
|
|
270
|
+
for (const warning of installWarnings) {
|
|
271
|
+
io.stdout.write(`- ${warning}\n`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (options.dryRun) {
|
|
275
|
+
io.stdout.write("Dry run enabled: no files were written.\n");
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return 0;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export { runPackageAddCommand };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ensureObject,
|
|
3
|
+
sortStrings
|
|
4
|
+
} from "../../shared/collectionUtils.js";
|
|
5
|
+
|
|
6
|
+
async function runPackageCreateCommand(ctx = {}, { positional, options, cwd, io }) {
|
|
7
|
+
const {
|
|
8
|
+
createCliError,
|
|
9
|
+
normalizeRelativePath,
|
|
10
|
+
normalizeRelativePosixPath,
|
|
11
|
+
resolveAppRootFromCwd,
|
|
12
|
+
loadAppPackageJson,
|
|
13
|
+
loadLockFile,
|
|
14
|
+
resolveLocalPackageId,
|
|
15
|
+
createLocalPackageScaffoldFiles,
|
|
16
|
+
path,
|
|
17
|
+
toFileDependencySpecifier,
|
|
18
|
+
fileExists,
|
|
19
|
+
mkdir,
|
|
20
|
+
writeFile,
|
|
21
|
+
applyPackageJsonField,
|
|
22
|
+
writeJsonFile,
|
|
23
|
+
runNpmInstall
|
|
24
|
+
} = ctx;
|
|
25
|
+
|
|
26
|
+
const targetType = String(positional[0] || "").trim();
|
|
27
|
+
const rawName = String(positional[1] || "").trim();
|
|
28
|
+
if (targetType !== "package" || !rawName) {
|
|
29
|
+
throw createCliError("create requires: create package <name>", { showUsage: true });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const appRoot = await resolveAppRootFromCwd(cwd);
|
|
33
|
+
const { packageJsonPath, packageJson } = await loadAppPackageJson(appRoot);
|
|
34
|
+
const { lockPath, lock } = await loadLockFile(appRoot);
|
|
35
|
+
const installedPackages = ensureObject(lock.installedPackages);
|
|
36
|
+
const dependencies = ensureObject(packageJson.dependencies);
|
|
37
|
+
const devDependencies = ensureObject(packageJson.devDependencies);
|
|
38
|
+
|
|
39
|
+
const { packageId, packageDirName } = resolveLocalPackageId({
|
|
40
|
+
rawName,
|
|
41
|
+
appPackageName: packageJson.name,
|
|
42
|
+
inlineOptions: options.inlineOptions
|
|
43
|
+
});
|
|
44
|
+
const localPackagesRoot = path.join(appRoot, "packages");
|
|
45
|
+
const packageRoot = path.join(localPackagesRoot, packageDirName);
|
|
46
|
+
const packageRelativePath = normalizeRelativePath(appRoot, packageRoot);
|
|
47
|
+
const descriptorRelativePath = `${normalizeRelativePosixPath(packageRelativePath)}/package.descriptor.mjs`;
|
|
48
|
+
const localDependencySpecifier = toFileDependencySpecifier(packageRelativePath);
|
|
49
|
+
const packageDescription = String(options.inlineOptions.description || "").trim() || `App-local package ${packageId}.`;
|
|
50
|
+
|
|
51
|
+
if (await fileExists(packageRoot)) {
|
|
52
|
+
throw createCliError(`Package directory already exists: ${normalizeRelativePath(appRoot, packageRoot)}`);
|
|
53
|
+
}
|
|
54
|
+
if (Object.prototype.hasOwnProperty.call(installedPackages, packageId)) {
|
|
55
|
+
throw createCliError(`Package is already present in lock file: ${packageId}`);
|
|
56
|
+
}
|
|
57
|
+
if (Object.prototype.hasOwnProperty.call(dependencies, packageId)) {
|
|
58
|
+
throw createCliError(`package.json dependencies already contains ${packageId}.`);
|
|
59
|
+
}
|
|
60
|
+
if (Object.prototype.hasOwnProperty.call(devDependencies, packageId)) {
|
|
61
|
+
throw createCliError(`package.json devDependencies already contains ${packageId}.`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const scaffoldFiles = createLocalPackageScaffoldFiles({
|
|
65
|
+
packageId,
|
|
66
|
+
packageDescription
|
|
67
|
+
});
|
|
68
|
+
const touchedFiles = new Set(["package.json", normalizeRelativePath(appRoot, lockPath)]);
|
|
69
|
+
for (const scaffoldFile of scaffoldFiles) {
|
|
70
|
+
touchedFiles.add(
|
|
71
|
+
`${normalizeRelativePosixPath(packageRelativePath)}/${normalizeRelativePosixPath(scaffoldFile.relativePath)}`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!options.dryRun) {
|
|
76
|
+
for (const scaffoldFile of scaffoldFiles) {
|
|
77
|
+
const absoluteFilePath = path.join(packageRoot, scaffoldFile.relativePath);
|
|
78
|
+
await mkdir(path.dirname(absoluteFilePath), { recursive: true });
|
|
79
|
+
await writeFile(absoluteFilePath, String(scaffoldFile.content || ""), "utf8");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const dependencyApplied = applyPackageJsonField(packageJson, "dependencies", packageId, localDependencySpecifier);
|
|
84
|
+
const managedRecord = {
|
|
85
|
+
packageId,
|
|
86
|
+
version: "0.1.0",
|
|
87
|
+
source: {
|
|
88
|
+
type: "local-package",
|
|
89
|
+
packagePath: normalizeRelativePosixPath(packageRelativePath),
|
|
90
|
+
descriptorPath: descriptorRelativePath
|
|
91
|
+
},
|
|
92
|
+
managed: {
|
|
93
|
+
packageJson: {
|
|
94
|
+
dependencies: {},
|
|
95
|
+
devDependencies: {},
|
|
96
|
+
scripts: {}
|
|
97
|
+
},
|
|
98
|
+
text: {},
|
|
99
|
+
vite: {},
|
|
100
|
+
files: [],
|
|
101
|
+
migrations: []
|
|
102
|
+
},
|
|
103
|
+
options: {},
|
|
104
|
+
installedAt: new Date().toISOString()
|
|
105
|
+
};
|
|
106
|
+
if (dependencyApplied.changed) {
|
|
107
|
+
managedRecord.managed.packageJson.dependencies[packageId] = dependencyApplied.managed;
|
|
108
|
+
}
|
|
109
|
+
lock.installedPackages[packageId] = managedRecord;
|
|
110
|
+
|
|
111
|
+
const touchedFileList = sortStrings([...touchedFiles]);
|
|
112
|
+
if (!options.dryRun) {
|
|
113
|
+
await writeJsonFile(packageJsonPath, packageJson);
|
|
114
|
+
await writeJsonFile(lockPath, lock);
|
|
115
|
+
if (options.runNpmInstall) {
|
|
116
|
+
await runNpmInstall(appRoot, io.stderr);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (options.json) {
|
|
121
|
+
io.stdout.write(
|
|
122
|
+
`${JSON.stringify(
|
|
123
|
+
{
|
|
124
|
+
targetType: "package",
|
|
125
|
+
packageId,
|
|
126
|
+
packageDirectory: normalizeRelativePosixPath(packageRelativePath),
|
|
127
|
+
descriptorPath: descriptorRelativePath,
|
|
128
|
+
dependency: localDependencySpecifier,
|
|
129
|
+
touchedFiles: touchedFileList,
|
|
130
|
+
lockPath: normalizeRelativePath(appRoot, lockPath),
|
|
131
|
+
dryRun: options.dryRun
|
|
132
|
+
},
|
|
133
|
+
null,
|
|
134
|
+
2
|
|
135
|
+
)}\n`
|
|
136
|
+
);
|
|
137
|
+
} else {
|
|
138
|
+
io.stdout.write(`Created local package ${packageId}.\n`);
|
|
139
|
+
io.stdout.write(`Directory: ${normalizeRelativePosixPath(packageRelativePath)}\n`);
|
|
140
|
+
io.stdout.write(`Dependency: ${packageId} -> ${localDependencySpecifier}\n`);
|
|
141
|
+
io.stdout.write(`Descriptor: ${descriptorRelativePath}\n`);
|
|
142
|
+
io.stdout.write(`Touched files (${touchedFileList.length}):\n`);
|
|
143
|
+
for (const touchedFile of touchedFileList) {
|
|
144
|
+
io.stdout.write(`- ${touchedFile}\n`);
|
|
145
|
+
}
|
|
146
|
+
io.stdout.write(`Lock file: ${normalizeRelativePath(appRoot, lockPath)}\n`);
|
|
147
|
+
if (options.dryRun) {
|
|
148
|
+
io.stdout.write("Dry run enabled: no files were written.\n");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export { runPackageCreateCommand };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
async function runPackageGenerateCommand(
|
|
2
|
+
ctx = {},
|
|
3
|
+
{ positional, options, cwd, io },
|
|
4
|
+
{ runCommandAdd }
|
|
5
|
+
) {
|
|
6
|
+
const {
|
|
7
|
+
createCliError,
|
|
8
|
+
resolveAppRootFromCwd,
|
|
9
|
+
loadPackageRegistry,
|
|
10
|
+
loadAppLocalPackageRegistry,
|
|
11
|
+
mergePackageRegistries,
|
|
12
|
+
resolvePackageIdFromRegistryOrNodeModules,
|
|
13
|
+
hydratePackageRegistryFromInstalledNodeModules,
|
|
14
|
+
resolvePackageKind,
|
|
15
|
+
resolveGeneratorPrimarySubcommand,
|
|
16
|
+
hasGeneratorSubcommandDefinition,
|
|
17
|
+
runGeneratorSubcommand
|
|
18
|
+
} = ctx;
|
|
19
|
+
|
|
20
|
+
const firstToken = String(positional[0] || "").trim();
|
|
21
|
+
const secondToken = String(positional[1] || "").trim();
|
|
22
|
+
const thirdToken = String(positional[2] || "").trim();
|
|
23
|
+
if (firstToken === "bundle") {
|
|
24
|
+
throw createCliError("generate supports packages only (generate <packageId>).", {
|
|
25
|
+
showUsage: true
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const targetId = firstToken === "package" ? secondToken : firstToken;
|
|
29
|
+
const subcommandName = firstToken === "package" ? thirdToken : secondToken;
|
|
30
|
+
const subcommandArgs = firstToken === "package" ? positional.slice(3) : positional.slice(2);
|
|
31
|
+
if (!targetId) {
|
|
32
|
+
throw createCliError("generate requires a package id (generate <packageId>).", {
|
|
33
|
+
showUsage: true
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (subcommandName) {
|
|
38
|
+
const appRoot = await resolveAppRootFromCwd(cwd);
|
|
39
|
+
const packageRegistry = await loadPackageRegistry();
|
|
40
|
+
const appLocalRegistry = await loadAppLocalPackageRegistry(appRoot);
|
|
41
|
+
const combinedPackageRegistry = mergePackageRegistries(packageRegistry, appLocalRegistry);
|
|
42
|
+
|
|
43
|
+
const resolvedPackageId = await resolvePackageIdFromRegistryOrNodeModules({
|
|
44
|
+
appRoot,
|
|
45
|
+
packageRegistry: combinedPackageRegistry,
|
|
46
|
+
packageIdInput: targetId
|
|
47
|
+
});
|
|
48
|
+
if (!resolvedPackageId) {
|
|
49
|
+
throw createCliError(
|
|
50
|
+
`Unknown package: ${targetId}. Install it first (npm install ${targetId}) if you want to run generator subcommands from node_modules.`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
await hydratePackageRegistryFromInstalledNodeModules({
|
|
55
|
+
appRoot,
|
|
56
|
+
packageRegistry: combinedPackageRegistry,
|
|
57
|
+
seedPackageIds: [resolvedPackageId]
|
|
58
|
+
});
|
|
59
|
+
const packageEntry = combinedPackageRegistry.get(resolvedPackageId);
|
|
60
|
+
if (!packageEntry) {
|
|
61
|
+
throw createCliError(`Unknown package: ${targetId}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (resolvePackageKind(packageEntry) !== "generator") {
|
|
65
|
+
throw createCliError(
|
|
66
|
+
`Package ${resolvedPackageId} is a runtime package. Use: jskit add package ${resolvedPackageId}`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const normalizedSubcommandName = String(subcommandName || "").trim().toLowerCase();
|
|
71
|
+
const primarySubcommand = resolveGeneratorPrimarySubcommand(packageEntry);
|
|
72
|
+
if (
|
|
73
|
+
normalizedSubcommandName &&
|
|
74
|
+
normalizedSubcommandName === primarySubcommand &&
|
|
75
|
+
!hasGeneratorSubcommandDefinition(packageEntry, normalizedSubcommandName)
|
|
76
|
+
) {
|
|
77
|
+
if (subcommandArgs.length > 0) {
|
|
78
|
+
throw createCliError(
|
|
79
|
+
`Generator command "${primarySubcommand}" for ${resolvedPackageId} does not accept positional arguments.`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
return runCommandAdd({
|
|
83
|
+
positional: ["package", resolvedPackageId],
|
|
84
|
+
options: {
|
|
85
|
+
...options,
|
|
86
|
+
commandMode: "generate"
|
|
87
|
+
},
|
|
88
|
+
cwd,
|
|
89
|
+
io
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return runGeneratorSubcommand({
|
|
94
|
+
packageEntry,
|
|
95
|
+
subcommandName,
|
|
96
|
+
subcommandArgs,
|
|
97
|
+
inlineOptions: options.inlineOptions,
|
|
98
|
+
appRoot,
|
|
99
|
+
io,
|
|
100
|
+
dryRun: options.dryRun,
|
|
101
|
+
json: options.json
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return runCommandAdd({
|
|
106
|
+
positional: ["package", targetId],
|
|
107
|
+
options: {
|
|
108
|
+
...options,
|
|
109
|
+
commandMode: "generate"
|
|
110
|
+
},
|
|
111
|
+
cwd,
|
|
112
|
+
io
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export { runPackageGenerateCommand };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ensureArray,
|
|
3
|
+
ensureObject,
|
|
4
|
+
sortStrings
|
|
5
|
+
} from "../../shared/collectionUtils.js";
|
|
6
|
+
|
|
7
|
+
async function runPackageMigrationsCommand(ctx = {}, { positional, options, cwd, io }) {
|
|
8
|
+
const {
|
|
9
|
+
createCliError,
|
|
10
|
+
normalizeRelativePath,
|
|
11
|
+
resolveAppRootFromCwd,
|
|
12
|
+
loadPackageRegistry,
|
|
13
|
+
loadAppLocalPackageRegistry,
|
|
14
|
+
mergePackageRegistries,
|
|
15
|
+
loadLockFile,
|
|
16
|
+
hydratePackageRegistryFromInstalledNodeModules,
|
|
17
|
+
resolveInstalledPackageIdInput,
|
|
18
|
+
validateInlineOptionsForPackage,
|
|
19
|
+
resolvePackageOptions,
|
|
20
|
+
applyPackageMigrationsOnly,
|
|
21
|
+
writeJsonFile
|
|
22
|
+
} = ctx;
|
|
23
|
+
|
|
24
|
+
const scope = String(positional[0] || "").trim().toLowerCase();
|
|
25
|
+
const targetId = String(positional[1] || "").trim();
|
|
26
|
+
if (!scope || (scope !== "all" && scope !== "changed" && scope !== "package")) {
|
|
27
|
+
throw createCliError("migrations requires: migrations <all|changed|package <packageId>>", {
|
|
28
|
+
showUsage: true
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (scope === "package" && !targetId) {
|
|
32
|
+
throw createCliError("migrations package requires a package id.", {
|
|
33
|
+
showUsage: true
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (scope !== "package" && Object.keys(ensureObject(options.inlineOptions)).length > 0) {
|
|
37
|
+
throw createCliError("Inline options are only supported with: migrations package <packageId>.");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const appRoot = await resolveAppRootFromCwd(cwd);
|
|
41
|
+
const packageRegistry = await loadPackageRegistry();
|
|
42
|
+
const appLocalRegistry = await loadAppLocalPackageRegistry(appRoot);
|
|
43
|
+
const combinedPackageRegistry = mergePackageRegistries(packageRegistry, appLocalRegistry);
|
|
44
|
+
const { lockPath, lock } = await loadLockFile(appRoot);
|
|
45
|
+
const installedPackages = ensureObject(lock.installedPackages);
|
|
46
|
+
const installedPackageIds = sortStrings(Object.keys(installedPackages));
|
|
47
|
+
await hydratePackageRegistryFromInstalledNodeModules({
|
|
48
|
+
appRoot,
|
|
49
|
+
packageRegistry: combinedPackageRegistry,
|
|
50
|
+
seedPackageIds: installedPackageIds
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
let requestedPackageIds = [];
|
|
54
|
+
if (scope === "all") {
|
|
55
|
+
requestedPackageIds = installedPackageIds;
|
|
56
|
+
} else if (scope === "package") {
|
|
57
|
+
const resolvedTargetId = resolveInstalledPackageIdInput(targetId, installedPackages);
|
|
58
|
+
if (!resolvedTargetId) {
|
|
59
|
+
throw createCliError(`Package is not installed: ${targetId}`);
|
|
60
|
+
}
|
|
61
|
+
requestedPackageIds = [resolvedTargetId];
|
|
62
|
+
} else {
|
|
63
|
+
requestedPackageIds = installedPackageIds.filter((packageId) => {
|
|
64
|
+
const packageEntry = combinedPackageRegistry.get(packageId);
|
|
65
|
+
if (!packageEntry) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
const lockEntry = ensureObject(installedPackages[packageId]);
|
|
69
|
+
const migrationSyncVersion = String(lockEntry.migrationSyncVersion || "").trim();
|
|
70
|
+
return migrationSyncVersion !== String(packageEntry.version || "").trim();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const touchedFiles = new Set();
|
|
75
|
+
const migratedRecords = [];
|
|
76
|
+
const migrationWarnings = [];
|
|
77
|
+
for (const packageId of requestedPackageIds) {
|
|
78
|
+
const packageEntry = combinedPackageRegistry.get(packageId);
|
|
79
|
+
if (!packageEntry) {
|
|
80
|
+
throw createCliError(
|
|
81
|
+
`Installed package descriptor not found: ${packageId}. Ensure it is available in catalog, app packages/, or node_modules.`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
validateInlineOptionsForPackage(packageEntry, options.inlineOptions);
|
|
85
|
+
const installedRecord = ensureObject(installedPackages[packageId]);
|
|
86
|
+
const mergedInlineOptions = scope === "package" ? ensureObject(options.inlineOptions) : {};
|
|
87
|
+
const resolvedOptions = await resolvePackageOptions(
|
|
88
|
+
packageEntry,
|
|
89
|
+
{
|
|
90
|
+
...ensureObject(installedRecord.options),
|
|
91
|
+
...mergedInlineOptions
|
|
92
|
+
},
|
|
93
|
+
io,
|
|
94
|
+
{ appRoot }
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const managedRecord = await applyPackageMigrationsOnly({
|
|
98
|
+
packageEntry,
|
|
99
|
+
packageOptions: resolvedOptions,
|
|
100
|
+
appRoot,
|
|
101
|
+
lock,
|
|
102
|
+
touchedFiles
|
|
103
|
+
});
|
|
104
|
+
migratedRecords.push(managedRecord);
|
|
105
|
+
for (const warning of ensureArray(ensureObject(managedRecord).warnings)) {
|
|
106
|
+
const normalizedWarning = String(warning || "").trim();
|
|
107
|
+
if (!normalizedWarning) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
migrationWarnings.push(normalizedWarning);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const touchedFileList = sortStrings([...touchedFiles]);
|
|
115
|
+
if (!options.dryRun) {
|
|
116
|
+
await writeJsonFile(lockPath, lock);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (options.json) {
|
|
120
|
+
io.stdout.write(`${JSON.stringify({
|
|
121
|
+
targetType: "migrations",
|
|
122
|
+
scope,
|
|
123
|
+
requestedPackages: requestedPackageIds,
|
|
124
|
+
touchedFiles: touchedFileList,
|
|
125
|
+
lockPath: normalizeRelativePath(appRoot, lockPath),
|
|
126
|
+
dryRun: options.dryRun,
|
|
127
|
+
migrated: migratedRecords,
|
|
128
|
+
warnings: migrationWarnings
|
|
129
|
+
}, null, 2)}\n`);
|
|
130
|
+
} else {
|
|
131
|
+
io.stdout.write(`Generated migrations (${scope}).\n`);
|
|
132
|
+
io.stdout.write(`Resolved packages (${requestedPackageIds.length}):\n`);
|
|
133
|
+
for (const packageId of requestedPackageIds) {
|
|
134
|
+
io.stdout.write(`- ${packageId}\n`);
|
|
135
|
+
}
|
|
136
|
+
io.stdout.write(`Touched files (${touchedFileList.length}):\n`);
|
|
137
|
+
for (const touchedFile of touchedFileList) {
|
|
138
|
+
io.stdout.write(`- ${touchedFile}\n`);
|
|
139
|
+
}
|
|
140
|
+
io.stdout.write(`Lock file: ${normalizeRelativePath(appRoot, lockPath)}\n`);
|
|
141
|
+
if (options.verbose && migrationWarnings.length > 0) {
|
|
142
|
+
io.stdout.write(`Warnings (${migrationWarnings.length}):\n`);
|
|
143
|
+
for (const warning of migrationWarnings) {
|
|
144
|
+
io.stdout.write(`- ${warning}\n`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (options.dryRun) {
|
|
148
|
+
io.stdout.write("Dry run enabled: no files were written.\n");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export { runPackageMigrationsCommand };
|