blockend-cli 1.3.0 → 1.3.1
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 +40 -127
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -4,103 +4,11 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
|
-
import path, { join
|
|
7
|
+
import path, { join } from "path";
|
|
8
8
|
import fs from "fs/promises";
|
|
9
|
-
import { existsSync as existsSync4 } from "fs";
|
|
10
|
-
import { intro, outro, select, text, confirm, spinner, isCancel } from "@clack/prompts";
|
|
11
|
-
|
|
12
|
-
// ../detector/dist/index.js
|
|
13
|
-
import { join as join5 } from "path";
|
|
14
|
-
import { readFile } from "fs/promises";
|
|
15
|
-
import { join } from "path";
|
|
16
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
17
9
|
import { existsSync } from "fs";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { join as join3 } from "path";
|
|
21
|
-
import { existsSync as existsSync3 } from "fs";
|
|
22
|
-
import { join as join4 } from "path";
|
|
23
|
-
async function readPackageJson(cwd) {
|
|
24
|
-
const pkgPath = join(cwd, "package.json");
|
|
25
|
-
try {
|
|
26
|
-
const content = await readFile(pkgPath, "utf-8");
|
|
27
|
-
return JSON.parse(content);
|
|
28
|
-
} catch {
|
|
29
|
-
throw new Error(`No package.json found at ${pkgPath}. Run blockend from your project root.`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
async function readTsConfig(cwd) {
|
|
33
|
-
const tsconfigPath = join2(cwd, "tsconfig.json");
|
|
34
|
-
if (!existsSync(tsconfigPath)) return null;
|
|
35
|
-
try {
|
|
36
|
-
const content = await readFile2(tsconfigPath, "utf-8");
|
|
37
|
-
const cleanLines = content.split(/\r?\n/).filter((line) => {
|
|
38
|
-
const trimmed = line.trim();
|
|
39
|
-
return !trimmed.startsWith("//") && !trimmed.startsWith("/*");
|
|
40
|
-
}).join("\n").replace(/,(\s*[}\]])/g, "$1");
|
|
41
|
-
return JSON.parse(cleanLines);
|
|
42
|
-
} catch {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
async function inferSrcDir(cwd) {
|
|
47
|
-
const candidates = ["src", "app", "lib"];
|
|
48
|
-
for (const dir of candidates) {
|
|
49
|
-
if (existsSync2(join3(cwd, dir))) {
|
|
50
|
-
return join3(cwd, dir);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return cwd;
|
|
54
|
-
}
|
|
55
|
-
function detectFramework(deps) {
|
|
56
|
-
if ("fastify" in deps) return "fastify";
|
|
57
|
-
if ("hono" in deps) return "hono";
|
|
58
|
-
if ("express" in deps) return "express";
|
|
59
|
-
if ("next" in deps) return "next";
|
|
60
|
-
return "none";
|
|
61
|
-
}
|
|
62
|
-
function detectRuntime(deps) {
|
|
63
|
-
if ("@types/bun" in deps || "bun-types" in deps) return "bun";
|
|
64
|
-
return "node";
|
|
65
|
-
}
|
|
66
|
-
function detectPackageManager(cwd) {
|
|
67
|
-
if (existsSync3(join4(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
68
|
-
if (existsSync3(join4(cwd, "bun.lockb"))) return "bun";
|
|
69
|
-
if (existsSync3(join4(cwd, "yarn.lock"))) return "yarn";
|
|
70
|
-
return "npm";
|
|
71
|
-
}
|
|
72
|
-
async function detectProject(cwd) {
|
|
73
|
-
const [pkg, tsConfig] = await Promise.all([readPackageJson(cwd), readTsConfig(cwd)]);
|
|
74
|
-
const allDeps = {
|
|
75
|
-
...pkg.dependencies,
|
|
76
|
-
...pkg.devDependencies,
|
|
77
|
-
...pkg.peerDependencies
|
|
78
|
-
};
|
|
79
|
-
const srcDir = await inferSrcDir(cwd);
|
|
80
|
-
return {
|
|
81
|
-
root: cwd,
|
|
82
|
-
language: tsConfig !== null ? "typescript" : "javascript",
|
|
83
|
-
runtime: detectRuntime(allDeps),
|
|
84
|
-
framework: detectFramework(allDeps),
|
|
85
|
-
packageManager: detectPackageManager(cwd),
|
|
86
|
-
hasRedis: "ioredis" in allDeps || "redis" in allDeps,
|
|
87
|
-
hasPrisma: "@prisma/client" in allDeps,
|
|
88
|
-
hasDrizzle: "drizzle-orm" in allDeps,
|
|
89
|
-
aliasMap: tsConfig?.compilerOptions?.paths ? flattenTsPaths(tsConfig.compilerOptions.paths) : {},
|
|
90
|
-
srcDir,
|
|
91
|
-
// Default blocks directory: srcDir/lib/blocks
|
|
92
|
-
blocksDir: join5(srcDir, "lib", "blocks")
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function flattenTsPaths(paths) {
|
|
96
|
-
const result = {};
|
|
97
|
-
for (const [alias, targets] of Object.entries(paths)) {
|
|
98
|
-
if (targets[0]) {
|
|
99
|
-
result[alias.replace("/*", "/")] = targets[0].replace("/*", "/");
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return result;
|
|
103
|
-
}
|
|
10
|
+
import { intro, outro, select, text, confirm, spinner, isCancel } from "@clack/prompts";
|
|
11
|
+
import { detectProject } from "@blockend/detector";
|
|
104
12
|
|
|
105
13
|
// src/ui/theme.ts
|
|
106
14
|
import pc from "picocolors";
|
|
@@ -140,7 +48,7 @@ var format = {
|
|
|
140
48
|
// src/commands/init.ts
|
|
141
49
|
async function initCommand() {
|
|
142
50
|
const cwd = process.cwd();
|
|
143
|
-
const configPath =
|
|
51
|
+
const configPath = join(cwd, "blockend.json");
|
|
144
52
|
console.log(`
|
|
145
53
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
146
54
|
\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
|
|
@@ -150,7 +58,7 @@ async function initCommand() {
|
|
|
150
58
|
\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D
|
|
151
59
|
`);
|
|
152
60
|
intro(format.title("Blockend \xB7 Intelligent Backend Blocks Setup"));
|
|
153
|
-
if (
|
|
61
|
+
if (existsSync(configPath)) {
|
|
154
62
|
const action = await select({
|
|
155
63
|
message: "blockend.json already exists. What do you want to do?",
|
|
156
64
|
options: [
|
|
@@ -212,7 +120,7 @@ async function initCommand() {
|
|
|
212
120
|
const aliasPrefix = availableAliases.find((a) => blockAlias.startsWith(a)) || "";
|
|
213
121
|
const physicalPrefix = aliasPrefix ? context.aliasMap[aliasPrefix] : "./";
|
|
214
122
|
const resolvedSubDir = blockAlias.replace(aliasPrefix, "");
|
|
215
|
-
const assumedPhysicalDir =
|
|
123
|
+
const assumedPhysicalDir = join(physicalPrefix, resolvedSubDir);
|
|
216
124
|
const relativeBlocksPath = path.relative(cwd, path.resolve(cwd, assumedPhysicalDir));
|
|
217
125
|
const normalizedPath = relativeBlocksPath.replace(/\\/g, "/");
|
|
218
126
|
const finalPath = normalizedPath.startsWith(".") ? normalizedPath : `./${normalizedPath}`;
|
|
@@ -250,7 +158,7 @@ async function initCommand() {
|
|
|
250
158
|
}
|
|
251
159
|
|
|
252
160
|
// src/commands/add.ts
|
|
253
|
-
import path2, { join as
|
|
161
|
+
import path2, { join as join2, dirname } from "path";
|
|
254
162
|
import fs2 from "fs/promises";
|
|
255
163
|
import { exec } from "child_process";
|
|
256
164
|
import { intro as intro2, outro as outro2, select as select2, spinner as spinner2, confirm as confirm2, isCancel as isCancel2 } from "@clack/prompts";
|
|
@@ -269,7 +177,7 @@ function handleCancel(value) {
|
|
|
269
177
|
async function findUp(filename, startDir) {
|
|
270
178
|
let dir = startDir;
|
|
271
179
|
while (true) {
|
|
272
|
-
const checkPath =
|
|
180
|
+
const checkPath = join2(dir, filename);
|
|
273
181
|
try {
|
|
274
182
|
await fs2.access(checkPath);
|
|
275
183
|
return checkPath;
|
|
@@ -404,7 +312,7 @@ async function addCommand(blockName) {
|
|
|
404
312
|
});
|
|
405
313
|
handleCancel(shouldInstallPrompt);
|
|
406
314
|
if (shouldInstallPrompt) {
|
|
407
|
-
const packageManager = await fs2.access(
|
|
315
|
+
const packageManager = await fs2.access(join2(packageJsonDir, "pnpm-lock.yaml")).then(() => "pnpm").catch(() => "npm");
|
|
408
316
|
s.start(`Preparing native workspace via ${packageManager}...`);
|
|
409
317
|
try {
|
|
410
318
|
const installCmd = packageManager === "pnpm" ? `pnpm add ${missingDeps.join(" ")}` : `npm install ${missingDeps.join(" ")}`;
|
|
@@ -433,29 +341,36 @@ async function addCommand(blockName) {
|
|
|
433
341
|
}
|
|
434
342
|
s.start(`Downloading clean production template block [${targetBlock}]...`);
|
|
435
343
|
try {
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
344
|
+
const remoteFilesToDownload = [];
|
|
345
|
+
if (envConfig.core) {
|
|
346
|
+
remoteFilesToDownload.push(envConfig.core);
|
|
347
|
+
}
|
|
348
|
+
if (variantMeta && Array.isArray(variantMeta.files)) {
|
|
349
|
+
remoteFilesToDownload.push(...variantMeta.files);
|
|
441
350
|
}
|
|
442
|
-
const coreCodeTemplate = await coreFetchResponse.text();
|
|
443
351
|
let physicalPath = config.paths.blocks;
|
|
444
352
|
if (physicalPath.startsWith("@")) {
|
|
445
353
|
physicalPath = "./src/blocks";
|
|
446
354
|
}
|
|
447
355
|
let targetFolder = path2.resolve(rootDir, physicalPath);
|
|
448
356
|
if (variantMeta && selectedVariant) {
|
|
449
|
-
targetFolder =
|
|
357
|
+
targetFolder = join2(targetFolder, targetBlock);
|
|
450
358
|
}
|
|
451
|
-
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
359
|
+
let fileExistsConflict = false;
|
|
360
|
+
for (const remoteFile of remoteFilesToDownload) {
|
|
361
|
+
const parsedFilename = path2.basename(remoteFile, ".txt");
|
|
362
|
+
const checkFileLocation = join2(targetFolder, parsedFilename);
|
|
363
|
+
try {
|
|
364
|
+
await fs2.access(checkFileLocation);
|
|
365
|
+
fileExistsConflict = true;
|
|
366
|
+
break;
|
|
367
|
+
} catch {
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (fileExistsConflict) {
|
|
456
371
|
s.stop();
|
|
457
372
|
const overwritePrompt = await confirm2({
|
|
458
|
-
message: `\u26A0
|
|
373
|
+
message: `\u26A0 Components in [${targetBlock}] already exist. Overwrite custom revisions?`,
|
|
459
374
|
initialValue: false
|
|
460
375
|
});
|
|
461
376
|
handleCancel(overwritePrompt);
|
|
@@ -463,21 +378,19 @@ async function addCommand(blockName) {
|
|
|
463
378
|
outro2(pc2.yellow("\u2139 Operation aborted safely. Local code modifications preserved."));
|
|
464
379
|
return;
|
|
465
380
|
}
|
|
466
|
-
s.start(`Re-downloading template block [${targetBlock}]...`);
|
|
467
|
-
} catch {
|
|
381
|
+
s.start(`Re-downloading template block files [${targetBlock}]...`);
|
|
468
382
|
}
|
|
469
|
-
await fs2.
|
|
470
|
-
|
|
471
|
-
const
|
|
472
|
-
const
|
|
473
|
-
if (!
|
|
474
|
-
throw new Error(
|
|
475
|
-
`Failed downloading storage variant file: ${variantFetchResponse.statusText}`
|
|
476
|
-
);
|
|
383
|
+
await fs2.mkdir(targetFolder, { recursive: true });
|
|
384
|
+
for (const remoteFilePath of remoteFilesToDownload) {
|
|
385
|
+
const fileUrl = `${RAW_CDN_BASE}/${remoteFilePath}`;
|
|
386
|
+
const response = await fetch(fileUrl);
|
|
387
|
+
if (!response.ok) {
|
|
388
|
+
throw new Error(`Failed downloading file: ${remoteFilePath} (${response.statusText})`);
|
|
477
389
|
}
|
|
478
|
-
const
|
|
479
|
-
const
|
|
480
|
-
|
|
390
|
+
const rawCodeTemplate = await response.text();
|
|
391
|
+
const targetFilename = path2.basename(remoteFilePath, ".txt");
|
|
392
|
+
const localWriteLocation = join2(targetFolder, targetFilename);
|
|
393
|
+
await fs2.writeFile(localWriteLocation, rawCodeTemplate, "utf-8");
|
|
481
394
|
}
|
|
482
395
|
const cleanDisplayPath = variantMeta && selectedVariant ? `${physicalPath.replace(/\\/g, "/")}/${targetBlock}` : physicalPath.replace(/\\/g, "/");
|
|
483
396
|
s.stop(pc2.green("\u2714 Component isolation structures written smoothly."));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blockend-cli",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Intelligent, modular backend blocks right inside your terminal",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -26,11 +26,13 @@
|
|
|
26
26
|
"license": "ISC",
|
|
27
27
|
"packageManager": "pnpm@10.33.2",
|
|
28
28
|
"dependencies": {
|
|
29
|
+
"@blockend/detector": "workspace:*",
|
|
29
30
|
"@clack/prompts": "^1.5.1",
|
|
30
31
|
"commander": "^15.0.0",
|
|
31
32
|
"picocolors": "^1.1.1"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
|
-
"@types/node": "^25.9.3"
|
|
35
|
+
"@types/node": "^25.9.3",
|
|
36
|
+
"tsup": "^8.5.1"
|
|
35
37
|
}
|
|
36
38
|
}
|