@solid-stack/mason 1.0.15 → 1.0.17
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.cjs +516 -302
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +515 -301
- package/dist/bin.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/commands/import.d.ts +11 -1
- package/dist/commands/import.d.ts.map +1 -1
- package/dist/index.cjs +515 -301
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +515 -301
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs4 from 'fs';
|
|
2
2
|
import path2 from 'path';
|
|
3
|
-
import * as
|
|
3
|
+
import * as p14 from '@clack/prompts';
|
|
4
4
|
import pc14 from 'picocolors';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { execSync } from 'child_process';
|
|
@@ -112,17 +112,17 @@ function writeFileSafely(absolutePath, content, options = {}) {
|
|
|
112
112
|
function printGeneratedFiles(files, isDryRun = false) {
|
|
113
113
|
console.log("");
|
|
114
114
|
if (isDryRun) {
|
|
115
|
-
|
|
115
|
+
p14.log.warn(pc14.yellow(pc14.bold("[DRY RUN PREVIEW] No files were written to disk.")));
|
|
116
116
|
}
|
|
117
117
|
for (const f of files) {
|
|
118
118
|
if (f.action === "created") {
|
|
119
|
-
|
|
119
|
+
p14.log.success(`${pc14.bold(f.relativePath)} ${pc14.dim("(created)")}`);
|
|
120
120
|
} else if (f.action === "overwritten") {
|
|
121
|
-
|
|
121
|
+
p14.log.warn(`${pc14.bold(f.relativePath)} ${pc14.dim("(overwritten)")}`);
|
|
122
122
|
} else if (f.action === "skipped") {
|
|
123
|
-
|
|
123
|
+
p14.log.info(`${pc14.dim(f.relativePath)} ${pc14.dim("(skipped - already exists, pass -o to overwrite)")}`);
|
|
124
124
|
} else {
|
|
125
|
-
|
|
125
|
+
p14.log.info(`${pc14.bold(f.relativePath)} ${pc14.dim("(preview)")}`);
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
}
|
|
@@ -318,23 +318,23 @@ var PromptCancelledError = class extends Error {
|
|
|
318
318
|
}
|
|
319
319
|
};
|
|
320
320
|
function handleCancel(result) {
|
|
321
|
-
if (
|
|
322
|
-
|
|
321
|
+
if (p14.isCancel(result)) {
|
|
322
|
+
p14.cancel("Operation cancelled.");
|
|
323
323
|
throw new PromptCancelledError();
|
|
324
324
|
}
|
|
325
325
|
return result;
|
|
326
326
|
}
|
|
327
327
|
function printBanner() {
|
|
328
328
|
console.log("");
|
|
329
|
-
|
|
329
|
+
p14.intro(pc14.bgCyan(pc14.black(" @solid-stack/mason ")));
|
|
330
330
|
}
|
|
331
331
|
async function promptProperties(options = {}) {
|
|
332
332
|
const properties = options.defaults ? [...options.defaults] : [];
|
|
333
333
|
if (properties.length > 0) {
|
|
334
334
|
const summary = properties.map((p16) => `${p16.name}: ${p16.type}${p16.optional ? "?" : ""}`).join(", ");
|
|
335
|
-
|
|
335
|
+
p14.log.info(pc14.dim(`Pre-configured fields: ${summary}`));
|
|
336
336
|
const addMore = handleCancel(
|
|
337
|
-
await
|
|
337
|
+
await p14.confirm({
|
|
338
338
|
message: "Would you like to add custom fields/properties?",
|
|
339
339
|
initialValue: false
|
|
340
340
|
})
|
|
@@ -343,10 +343,10 @@ async function promptProperties(options = {}) {
|
|
|
343
343
|
return properties;
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
|
-
|
|
346
|
+
p14.log.info(pc14.cyan("Enter properties (leave name blank when finished):"));
|
|
347
347
|
while (true) {
|
|
348
348
|
const name = handleCancel(
|
|
349
|
-
await
|
|
349
|
+
await p14.text({
|
|
350
350
|
message: "Property name (or press Enter to finish):",
|
|
351
351
|
placeholder: "e.g. title, email, status"
|
|
352
352
|
})
|
|
@@ -355,7 +355,7 @@ async function promptProperties(options = {}) {
|
|
|
355
355
|
break;
|
|
356
356
|
}
|
|
357
357
|
const type = handleCancel(
|
|
358
|
-
await
|
|
358
|
+
await p14.select({
|
|
359
359
|
message: `Type for property "${name.trim()}":`,
|
|
360
360
|
options: [
|
|
361
361
|
{ value: "string", label: "string" },
|
|
@@ -372,7 +372,7 @@ async function promptProperties(options = {}) {
|
|
|
372
372
|
let finalType = type;
|
|
373
373
|
if (type === "custom") {
|
|
374
374
|
finalType = handleCancel(
|
|
375
|
-
await
|
|
375
|
+
await p14.text({
|
|
376
376
|
message: `Enter custom type for "${name.trim()}":`,
|
|
377
377
|
placeholder: "e.g. UserRole, string | null",
|
|
378
378
|
validate: (val) => val && val.trim() ? void 0 : "Type cannot be empty"
|
|
@@ -380,7 +380,7 @@ async function promptProperties(options = {}) {
|
|
|
380
380
|
);
|
|
381
381
|
}
|
|
382
382
|
const optional = handleCancel(
|
|
383
|
-
await
|
|
383
|
+
await p14.confirm({
|
|
384
384
|
message: `Is "${name.trim()}" optional?`,
|
|
385
385
|
initialValue: false
|
|
386
386
|
})
|
|
@@ -390,7 +390,7 @@ async function promptProperties(options = {}) {
|
|
|
390
390
|
type: finalType.trim(),
|
|
391
391
|
optional
|
|
392
392
|
});
|
|
393
|
-
|
|
393
|
+
p14.log.success(
|
|
394
394
|
pc14.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
|
|
395
395
|
);
|
|
396
396
|
}
|
|
@@ -613,7 +613,7 @@ function installNpmPackages(projectRoot, packages, pm) {
|
|
|
613
613
|
installCmd = `npm install ${packages.join(" ")}`;
|
|
614
614
|
break;
|
|
615
615
|
}
|
|
616
|
-
|
|
616
|
+
p14.log.info(`Installing missing dependencies with ${manager}: ${pc14.cyan(packages.join(", "))}`);
|
|
617
617
|
execSync(installCmd, {
|
|
618
618
|
cwd: projectRoot,
|
|
619
619
|
stdio: "inherit"
|
|
@@ -2086,7 +2086,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2086
2086
|
}
|
|
2087
2087
|
if (!name) {
|
|
2088
2088
|
name = handleCancel(
|
|
2089
|
-
await
|
|
2089
|
+
await p14.text({
|
|
2090
2090
|
message: "What is the feature name?",
|
|
2091
2091
|
placeholder: "e.g. users, auth, orders, billing",
|
|
2092
2092
|
validate: (val) => {
|
|
@@ -2102,7 +2102,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2102
2102
|
let targetDirRel = defaultDir;
|
|
2103
2103
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
2104
2104
|
targetDirRel = handleCancel(
|
|
2105
|
-
await
|
|
2105
|
+
await p14.text({
|
|
2106
2106
|
message: "Target directory for the feature:",
|
|
2107
2107
|
initialValue: defaultDir,
|
|
2108
2108
|
defaultValue: defaultDir
|
|
@@ -2124,14 +2124,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2124
2124
|
const customUseCases = [];
|
|
2125
2125
|
if (!flags.yes) {
|
|
2126
2126
|
includeExpress = handleCancel(
|
|
2127
|
-
await
|
|
2127
|
+
await p14.confirm({
|
|
2128
2128
|
message: "Include Express Presentation layer (pxExpress routes & handlers)?",
|
|
2129
2129
|
initialValue: true
|
|
2130
2130
|
})
|
|
2131
2131
|
);
|
|
2132
2132
|
if (includeExpress) {
|
|
2133
2133
|
routePath = handleCancel(
|
|
2134
|
-
await
|
|
2134
|
+
await p14.text({
|
|
2135
2135
|
message: "Express route prefix:",
|
|
2136
2136
|
initialValue: `/${normalizedName}`,
|
|
2137
2137
|
defaultValue: `/${normalizedName}`
|
|
@@ -2139,14 +2139,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2139
2139
|
).trim();
|
|
2140
2140
|
}
|
|
2141
2141
|
includeDomain = handleCancel(
|
|
2142
|
-
await
|
|
2142
|
+
await p14.confirm({
|
|
2143
2143
|
message: "Include Domain Entity/Entities?",
|
|
2144
2144
|
initialValue: true
|
|
2145
2145
|
})
|
|
2146
2146
|
);
|
|
2147
2147
|
if (includeDomain) {
|
|
2148
2148
|
const domainInput = handleCancel(
|
|
2149
|
-
await
|
|
2149
|
+
await p14.text({
|
|
2150
2150
|
message: "Domain entity name(s) (comma-separated):",
|
|
2151
2151
|
initialValue: defaultEntity,
|
|
2152
2152
|
defaultValue: defaultEntity,
|
|
@@ -2165,21 +2165,21 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2165
2165
|
entityNames = [defaultEntity];
|
|
2166
2166
|
}
|
|
2167
2167
|
includeInterface = handleCancel(
|
|
2168
|
-
await
|
|
2168
|
+
await p14.confirm({
|
|
2169
2169
|
message: `Include Domain Repository interface(s) for ${entityNames.join(", ")}?`,
|
|
2170
2170
|
initialValue: true
|
|
2171
2171
|
})
|
|
2172
2172
|
);
|
|
2173
2173
|
if (includeInterface) {
|
|
2174
2174
|
includeInfra = handleCancel(
|
|
2175
|
-
await
|
|
2175
|
+
await p14.confirm({
|
|
2176
2176
|
message: `Include Infrastructure Stub implementation(s) for ${entityNames.join(", ")}?`,
|
|
2177
2177
|
initialValue: true
|
|
2178
2178
|
})
|
|
2179
2179
|
);
|
|
2180
2180
|
}
|
|
2181
2181
|
includeUseCase = handleCancel(
|
|
2182
|
-
await
|
|
2182
|
+
await p14.confirm({
|
|
2183
2183
|
message: "Include Domain Use Cases?",
|
|
2184
2184
|
initialValue: true
|
|
2185
2185
|
})
|
|
@@ -2193,7 +2193,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2193
2193
|
{ value: "delete", label: "Delete (Delete<Entity>)", hint: "DELETE /:id" }
|
|
2194
2194
|
];
|
|
2195
2195
|
crudOperations = handleCancel(
|
|
2196
|
-
await
|
|
2196
|
+
await p14.multiselect({
|
|
2197
2197
|
message: "Select standard CRUD use cases to generate:",
|
|
2198
2198
|
options: crudChoices,
|
|
2199
2199
|
initialValues: ["create", "read", "list", "update", "delete"],
|
|
@@ -2201,13 +2201,13 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2201
2201
|
})
|
|
2202
2202
|
);
|
|
2203
2203
|
includeTests = handleCancel(
|
|
2204
|
-
await
|
|
2204
|
+
await p14.confirm({
|
|
2205
2205
|
message: "Generate Vitest unit tests for use cases?",
|
|
2206
2206
|
initialValue: true
|
|
2207
2207
|
})
|
|
2208
2208
|
);
|
|
2209
2209
|
const wantCustom = handleCancel(
|
|
2210
|
-
await
|
|
2210
|
+
await p14.confirm({
|
|
2211
2211
|
message: "Do you want to add any custom use cases to this feature?",
|
|
2212
2212
|
initialValue: false
|
|
2213
2213
|
})
|
|
@@ -2217,7 +2217,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2217
2217
|
const availableIfaces = entityNames.map((e) => `I${e}Repository`);
|
|
2218
2218
|
while (addMore) {
|
|
2219
2219
|
const customName = handleCancel(
|
|
2220
|
-
await
|
|
2220
|
+
await p14.text({
|
|
2221
2221
|
message: "Custom Use Case name:",
|
|
2222
2222
|
placeholder: "e.g. ResetPassword, VerifyEmail, AssignRole",
|
|
2223
2223
|
validate: (v) => v && v.trim() ? void 0 : "Use Case name cannot be empty"
|
|
@@ -2227,7 +2227,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2227
2227
|
let selectedDeps = [];
|
|
2228
2228
|
if (availableIfaces.length > 0) {
|
|
2229
2229
|
selectedDeps = handleCancel(
|
|
2230
|
-
await
|
|
2230
|
+
await p14.multiselect({
|
|
2231
2231
|
message: `Select domain interfaces to inject into ${customUcName}:`,
|
|
2232
2232
|
options: availableIfaces.map((i) => ({ value: i, label: i })),
|
|
2233
2233
|
required: false
|
|
@@ -2236,7 +2236,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2236
2236
|
}
|
|
2237
2237
|
let customInputProps = [];
|
|
2238
2238
|
const hasInput = handleCancel(
|
|
2239
|
-
await
|
|
2239
|
+
await p14.confirm({
|
|
2240
2240
|
message: `Does ${customUcName} accept input arguments?`,
|
|
2241
2241
|
initialValue: true
|
|
2242
2242
|
})
|
|
@@ -2247,7 +2247,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2247
2247
|
}
|
|
2248
2248
|
let customOutputProps = [];
|
|
2249
2249
|
const hasCustomOutput = handleCancel(
|
|
2250
|
-
await
|
|
2250
|
+
await p14.confirm({
|
|
2251
2251
|
message: `Does ${customUcName} return custom output data?`,
|
|
2252
2252
|
initialValue: false
|
|
2253
2253
|
})
|
|
@@ -2257,7 +2257,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2257
2257
|
});
|
|
2258
2258
|
}
|
|
2259
2259
|
const customIncludeTest = handleCancel(
|
|
2260
|
-
await
|
|
2260
|
+
await p14.confirm({
|
|
2261
2261
|
message: `Generate Vitest unit test for ${customUcName}?`,
|
|
2262
2262
|
initialValue: true
|
|
2263
2263
|
})
|
|
@@ -2267,14 +2267,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2267
2267
|
let customHttpPath = `/${toKebabCase(customUcName)}`;
|
|
2268
2268
|
if (includeExpress) {
|
|
2269
2269
|
customCreateHttp = handleCancel(
|
|
2270
|
-
await
|
|
2270
|
+
await p14.confirm({
|
|
2271
2271
|
message: `Generate a matching ExpressRoute HTTP handler for ${customUcName}?`,
|
|
2272
2272
|
initialValue: true
|
|
2273
2273
|
})
|
|
2274
2274
|
);
|
|
2275
2275
|
if (customCreateHttp) {
|
|
2276
2276
|
customHttpMethod = handleCancel(
|
|
2277
|
-
await
|
|
2277
|
+
await p14.select({
|
|
2278
2278
|
message: "HTTP method for route:",
|
|
2279
2279
|
options: [
|
|
2280
2280
|
{ value: "post", label: "POST" },
|
|
@@ -2287,7 +2287,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2287
2287
|
})
|
|
2288
2288
|
);
|
|
2289
2289
|
customHttpPath = handleCancel(
|
|
2290
|
-
await
|
|
2290
|
+
await p14.text({
|
|
2291
2291
|
message: "Route subpath (relative to feature prefix):",
|
|
2292
2292
|
initialValue: `/${toKebabCase(customUcName)}`,
|
|
2293
2293
|
defaultValue: `/${toKebabCase(customUcName)}`
|
|
@@ -2308,7 +2308,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2308
2308
|
validationFields: customInputProps
|
|
2309
2309
|
});
|
|
2310
2310
|
addMore = handleCancel(
|
|
2311
|
-
await
|
|
2311
|
+
await p14.confirm({
|
|
2312
2312
|
message: "Add another custom use case?",
|
|
2313
2313
|
initialValue: false
|
|
2314
2314
|
})
|
|
@@ -2317,7 +2317,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2317
2317
|
}
|
|
2318
2318
|
}
|
|
2319
2319
|
includeDiProvider = handleCancel(
|
|
2320
|
-
await
|
|
2320
|
+
await p14.confirm({
|
|
2321
2321
|
message: "Generate / update feature DI provider (diProvider.ts)?",
|
|
2322
2322
|
initialValue: true
|
|
2323
2323
|
})
|
|
@@ -2329,7 +2329,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2329
2329
|
entityNames = [defaultEntity];
|
|
2330
2330
|
}
|
|
2331
2331
|
}
|
|
2332
|
-
const s =
|
|
2332
|
+
const s = p14.spinner();
|
|
2333
2333
|
s.start(`Generating feature '${pc14.cyan(normalizedName)}'...`);
|
|
2334
2334
|
const options = {
|
|
2335
2335
|
name: normalizedName,
|
|
@@ -2376,7 +2376,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2376
2376
|
}
|
|
2377
2377
|
s.stop("Feature scaffolded successfully!");
|
|
2378
2378
|
printGeneratedFiles(files, options.dryRun);
|
|
2379
|
-
|
|
2379
|
+
p14.outro(
|
|
2380
2380
|
pc14.green(
|
|
2381
2381
|
`\u2713 Feature '${normalizedName}' initialized in ${path2.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2382
2382
|
)
|
|
@@ -2491,7 +2491,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2491
2491
|
}
|
|
2492
2492
|
if (!name) {
|
|
2493
2493
|
name = handleCancel(
|
|
2494
|
-
await
|
|
2494
|
+
await p14.text({
|
|
2495
2495
|
message: "What is the shared module name?",
|
|
2496
2496
|
placeholder: "e.g. hasher, time, uuid, cache, logger",
|
|
2497
2497
|
validate: (val) => {
|
|
@@ -2507,7 +2507,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2507
2507
|
let targetDirRel = defaultDir;
|
|
2508
2508
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
2509
2509
|
targetDirRel = handleCancel(
|
|
2510
|
-
await
|
|
2510
|
+
await p14.text({
|
|
2511
2511
|
message: "Target directory for the shared module:",
|
|
2512
2512
|
initialValue: defaultDir,
|
|
2513
2513
|
defaultValue: defaultDir
|
|
@@ -2519,20 +2519,20 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2519
2519
|
let includeStub = true;
|
|
2520
2520
|
if (!flags.yes) {
|
|
2521
2521
|
description = handleCancel(
|
|
2522
|
-
await
|
|
2522
|
+
await p14.text({
|
|
2523
2523
|
message: "Module description (optional):",
|
|
2524
2524
|
placeholder: "e.g. In-memory caching and Redis port abstraction",
|
|
2525
2525
|
initialValue: ""
|
|
2526
2526
|
})
|
|
2527
2527
|
).trim();
|
|
2528
2528
|
includeStub = handleCancel(
|
|
2529
|
-
await
|
|
2529
|
+
await p14.confirm({
|
|
2530
2530
|
message: "Include stub implementation in infrastructure/?",
|
|
2531
2531
|
initialValue: true
|
|
2532
2532
|
})
|
|
2533
2533
|
);
|
|
2534
2534
|
}
|
|
2535
|
-
const s =
|
|
2535
|
+
const s = p14.spinner();
|
|
2536
2536
|
s.start(`Generating shared module '${pc14.cyan(normalizedName)}'...`);
|
|
2537
2537
|
const files = generateShared({
|
|
2538
2538
|
name: normalizedName,
|
|
@@ -2546,7 +2546,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2546
2546
|
});
|
|
2547
2547
|
s.stop("Shared module generated successfully!");
|
|
2548
2548
|
printGeneratedFiles(files, Boolean(flags.dryRun));
|
|
2549
|
-
|
|
2549
|
+
p14.outro(
|
|
2550
2550
|
pc14.green(
|
|
2551
2551
|
`\u2713 Shared module '${normalizedName}' initialized in ${path2.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2552
2552
|
)
|
|
@@ -2561,14 +2561,14 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2561
2561
|
const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
|
|
2562
2562
|
featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
|
|
2563
2563
|
const selected = handleCancel(
|
|
2564
|
-
await
|
|
2564
|
+
await p14.select({
|
|
2565
2565
|
message: "Select target feature:",
|
|
2566
2566
|
options: featureOptions
|
|
2567
2567
|
})
|
|
2568
2568
|
);
|
|
2569
2569
|
if (selected === "__custom__") {
|
|
2570
2570
|
feature = handleCancel(
|
|
2571
|
-
await
|
|
2571
|
+
await p14.text({
|
|
2572
2572
|
message: "Enter feature name:",
|
|
2573
2573
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
2574
2574
|
})
|
|
@@ -2578,7 +2578,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2578
2578
|
}
|
|
2579
2579
|
} else {
|
|
2580
2580
|
feature = handleCancel(
|
|
2581
|
-
await
|
|
2581
|
+
await p14.text({
|
|
2582
2582
|
message: "Enter target feature name:",
|
|
2583
2583
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
2584
2584
|
})
|
|
@@ -2589,7 +2589,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2589
2589
|
let name = useCaseNameArg?.trim();
|
|
2590
2590
|
if (!name) {
|
|
2591
2591
|
name = handleCancel(
|
|
2592
|
-
await
|
|
2592
|
+
await p14.text({
|
|
2593
2593
|
message: "Use Case name:",
|
|
2594
2594
|
placeholder: "e.g. CreateUser, GetUser, CancelOrder",
|
|
2595
2595
|
validate: (v) => v && v.trim() ? void 0 : "Use Case name cannot be empty"
|
|
@@ -2605,7 +2605,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2605
2605
|
label: i.name
|
|
2606
2606
|
}));
|
|
2607
2607
|
selectedDeps = handleCancel(
|
|
2608
|
-
await
|
|
2608
|
+
await p14.multiselect({
|
|
2609
2609
|
message: "Select domain interfaces to inject into use case:",
|
|
2610
2610
|
options: multiOptions,
|
|
2611
2611
|
required: false
|
|
@@ -2620,7 +2620,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2620
2620
|
let httpPath = "/";
|
|
2621
2621
|
if (!flags.yes) {
|
|
2622
2622
|
const hasInput = handleCancel(
|
|
2623
|
-
await
|
|
2623
|
+
await p14.confirm({
|
|
2624
2624
|
message: "Does this use case accept input arguments?",
|
|
2625
2625
|
initialValue: true
|
|
2626
2626
|
})
|
|
@@ -2630,7 +2630,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2630
2630
|
});
|
|
2631
2631
|
}
|
|
2632
2632
|
const hasCustomOutput = handleCancel(
|
|
2633
|
-
await
|
|
2633
|
+
await p14.confirm({
|
|
2634
2634
|
message: "Does this use case return custom output data?",
|
|
2635
2635
|
initialValue: false
|
|
2636
2636
|
})
|
|
@@ -2640,20 +2640,20 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2640
2640
|
});
|
|
2641
2641
|
}
|
|
2642
2642
|
includeTest = handleCancel(
|
|
2643
|
-
await
|
|
2643
|
+
await p14.confirm({
|
|
2644
2644
|
message: "Generate Vitest unit test skeleton (.test.ts)?",
|
|
2645
2645
|
initialValue: true
|
|
2646
2646
|
})
|
|
2647
2647
|
);
|
|
2648
2648
|
createHttp = handleCancel(
|
|
2649
|
-
await
|
|
2649
|
+
await p14.confirm({
|
|
2650
2650
|
message: "Generate a matching ExpressRoute HTTP handler?",
|
|
2651
2651
|
initialValue: false
|
|
2652
2652
|
})
|
|
2653
2653
|
);
|
|
2654
2654
|
if (createHttp) {
|
|
2655
2655
|
httpMethod = handleCancel(
|
|
2656
|
-
await
|
|
2656
|
+
await p14.select({
|
|
2657
2657
|
message: "HTTP method for route:",
|
|
2658
2658
|
options: [
|
|
2659
2659
|
{ value: "post", label: "POST" },
|
|
@@ -2666,7 +2666,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2666
2666
|
})
|
|
2667
2667
|
);
|
|
2668
2668
|
httpPath = handleCancel(
|
|
2669
|
-
await
|
|
2669
|
+
await p14.text({
|
|
2670
2670
|
message: "Route subpath (relative to feature prefix):",
|
|
2671
2671
|
initialValue: "/",
|
|
2672
2672
|
defaultValue: "/"
|
|
@@ -2674,7 +2674,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2674
2674
|
).trim();
|
|
2675
2675
|
}
|
|
2676
2676
|
}
|
|
2677
|
-
const s =
|
|
2677
|
+
const s = p14.spinner();
|
|
2678
2678
|
s.start(`Generating use case '${pc14.cyan(ucName)}'...`);
|
|
2679
2679
|
const options = {
|
|
2680
2680
|
name: ucName,
|
|
@@ -2707,7 +2707,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2707
2707
|
}
|
|
2708
2708
|
s.stop("Use case generated successfully!");
|
|
2709
2709
|
printGeneratedFiles(files, options.dryRun);
|
|
2710
|
-
|
|
2710
|
+
p14.outro(pc14.green(`Use case '${ucName}' created in feature '${feature}'!`));
|
|
2711
2711
|
}
|
|
2712
2712
|
async function runDomainCommand(domainNameArg, flags = {}) {
|
|
2713
2713
|
const projectRoot = findProjectRoot();
|
|
@@ -2718,14 +2718,14 @@ async function runDomainCommand(domainNameArg, flags = {}) {
|
|
|
2718
2718
|
const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
|
|
2719
2719
|
featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
|
|
2720
2720
|
const selected = handleCancel(
|
|
2721
|
-
await
|
|
2721
|
+
await p14.select({
|
|
2722
2722
|
message: "Select target feature:",
|
|
2723
2723
|
options: featureOptions
|
|
2724
2724
|
})
|
|
2725
2725
|
);
|
|
2726
2726
|
if (selected === "__custom__") {
|
|
2727
2727
|
feature = handleCancel(
|
|
2728
|
-
await
|
|
2728
|
+
await p14.text({
|
|
2729
2729
|
message: "Enter feature name:",
|
|
2730
2730
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
2731
2731
|
})
|
|
@@ -2735,7 +2735,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
|
|
|
2735
2735
|
}
|
|
2736
2736
|
} else {
|
|
2737
2737
|
feature = handleCancel(
|
|
2738
|
-
await
|
|
2738
|
+
await p14.text({
|
|
2739
2739
|
message: "Enter target feature name:",
|
|
2740
2740
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
2741
2741
|
})
|
|
@@ -2746,7 +2746,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
|
|
|
2746
2746
|
let name = domainNameArg?.trim();
|
|
2747
2747
|
if (!name) {
|
|
2748
2748
|
name = handleCancel(
|
|
2749
|
-
await
|
|
2749
|
+
await p14.text({
|
|
2750
2750
|
message: "Domain model / entity name:",
|
|
2751
2751
|
placeholder: "e.g. User, Order, Product, Address",
|
|
2752
2752
|
validate: (v) => v && v.trim() ? void 0 : "Domain name cannot be empty"
|
|
@@ -2758,7 +2758,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
|
|
|
2758
2758
|
let properties = [];
|
|
2759
2759
|
if (!flags.yes) {
|
|
2760
2760
|
kind = handleCancel(
|
|
2761
|
-
await
|
|
2761
|
+
await p14.select({
|
|
2762
2762
|
message: "Select domain model type:",
|
|
2763
2763
|
options: [
|
|
2764
2764
|
{ value: "interface", label: "TypeScript Interface (pure data structure)" },
|
|
@@ -2777,7 +2777,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
|
|
|
2777
2777
|
defaults: kind === "valueObject" ? [{ name: "value", type: "string" }] : defaultProps
|
|
2778
2778
|
});
|
|
2779
2779
|
}
|
|
2780
|
-
const s =
|
|
2780
|
+
const s = p14.spinner();
|
|
2781
2781
|
s.start(`Generating domain model '${pc14.cyan(entityName)}'...`);
|
|
2782
2782
|
const options = {
|
|
2783
2783
|
name: entityName,
|
|
@@ -2791,7 +2791,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
|
|
|
2791
2791
|
const file = generateDomain(options);
|
|
2792
2792
|
s.stop("Domain model generated successfully!");
|
|
2793
2793
|
printGeneratedFiles([file], options.dryRun);
|
|
2794
|
-
|
|
2794
|
+
p14.outro(pc14.green(`Domain model '${entityName}' created in feature '${feature}'!`));
|
|
2795
2795
|
}
|
|
2796
2796
|
async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
2797
2797
|
const projectRoot = findProjectRoot();
|
|
@@ -2800,7 +2800,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2800
2800
|
let feature = flags.feature?.trim();
|
|
2801
2801
|
if (!flags.yes && !flags.global && !flags.feature) {
|
|
2802
2802
|
scope = handleCancel(
|
|
2803
|
-
await
|
|
2803
|
+
await p14.select({
|
|
2804
2804
|
message: "Where should this infrastructure implementation be placed?",
|
|
2805
2805
|
options: [
|
|
2806
2806
|
{ value: "feature", label: "Feature-level (features/<feature>/infrastructure)" },
|
|
@@ -2815,14 +2815,14 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2815
2815
|
const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
|
|
2816
2816
|
featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
|
|
2817
2817
|
const selected = handleCancel(
|
|
2818
|
-
await
|
|
2818
|
+
await p14.select({
|
|
2819
2819
|
message: "Select target feature:",
|
|
2820
2820
|
options: featureOptions
|
|
2821
2821
|
})
|
|
2822
2822
|
);
|
|
2823
2823
|
if (selected === "__custom__") {
|
|
2824
2824
|
feature = handleCancel(
|
|
2825
|
-
await
|
|
2825
|
+
await p14.text({
|
|
2826
2826
|
message: "Enter feature name:",
|
|
2827
2827
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
2828
2828
|
})
|
|
@@ -2832,7 +2832,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2832
2832
|
}
|
|
2833
2833
|
} else {
|
|
2834
2834
|
feature = handleCancel(
|
|
2835
|
-
await
|
|
2835
|
+
await p14.text({
|
|
2836
2836
|
message: "Enter target feature name:",
|
|
2837
2837
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
2838
2838
|
})
|
|
@@ -2845,7 +2845,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2845
2845
|
let name = classNameArg?.trim();
|
|
2846
2846
|
if (!name) {
|
|
2847
2847
|
name = handleCancel(
|
|
2848
|
-
await
|
|
2848
|
+
await p14.text({
|
|
2849
2849
|
message: "Infrastructure class name:",
|
|
2850
2850
|
placeholder: "e.g. StubUserRepository, PostgresUserRepository, S3FileStorage",
|
|
2851
2851
|
validate: (v) => v && v.trim() ? void 0 : "Class name cannot be empty"
|
|
@@ -2864,7 +2864,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2864
2864
|
(i) => className.toLowerCase().includes(removeInterfacePrefix(i.name).toLowerCase())
|
|
2865
2865
|
);
|
|
2866
2866
|
const ifaceChoice = handleCancel(
|
|
2867
|
-
await
|
|
2867
|
+
await p14.select({
|
|
2868
2868
|
message: "Does this class implement a domain interface or abstract class?",
|
|
2869
2869
|
options: [
|
|
2870
2870
|
...interfaces.map((i) => ({ value: i.name, label: i.name })),
|
|
@@ -2876,7 +2876,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2876
2876
|
);
|
|
2877
2877
|
if (ifaceChoice === "__custom__") {
|
|
2878
2878
|
implementsInterface = handleCancel(
|
|
2879
|
-
await
|
|
2879
|
+
await p14.text({
|
|
2880
2880
|
message: "Enter interface or abstract class name:",
|
|
2881
2881
|
placeholder: "e.g. IUserRepository, BaseStorageAdapter",
|
|
2882
2882
|
validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
|
|
@@ -2887,14 +2887,14 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2887
2887
|
}
|
|
2888
2888
|
} else {
|
|
2889
2889
|
const wantImpl = handleCancel(
|
|
2890
|
-
await
|
|
2890
|
+
await p14.confirm({
|
|
2891
2891
|
message: "Does this class implement a domain interface or abstract class?",
|
|
2892
2892
|
initialValue: false
|
|
2893
2893
|
})
|
|
2894
2894
|
);
|
|
2895
2895
|
if (wantImpl) {
|
|
2896
2896
|
implementsInterface = handleCancel(
|
|
2897
|
-
await
|
|
2897
|
+
await p14.text({
|
|
2898
2898
|
message: "Enter interface or abstract class name:",
|
|
2899
2899
|
placeholder: "e.g. IUserRepository, BaseStorageAdapter",
|
|
2900
2900
|
validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
|
|
@@ -2914,7 +2914,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2914
2914
|
const allInterfaces = listDomainInterfaces(projectRoot);
|
|
2915
2915
|
if (allInterfaces.length > 0) {
|
|
2916
2916
|
const ifaceChoice = handleCancel(
|
|
2917
|
-
await
|
|
2917
|
+
await p14.select({
|
|
2918
2918
|
message: "Does this class implement an interface or abstract class?",
|
|
2919
2919
|
options: [
|
|
2920
2920
|
...allInterfaces.map((i) => ({ value: i.name, label: `${i.name} (${i.feature})` })),
|
|
@@ -2926,7 +2926,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2926
2926
|
);
|
|
2927
2927
|
if (ifaceChoice === "__custom__") {
|
|
2928
2928
|
implementsInterface = handleCancel(
|
|
2929
|
-
await
|
|
2929
|
+
await p14.text({
|
|
2930
2930
|
message: "Enter interface or abstract class name:",
|
|
2931
2931
|
placeholder: "e.g. IAuthRepository",
|
|
2932
2932
|
validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
|
|
@@ -2939,7 +2939,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2939
2939
|
}
|
|
2940
2940
|
if (!flags.yes && !flags.kind) {
|
|
2941
2941
|
kind = handleCancel(
|
|
2942
|
-
await
|
|
2942
|
+
await p14.select({
|
|
2943
2943
|
message: "Select implementation kind:",
|
|
2944
2944
|
options: [
|
|
2945
2945
|
{ value: "stub", label: "In-Memory Stub (with in-memory store and test helper methods)" },
|
|
@@ -2950,7 +2950,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2950
2950
|
);
|
|
2951
2951
|
if (scope === "feature" && feature && implementsInterface) {
|
|
2952
2952
|
registerInProvider = handleCancel(
|
|
2953
|
-
await
|
|
2953
|
+
await p14.confirm({
|
|
2954
2954
|
message: "Register this implementation in feature diProvider.ts?",
|
|
2955
2955
|
initialValue: true
|
|
2956
2956
|
})
|
|
@@ -2959,7 +2959,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2959
2959
|
} else if (flags.yes && scope === "feature" && feature && implementsInterface) {
|
|
2960
2960
|
registerInProvider = !flags.noProvider;
|
|
2961
2961
|
}
|
|
2962
|
-
const s =
|
|
2962
|
+
const s = p14.spinner();
|
|
2963
2963
|
s.start(`Generating infrastructure class '${pc14.cyan(className)}'...`);
|
|
2964
2964
|
const files = [];
|
|
2965
2965
|
const options = {
|
|
@@ -2995,7 +2995,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2995
2995
|
}
|
|
2996
2996
|
s.stop("Infrastructure class generated successfully!");
|
|
2997
2997
|
printGeneratedFiles(files, options.dryRun);
|
|
2998
|
-
|
|
2998
|
+
p14.outro(pc14.green(`Infrastructure class '${className}' created!`));
|
|
2999
2999
|
}
|
|
3000
3000
|
async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
3001
3001
|
const projectRoot = findProjectRoot();
|
|
@@ -3006,14 +3006,14 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3006
3006
|
const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
|
|
3007
3007
|
featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
|
|
3008
3008
|
const selected = handleCancel(
|
|
3009
|
-
await
|
|
3009
|
+
await p14.select({
|
|
3010
3010
|
message: "Select target feature:",
|
|
3011
3011
|
options: featureOptions
|
|
3012
3012
|
})
|
|
3013
3013
|
);
|
|
3014
3014
|
if (selected === "__custom__") {
|
|
3015
3015
|
feature = handleCancel(
|
|
3016
|
-
await
|
|
3016
|
+
await p14.text({
|
|
3017
3017
|
message: "Enter feature name:",
|
|
3018
3018
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
3019
3019
|
})
|
|
@@ -3023,7 +3023,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3023
3023
|
}
|
|
3024
3024
|
} else {
|
|
3025
3025
|
feature = handleCancel(
|
|
3026
|
-
await
|
|
3026
|
+
await p14.text({
|
|
3027
3027
|
message: "Enter target feature name:",
|
|
3028
3028
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
3029
3029
|
})
|
|
@@ -3034,7 +3034,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3034
3034
|
let name = interfaceNameArg?.trim();
|
|
3035
3035
|
if (!name) {
|
|
3036
3036
|
name = handleCancel(
|
|
3037
|
-
await
|
|
3037
|
+
await p14.text({
|
|
3038
3038
|
message: "Domain interface name:",
|
|
3039
3039
|
placeholder: "e.g. IUserRepository, IPaymentService",
|
|
3040
3040
|
validate: (v) => v && v.trim() ? void 0 : "Interface name cannot be empty"
|
|
@@ -3052,7 +3052,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3052
3052
|
if (!flags.yes) {
|
|
3053
3053
|
if (existingEntities.length > 0) {
|
|
3054
3054
|
const selectedEntity = handleCancel(
|
|
3055
|
-
await
|
|
3055
|
+
await p14.select({
|
|
3056
3056
|
message: "Select domain entity associated with this interface:",
|
|
3057
3057
|
options: [
|
|
3058
3058
|
...existingEntities.map((e) => ({ value: e.name, label: e.name })),
|
|
@@ -3063,7 +3063,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3063
3063
|
);
|
|
3064
3064
|
if (selectedEntity === "__custom__") {
|
|
3065
3065
|
entityName = handleCancel(
|
|
3066
|
-
await
|
|
3066
|
+
await p14.text({
|
|
3067
3067
|
message: "Enter entity name:",
|
|
3068
3068
|
defaultValue: removeInterfacePrefix(ifaceName).replace(/Repository$|Service$/, "")
|
|
3069
3069
|
})
|
|
@@ -3073,7 +3073,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3073
3073
|
}
|
|
3074
3074
|
}
|
|
3075
3075
|
kind = handleCancel(
|
|
3076
|
-
await
|
|
3076
|
+
await p14.select({
|
|
3077
3077
|
message: "Interface definition style:",
|
|
3078
3078
|
options: [
|
|
3079
3079
|
{ value: "abstractClass", label: "Abstract Class (Recommended - acts as DI token & type)" },
|
|
@@ -3083,7 +3083,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3083
3083
|
})
|
|
3084
3084
|
);
|
|
3085
3085
|
methodsPreset = handleCancel(
|
|
3086
|
-
await
|
|
3086
|
+
await p14.select({
|
|
3087
3087
|
message: "Method signature preset:",
|
|
3088
3088
|
options: [
|
|
3089
3089
|
{ value: "crud", label: "Standard CRUD (GetById, List, Create, Update, Delete)" },
|
|
@@ -3093,21 +3093,21 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3093
3093
|
})
|
|
3094
3094
|
);
|
|
3095
3095
|
generateStub = handleCancel(
|
|
3096
|
-
await
|
|
3096
|
+
await p14.confirm({
|
|
3097
3097
|
message: "Auto-generate Infrastructure Stub implementation?",
|
|
3098
3098
|
initialValue: true
|
|
3099
3099
|
})
|
|
3100
3100
|
);
|
|
3101
3101
|
if (generateStub) {
|
|
3102
3102
|
registerInProvider = handleCancel(
|
|
3103
|
-
await
|
|
3103
|
+
await p14.confirm({
|
|
3104
3104
|
message: "Auto-register interface & stub in feature diProvider.ts?",
|
|
3105
3105
|
initialValue: true
|
|
3106
3106
|
})
|
|
3107
3107
|
);
|
|
3108
3108
|
}
|
|
3109
3109
|
}
|
|
3110
|
-
const s =
|
|
3110
|
+
const s = p14.spinner();
|
|
3111
3111
|
s.start(`Generating interface '${pc14.cyan(ifaceName)}'...`);
|
|
3112
3112
|
const files = [];
|
|
3113
3113
|
const options = {
|
|
@@ -3157,7 +3157,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3157
3157
|
}
|
|
3158
3158
|
s.stop("Interface generated successfully!");
|
|
3159
3159
|
printGeneratedFiles(files, options.dryRun);
|
|
3160
|
-
|
|
3160
|
+
p14.outro(pc14.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
|
|
3161
3161
|
}
|
|
3162
3162
|
async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
3163
3163
|
const projectRoot = findProjectRoot();
|
|
@@ -3168,14 +3168,14 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3168
3168
|
const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
|
|
3169
3169
|
featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
|
|
3170
3170
|
const selected = handleCancel(
|
|
3171
|
-
await
|
|
3171
|
+
await p14.select({
|
|
3172
3172
|
message: "Select target feature:",
|
|
3173
3173
|
options: featureOptions
|
|
3174
3174
|
})
|
|
3175
3175
|
);
|
|
3176
3176
|
if (selected === "__custom__") {
|
|
3177
3177
|
feature = handleCancel(
|
|
3178
|
-
await
|
|
3178
|
+
await p14.text({
|
|
3179
3179
|
message: "Enter feature name:",
|
|
3180
3180
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
3181
3181
|
})
|
|
@@ -3185,7 +3185,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3185
3185
|
}
|
|
3186
3186
|
} else {
|
|
3187
3187
|
feature = handleCancel(
|
|
3188
|
-
await
|
|
3188
|
+
await p14.text({
|
|
3189
3189
|
message: "Enter target feature name:",
|
|
3190
3190
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
3191
3191
|
})
|
|
@@ -3196,7 +3196,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3196
3196
|
let name = handlerNameArg?.trim();
|
|
3197
3197
|
if (!name) {
|
|
3198
3198
|
name = handleCancel(
|
|
3199
|
-
await
|
|
3199
|
+
await p14.text({
|
|
3200
3200
|
message: "HTTP Route Handler class name:",
|
|
3201
3201
|
placeholder: "e.g. CreateUserHttp, GetUserHttp, HealthHttp",
|
|
3202
3202
|
validate: (v) => v && v.trim() ? void 0 : "Handler name cannot be empty"
|
|
@@ -3215,7 +3215,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3215
3215
|
let validationFields = [];
|
|
3216
3216
|
if (!flags.yes) {
|
|
3217
3217
|
method = handleCancel(
|
|
3218
|
-
await
|
|
3218
|
+
await p14.select({
|
|
3219
3219
|
message: "HTTP Method:",
|
|
3220
3220
|
options: [
|
|
3221
3221
|
{ value: "get", label: "GET" },
|
|
@@ -3229,7 +3229,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3229
3229
|
);
|
|
3230
3230
|
const defaultPath = method === "get" ? "/" : "/";
|
|
3231
3231
|
subPath = handleCancel(
|
|
3232
|
-
await
|
|
3232
|
+
await p14.text({
|
|
3233
3233
|
message: "Route subpath (relative to feature prefix):",
|
|
3234
3234
|
initialValue: defaultPath,
|
|
3235
3235
|
defaultValue: defaultPath
|
|
@@ -3240,7 +3240,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3240
3240
|
const ucOptions = useCases.map((u) => ({ value: u.name, label: u.name }));
|
|
3241
3241
|
ucOptions.push({ value: "__none__", label: "None / Standalone Handler" });
|
|
3242
3242
|
const chosenUc = handleCancel(
|
|
3243
|
-
await
|
|
3243
|
+
await p14.select({
|
|
3244
3244
|
message: "Connect this handler to a domain Use Case?",
|
|
3245
3245
|
options: ucOptions,
|
|
3246
3246
|
initialValue: ucOptions[0]?.value
|
|
@@ -3251,7 +3251,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3251
3251
|
}
|
|
3252
3252
|
}
|
|
3253
3253
|
includeValidation = handleCancel(
|
|
3254
|
-
await
|
|
3254
|
+
await p14.confirm({
|
|
3255
3255
|
message: "Include Zod inbound request validation?",
|
|
3256
3256
|
initialValue: method !== "delete"
|
|
3257
3257
|
})
|
|
@@ -3259,7 +3259,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3259
3259
|
if (includeValidation) {
|
|
3260
3260
|
const defaultSource = method === "get" ? "query" : "body";
|
|
3261
3261
|
validationSource = handleCancel(
|
|
3262
|
-
await
|
|
3262
|
+
await p14.select({
|
|
3263
3263
|
message: "Validation data source:",
|
|
3264
3264
|
options: [
|
|
3265
3265
|
{ value: "body", label: "req.body (for POST / PUT / PATCH payloads)" },
|
|
@@ -3273,7 +3273,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3273
3273
|
});
|
|
3274
3274
|
}
|
|
3275
3275
|
}
|
|
3276
|
-
const s =
|
|
3276
|
+
const s = p14.spinner();
|
|
3277
3277
|
s.start(`Generating HTTP handler '${pc14.cyan(className)}'...`);
|
|
3278
3278
|
const options = {
|
|
3279
3279
|
name: className,
|
|
@@ -3292,7 +3292,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3292
3292
|
const files = generateHttpHandler(options);
|
|
3293
3293
|
s.stop("HTTP handler generated successfully!");
|
|
3294
3294
|
printGeneratedFiles(files, options.dryRun);
|
|
3295
|
-
|
|
3295
|
+
p14.outro(pc14.green(`HTTP handler '${className}' created in feature '${feature}'!`));
|
|
3296
3296
|
}
|
|
3297
3297
|
function generateInjectableCode(className, dependencies = [], methods = [], implementsInterface) {
|
|
3298
3298
|
const formattedDeps = dependencies.map((dep) => ({
|
|
@@ -3372,7 +3372,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
|
|
|
3372
3372
|
let name = classNameArg?.trim();
|
|
3373
3373
|
if (!name) {
|
|
3374
3374
|
name = handleCancel(
|
|
3375
|
-
await
|
|
3375
|
+
await p14.text({
|
|
3376
3376
|
message: "Injectable class name:",
|
|
3377
3377
|
placeholder: "e.g. PasswordHasher, TokenService, UserTransformer",
|
|
3378
3378
|
validate: (v) => v && v.trim() ? void 0 : "Class name cannot be empty"
|
|
@@ -3385,7 +3385,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
|
|
|
3385
3385
|
let customPath;
|
|
3386
3386
|
if (!flags.yes) {
|
|
3387
3387
|
location = handleCancel(
|
|
3388
|
-
await
|
|
3388
|
+
await p14.select({
|
|
3389
3389
|
message: "Select target architectural location:",
|
|
3390
3390
|
options: [
|
|
3391
3391
|
{ value: "feature-service", label: "Feature Service (features/<feature>/services/)" },
|
|
@@ -3403,14 +3403,14 @@ async function runInjectableCommand(classNameArg, flags = {}) {
|
|
|
3403
3403
|
const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
|
|
3404
3404
|
featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
|
|
3405
3405
|
const selected = handleCancel(
|
|
3406
|
-
await
|
|
3406
|
+
await p14.select({
|
|
3407
3407
|
message: "Select target feature:",
|
|
3408
3408
|
options: featureOptions
|
|
3409
3409
|
})
|
|
3410
3410
|
);
|
|
3411
3411
|
if (selected === "__custom__") {
|
|
3412
3412
|
feature = handleCancel(
|
|
3413
|
-
await
|
|
3413
|
+
await p14.text({
|
|
3414
3414
|
message: "Enter feature name:",
|
|
3415
3415
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
3416
3416
|
})
|
|
@@ -3420,7 +3420,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
|
|
|
3420
3420
|
}
|
|
3421
3421
|
} else {
|
|
3422
3422
|
feature = handleCancel(
|
|
3423
|
-
await
|
|
3423
|
+
await p14.text({
|
|
3424
3424
|
message: "Enter target feature name:",
|
|
3425
3425
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
3426
3426
|
})
|
|
@@ -3430,7 +3430,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
|
|
|
3430
3430
|
feature = toCamelCase(feature);
|
|
3431
3431
|
} else if (location === "custom") {
|
|
3432
3432
|
customPath = handleCancel(
|
|
3433
|
-
await
|
|
3433
|
+
await p14.text({
|
|
3434
3434
|
message: "Enter destination directory path:",
|
|
3435
3435
|
placeholder: "e.g. src/services or lib/utils",
|
|
3436
3436
|
validate: (v) => v && v.trim() ? void 0 : "Path cannot be empty"
|
|
@@ -3441,13 +3441,13 @@ async function runInjectableCommand(classNameArg, flags = {}) {
|
|
|
3441
3441
|
let generateInterface2 = false;
|
|
3442
3442
|
if (!flags.yes) {
|
|
3443
3443
|
generateInterface2 = handleCancel(
|
|
3444
|
-
await
|
|
3444
|
+
await p14.confirm({
|
|
3445
3445
|
message: `Generate abstract class interface (I${className}) as runtime DI token?`,
|
|
3446
3446
|
initialValue: false
|
|
3447
3447
|
})
|
|
3448
3448
|
);
|
|
3449
3449
|
}
|
|
3450
|
-
const s =
|
|
3450
|
+
const s = p14.spinner();
|
|
3451
3451
|
s.start(`Generating injectable '${pc14.cyan(className)}'...`);
|
|
3452
3452
|
const options = {
|
|
3453
3453
|
name: className,
|
|
@@ -3462,12 +3462,12 @@ async function runInjectableCommand(classNameArg, flags = {}) {
|
|
|
3462
3462
|
const files = generateInjectable(options);
|
|
3463
3463
|
s.stop("Injectable class generated successfully!");
|
|
3464
3464
|
printGeneratedFiles(files, options.dryRun);
|
|
3465
|
-
|
|
3465
|
+
p14.outro(pc14.green(`Injectable '${className}' created successfully!`));
|
|
3466
3466
|
}
|
|
3467
3467
|
async function runInteractiveMenu(flags = {}) {
|
|
3468
3468
|
printBanner();
|
|
3469
3469
|
const choice = handleCancel(
|
|
3470
|
-
await
|
|
3470
|
+
await p14.select({
|
|
3471
3471
|
message: "What would you like to create in your architecture?",
|
|
3472
3472
|
options: [
|
|
3473
3473
|
{
|
|
@@ -3575,7 +3575,7 @@ async function handleCreate(type, name, flags = {}) {
|
|
|
3575
3575
|
await runInjectableCommand(name, flags);
|
|
3576
3576
|
break;
|
|
3577
3577
|
default:
|
|
3578
|
-
|
|
3578
|
+
p14.log.error(
|
|
3579
3579
|
pc14.red(
|
|
3580
3580
|
`Unknown component type: "${type}". Allowed types: feature, shared, usecase, domain, infrastructure, interface, httpHandler, injectable.`
|
|
3581
3581
|
)
|
|
@@ -3584,8 +3584,8 @@ async function handleCreate(type, name, flags = {}) {
|
|
|
3584
3584
|
}
|
|
3585
3585
|
}
|
|
3586
3586
|
var importPrompts = {
|
|
3587
|
-
select:
|
|
3588
|
-
text:
|
|
3587
|
+
select: p14.select,
|
|
3588
|
+
text: p14.text
|
|
3589
3589
|
};
|
|
3590
3590
|
function rewriteImports(content, aliasMap) {
|
|
3591
3591
|
if (aliasMap.size === 0) return content;
|
|
@@ -3614,125 +3614,47 @@ function getExistingModuleDir(projectRoot, modType, modName) {
|
|
|
3614
3614
|
function checkModuleExists(projectRoot, modType, modName) {
|
|
3615
3615
|
return getExistingModuleDir(projectRoot, modType, modName) !== null;
|
|
3616
3616
|
}
|
|
3617
|
-
|
|
3618
|
-
const folder =
|
|
3619
|
-
const
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
if (!existingDir) {
|
|
3635
|
-
p4.log.info(` \u21B3 Resolving required ${depType} dependency: ${pc14.cyan(depName)}`);
|
|
3636
|
-
await importModuleRecursive(depType, depName, depName);
|
|
3637
|
-
return;
|
|
3638
|
-
}
|
|
3639
|
-
const entry = (depType === "shared" ? index?.shared : index?.features)?.find(
|
|
3640
|
-
(item) => item.name === depName
|
|
3641
|
-
);
|
|
3642
|
-
let remoteIntegrity = entry?.integrity;
|
|
3643
|
-
if (!remoteIntegrity) {
|
|
3644
|
-
try {
|
|
3645
|
-
const depManifest = await fetchModuleManifest(registryBase, depType, depName);
|
|
3646
|
-
remoteIntegrity = depManifest.integrity;
|
|
3647
|
-
} catch {
|
|
3648
|
-
}
|
|
3649
|
-
}
|
|
3650
|
-
const localHash = computeDirectoryHash(existingDir);
|
|
3651
|
-
if (remoteIntegrity && localHash === remoteIntegrity) {
|
|
3652
|
-
p4.log.step(`\u2713 ${depType}/${depName} is up to date (reusing existing)`);
|
|
3653
|
-
importedModules.add(`${depType}:${depName}`);
|
|
3654
|
-
return;
|
|
3655
|
-
}
|
|
3656
|
-
if (flags.overwrite) {
|
|
3657
|
-
p4.log.step(`Overwriting '${depType}/${depName}' with registry version (--overwrite)...`);
|
|
3658
|
-
const baseDir = getModuleBaseDir(projectRoot, depType);
|
|
3659
|
-
const targetDir = path2.join(baseDir, depName);
|
|
3660
|
-
if (!flags.dryRun) {
|
|
3661
|
-
if (existingDir !== targetDir) {
|
|
3662
|
-
fs4.rmSync(existingDir, { recursive: true, force: true });
|
|
3617
|
+
function getAvailableLocalModules(projectRoot, modType) {
|
|
3618
|
+
const folder = modType === "shared" ? "shared" : "features";
|
|
3619
|
+
const dirs = [
|
|
3620
|
+
path2.join(projectRoot, "src", folder),
|
|
3621
|
+
path2.join(projectRoot, folder)
|
|
3622
|
+
];
|
|
3623
|
+
const set = /* @__PURE__ */ new Set();
|
|
3624
|
+
for (const dir of dirs) {
|
|
3625
|
+
if (fs4.existsSync(dir)) {
|
|
3626
|
+
try {
|
|
3627
|
+
const entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
3628
|
+
for (const e of entries) {
|
|
3629
|
+
if (e.isDirectory() && !e.name.startsWith(".")) {
|
|
3630
|
+
set.add(e.name);
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
} catch {
|
|
3663
3634
|
}
|
|
3664
|
-
fs4.rmSync(targetDir, { recursive: true, force: true });
|
|
3665
3635
|
}
|
|
3666
|
-
await importModuleRecursive(depType, depName, depName, { overwrite: true });
|
|
3667
|
-
return;
|
|
3668
|
-
}
|
|
3669
|
-
if (flags.yes) {
|
|
3670
|
-
p4.log.info(`Using existing '${depType}/${depName}'`);
|
|
3671
|
-
importedModules.add(`${depType}:${depName}`);
|
|
3672
|
-
return;
|
|
3673
|
-
}
|
|
3674
|
-
const choice = handleCancel(
|
|
3675
|
-
await importPrompts.select({
|
|
3676
|
-
message: `Local '${depType}/${depName}' differs from the registry version.`,
|
|
3677
|
-
options: [
|
|
3678
|
-
{
|
|
3679
|
-
value: "keep_existing",
|
|
3680
|
-
label: `Keep existing local version (Recommended)`
|
|
3681
|
-
},
|
|
3682
|
-
{
|
|
3683
|
-
value: "rename",
|
|
3684
|
-
label: "Import fresh copy under a new name (alias)"
|
|
3685
|
-
},
|
|
3686
|
-
{
|
|
3687
|
-
value: "overwrite",
|
|
3688
|
-
label: "Overwrite local version with registry version"
|
|
3689
|
-
}
|
|
3690
|
-
]
|
|
3691
|
-
})
|
|
3692
|
-
);
|
|
3693
|
-
if (choice === "keep_existing" || choice === "use_existing") {
|
|
3694
|
-
p4.log.step(`Skipping '${depType}/${depName}' (keeping existing)`);
|
|
3695
|
-
importedModules.add(`${depType}:${depName}`);
|
|
3696
|
-
return;
|
|
3697
3636
|
}
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3637
|
+
return Array.from(set).sort();
|
|
3638
|
+
}
|
|
3639
|
+
function parseMappingFlags(raw) {
|
|
3640
|
+
if (!raw) return {};
|
|
3641
|
+
if (typeof raw === "object" && !Array.isArray(raw)) return raw;
|
|
3642
|
+
const result = {};
|
|
3643
|
+
const entries = Array.isArray(raw) ? raw : [raw];
|
|
3644
|
+
for (const entry of entries) {
|
|
3645
|
+
if (typeof entry === "string") {
|
|
3646
|
+
const idx = entry.indexOf(":");
|
|
3647
|
+
if (idx !== -1) {
|
|
3648
|
+
let key = entry.slice(0, idx).trim();
|
|
3649
|
+
let val = entry.slice(idx + 1).trim();
|
|
3650
|
+
val = val.replace(/^@\/(shared|features)\//, "").replace(/^(src\/)?(shared|features)\//, "").replace(/^\/+|\/+$/g, "").trim();
|
|
3651
|
+
if (key && val) {
|
|
3652
|
+
result[key] = val;
|
|
3713
3653
|
}
|
|
3714
|
-
})
|
|
3715
|
-
).trim();
|
|
3716
|
-
aliasMap.set(`@/${folder}/${depName}/`, `@/${folder}/${newName}/`);
|
|
3717
|
-
const baseDir = getModuleBaseDir(projectRoot, depType);
|
|
3718
|
-
const displayDest = path2.relative(projectRoot, path2.join(baseDir, newName));
|
|
3719
|
-
p4.log.step(`Importing '${depName}' into '${displayDest}'...`);
|
|
3720
|
-
await importModuleRecursive(depType, depName, newName);
|
|
3721
|
-
return;
|
|
3722
|
-
}
|
|
3723
|
-
if (choice === "overwrite") {
|
|
3724
|
-
p4.log.step(`Overwriting '${depType}/${depName}' with registry version...`);
|
|
3725
|
-
const baseDir = getModuleBaseDir(projectRoot, depType);
|
|
3726
|
-
const targetDir = path2.join(baseDir, depName);
|
|
3727
|
-
if (!flags.dryRun) {
|
|
3728
|
-
if (existingDir !== targetDir) {
|
|
3729
|
-
fs4.rmSync(existingDir, { recursive: true, force: true });
|
|
3730
3654
|
}
|
|
3731
|
-
fs4.rmSync(targetDir, { recursive: true, force: true });
|
|
3732
3655
|
}
|
|
3733
|
-
await importModuleRecursive(depType, depName, depName, { overwrite: true });
|
|
3734
|
-
return;
|
|
3735
3656
|
}
|
|
3657
|
+
return result;
|
|
3736
3658
|
}
|
|
3737
3659
|
async function handleImport(typeArg, nameArg, flags = {}) {
|
|
3738
3660
|
const projectRoot = findProjectRoot();
|
|
@@ -3758,11 +3680,11 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
3758
3680
|
);
|
|
3759
3681
|
}
|
|
3760
3682
|
if (type !== "shared" && type !== "feature") {
|
|
3761
|
-
|
|
3683
|
+
p14.log.error(pc14.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
|
|
3762
3684
|
process.exit(1);
|
|
3763
3685
|
}
|
|
3764
3686
|
const registryBase = getRegistryBase(projectRoot, flags.registry);
|
|
3765
|
-
const s =
|
|
3687
|
+
const s = p14.spinner();
|
|
3766
3688
|
s.start(`Connecting to registry at ${pc14.dim(registryBase)}...`);
|
|
3767
3689
|
let index;
|
|
3768
3690
|
try {
|
|
@@ -3770,7 +3692,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
3770
3692
|
s.stop("Registry index loaded.");
|
|
3771
3693
|
} catch (err) {
|
|
3772
3694
|
s.stop(pc14.red("Failed to load registry index."));
|
|
3773
|
-
|
|
3695
|
+
p14.log.error(pc14.red(err.message || String(err)));
|
|
3774
3696
|
process.exit(1);
|
|
3775
3697
|
}
|
|
3776
3698
|
const availableList = type === "shared" ? index.shared : index.features;
|
|
@@ -3778,7 +3700,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
3778
3700
|
let name = nameArg?.trim();
|
|
3779
3701
|
if (!name) {
|
|
3780
3702
|
if (availableList.length === 0) {
|
|
3781
|
-
|
|
3703
|
+
p14.log.warn(`No ${type} modules available in the registry.`);
|
|
3782
3704
|
return;
|
|
3783
3705
|
}
|
|
3784
3706
|
name = handleCancel(
|
|
@@ -3794,108 +3716,400 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
3794
3716
|
}
|
|
3795
3717
|
if (!candidateNames.includes(name)) {
|
|
3796
3718
|
const closest = findClosestMatch(name, candidateNames);
|
|
3797
|
-
|
|
3719
|
+
p14.log.error(pc14.red(`\u274C Module '${name}' not found in the ${type} registry.`));
|
|
3798
3720
|
if (closest) {
|
|
3799
|
-
|
|
3721
|
+
p14.log.info(pc14.yellow(`Did you mean '${pc14.cyan(closest)}'?`));
|
|
3800
3722
|
}
|
|
3801
3723
|
process.exit(1);
|
|
3802
3724
|
}
|
|
3803
|
-
const
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
const modKey = `${modType}:${targetName}`;
|
|
3808
|
-
if (importedModules.has(modKey)) return;
|
|
3809
|
-
p4.log.step(
|
|
3810
|
-
`Importing ${modType}: ${pc14.cyan(targetName)}${targetName !== registryName ? pc14.dim(` (from ${registryName})`) : ""}...`
|
|
3811
|
-
);
|
|
3725
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
3726
|
+
async function discoverGraph(modType, modName) {
|
|
3727
|
+
const key = `${modType}:${modName}`;
|
|
3728
|
+
if (nodes.has(key)) return;
|
|
3812
3729
|
let manifest;
|
|
3813
3730
|
try {
|
|
3814
|
-
manifest = await fetchModuleManifest(registryBase, modType,
|
|
3731
|
+
manifest = await fetchModuleManifest(registryBase, modType, modName);
|
|
3815
3732
|
} catch (err) {
|
|
3816
|
-
|
|
3733
|
+
p14.log.error(pc14.red(`Failed to fetch manifest for ${modType} '${modName}': ${err.message}`));
|
|
3817
3734
|
process.exit(1);
|
|
3818
3735
|
}
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
}
|
|
3824
|
-
const
|
|
3825
|
-
|
|
3826
|
-
await resolveDependency(
|
|
3827
|
-
"shared",
|
|
3828
|
-
dep,
|
|
3829
|
-
aliasMap,
|
|
3830
|
-
projectRoot,
|
|
3831
|
-
flags,
|
|
3832
|
-
importModuleRecursive,
|
|
3833
|
-
importedModules,
|
|
3834
|
-
registryBase,
|
|
3835
|
-
index
|
|
3836
|
-
);
|
|
3736
|
+
nodes.set(key, {
|
|
3737
|
+
type: modType,
|
|
3738
|
+
name: modName,
|
|
3739
|
+
manifest
|
|
3740
|
+
});
|
|
3741
|
+
for (const dep of manifest.dependencies?.shared || []) {
|
|
3742
|
+
await discoverGraph("shared", dep);
|
|
3837
3743
|
}
|
|
3838
|
-
const
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3744
|
+
for (const dep of manifest.dependencies?.features || []) {
|
|
3745
|
+
await discoverGraph("feature", dep);
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
await discoverGraph(type, name);
|
|
3749
|
+
const orderedKeys = [];
|
|
3750
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
3751
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3752
|
+
function buildTopologicalOrder(key) {
|
|
3753
|
+
if (visited.has(key)) return;
|
|
3754
|
+
if (visiting.has(key)) return;
|
|
3755
|
+
visiting.add(key);
|
|
3756
|
+
const node = nodes.get(key);
|
|
3757
|
+
if (node) {
|
|
3758
|
+
for (const dep of node.manifest.dependencies?.shared || []) {
|
|
3759
|
+
buildTopologicalOrder(`shared:${dep}`);
|
|
3760
|
+
}
|
|
3761
|
+
for (const dep of node.manifest.dependencies?.features || []) {
|
|
3762
|
+
buildTopologicalOrder(`feature:${dep}`);
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3765
|
+
visiting.delete(key);
|
|
3766
|
+
visited.add(key);
|
|
3767
|
+
orderedKeys.push(key);
|
|
3768
|
+
}
|
|
3769
|
+
const rootKey = `${type}:${name}`;
|
|
3770
|
+
buildTopologicalOrder(rootKey);
|
|
3771
|
+
const resolutions = /* @__PURE__ */ new Map();
|
|
3772
|
+
const globalAliasMap = /* @__PURE__ */ new Map();
|
|
3773
|
+
p14.log.step(`Importing ${type}: ${pc14.cyan(name)}...`);
|
|
3774
|
+
for (const key of orderedKeys) {
|
|
3775
|
+
const node = nodes.get(key);
|
|
3776
|
+
const { type: modType, name: registryName, manifest } = node;
|
|
3777
|
+
const folder = modType === "shared" ? "shared" : "features";
|
|
3778
|
+
if (key === rootKey) {
|
|
3779
|
+
const aliasMap2 = parseMappingFlags(flags.alias);
|
|
3780
|
+
const programmaticAlias2 = aliasMap2[registryName] || aliasMap2[`${modType}/${registryName}`];
|
|
3781
|
+
const targetName = programmaticAlias2 || registryName;
|
|
3782
|
+
if (targetName !== registryName) {
|
|
3783
|
+
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${targetName}/`);
|
|
3784
|
+
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${targetName}`);
|
|
3785
|
+
}
|
|
3786
|
+
resolutions.set(key, {
|
|
3787
|
+
type: modType,
|
|
3788
|
+
registryName,
|
|
3789
|
+
targetName,
|
|
3790
|
+
action: flags.overwrite ? "overwrite" : "download",
|
|
3791
|
+
manifest
|
|
3792
|
+
});
|
|
3793
|
+
continue;
|
|
3794
|
+
}
|
|
3795
|
+
const aliasMap = parseMappingFlags(flags.alias);
|
|
3796
|
+
const pointToMap = {
|
|
3797
|
+
...parseMappingFlags(flags.pointTo),
|
|
3798
|
+
...parseMappingFlags(flags["point-to"]),
|
|
3799
|
+
...parseMappingFlags(flags.useExisting),
|
|
3800
|
+
...parseMappingFlags(flags["use-existing"])
|
|
3801
|
+
};
|
|
3802
|
+
const programmaticPointTo = pointToMap[registryName] || pointToMap[`${modType}/${registryName}`];
|
|
3803
|
+
if (programmaticPointTo) {
|
|
3804
|
+
if (!checkModuleExists(projectRoot, modType, programmaticPointTo)) {
|
|
3805
|
+
p14.log.error(
|
|
3806
|
+
pc14.red(
|
|
3807
|
+
`Module '${programmaticPointTo}' specified in --point-to does not exist in ${folder}.`
|
|
3808
|
+
)
|
|
3809
|
+
);
|
|
3810
|
+
process.exit(1);
|
|
3811
|
+
}
|
|
3812
|
+
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticPointTo}/`);
|
|
3813
|
+
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticPointTo}`);
|
|
3814
|
+
p14.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${programmaticPointTo}'`);
|
|
3815
|
+
resolutions.set(key, {
|
|
3816
|
+
type: modType,
|
|
3817
|
+
registryName,
|
|
3818
|
+
targetName: programmaticPointTo,
|
|
3819
|
+
action: "reuse",
|
|
3820
|
+
manifest
|
|
3821
|
+
});
|
|
3822
|
+
continue;
|
|
3823
|
+
}
|
|
3824
|
+
const programmaticAlias = aliasMap[registryName] || aliasMap[`${modType}/${registryName}`];
|
|
3825
|
+
if (programmaticAlias) {
|
|
3826
|
+
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticAlias}/`);
|
|
3827
|
+
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
|
|
3828
|
+
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
3829
|
+
const displayDest = path2.relative(projectRoot, path2.join(baseDir, programmaticAlias));
|
|
3830
|
+
p14.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
3831
|
+
resolutions.set(key, {
|
|
3832
|
+
type: modType,
|
|
3833
|
+
registryName,
|
|
3834
|
+
targetName: programmaticAlias,
|
|
3835
|
+
action: "alias",
|
|
3836
|
+
manifest
|
|
3837
|
+
});
|
|
3838
|
+
continue;
|
|
3839
|
+
}
|
|
3840
|
+
const existingDir = getExistingModuleDir(projectRoot, modType, registryName);
|
|
3841
|
+
if (!existingDir) {
|
|
3842
|
+
p14.log.info(` \u21B3 Resolving required ${modType} dependency: ${pc14.cyan(registryName)}`);
|
|
3843
|
+
resolutions.set(key, {
|
|
3844
|
+
type: modType,
|
|
3845
|
+
registryName,
|
|
3846
|
+
targetName: registryName,
|
|
3847
|
+
action: "download",
|
|
3848
|
+
manifest
|
|
3849
|
+
});
|
|
3850
|
+
continue;
|
|
3851
|
+
}
|
|
3852
|
+
const entry = (modType === "shared" ? index.shared : index.features)?.find(
|
|
3853
|
+
(item) => item.name === registryName
|
|
3854
|
+
);
|
|
3855
|
+
const remoteIntegrity = manifest.integrity || entry?.integrity;
|
|
3856
|
+
const localHash = computeDirectoryHash(existingDir);
|
|
3857
|
+
if (remoteIntegrity && localHash === remoteIntegrity) {
|
|
3858
|
+
p14.log.step(`\u2713 ${modType}/${registryName} is up to date (reusing existing)`);
|
|
3859
|
+
resolutions.set(key, {
|
|
3860
|
+
type: modType,
|
|
3861
|
+
registryName,
|
|
3862
|
+
targetName: registryName,
|
|
3863
|
+
action: "reuse",
|
|
3864
|
+
manifest
|
|
3865
|
+
});
|
|
3866
|
+
continue;
|
|
3867
|
+
}
|
|
3868
|
+
if (flags.overwrite) {
|
|
3869
|
+
p14.log.step(`Overwriting '${modType}/${registryName}' with registry version (--overwrite)...`);
|
|
3870
|
+
resolutions.set(key, {
|
|
3871
|
+
type: modType,
|
|
3872
|
+
registryName,
|
|
3873
|
+
targetName: registryName,
|
|
3874
|
+
action: "overwrite",
|
|
3875
|
+
manifest
|
|
3876
|
+
});
|
|
3877
|
+
continue;
|
|
3878
|
+
}
|
|
3879
|
+
if (flags.yes) {
|
|
3880
|
+
p14.log.info(`Using existing '${modType}/${registryName}'`);
|
|
3881
|
+
resolutions.set(key, {
|
|
3882
|
+
type: modType,
|
|
3883
|
+
registryName,
|
|
3884
|
+
targetName: registryName,
|
|
3885
|
+
action: "reuse",
|
|
3886
|
+
manifest
|
|
3887
|
+
});
|
|
3888
|
+
continue;
|
|
3889
|
+
}
|
|
3890
|
+
const choice = handleCancel(
|
|
3891
|
+
await importPrompts.select({
|
|
3892
|
+
message: `Local '${modType}/${registryName}' differs from the registry version.`,
|
|
3893
|
+
options: [
|
|
3894
|
+
{
|
|
3895
|
+
value: "keep_existing",
|
|
3896
|
+
label: `Keep existing local version (Recommended)`
|
|
3897
|
+
},
|
|
3898
|
+
{
|
|
3899
|
+
value: "point_existing",
|
|
3900
|
+
label: `Point to another existing ${modType} module`
|
|
3901
|
+
},
|
|
3902
|
+
{
|
|
3903
|
+
value: "rename",
|
|
3904
|
+
label: "Import fresh copy under a new name (alias)"
|
|
3905
|
+
},
|
|
3906
|
+
{
|
|
3907
|
+
value: "overwrite",
|
|
3908
|
+
label: "Overwrite local version with registry version"
|
|
3909
|
+
}
|
|
3910
|
+
]
|
|
3911
|
+
})
|
|
3912
|
+
);
|
|
3913
|
+
if (choice === "keep_existing" || choice === "use_existing") {
|
|
3914
|
+
p14.log.step(`Skipping '${modType}/${registryName}' (keeping existing)`);
|
|
3915
|
+
resolutions.set(key, {
|
|
3916
|
+
type: modType,
|
|
3917
|
+
registryName,
|
|
3918
|
+
targetName: registryName,
|
|
3919
|
+
action: "reuse",
|
|
3920
|
+
manifest
|
|
3921
|
+
});
|
|
3922
|
+
continue;
|
|
3923
|
+
}
|
|
3924
|
+
if (choice === "point_existing" || choice === "point_to_existing") {
|
|
3925
|
+
const candidates = getAvailableLocalModules(projectRoot, modType).filter(
|
|
3926
|
+
(m) => m !== registryName
|
|
3850
3927
|
);
|
|
3928
|
+
let chosenName;
|
|
3929
|
+
if (candidates.length > 0) {
|
|
3930
|
+
const selection = handleCancel(
|
|
3931
|
+
await importPrompts.select({
|
|
3932
|
+
message: `Select an existing ${modType} module to point to:`,
|
|
3933
|
+
options: [
|
|
3934
|
+
...candidates.map((m) => ({
|
|
3935
|
+
value: m,
|
|
3936
|
+
label: m
|
|
3937
|
+
})),
|
|
3938
|
+
{
|
|
3939
|
+
value: "__custom__",
|
|
3940
|
+
label: "Enter name manually..."
|
|
3941
|
+
}
|
|
3942
|
+
]
|
|
3943
|
+
})
|
|
3944
|
+
);
|
|
3945
|
+
if (selection === "__custom__") {
|
|
3946
|
+
chosenName = handleCancel(
|
|
3947
|
+
await importPrompts.text({
|
|
3948
|
+
message: `Enter the name of the existing ${modType} module:`,
|
|
3949
|
+
placeholder: `my${registryName}`,
|
|
3950
|
+
validate(val) {
|
|
3951
|
+
const v = val?.trim();
|
|
3952
|
+
if (!v) return "Name cannot be empty";
|
|
3953
|
+
if (v === registryName) {
|
|
3954
|
+
return `Name must be different from '${registryName}' (or select 'Keep existing local version')`;
|
|
3955
|
+
}
|
|
3956
|
+
if (!checkModuleExists(projectRoot, modType, v)) {
|
|
3957
|
+
return `'${v}' does not exist in ${folder}`;
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
})
|
|
3961
|
+
).trim();
|
|
3962
|
+
} else {
|
|
3963
|
+
chosenName = selection;
|
|
3964
|
+
}
|
|
3965
|
+
} else {
|
|
3966
|
+
chosenName = handleCancel(
|
|
3967
|
+
await importPrompts.text({
|
|
3968
|
+
message: `Enter the name of the existing ${modType} module:`,
|
|
3969
|
+
placeholder: `my${registryName}`,
|
|
3970
|
+
validate(val) {
|
|
3971
|
+
const v = val?.trim();
|
|
3972
|
+
if (!v) return "Name cannot be empty";
|
|
3973
|
+
if (v === registryName) {
|
|
3974
|
+
return `Name must be different from '${registryName}' (or select 'Keep existing local version')`;
|
|
3975
|
+
}
|
|
3976
|
+
if (!checkModuleExists(projectRoot, modType, v)) {
|
|
3977
|
+
return `'${v}' does not exist in ${folder}`;
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3980
|
+
})
|
|
3981
|
+
).trim();
|
|
3982
|
+
}
|
|
3983
|
+
chosenName = chosenName.replace(/^@\/(shared|features)\//, "").replace(/^(src\/)?(shared|features)\//, "").replace(/^\/+|\/+$/g, "").trim();
|
|
3984
|
+
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${chosenName}/`);
|
|
3985
|
+
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${chosenName}`);
|
|
3986
|
+
p14.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${chosenName}'`);
|
|
3987
|
+
resolutions.set(key, {
|
|
3988
|
+
type: modType,
|
|
3989
|
+
registryName,
|
|
3990
|
+
targetName: chosenName,
|
|
3991
|
+
action: "reuse",
|
|
3992
|
+
manifest
|
|
3993
|
+
});
|
|
3994
|
+
continue;
|
|
3851
3995
|
}
|
|
3852
|
-
|
|
3996
|
+
if (choice === "rename") {
|
|
3997
|
+
const newName = handleCancel(
|
|
3998
|
+
await importPrompts.text({
|
|
3999
|
+
message: "What should we name the new copy?",
|
|
4000
|
+
placeholder: `${registryName}-copy`,
|
|
4001
|
+
validate(val) {
|
|
4002
|
+
const v = val?.trim();
|
|
4003
|
+
if (!v) return "Name cannot be empty";
|
|
4004
|
+
if (v === registryName) return `Name must be different from '${registryName}'`;
|
|
4005
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(v)) {
|
|
4006
|
+
return "Name can only contain alphanumeric characters, dashes, and underscores";
|
|
4007
|
+
}
|
|
4008
|
+
if (checkModuleExists(projectRoot, modType, v)) {
|
|
4009
|
+
return `'${v}' already exists in ${folder}, please choose another name`;
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
})
|
|
4013
|
+
).trim();
|
|
4014
|
+
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${newName}/`);
|
|
4015
|
+
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${newName}`);
|
|
4016
|
+
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
4017
|
+
const displayDest = path2.relative(projectRoot, path2.join(baseDir, newName));
|
|
4018
|
+
p14.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
4019
|
+
resolutions.set(key, {
|
|
4020
|
+
type: modType,
|
|
4021
|
+
registryName,
|
|
4022
|
+
targetName: newName,
|
|
4023
|
+
action: "alias",
|
|
4024
|
+
manifest
|
|
4025
|
+
});
|
|
4026
|
+
continue;
|
|
4027
|
+
}
|
|
4028
|
+
if (choice === "overwrite") {
|
|
4029
|
+
p14.log.step(`Overwriting '${modType}/${registryName}' with registry version...`);
|
|
4030
|
+
resolutions.set(key, {
|
|
4031
|
+
type: modType,
|
|
4032
|
+
registryName,
|
|
4033
|
+
targetName: registryName,
|
|
4034
|
+
action: "overwrite",
|
|
4035
|
+
manifest
|
|
4036
|
+
});
|
|
4037
|
+
continue;
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4040
|
+
const totalWrittenFiles = [];
|
|
4041
|
+
const requiredNpmPackages = /* @__PURE__ */ new Set();
|
|
4042
|
+
for (const node of nodes.values()) {
|
|
4043
|
+
for (const pkg of node.manifest.dependencies?.npm || []) {
|
|
3853
4044
|
requiredNpmPackages.add(pkg);
|
|
3854
4045
|
}
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
const
|
|
3858
|
-
|
|
4046
|
+
}
|
|
4047
|
+
for (const key of orderedKeys) {
|
|
4048
|
+
const res = resolutions.get(key);
|
|
4049
|
+
if (res.action === "reuse") {
|
|
4050
|
+
continue;
|
|
4051
|
+
}
|
|
4052
|
+
const folder = res.type === "shared" ? "shared" : "features";
|
|
4053
|
+
const targetDir = path2.join(getModuleBaseDir(projectRoot, res.type), res.targetName);
|
|
4054
|
+
const isOverwrite = Boolean(flags.overwrite || res.action === "overwrite");
|
|
4055
|
+
if (key !== rootKey) {
|
|
4056
|
+
p14.log.step(
|
|
4057
|
+
`Importing ${res.type}: ${pc14.cyan(res.targetName)}${res.targetName !== res.registryName ? pc14.dim(` (from ${res.registryName})`) : ""}...`
|
|
4058
|
+
);
|
|
4059
|
+
}
|
|
4060
|
+
if (res.action === "overwrite" && !flags.dryRun) {
|
|
4061
|
+
const existingDir = getExistingModuleDir(projectRoot, res.type, res.targetName);
|
|
4062
|
+
if (existingDir && existingDir !== targetDir) {
|
|
4063
|
+
fs4.rmSync(existingDir, { recursive: true, force: true });
|
|
4064
|
+
}
|
|
4065
|
+
if (fs4.existsSync(targetDir)) {
|
|
4066
|
+
fs4.rmSync(targetDir, { recursive: true, force: true });
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
for (const file of res.manifest.files || []) {
|
|
3859
4070
|
if (file === "registry.json") continue;
|
|
3860
|
-
const remoteFilePath = `${folder}/${registryName}/${file}`;
|
|
4071
|
+
const remoteFilePath = `${folder}/${res.registryName}/${file}`;
|
|
3861
4072
|
const localFilePath = path2.join(targetDir, file);
|
|
3862
4073
|
if (fs4.existsSync(localFilePath) && !isOverwrite) {
|
|
3863
|
-
|
|
4074
|
+
p14.log.warn(
|
|
3864
4075
|
` \u21B3 Skipping existing file: ${pc14.dim(path2.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
|
|
3865
4076
|
);
|
|
3866
4077
|
continue;
|
|
3867
4078
|
}
|
|
3868
4079
|
let content = await fetchRegistryFile(registryBase, remoteFilePath);
|
|
3869
|
-
content = rewriteImports(content,
|
|
4080
|
+
content = rewriteImports(content, globalAliasMap);
|
|
3870
4081
|
if (!flags.dryRun) {
|
|
3871
4082
|
fs4.mkdirSync(path2.dirname(localFilePath), { recursive: true });
|
|
3872
4083
|
fs4.writeFileSync(localFilePath, content, "utf-8");
|
|
3873
4084
|
}
|
|
3874
4085
|
const displayPath = path2.relative(projectRoot, localFilePath);
|
|
3875
4086
|
totalWrittenFiles.push(displayPath);
|
|
3876
|
-
|
|
4087
|
+
p14.log.message(` ${pc14.green("+")} ${displayPath}`);
|
|
3877
4088
|
}
|
|
3878
|
-
importedModules.add(modKey);
|
|
3879
4089
|
}
|
|
3880
|
-
await importModuleRecursive(type, name, name);
|
|
3881
4090
|
const missingNpm = getMissingNpmPackages(projectRoot, Array.from(requiredNpmPackages));
|
|
3882
4091
|
if (missingNpm.length > 0) {
|
|
3883
4092
|
if (flags.skipInstall || flags.dryRun) {
|
|
3884
|
-
|
|
4093
|
+
p14.log.info(`Required npm dependencies to install: ${pc14.cyan(missingNpm.join(", "))}`);
|
|
3885
4094
|
} else {
|
|
3886
4095
|
try {
|
|
3887
4096
|
installNpmPackages(projectRoot, missingNpm);
|
|
3888
4097
|
} catch (err) {
|
|
3889
|
-
|
|
4098
|
+
p14.log.warn(
|
|
4099
|
+
`Could not automatically install packages (${err.message}). Please run: npm install ${missingNpm.join(
|
|
4100
|
+
" "
|
|
4101
|
+
)}`
|
|
4102
|
+
);
|
|
3890
4103
|
}
|
|
3891
4104
|
}
|
|
3892
4105
|
}
|
|
3893
4106
|
if (flags.dryRun) {
|
|
3894
|
-
|
|
4107
|
+
p14.log.info(pc14.yellow("Dry run complete: No files were written to disk."));
|
|
3895
4108
|
} else {
|
|
4109
|
+
const rootRes = resolutions.get(rootKey);
|
|
3896
4110
|
const baseDir = getModuleBaseDir(projectRoot, type);
|
|
3897
|
-
const displayTarget = path2.relative(projectRoot, path2.join(baseDir,
|
|
3898
|
-
|
|
4111
|
+
const displayTarget = path2.relative(projectRoot, path2.join(baseDir, rootRes.targetName));
|
|
4112
|
+
p14.log.success(
|
|
3899
4113
|
pc14.green(
|
|
3900
4114
|
`Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
|
|
3901
4115
|
)
|
|
@@ -3906,9 +4120,9 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
3906
4120
|
// src/cli.ts
|
|
3907
4121
|
function createCli() {
|
|
3908
4122
|
const cli = cac("mason");
|
|
3909
|
-
cli.command("[...args]", "Interactive scaffolding and import wizard").option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing files").option("-y, --yes", "Accept defaults and skip interactive prompts").option("--registry", "Run in registry mode").action(async (args, flags) => {
|
|
4123
|
+
cli.command("[...args]", "Interactive scaffolding and import wizard").option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing files").option("-y, --yes", "Accept defaults and skip interactive prompts").option("--registry", "Run in registry mode").option("--point-to <mapping>", "Point a dependency to an existing local module (e.g. --point-to time:mytime)").option("--alias <mapping>", "Import a dependency fresh under an alias name (e.g. --alias time:time-mason)").action(async (args, flags) => {
|
|
3910
4124
|
if (args && args.length > 0) {
|
|
3911
|
-
|
|
4125
|
+
p14.log.error(
|
|
3912
4126
|
pc14.red(
|
|
3913
4127
|
`Unknown command: "${args.join(" ")}". Use --help to list available commands.`
|
|
3914
4128
|
)
|
|
@@ -3920,7 +4134,7 @@ function createCli() {
|
|
|
3920
4134
|
const mode = getEffectiveMode(projectRoot, flags);
|
|
3921
4135
|
if (mode === "registry") {
|
|
3922
4136
|
const choice2 = handleCancel(
|
|
3923
|
-
await
|
|
4137
|
+
await p14.select({
|
|
3924
4138
|
message: "\u{1F3DB}\uFE0F Mason (Registry Mode): What would you like to scaffold?",
|
|
3925
4139
|
options: [
|
|
3926
4140
|
{
|
|
@@ -3963,7 +4177,7 @@ function createCli() {
|
|
|
3963
4177
|
return;
|
|
3964
4178
|
}
|
|
3965
4179
|
const choice = handleCancel(
|
|
3966
|
-
await
|
|
4180
|
+
await p14.select({
|
|
3967
4181
|
message: "\u{1F9F1} Mason: What would you like to do?",
|
|
3968
4182
|
options: [
|
|
3969
4183
|
{
|
|
@@ -4001,7 +4215,7 @@ function createCli() {
|
|
|
4001
4215
|
cli.command(
|
|
4002
4216
|
"import [type] [name]",
|
|
4003
4217
|
"Import pre-built shared modules or features from the Solid Stack Registry"
|
|
4004
|
-
).option("-o, --overwrite", "Overwrite existing files").option("-d, --dry-run", "Preview changes without writing to disk").option("-y, --yes", "Accept defaults and skip interactive prompts").option("--registry <url>", "Registry base URL or local directory").option("--skip-install", "Skip automatic package manager dependency installation").example("mason import shared hasher").example("mason import feature authn").example("mason import shared").example("mason import feature").action(async (type, name, flags = {}) => {
|
|
4218
|
+
).option("-o, --overwrite", "Overwrite existing files").option("-d, --dry-run", "Preview changes without writing to disk").option("-y, --yes", "Accept defaults and skip interactive prompts").option("--registry <url>", "Registry base URL or local directory").option("--skip-install", "Skip automatic package manager dependency installation").option("--point-to <mapping>", "Point a dependency to an existing local module (e.g. --point-to time:mytime)").option("--alias <mapping>", "Import a dependency fresh under an alias name (e.g. --alias time:time-mason)").example("mason import shared hasher").example("mason import feature authn").example("mason import shared").example("mason import feature").action(async (type, name, flags = {}) => {
|
|
4005
4219
|
try {
|
|
4006
4220
|
await handleImport(type, name, flags);
|
|
4007
4221
|
} catch (err) {
|
|
@@ -4031,7 +4245,7 @@ function createCli() {
|
|
|
4031
4245
|
});
|
|
4032
4246
|
}
|
|
4033
4247
|
cli.help();
|
|
4034
|
-
cli.version("1.0.
|
|
4248
|
+
cli.version("1.0.17");
|
|
4035
4249
|
return cli;
|
|
4036
4250
|
}
|
|
4037
4251
|
function handleCliError(err) {
|
|
@@ -4039,7 +4253,7 @@ function handleCliError(err) {
|
|
|
4039
4253
|
process.exit(0);
|
|
4040
4254
|
}
|
|
4041
4255
|
const message = err instanceof Error ? err.message : String(err);
|
|
4042
|
-
|
|
4256
|
+
p14.log.error(pc14.red(`Error: ${message}`));
|
|
4043
4257
|
process.exit(1);
|
|
4044
4258
|
}
|
|
4045
4259
|
async function runCli(argv = process.argv) {
|