blockend-cli 1.0.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +90 -28
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -264,14 +264,21 @@ async function initCommand() {
|
|
|
264
264
|
// src/commands/add.ts
|
|
265
265
|
import path2, { join as join7 } from "path";
|
|
266
266
|
import fs2 from "fs/promises";
|
|
267
|
-
import {
|
|
268
|
-
import { intro as intro2, outro as outro2, select as select2, spinner as spinner2, confirm as confirm2 } from "@clack/prompts";
|
|
267
|
+
import { exec } from "child_process";
|
|
268
|
+
import { intro as intro2, outro as outro2, select as select2, spinner as spinner2, confirm as confirm2, isCancel as isCancel2 } from "@clack/prompts";
|
|
269
269
|
import pc2 from "picocolors";
|
|
270
|
-
var REPO_OWNER = "
|
|
270
|
+
var REPO_OWNER = "codewithnuh";
|
|
271
271
|
var REPO_NAME = "blockend";
|
|
272
272
|
var BRANCH = "master";
|
|
273
|
+
var LOCAL_DEV_URL = "http://localhost:5000";
|
|
273
274
|
var RAW_CDN_BASE = `https://raw.githubusercontent.com/${REPO_OWNER}/${REPO_NAME}/${BRANCH}`;
|
|
274
275
|
var MANIFEST_URL = `${RAW_CDN_BASE}/registry/index.json`;
|
|
276
|
+
function handleCancel(value) {
|
|
277
|
+
if (isCancel2(value)) {
|
|
278
|
+
outro2(pc2.yellow("\u26A0 Operation cancelled. Exiting Blockend CLI cleanly."));
|
|
279
|
+
process.exit(0);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
275
282
|
async function addCommand(blockName) {
|
|
276
283
|
intro2(pc2.bgBlack(pc2.magenta(" Blockend Component Ingestion ")));
|
|
277
284
|
const cwd = process.cwd();
|
|
@@ -300,16 +307,18 @@ async function addCommand(blockName) {
|
|
|
300
307
|
if (!targetBlock) {
|
|
301
308
|
const availableOptions = Object.keys(registry).map((key) => ({
|
|
302
309
|
value: key,
|
|
303
|
-
label: `${key} -
|
|
310
|
+
label: `${key} - ${pc2.dim(registry[key].description)}`
|
|
304
311
|
}));
|
|
305
312
|
if (availableOptions.length === 0) {
|
|
306
313
|
outro2(pc2.yellow("\u26A0 The remote block registry is currently empty."));
|
|
307
314
|
return;
|
|
308
315
|
}
|
|
309
|
-
|
|
316
|
+
const selectBlockPrompt = await select2({
|
|
310
317
|
message: "Which backend block would you like to inject?",
|
|
311
318
|
options: availableOptions
|
|
312
319
|
});
|
|
320
|
+
handleCancel(selectBlockPrompt);
|
|
321
|
+
targetBlock = selectBlockPrompt;
|
|
313
322
|
}
|
|
314
323
|
const blockMeta = registry[targetBlock];
|
|
315
324
|
if (!blockMeta) {
|
|
@@ -320,15 +329,34 @@ async function addCommand(blockName) {
|
|
|
320
329
|
const envConfig = blockMeta.environments[envKey];
|
|
321
330
|
if (!envConfig) {
|
|
322
331
|
outro2(
|
|
323
|
-
pc2.red(
|
|
324
|
-
`\u2716 The block "${targetBlock}" does not support your environment layout: ${String(config.environment)}`
|
|
325
|
-
)
|
|
332
|
+
pc2.red(`\u2716 The block "${targetBlock}" does not support your environment layout: ${envKey}`)
|
|
326
333
|
);
|
|
327
334
|
return;
|
|
328
335
|
}
|
|
336
|
+
let selectedVariant;
|
|
337
|
+
let variantMeta = void 0;
|
|
338
|
+
if (envConfig.variants && Object.keys(envConfig.variants).length > 0) {
|
|
339
|
+
const variantKeys = Object.keys(envConfig.variants);
|
|
340
|
+
if (variantKeys.includes("redis") && config.redisEnabled) {
|
|
341
|
+
selectedVariant = "redis";
|
|
342
|
+
} else {
|
|
343
|
+
const selectVariantPrompt = await select2({
|
|
344
|
+
message: "Which architectural storage variant do you want to back this block?",
|
|
345
|
+
options: variantKeys.map((vKey) => ({
|
|
346
|
+
value: vKey,
|
|
347
|
+
label: vKey.toUpperCase()
|
|
348
|
+
}))
|
|
349
|
+
});
|
|
350
|
+
handleCancel(selectVariantPrompt);
|
|
351
|
+
selectedVariant = selectVariantPrompt;
|
|
352
|
+
}
|
|
353
|
+
variantMeta = envConfig.variants[selectedVariant];
|
|
354
|
+
}
|
|
329
355
|
let packageJson;
|
|
330
356
|
try {
|
|
331
|
-
packageJson = JSON.parse(
|
|
357
|
+
packageJson = JSON.parse(
|
|
358
|
+
await fs2.readFile(join7(cwd, "package.json"), "utf-8")
|
|
359
|
+
);
|
|
332
360
|
} catch {
|
|
333
361
|
outro2(pc2.red("\u2716 Could not locate package.json in your current workspace directory."));
|
|
334
362
|
return;
|
|
@@ -337,23 +365,41 @@ async function addCommand(blockName) {
|
|
|
337
365
|
...packageJson.dependencies ?? {},
|
|
338
366
|
...packageJson.devDependencies ?? {}
|
|
339
367
|
};
|
|
340
|
-
const
|
|
368
|
+
const allRequiredDeps = [...envConfig.dependencies ?? [], ...variantMeta?.dependencies ?? []];
|
|
369
|
+
const missingDeps = allRequiredDeps.filter((dep) => !(dep in installedDeps));
|
|
341
370
|
if (missingDeps.length > 0) {
|
|
342
371
|
console.log(
|
|
343
372
|
pc2.yellow(`
|
|
344
373
|
\u26A0\uFE0F Missing required infrastructure packages: ${missingDeps.join(", ")}`)
|
|
345
374
|
);
|
|
346
|
-
const
|
|
375
|
+
const shouldInstallPrompt = await confirm2({
|
|
347
376
|
message: "Would you like the CLI to automatically install these dependencies?",
|
|
348
377
|
initialValue: true
|
|
349
378
|
});
|
|
350
|
-
|
|
351
|
-
|
|
379
|
+
handleCancel(shouldInstallPrompt);
|
|
380
|
+
if (shouldInstallPrompt) {
|
|
381
|
+
const packageManager = await fs2.access(join7(cwd, "pnpm-lock.yaml")).then(() => "pnpm").catch(() => "npm");
|
|
382
|
+
s.start(`Preparing native workspace via ${packageManager}...`);
|
|
352
383
|
try {
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
384
|
+
const installCmd = packageManager === "pnpm" ? `pnpm add ${missingDeps.join(" ")}` : `npm install ${missingDeps.join(" ")}`;
|
|
385
|
+
s.stop(pc2.cyan(`Executing: ${installCmd}
|
|
386
|
+
`));
|
|
387
|
+
await new Promise((resolve, reject) => {
|
|
388
|
+
const child = exec(installCmd, { cwd });
|
|
389
|
+
child.stdout?.on("data", (data) => {
|
|
390
|
+
process.stdout.write(pc2.dim(data));
|
|
391
|
+
});
|
|
392
|
+
child.stderr?.on("data", (data) => {
|
|
393
|
+
process.stdout.write(pc2.dim(pc2.red(data)));
|
|
394
|
+
});
|
|
395
|
+
child.on("close", (code) => {
|
|
396
|
+
if (code === 0) resolve();
|
|
397
|
+
else reject(new Error(`Exit code ${code}`));
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
console.log("");
|
|
401
|
+
s.start(pc2.green("\u2714 Dependencies installed cleanly. Continuing components build..."));
|
|
402
|
+
s.stop();
|
|
357
403
|
} catch {
|
|
358
404
|
s.stop(pc2.red("\u2716 Automated dependency installation failed. Please run setup manually."));
|
|
359
405
|
}
|
|
@@ -361,26 +407,42 @@ async function addCommand(blockName) {
|
|
|
361
407
|
}
|
|
362
408
|
s.start(`Downloading clean production template block [${targetBlock}]...`);
|
|
363
409
|
try {
|
|
364
|
-
const
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
410
|
+
const BASE_URL = MANIFEST_URL.includes("localhost") ? LOCAL_DEV_URL : RAW_CDN_BASE;
|
|
411
|
+
const coreFileUrl = `${BASE_URL}/${envConfig.core}`;
|
|
412
|
+
const coreFetchResponse = await fetch(coreFileUrl);
|
|
413
|
+
if (!coreFetchResponse.ok) {
|
|
414
|
+
throw new Error(`Failed downloading core component file: ${coreFetchResponse.statusText}`);
|
|
368
415
|
}
|
|
369
|
-
const
|
|
416
|
+
const coreCodeTemplate = await coreFetchResponse.text();
|
|
370
417
|
let physicalPath = config.paths.blocks;
|
|
371
418
|
if (physicalPath.startsWith("@")) {
|
|
372
419
|
physicalPath = "./src/blocks";
|
|
373
420
|
}
|
|
374
|
-
|
|
421
|
+
let targetFolder = path2.resolve(cwd, physicalPath);
|
|
422
|
+
if (variantMeta && selectedVariant) {
|
|
423
|
+
targetFolder = join7(targetFolder, targetBlock);
|
|
424
|
+
}
|
|
375
425
|
await fs2.mkdir(targetFolder, { recursive: true });
|
|
376
426
|
const fileExtension = config.language === "typescript" ? "ts" : "js";
|
|
377
|
-
const
|
|
378
|
-
await fs2.writeFile(join7(targetFolder,
|
|
379
|
-
|
|
380
|
-
|
|
427
|
+
const coreOutputFilename = `${targetBlock}.${fileExtension}`;
|
|
428
|
+
await fs2.writeFile(join7(targetFolder, coreOutputFilename), coreCodeTemplate, "utf-8");
|
|
429
|
+
if (variantMeta && selectedVariant) {
|
|
430
|
+
const variantFileUrl = `${BASE_URL}/${variantMeta.path}`;
|
|
431
|
+
const variantFetchResponse = await fetch(variantFileUrl);
|
|
432
|
+
if (!variantFetchResponse.ok) {
|
|
433
|
+
throw new Error(
|
|
434
|
+
`Failed downloading storage variant file: ${variantFetchResponse.statusText}`
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
const variantCodeTemplate = await variantFetchResponse.text();
|
|
438
|
+
const variantOutputFilename = `store-${selectedVariant}.${fileExtension}`;
|
|
439
|
+
await fs2.writeFile(join7(targetFolder, variantOutputFilename), variantCodeTemplate, "utf-8");
|
|
440
|
+
}
|
|
441
|
+
const cleanDisplayPath = variantMeta && selectedVariant ? `${physicalPath.replace(/\\/g, "/")}/${targetBlock}` : physicalPath.replace(/\\/g, "/");
|
|
442
|
+
s.stop(pc2.green("\u2714 Component isolation structures written smoothly."));
|
|
381
443
|
outro2(
|
|
382
444
|
pc2.cyan(
|
|
383
|
-
`\u2728 Source blocks written to ${cleanDisplayPath}
|
|
445
|
+
`\u2728 Source blocks written to ${cleanDisplayPath}/ layout. Code ownership transferred!`
|
|
384
446
|
)
|
|
385
447
|
);
|
|
386
448
|
} catch (error) {
|