blockend-cli 1.4.2 → 1.4.4
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 +266 -312
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,18 +7,34 @@ import { defineCommand, runMain } from "citty";
|
|
|
7
7
|
import path, { join, dirname } from "path";
|
|
8
8
|
import fs from "fs/promises";
|
|
9
9
|
import { exec } from "child_process";
|
|
10
|
-
import { intro, outro, select, spinner, confirm, isCancel } from "@clack/prompts";
|
|
10
|
+
import { intro, outro, select, spinner, confirm, isCancel, log } from "@clack/prompts";
|
|
11
11
|
import pc from "picocolors";
|
|
12
12
|
var REPO_OWNER = "codewithnuh";
|
|
13
13
|
var REPO_NAME = "blockend";
|
|
14
14
|
var BRANCH = "master";
|
|
15
15
|
var RAW_CDN_BASE = `https://raw.githubusercontent.com/${REPO_OWNER}/${REPO_NAME}/${BRANCH}`;
|
|
16
16
|
var MANIFEST_URL = `${RAW_CDN_BASE}/registry/index.json`;
|
|
17
|
+
function rewriteFileImports(fileContent, writtenFilePath, blocksRoot, aliasBase) {
|
|
18
|
+
if (!aliasBase || aliasBase.startsWith(".")) {
|
|
19
|
+
return fileContent;
|
|
20
|
+
}
|
|
21
|
+
const importRegex = /((?:from|import)\s*)(['"`])(\.\.?\/[^'"` \n\r]+)(['"`])/g;
|
|
22
|
+
const fileDir = dirname(writtenFilePath);
|
|
23
|
+
return fileContent.replace(importRegex, (match, keyword, openQuote, relativePath, closeQuote) => {
|
|
24
|
+
const absoluteTarget = path.resolve(fileDir, relativePath);
|
|
25
|
+
const fromBlocksRoot = path.relative(blocksRoot, absoluteTarget).replace(/\\/g, "/");
|
|
26
|
+
if (fromBlocksRoot.startsWith("..")) {
|
|
27
|
+
return match;
|
|
28
|
+
}
|
|
29
|
+
const aliased = `${aliasBase}/${fromBlocksRoot}`.replace(/\/+/g, "/");
|
|
30
|
+
return `${keyword}${openQuote}${aliased}${closeQuote}`;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
17
33
|
function outputError(json, message) {
|
|
18
34
|
if (json) {
|
|
19
35
|
process.stdout.write(JSON.stringify({ success: false, error: message }) + "\n");
|
|
20
36
|
} else {
|
|
21
|
-
|
|
37
|
+
log.error(message);
|
|
22
38
|
}
|
|
23
39
|
}
|
|
24
40
|
function outputResult(json, result) {
|
|
@@ -26,7 +42,7 @@ function outputResult(json, result) {
|
|
|
26
42
|
process.stdout.write(JSON.stringify(result) + "\n");
|
|
27
43
|
} else {
|
|
28
44
|
if (result.success) {
|
|
29
|
-
outro(pc.
|
|
45
|
+
outro(pc.green(`\u2728 ${result.message}`));
|
|
30
46
|
} else {
|
|
31
47
|
outro(pc.yellow(`\u2139 ${result.message}`));
|
|
32
48
|
}
|
|
@@ -34,17 +50,17 @@ function outputResult(json, result) {
|
|
|
34
50
|
}
|
|
35
51
|
function handleCancel(value) {
|
|
36
52
|
if (isCancel(value)) {
|
|
37
|
-
outro(pc.
|
|
53
|
+
outro(pc.dim("Operation cancelled."));
|
|
38
54
|
process.exit(0);
|
|
39
55
|
}
|
|
40
56
|
}
|
|
41
57
|
async function findUp(filename, startDir) {
|
|
42
58
|
let dir = startDir;
|
|
43
59
|
while (true) {
|
|
44
|
-
const
|
|
60
|
+
const candidate = join(dir, filename);
|
|
45
61
|
try {
|
|
46
|
-
await fs.access(
|
|
47
|
-
return
|
|
62
|
+
await fs.access(candidate);
|
|
63
|
+
return candidate;
|
|
48
64
|
} catch {
|
|
49
65
|
const parent = dirname(dir);
|
|
50
66
|
if (parent === dir) break;
|
|
@@ -56,7 +72,8 @@ async function findUp(filename, startDir) {
|
|
|
56
72
|
async function addCommand(blockName, options = {}) {
|
|
57
73
|
const { yes = false, json = false } = options;
|
|
58
74
|
if (!json) {
|
|
59
|
-
|
|
75
|
+
console.log("");
|
|
76
|
+
intro(`${pc.bgCyan(pc.black(" blockend "))} ${pc.dim("add")}`);
|
|
60
77
|
}
|
|
61
78
|
const cwd = process.cwd();
|
|
62
79
|
const configPath = await findUp("blockend.json", cwd);
|
|
@@ -67,296 +84,242 @@ async function addCommand(blockName, options = {}) {
|
|
|
67
84
|
const rootDir = dirname(configPath);
|
|
68
85
|
let config;
|
|
69
86
|
try {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (!json) console.error(pc.dim(String(error)));
|
|
87
|
+
config = JSON.parse(await fs.readFile(configPath, "utf-8"));
|
|
88
|
+
} catch (err) {
|
|
89
|
+
outputError(json, "Failed to parse blockend.json.");
|
|
90
|
+
if (!json) log.error(pc.dim(String(err)));
|
|
75
91
|
return;
|
|
76
92
|
}
|
|
77
93
|
if (config.language !== "typescript") {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
json,
|
|
81
|
-
"Blockend forces modern architectural standards. Registry exclusively supports TypeScript."
|
|
82
|
-
);
|
|
83
|
-
} else {
|
|
84
|
-
outro(
|
|
85
|
-
pc.yellow(
|
|
86
|
-
`
|
|
87
|
-
\u2139 Blockend forces modern architectural standards. Registry exclusively supports TypeScript.`
|
|
88
|
-
)
|
|
89
|
-
);
|
|
90
|
-
}
|
|
94
|
+
outputError(json, "Blockend currently only supports TypeScript projects.");
|
|
95
|
+
if (!json) outro(pc.dim("Exiting."));
|
|
91
96
|
return;
|
|
92
97
|
}
|
|
98
|
+
const physicalBlocksPath = config.paths.blocks;
|
|
99
|
+
const blocksRootAbsolute = path.resolve(rootDir, physicalBlocksPath);
|
|
100
|
+
const aliasBase = config.aliases.blocks;
|
|
101
|
+
const envKey = config.environment;
|
|
93
102
|
const s = spinner();
|
|
94
|
-
if (!json)
|
|
95
|
-
s.start("Connecting to remote Blockend Registry manifest...");
|
|
96
|
-
}
|
|
103
|
+
if (!json) s.start("Fetching registry...");
|
|
97
104
|
let registry;
|
|
98
105
|
try {
|
|
99
|
-
const
|
|
100
|
-
if (!
|
|
101
|
-
registry = await
|
|
102
|
-
if (!json) s.stop(
|
|
103
|
-
} catch (
|
|
106
|
+
const res = await fetch(MANIFEST_URL);
|
|
107
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
108
|
+
registry = await res.json();
|
|
109
|
+
if (!json) s.stop("Registry synced.");
|
|
110
|
+
} catch (err) {
|
|
104
111
|
if (!json) {
|
|
105
|
-
s.stop(
|
|
106
|
-
|
|
112
|
+
s.stop("Failed to fetch registry.");
|
|
113
|
+
log.error(pc.dim(String(err)));
|
|
107
114
|
} else {
|
|
108
|
-
outputError(json, "Failed to fetch the component registry from GitHub
|
|
115
|
+
outputError(json, "Failed to fetch the component registry from GitHub.");
|
|
109
116
|
}
|
|
110
117
|
return;
|
|
111
118
|
}
|
|
112
|
-
|
|
113
|
-
if (
|
|
114
|
-
|
|
115
|
-
} else {
|
|
116
|
-
blockMap = registry;
|
|
117
|
-
}
|
|
118
|
-
const envKey = String(config.environment);
|
|
119
|
-
let targetBlock = blockName;
|
|
120
|
-
if (yes && !targetBlock) {
|
|
121
|
-
outputError(
|
|
122
|
-
json,
|
|
123
|
-
"Block name required when using --yes flag. Usage: blockend add <block> --yes"
|
|
124
|
-
);
|
|
119
|
+
const blockMap = registry.blocks && typeof registry.blocks === "object" && !Array.isArray(registry.blocks) ? registry.blocks : registry;
|
|
120
|
+
if (yes && !blockName) {
|
|
121
|
+
outputError(json, "Block name required with --yes. Usage: blockend add <block> --yes");
|
|
125
122
|
return;
|
|
126
123
|
}
|
|
124
|
+
let targetBlock = blockName;
|
|
127
125
|
if (!targetBlock) {
|
|
128
|
-
const
|
|
126
|
+
const available = Object.entries(blockMap).filter(([, block]) => {
|
|
129
127
|
if (!block) return false;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}));
|
|
137
|
-
if (availableOptions.length === 0) {
|
|
138
|
-
if (json) {
|
|
139
|
-
outputError(
|
|
140
|
-
json,
|
|
141
|
-
`No backend blocks are currently available for your framework layer: [${envKey}].`
|
|
142
|
-
);
|
|
143
|
-
} else {
|
|
144
|
-
outro(
|
|
145
|
-
pc.yellow(
|
|
146
|
-
`\u26A0 No backend blocks are currently available for your framework layer: [${envKey}].`
|
|
147
|
-
)
|
|
148
|
-
);
|
|
149
|
-
}
|
|
128
|
+
return block.adapters?.[envKey] !== void 0 || block.environments?.[envKey] !== void 0;
|
|
129
|
+
});
|
|
130
|
+
if (available.length === 0) {
|
|
131
|
+
const msg = `No blocks available for environment: ${envKey}`;
|
|
132
|
+
outputError(json, msg);
|
|
133
|
+
if (!json) outro(pc.dim("Exiting."));
|
|
150
134
|
return;
|
|
151
135
|
}
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
136
|
+
const maxKeyLen = Math.max(...available.map(([k]) => k.length), 0);
|
|
137
|
+
const selectPrompt = await select({
|
|
138
|
+
message: "Which block would you like to add?",
|
|
139
|
+
options: available.map(([key, block], i) => ({
|
|
140
|
+
value: key,
|
|
141
|
+
label: `${pc.dim(`${i + 1}.`)} ${pc.bold(pc.cyan(key.padEnd(maxKeyLen + 4)))}${pc.dim(
|
|
142
|
+
block.description.length > 60 ? `${block.description.slice(0, 60)}...` : block.description
|
|
143
|
+
)}`
|
|
144
|
+
}))
|
|
155
145
|
});
|
|
156
|
-
handleCancel(
|
|
157
|
-
targetBlock =
|
|
146
|
+
handleCancel(selectPrompt);
|
|
147
|
+
targetBlock = selectPrompt;
|
|
158
148
|
}
|
|
159
149
|
const blockMeta = blockMap[targetBlock];
|
|
160
150
|
if (!blockMeta) {
|
|
161
|
-
outputError(json, `Block "${targetBlock}" does not exist in the
|
|
151
|
+
outputError(json, `Block "${targetBlock}" does not exist in the registry.`);
|
|
162
152
|
return;
|
|
163
153
|
}
|
|
164
154
|
const adapterContext = blockMeta.adapters?.[envKey] ?? blockMeta.environments?.[envKey];
|
|
165
155
|
if (!adapterContext) {
|
|
166
|
-
outputError(
|
|
167
|
-
json,
|
|
168
|
-
`The block "${targetBlock}" does not support your environment layout: ${envKey}`
|
|
169
|
-
);
|
|
156
|
+
outputError(json, `Block "${targetBlock}" does not support environment: ${envKey}`);
|
|
170
157
|
return;
|
|
171
158
|
}
|
|
172
|
-
|
|
173
|
-
const variantKeys = Object.keys(adapterContext.variants || {});
|
|
159
|
+
const variantKeys = Object.keys(adapterContext.variants ?? {});
|
|
174
160
|
if (variantKeys.length === 0) {
|
|
175
|
-
outputError(
|
|
176
|
-
json,
|
|
177
|
-
`No architecture storage layout variants found for block framework: ${envKey}`
|
|
178
|
-
);
|
|
161
|
+
outputError(json, `No variants found for block "${targetBlock}" in environment "${envKey}"`);
|
|
179
162
|
return;
|
|
180
163
|
}
|
|
164
|
+
let selectedVariant;
|
|
181
165
|
if (yes) {
|
|
182
166
|
selectedVariant = variantKeys.includes("memory") ? "memory" : variantKeys[0];
|
|
183
|
-
} else if (variantKeys.includes("redis") && config.redisEnabled) {
|
|
167
|
+
} else if (variantKeys.includes("redis") && (config.redisEnabled || config.includeRedis)) {
|
|
184
168
|
selectedVariant = "redis";
|
|
185
169
|
} else if (variantKeys.length === 1) {
|
|
186
170
|
selectedVariant = variantKeys[0];
|
|
187
171
|
} else {
|
|
188
|
-
const
|
|
189
|
-
message: "
|
|
190
|
-
options: variantKeys.map((
|
|
191
|
-
value: vKey,
|
|
192
|
-
label: vKey.toUpperCase()
|
|
193
|
-
}))
|
|
172
|
+
const variantPrompt = await select({
|
|
173
|
+
message: "Select a storage variant:",
|
|
174
|
+
options: variantKeys.map((v) => ({ value: v, label: v.toUpperCase() }))
|
|
194
175
|
});
|
|
195
|
-
handleCancel(
|
|
196
|
-
selectedVariant =
|
|
176
|
+
handleCancel(variantPrompt);
|
|
177
|
+
selectedVariant = variantPrompt;
|
|
197
178
|
}
|
|
198
179
|
const variantMeta = adapterContext.variants[selectedVariant];
|
|
199
|
-
|
|
200
|
-
if (physicalPath.startsWith("@")) {
|
|
201
|
-
physicalPath = "./src/blocks";
|
|
202
|
-
}
|
|
203
|
-
const targetFolder = path.resolve(rootDir, physicalPath, targetBlock);
|
|
180
|
+
const targetFolder = path.resolve(blocksRootAbsolute, targetBlock);
|
|
204
181
|
const packageJsonPath = await findUp("package.json", rootDir);
|
|
205
182
|
if (!packageJsonPath) {
|
|
206
|
-
outputError(json, "Could not locate package.json
|
|
183
|
+
outputError(json, "Could not locate package.json.");
|
|
207
184
|
return;
|
|
208
185
|
}
|
|
209
186
|
const packageJsonDir = dirname(packageJsonPath);
|
|
210
187
|
let packageJson;
|
|
211
188
|
try {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (!json) console.error(pc.dim(String(error)));
|
|
189
|
+
packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
|
|
190
|
+
} catch (err) {
|
|
191
|
+
outputError(json, "Failed to parse package.json.");
|
|
192
|
+
if (!json) log.error(pc.dim(String(err)));
|
|
217
193
|
return;
|
|
218
194
|
}
|
|
219
195
|
const installedDeps = {
|
|
220
196
|
...packageJson.dependencies ?? {},
|
|
221
197
|
...packageJson.devDependencies ?? {}
|
|
222
198
|
};
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
199
|
+
const missingProd = [
|
|
200
|
+
...adapterContext.dependencies ?? [],
|
|
201
|
+
...variantMeta?.dependencies ?? []
|
|
202
|
+
].filter((d) => !(d in installedDeps));
|
|
203
|
+
const missingDev = [
|
|
204
|
+
...adapterContext.devDependencies ?? [],
|
|
205
|
+
...variantMeta?.devDependencies ?? []
|
|
206
|
+
].filter((d) => !(d in installedDeps));
|
|
207
|
+
if (missingProd.length > 0 || missingDev.length > 0) {
|
|
228
208
|
if (!json) {
|
|
229
|
-
|
|
230
|
-
pc.yellow(`
|
|
231
|
-
\u26A0\uFE0F Missing required infrastructure packages: ${allMissingNames.join(", ")}`)
|
|
232
|
-
);
|
|
209
|
+
log.warn(`Missing dependencies: ${pc.cyan([...missingProd, ...missingDev].join(", "))}`);
|
|
233
210
|
}
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
211
|
+
const shouldInstall = yes ? true : await confirm({ message: "Install missing dependencies?", initialValue: true });
|
|
212
|
+
if (!yes) handleCancel(shouldInstall);
|
|
213
|
+
if (shouldInstall) {
|
|
214
|
+
const pm = await fs.access(join(packageJsonDir, "pnpm-lock.yaml")).then(() => "pnpm").catch(() => "npm");
|
|
215
|
+
if (!json) s.start(`Installing via ${pm}...`);
|
|
216
|
+
const tasks = [];
|
|
217
|
+
if (missingProd.length > 0) {
|
|
218
|
+
tasks.push(
|
|
219
|
+
pm === "pnpm" ? `pnpm add ${missingProd.join(" ")}` : `npm install ${missingProd.join(" ")}`
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
if (missingDev.length > 0) {
|
|
223
|
+
tasks.push(
|
|
224
|
+
pm === "pnpm" ? `pnpm add -D ${missingDev.join(" ")}` : `npm install --save-dev ${missingDev.join(" ")}`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
242
227
|
try {
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
installTasks.push(
|
|
246
|
-
packageManager === "pnpm" ? `pnpm add ${missingProdDeps.join(" ")}` : `npm install ${missingProdDeps.join(" ")}`
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
if (missingDevDeps.length > 0) {
|
|
250
|
-
installTasks.push(
|
|
251
|
-
packageManager === "pnpm" ? `pnpm add -D ${missingDevDeps.join(" ")}` : `npm install --save-dev ${missingDevDeps.join(" ")}`
|
|
252
|
-
);
|
|
253
|
-
}
|
|
254
|
-
for (const installCmd of installTasks) {
|
|
255
|
-
if (!json) s.stop(pc.cyan(`Executing: ${installCmd}
|
|
256
|
-
`));
|
|
228
|
+
for (const cmd of tasks) {
|
|
229
|
+
if (!json) s.message(`Running: ${cmd}`);
|
|
257
230
|
await new Promise((resolve, reject) => {
|
|
258
|
-
const child = exec(
|
|
259
|
-
child.stdout?.on("data", (
|
|
260
|
-
if (!json) process.stdout.write(pc.dim(
|
|
231
|
+
const child = exec(cmd, { cwd: packageJsonDir });
|
|
232
|
+
child.stdout?.on("data", (d) => {
|
|
233
|
+
if (!json) process.stdout.write(pc.dim(d));
|
|
261
234
|
});
|
|
262
|
-
child.stderr?.on("data", (
|
|
263
|
-
if (!json) process.stdout.write(pc.dim(pc.red(
|
|
235
|
+
child.stderr?.on("data", (d) => {
|
|
236
|
+
if (!json) process.stdout.write(pc.dim(pc.red(d)));
|
|
264
237
|
});
|
|
265
238
|
child.on(
|
|
266
239
|
"close",
|
|
267
|
-
(code) => code === 0 ? resolve() : reject(new Error(`Exit
|
|
240
|
+
(code) => code === 0 ? resolve() : reject(new Error(`Exit ${code}`))
|
|
268
241
|
);
|
|
269
242
|
});
|
|
270
243
|
}
|
|
244
|
+
if (!json) s.stop("Dependencies installed.");
|
|
245
|
+
} catch (err) {
|
|
271
246
|
if (!json) {
|
|
272
|
-
s.
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
} catch (error) {
|
|
276
|
-
if (!json) {
|
|
277
|
-
s.stop(pc.red("\u2716 Automated dependency installation failed. Please run setup manually."));
|
|
278
|
-
console.error(pc.dim(String(error)));
|
|
247
|
+
s.stop("Installation failed.");
|
|
248
|
+
log.error(pc.dim(String(err)));
|
|
279
249
|
} else {
|
|
280
|
-
outputError(json, "
|
|
250
|
+
outputError(json, "Dependency installation failed.");
|
|
281
251
|
}
|
|
282
252
|
return;
|
|
283
253
|
}
|
|
284
254
|
}
|
|
285
255
|
}
|
|
286
256
|
const filesToDownload = [];
|
|
287
|
-
if (blockMeta.baseFiles) {
|
|
257
|
+
if (blockMeta.baseFiles && Array.isArray(blockMeta.baseFiles)) {
|
|
288
258
|
filesToDownload.push(...blockMeta.baseFiles);
|
|
289
259
|
}
|
|
290
260
|
if (adapterContext.core) {
|
|
291
261
|
filesToDownload.push({
|
|
292
262
|
source: adapterContext.core,
|
|
293
|
-
target: path.basename(adapterContext.core
|
|
263
|
+
target: path.basename(adapterContext.core)
|
|
294
264
|
});
|
|
295
265
|
}
|
|
296
266
|
if (variantMeta && Array.isArray(variantMeta.files)) {
|
|
297
|
-
for (const
|
|
298
|
-
if (typeof
|
|
299
|
-
filesToDownload.push({
|
|
300
|
-
source: fileEntry,
|
|
301
|
-
target: path.basename(fileEntry, ".txt")
|
|
302
|
-
});
|
|
267
|
+
for (const entry of variantMeta.files) {
|
|
268
|
+
if (typeof entry === "string") {
|
|
269
|
+
filesToDownload.push({ source: entry, target: path.basename(entry) });
|
|
303
270
|
} else {
|
|
304
|
-
filesToDownload.push(
|
|
271
|
+
filesToDownload.push(entry);
|
|
305
272
|
}
|
|
306
273
|
}
|
|
307
274
|
}
|
|
308
|
-
let
|
|
309
|
-
for (const
|
|
310
|
-
const destinationPath = join(targetFolder, fileMap.target);
|
|
275
|
+
let hasConflict = false;
|
|
276
|
+
for (const fm of filesToDownload) {
|
|
311
277
|
try {
|
|
312
|
-
await fs.access(
|
|
313
|
-
|
|
278
|
+
await fs.access(join(targetFolder, fm.target));
|
|
279
|
+
hasConflict = true;
|
|
314
280
|
break;
|
|
315
281
|
} catch {
|
|
316
282
|
}
|
|
317
283
|
}
|
|
318
|
-
if (
|
|
319
|
-
const
|
|
320
|
-
message:
|
|
284
|
+
if (hasConflict) {
|
|
285
|
+
const overwrite = yes ? true : await confirm({
|
|
286
|
+
message: `Files for "${targetBlock}" already exist. Overwrite?`,
|
|
321
287
|
initialValue: false
|
|
322
288
|
});
|
|
323
|
-
if (!yes) handleCancel(
|
|
324
|
-
if (!
|
|
325
|
-
outputResult(json, {
|
|
326
|
-
success: false,
|
|
327
|
-
reason: "aborted",
|
|
328
|
-
message: "Local modifications preserved."
|
|
329
|
-
});
|
|
289
|
+
if (!yes) handleCancel(overwrite);
|
|
290
|
+
if (!overwrite) {
|
|
291
|
+
outputResult(json, { success: false, reason: "aborted", message: "Local files preserved." });
|
|
330
292
|
return;
|
|
331
293
|
}
|
|
332
294
|
}
|
|
333
|
-
if (!json) {
|
|
334
|
-
|
|
335
|
-
}
|
|
295
|
+
if (!json) s.start(`Downloading ${targetBlock}...`);
|
|
296
|
+
const filesWritten = [];
|
|
336
297
|
try {
|
|
337
|
-
for (const
|
|
338
|
-
const fileUrl = `${RAW_CDN_BASE}/${
|
|
339
|
-
const
|
|
340
|
-
if (!
|
|
341
|
-
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
await fs.
|
|
298
|
+
for (const fm of filesToDownload) {
|
|
299
|
+
const fileUrl = `${RAW_CDN_BASE}/${fm.source}`;
|
|
300
|
+
const res = await fetch(fileUrl);
|
|
301
|
+
if (!res.ok) throw new Error(`Failed to download: ${fm.source} (HTTP ${res.status})`);
|
|
302
|
+
let content = await res.text();
|
|
303
|
+
const writtenFilePath = path.join(targetFolder, fm.target);
|
|
304
|
+
content = rewriteFileImports(content, writtenFilePath, blocksRootAbsolute, aliasBase);
|
|
305
|
+
await fs.mkdir(dirname(writtenFilePath), { recursive: true });
|
|
306
|
+
await fs.writeFile(writtenFilePath, content, "utf-8");
|
|
307
|
+
filesWritten.push(writtenFilePath);
|
|
345
308
|
}
|
|
346
|
-
if (!json) s.stop(
|
|
309
|
+
if (!json) s.stop("Files written.");
|
|
347
310
|
outputResult(json, {
|
|
348
311
|
success: true,
|
|
349
312
|
block: targetBlock,
|
|
350
|
-
filesWritten
|
|
351
|
-
dependenciesInstalled: [...
|
|
352
|
-
message:
|
|
313
|
+
filesWritten,
|
|
314
|
+
dependenciesInstalled: [...missingProd, ...missingDev],
|
|
315
|
+
message: `${targetBlock} added to ${physicalBlocksPath}/${targetBlock}`
|
|
353
316
|
});
|
|
354
|
-
} catch (
|
|
317
|
+
} catch (err) {
|
|
355
318
|
if (!json) {
|
|
356
|
-
s.stop(
|
|
357
|
-
|
|
319
|
+
s.stop("Failed.");
|
|
320
|
+
log.error(pc.dim(String(err)));
|
|
358
321
|
} else {
|
|
359
|
-
outputError(json, "Fatal error
|
|
322
|
+
outputError(json, "Fatal error while writing block files.");
|
|
360
323
|
}
|
|
361
324
|
}
|
|
362
325
|
}
|
|
@@ -365,7 +328,8 @@ async function addCommand(blockName, options = {}) {
|
|
|
365
328
|
import path2, { join as join7 } from "path";
|
|
366
329
|
import fs2 from "fs/promises";
|
|
367
330
|
import { existsSync as existsSync4 } from "fs";
|
|
368
|
-
import { intro as intro2, outro as outro2, select as select2, text, confirm as confirm2, spinner as spinner2, isCancel as isCancel2 } from "@clack/prompts";
|
|
331
|
+
import { intro as intro2, outro as outro2, select as select2, text, confirm as confirm2, spinner as spinner2, isCancel as isCancel2, log as log2 } from "@clack/prompts";
|
|
332
|
+
import pc3 from "picocolors";
|
|
369
333
|
|
|
370
334
|
// src/detectors/index.ts
|
|
371
335
|
import { join as join6 } from "path";
|
|
@@ -478,7 +442,7 @@ function flattenTsPaths(paths) {
|
|
|
478
442
|
import pc2 from "picocolors";
|
|
479
443
|
var theme = {
|
|
480
444
|
brand: {
|
|
481
|
-
primary: pc2.
|
|
445
|
+
primary: pc2.cyan,
|
|
482
446
|
title: pc2.bold,
|
|
483
447
|
logo: pc2.white
|
|
484
448
|
},
|
|
@@ -491,7 +455,7 @@ var theme = {
|
|
|
491
455
|
success: pc2.green,
|
|
492
456
|
warning: pc2.yellow,
|
|
493
457
|
error: pc2.red,
|
|
494
|
-
info: pc2.
|
|
458
|
+
info: pc2.cyan
|
|
495
459
|
},
|
|
496
460
|
emphasis: {
|
|
497
461
|
strong: pc2.bold,
|
|
@@ -502,36 +466,56 @@ var theme = {
|
|
|
502
466
|
// src/ui/format.ts
|
|
503
467
|
var format = {
|
|
504
468
|
title: (text2) => theme.brand.primary(theme.brand.title(text2)),
|
|
505
|
-
success: (text2) => theme.state.success(
|
|
506
|
-
error: (text2) => theme.state.error(
|
|
469
|
+
success: (text2) => theme.state.success(text2),
|
|
470
|
+
error: (text2) => theme.state.error(text2),
|
|
507
471
|
warning: (text2) => theme.state.warning(text2),
|
|
508
472
|
muted: (text2) => theme.text.muted(text2),
|
|
509
|
-
info: (text2) => theme.state.info(text2)
|
|
473
|
+
info: (text2) => theme.state.info(text2),
|
|
474
|
+
highlight: (text2) => theme.brand.primary(text2)
|
|
510
475
|
};
|
|
511
476
|
|
|
512
477
|
// src/commands/init.ts
|
|
513
478
|
async function resolveTsConfigPaths(cwd) {
|
|
514
|
-
const
|
|
515
|
-
for (const configPath of
|
|
516
|
-
if (existsSync4(configPath))
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
}
|
|
525
|
-
|
|
479
|
+
const candidates = [join7(cwd, "tsconfig.json"), join7(cwd, "jsconfig.json")];
|
|
480
|
+
for (const configPath of candidates) {
|
|
481
|
+
if (!existsSync4(configPath)) continue;
|
|
482
|
+
try {
|
|
483
|
+
const raw = await fs2.readFile(configPath, "utf-8");
|
|
484
|
+
const clean = raw.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "$1");
|
|
485
|
+
const parsed = JSON.parse(clean);
|
|
486
|
+
return {
|
|
487
|
+
baseUrl: parsed.compilerOptions?.baseUrl ?? ".",
|
|
488
|
+
paths: parsed.compilerOptions?.paths ?? {}
|
|
489
|
+
};
|
|
490
|
+
} catch {
|
|
526
491
|
}
|
|
527
492
|
}
|
|
528
493
|
return { baseUrl: ".", paths: {} };
|
|
529
494
|
}
|
|
495
|
+
function deriveBlockAlias(tsPaths, blocksPhysicalPath) {
|
|
496
|
+
const normalizedBlocksPath = blocksPhysicalPath.replace(/^\.\//, "").replace(/\\/g, "/");
|
|
497
|
+
for (const [aliasKey, targets] of Object.entries(tsPaths)) {
|
|
498
|
+
if (!Array.isArray(targets) || targets.length === 0) continue;
|
|
499
|
+
const rawTarget = targets[0].replace(/\\/g, "/");
|
|
500
|
+
const targetRoot = rawTarget.replace(/\*$/, "").replace(/\/$/, "").replace(/^\.\//, "");
|
|
501
|
+
const aliasRoot = aliasKey.replace(/\/\*$/, "").replace(/\*$/, "");
|
|
502
|
+
if (targetRoot === "") {
|
|
503
|
+
return `${aliasRoot}/${normalizedBlocksPath}`.replace(/\/+/g, "/");
|
|
504
|
+
}
|
|
505
|
+
if (normalizedBlocksPath === targetRoot || normalizedBlocksPath.startsWith(`${targetRoot}/`)) {
|
|
506
|
+
const suffix = normalizedBlocksPath.slice(targetRoot.length).replace(/^\//, "");
|
|
507
|
+
const alias = suffix ? `${aliasRoot}/${suffix}` : aliasRoot;
|
|
508
|
+
return alias.replace(/\/+/g, "/");
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return void 0;
|
|
512
|
+
}
|
|
530
513
|
function outputInitError(json, message) {
|
|
531
514
|
if (json) {
|
|
532
515
|
process.stdout.write(JSON.stringify({ success: false, error: message }) + "\n");
|
|
533
516
|
} else {
|
|
534
|
-
|
|
517
|
+
log2.error(format.error(message));
|
|
518
|
+
process.exit(1);
|
|
535
519
|
}
|
|
536
520
|
}
|
|
537
521
|
function outputInitResult(json, result) {
|
|
@@ -546,15 +530,8 @@ async function initCommand(options = {}) {
|
|
|
546
530
|
const cwd = process.cwd();
|
|
547
531
|
const configPath = join7(cwd, "blockend.json");
|
|
548
532
|
if (!json) {
|
|
549
|
-
console.log(
|
|
550
|
-
|
|
551
|
-
\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
|
|
552
|
-
\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
553
|
-
\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2588\u2588\u2557 \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
554
|
-
\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
555
|
-
\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
|
|
556
|
-
`);
|
|
557
|
-
intro2(theme.brand.primary(" Blockend \xB7 Intelligent Backend Blocks Setup "));
|
|
533
|
+
console.log("");
|
|
534
|
+
intro2(`${pc3.bgCyan(pc3.black(" blockend "))} ${theme.text.muted("setup")}`);
|
|
558
535
|
}
|
|
559
536
|
if (existsSync4(configPath)) {
|
|
560
537
|
let action;
|
|
@@ -562,21 +539,21 @@ async function initCommand(options = {}) {
|
|
|
562
539
|
action = "overwrite";
|
|
563
540
|
} else {
|
|
564
541
|
const actionPrompt = await select2({
|
|
565
|
-
message: "blockend.json already exists. What
|
|
542
|
+
message: "A blockend.json already exists. What would you like to do?",
|
|
566
543
|
options: [
|
|
567
|
-
{ value: "keep", label: "Keep existing
|
|
568
|
-
{ value: "overwrite", label: "Overwrite
|
|
569
|
-
{ value: "regenerate", label: "Delete and regenerate" }
|
|
544
|
+
{ value: "keep", label: "Keep existing (cancel setup)" },
|
|
545
|
+
{ value: "overwrite", label: "Overwrite current configuration" },
|
|
546
|
+
{ value: "regenerate", label: "Delete and regenerate from scratch" }
|
|
570
547
|
]
|
|
571
548
|
});
|
|
572
549
|
if (isCancel2(actionPrompt) || actionPrompt === "keep") {
|
|
573
550
|
if (json) {
|
|
574
551
|
outputInitResult(json, {
|
|
575
552
|
success: false,
|
|
576
|
-
message: "
|
|
553
|
+
message: "Setup cancelled. Existing config preserved."
|
|
577
554
|
});
|
|
578
555
|
} else {
|
|
579
|
-
outro2(format.muted("
|
|
556
|
+
outro2(format.muted("Setup cancelled. Existing config preserved."));
|
|
580
557
|
}
|
|
581
558
|
return;
|
|
582
559
|
}
|
|
@@ -584,22 +561,22 @@ async function initCommand(options = {}) {
|
|
|
584
561
|
}
|
|
585
562
|
if (action === "regenerate") {
|
|
586
563
|
await fs2.unlink(configPath);
|
|
587
|
-
if (!json)
|
|
564
|
+
if (!json) log2.warn("Existing configuration deleted.");
|
|
588
565
|
}
|
|
589
566
|
}
|
|
590
567
|
const s = spinner2();
|
|
591
|
-
if (!json) s.start("Scanning project
|
|
568
|
+
if (!json) s.start("Scanning project architecture...");
|
|
592
569
|
const context = await detectProject(cwd);
|
|
593
570
|
const hasSrcDir = existsSync4(join7(cwd, "src"));
|
|
594
571
|
const tsConfig = await resolveTsConfigPaths(cwd);
|
|
595
|
-
if (!json) s.stop(
|
|
572
|
+
if (!json) s.stop("Project scanned.");
|
|
596
573
|
let framework = context.framework;
|
|
597
574
|
if (!framework) {
|
|
598
575
|
if (yes) {
|
|
599
576
|
framework = "express";
|
|
600
577
|
} else {
|
|
601
578
|
const frameworkSelect = await select2({
|
|
602
|
-
message: "
|
|
579
|
+
message: "Which framework does this project use?",
|
|
603
580
|
options: [
|
|
604
581
|
{ value: "express", label: "Express.js" },
|
|
605
582
|
{ value: "fastify", label: "Fastify" },
|
|
@@ -609,67 +586,42 @@ async function initCommand(options = {}) {
|
|
|
609
586
|
});
|
|
610
587
|
if (isCancel2(frameworkSelect)) {
|
|
611
588
|
if (json) {
|
|
612
|
-
outputInitResult(json, { success: false, message: "
|
|
589
|
+
outputInitResult(json, { success: false, message: "Setup cancelled." });
|
|
613
590
|
} else {
|
|
614
|
-
outro2(format.muted("
|
|
591
|
+
outro2(format.muted("Setup cancelled."));
|
|
615
592
|
}
|
|
616
593
|
return;
|
|
617
594
|
}
|
|
618
595
|
framework = frameworkSelect;
|
|
619
596
|
}
|
|
620
597
|
} else if (!json) {
|
|
621
|
-
|
|
622
|
-
`${format.success("\u2714")} Framework environment detected: ${theme.state.info(framework)}`
|
|
623
|
-
);
|
|
598
|
+
log2.info(`Framework detected: ${theme.state.info(framework)}`);
|
|
624
599
|
}
|
|
625
600
|
const defaultDir = hasSrcDir ? "src/blocks" : "blocks";
|
|
626
601
|
let rawPhysicalInput = defaultDir;
|
|
627
602
|
if (!yes) {
|
|
628
603
|
const directoryPrompt = await text({
|
|
629
|
-
message: "
|
|
604
|
+
message: "Where would you like to install blocks?",
|
|
630
605
|
placeholder: defaultDir,
|
|
631
606
|
initialValue: defaultDir,
|
|
632
607
|
validate(value) {
|
|
633
|
-
if (value
|
|
608
|
+
if (!value || value.trim().length === 0) return "Directory path cannot be empty.";
|
|
634
609
|
}
|
|
635
610
|
});
|
|
636
611
|
if (isCancel2(directoryPrompt)) {
|
|
637
|
-
outro2(format.muted("
|
|
612
|
+
outro2(format.muted("Setup cancelled."));
|
|
638
613
|
return;
|
|
639
614
|
}
|
|
640
615
|
rawPhysicalInput = String(directoryPrompt).trim();
|
|
641
616
|
}
|
|
642
|
-
const relativeBlocksPath = path2.relative(cwd, path2.resolve(cwd, rawPhysicalInput));
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
const matchedAliasKey = configuredAliases.find((alias) => {
|
|
651
|
-
const targets = tsConfig.paths[alias];
|
|
652
|
-
if (Array.isArray(targets) && targets.length > 0) {
|
|
653
|
-
const targetSubPath = targets[0].replace(/\*$/, "").replace(/\\/g, "/");
|
|
654
|
-
return finalPath.replace(/^\.\//, "").startsWith(targetSubPath);
|
|
655
|
-
}
|
|
656
|
-
return false;
|
|
657
|
-
});
|
|
658
|
-
if (matchedAliasKey) {
|
|
659
|
-
const cleanKey = matchedAliasKey.replace(/\*$/, "");
|
|
660
|
-
const targets = tsConfig.paths[matchedAliasKey];
|
|
661
|
-
const targetSubPath = targets[0].replace(/\*$/, "").replace(/\\/g, "/");
|
|
662
|
-
const relativeSuffix = finalPath.replace(/^\.\//, "").replace(targetSubPath, "");
|
|
663
|
-
finalBlockAlias = `${cleanKey}${relativeSuffix}`.replace(/\/$/, "");
|
|
664
|
-
} else {
|
|
665
|
-
const primaryAlias = configuredAliases.find((k) => k.startsWith("@")) || configuredAliases[0];
|
|
666
|
-
const inferredBaseAlias = primaryAlias.replace(/\*$/, "");
|
|
667
|
-
const folderSegmentName = path2.basename(finalPath);
|
|
668
|
-
finalBlockAlias = `${inferredBaseAlias}${folderSegmentName}`;
|
|
669
|
-
}
|
|
670
|
-
} else {
|
|
671
|
-
const folderSegmentName = path2.basename(finalPath);
|
|
672
|
-
finalBlockAlias = `@/${folderSegmentName}`;
|
|
617
|
+
const relativeBlocksPath = path2.relative(cwd, path2.resolve(cwd, rawPhysicalInput)).replace(/\\/g, "/");
|
|
618
|
+
const finalPath = relativeBlocksPath.startsWith(".") ? relativeBlocksPath : `./${relativeBlocksPath}`;
|
|
619
|
+
const hasTsPaths = Object.keys(tsConfig.paths).length > 0;
|
|
620
|
+
const finalBlockAlias = hasTsPaths ? deriveBlockAlias(tsConfig.paths, relativeBlocksPath) : void 0;
|
|
621
|
+
if (!json) {
|
|
622
|
+
log2.info(
|
|
623
|
+
`Import alias: ${theme.state.info(finalBlockAlias === void 0 ? " relative imports" : finalBlockAlias)}`
|
|
624
|
+
);
|
|
673
625
|
}
|
|
674
626
|
let includeRedis = false;
|
|
675
627
|
if (context.hasRedis) {
|
|
@@ -677,7 +629,7 @@ async function initCommand(options = {}) {
|
|
|
677
629
|
includeRedis = true;
|
|
678
630
|
} else {
|
|
679
631
|
const redisConfirm = await confirm2({
|
|
680
|
-
message: "Redis detected. Enable Redis-backed
|
|
632
|
+
message: "Redis detected in project. Enable Redis-backed variants?",
|
|
681
633
|
initialValue: true
|
|
682
634
|
});
|
|
683
635
|
if (!isCancel2(redisConfirm)) {
|
|
@@ -686,38 +638,40 @@ async function initCommand(options = {}) {
|
|
|
686
638
|
}
|
|
687
639
|
}
|
|
688
640
|
const configPayload = {
|
|
689
|
-
$schema: "https://blockend.
|
|
641
|
+
$schema: "https://blockend.noorulhassan.com/schema.json",
|
|
690
642
|
environment: framework || "express",
|
|
691
643
|
language: context.language || "typescript",
|
|
692
644
|
includeRedis,
|
|
693
645
|
aliases: {
|
|
694
|
-
blocks: finalBlockAlias
|
|
646
|
+
...finalBlockAlias ? { blocks: finalBlockAlias } : {}
|
|
695
647
|
},
|
|
696
648
|
paths: {
|
|
697
649
|
blocks: finalPath
|
|
650
|
+
// e.g. "./src/blocks" — always a real physical path
|
|
698
651
|
}
|
|
699
652
|
};
|
|
700
|
-
if (!json) s.start("
|
|
653
|
+
if (!json) s.start("Writing configuration...");
|
|
701
654
|
try {
|
|
702
655
|
await fs2.writeFile(configPath, JSON.stringify(configPayload, null, 2), "utf-8");
|
|
703
|
-
if (!json) s.stop(
|
|
656
|
+
if (!json) s.stop("Configuration saved.");
|
|
704
657
|
outputInitResult(json, {
|
|
705
658
|
success: true,
|
|
706
|
-
message: "
|
|
659
|
+
message: "Blockend initialized! Run: npx blockend add <block>",
|
|
707
660
|
config: configPayload
|
|
708
661
|
});
|
|
709
662
|
} catch {
|
|
710
663
|
if (!json) {
|
|
711
|
-
s.stop(
|
|
664
|
+
s.stop("Failed");
|
|
665
|
+
outputInitError(false, "Failed to write configuration file.");
|
|
712
666
|
} else {
|
|
713
|
-
outputInitError(
|
|
667
|
+
outputInitError(true, "Failed to write configuration file.");
|
|
714
668
|
}
|
|
715
669
|
}
|
|
716
670
|
}
|
|
717
671
|
|
|
718
672
|
// src/commands/detect.ts
|
|
719
673
|
import { outro as outro3 } from "@clack/prompts";
|
|
720
|
-
import
|
|
674
|
+
import pc4 from "picocolors";
|
|
721
675
|
async function detectCommand(options = {}) {
|
|
722
676
|
const { json = false } = options;
|
|
723
677
|
try {
|
|
@@ -727,27 +681,27 @@ async function detectCommand(options = {}) {
|
|
|
727
681
|
return;
|
|
728
682
|
}
|
|
729
683
|
console.log();
|
|
730
|
-
console.log(
|
|
684
|
+
console.log(pc4.bold(" Detected project configuration:"));
|
|
731
685
|
console.log();
|
|
732
|
-
console.log(` Framework: ${
|
|
733
|
-
console.log(` Language: ${
|
|
734
|
-
console.log(` Package manager: ${
|
|
735
|
-
console.log(` Source dir: ${
|
|
686
|
+
console.log(` Framework: ${pc4.cyan(context.framework)}`);
|
|
687
|
+
console.log(` Language: ${pc4.cyan(context.language)}`);
|
|
688
|
+
console.log(` Package manager: ${pc4.cyan(context.packageManager)}`);
|
|
689
|
+
console.log(` Source dir: ${pc4.dim(context.srcDir)}`);
|
|
736
690
|
console.log(
|
|
737
|
-
` Redis: ${context.hasRedis ?
|
|
691
|
+
` Redis: ${context.hasRedis ? pc4.green("detected") : pc4.dim("not found")}`
|
|
738
692
|
);
|
|
739
693
|
console.log(
|
|
740
|
-
` Prisma: ${context.hasPrisma ?
|
|
694
|
+
` Prisma: ${context.hasPrisma ? pc4.green("detected") : pc4.dim("not found")}`
|
|
741
695
|
);
|
|
742
696
|
console.log(
|
|
743
|
-
` Drizzle: ${context.hasDrizzle ?
|
|
697
|
+
` Drizzle: ${context.hasDrizzle ? pc4.green("detected") : pc4.dim("not found")}`
|
|
744
698
|
);
|
|
745
699
|
console.log();
|
|
746
700
|
} catch (error) {
|
|
747
701
|
if (json) {
|
|
748
702
|
process.stdout.write(JSON.stringify({ success: false, error: String(error) }) + "\n");
|
|
749
703
|
} else {
|
|
750
|
-
outro3(
|
|
704
|
+
outro3(pc4.red(`\u2716 Detection failed: ${String(error)}`));
|
|
751
705
|
}
|
|
752
706
|
process.exit(1);
|
|
753
707
|
}
|
|
@@ -756,7 +710,7 @@ async function detectCommand(options = {}) {
|
|
|
756
710
|
// src/commands/list.ts
|
|
757
711
|
import { dirname as dirname2, join as join8 } from "path";
|
|
758
712
|
import fs3 from "fs/promises";
|
|
759
|
-
import
|
|
713
|
+
import pc5 from "picocolors";
|
|
760
714
|
import { outro as outro4, spinner as spinner3 } from "@clack/prompts";
|
|
761
715
|
var REPO_OWNER2 = "codewithnuh";
|
|
762
716
|
var REPO_NAME2 = "blockend";
|
|
@@ -787,7 +741,7 @@ async function listCommand(options = {}) {
|
|
|
787
741
|
JSON.stringify({ success: false, error: "blockend.json not found." }) + "\n"
|
|
788
742
|
);
|
|
789
743
|
} else {
|
|
790
|
-
outro4(
|
|
744
|
+
outro4(pc5.red("\u2716 blockend.json not found. Run 'npx blockend init' first."));
|
|
791
745
|
}
|
|
792
746
|
return;
|
|
793
747
|
}
|
|
@@ -801,7 +755,7 @@ async function listCommand(options = {}) {
|
|
|
801
755
|
JSON.stringify({ success: false, error: "Failed to parse configuration matrix." }) + "\n"
|
|
802
756
|
);
|
|
803
757
|
} else {
|
|
804
|
-
outro4(
|
|
758
|
+
outro4(pc5.red("\u2716 Failed to parse blockend.json layout configuration."));
|
|
805
759
|
}
|
|
806
760
|
return;
|
|
807
761
|
}
|
|
@@ -815,14 +769,14 @@ async function listCommand(options = {}) {
|
|
|
815
769
|
const response = await fetch(MANIFEST_URL2);
|
|
816
770
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
817
771
|
registry = await response.json();
|
|
818
|
-
if (!json) s.stop(
|
|
772
|
+
if (!json) s.stop(pc5.green("\u2714 Synced available block registries."));
|
|
819
773
|
} catch {
|
|
820
774
|
if (json) {
|
|
821
775
|
process.stdout.write(
|
|
822
776
|
JSON.stringify({ success: false, error: "Network sync failure." }) + "\n"
|
|
823
777
|
);
|
|
824
778
|
} else {
|
|
825
|
-
s.stop(
|
|
779
|
+
s.stop(pc5.red("\u2716 Failed to fetch the component registry from GitHub network paths."));
|
|
826
780
|
}
|
|
827
781
|
return;
|
|
828
782
|
}
|
|
@@ -851,15 +805,15 @@ async function listCommand(options = {}) {
|
|
|
851
805
|
);
|
|
852
806
|
} else {
|
|
853
807
|
console.log(`
|
|
854
|
-
Available backend blocks for ${
|
|
808
|
+
Available backend blocks for ${pc5.magenta(envKey)}:`);
|
|
855
809
|
if (blocksForEnv.length === 0) {
|
|
856
|
-
console.log(
|
|
810
|
+
console.log(pc5.dim(" No blocks found for this framework."));
|
|
857
811
|
} else {
|
|
858
812
|
blocksForEnv.forEach((b) => {
|
|
859
813
|
console.log(`
|
|
860
|
-
${
|
|
861
|
-
console.log(` ${
|
|
862
|
-
console.log(` Storage Variants: ${b.variants.map((v) =>
|
|
814
|
+
${pc5.cyan(b.name)}`);
|
|
815
|
+
console.log(` ${pc5.dim(b.description)}`);
|
|
816
|
+
console.log(` Storage Variants: ${b.variants.map((v) => pc5.yellow(v)).join(", ")}`);
|
|
863
817
|
});
|
|
864
818
|
console.log();
|
|
865
819
|
}
|
|
@@ -869,7 +823,7 @@ Available backend blocks for ${pc4.magenta(envKey)}:`);
|
|
|
869
823
|
// src/commands/mcp.ts
|
|
870
824
|
import path3, { join as join9 } from "path";
|
|
871
825
|
import fs4 from "fs/promises";
|
|
872
|
-
import
|
|
826
|
+
import pc6 from "picocolors";
|
|
873
827
|
import { outro as outro5, spinner as spinner4, select as select3, confirm as confirm3 } from "@clack/prompts";
|
|
874
828
|
var CLIENT_MATRIX = {
|
|
875
829
|
claude: {
|
|
@@ -992,22 +946,22 @@ async function mcpInitCommand(options) {
|
|
|
992
946
|
}
|
|
993
947
|
const meta = CLIENT_MATRIX[chosenClient];
|
|
994
948
|
if (!meta) {
|
|
995
|
-
outro5(
|
|
949
|
+
outro5(pc6.red(`\u2716 Unsupported client identifier profile: ${options.client}`));
|
|
996
950
|
return;
|
|
997
951
|
}
|
|
998
952
|
const absoluteConfigTarget = join9(cwd, meta.relativePath);
|
|
999
953
|
if (options.dryRun) {
|
|
1000
954
|
console.log(
|
|
1001
|
-
|
|
1002
|
-
\u2139 Dry-run mode active. Intended path location: ${
|
|
955
|
+
pc6.cyan(`
|
|
956
|
+
\u2139 Dry-run mode active. Intended path location: ${pc6.dim(meta.relativePath)}`)
|
|
1003
957
|
);
|
|
1004
958
|
if (meta.format === "json") {
|
|
1005
959
|
const jsonConfig = meta.isCustomSchema ? { "mcp.mcpServers": { blockend: BLOCKEND_SERVER } } : { mcpServers: { blockend: BLOCKEND_SERVER } };
|
|
1006
|
-
console.log(
|
|
960
|
+
console.log(pc6.gray(JSON.stringify(jsonConfig, null, 2)));
|
|
1007
961
|
} else {
|
|
1008
|
-
console.log(
|
|
962
|
+
console.log(pc6.gray(generateToml(BLOCKEND_SERVER)));
|
|
1009
963
|
}
|
|
1010
|
-
outro5(
|
|
964
|
+
outro5(pc6.green("\u2714 Dry run evaluation complete."));
|
|
1011
965
|
return;
|
|
1012
966
|
}
|
|
1013
967
|
let fileConflict = false;
|
|
@@ -1022,12 +976,12 @@ async function mcpInitCommand(options) {
|
|
|
1022
976
|
initialValue: false
|
|
1023
977
|
});
|
|
1024
978
|
if (!shouldOverwrite || typeof shouldOverwrite === "symbol") {
|
|
1025
|
-
outro5(
|
|
979
|
+
outro5(pc6.yellow("\u2139 Operations halted. Project layout revisions preserved."));
|
|
1026
980
|
return;
|
|
1027
981
|
}
|
|
1028
982
|
}
|
|
1029
983
|
const s = spinner4();
|
|
1030
|
-
s.start(`Writing localized project configurations inside ${
|
|
984
|
+
s.start(`Writing localized project configurations inside ${pc6.dim(meta.relativePath)}...`);
|
|
1031
985
|
try {
|
|
1032
986
|
await fs4.mkdir(path3.dirname(absoluteConfigTarget), { recursive: true });
|
|
1033
987
|
if (meta.format === "json") {
|
|
@@ -1052,11 +1006,11 @@ async function mcpInitCommand(options) {
|
|
|
1052
1006
|
await fs4.writeFile(absoluteConfigTarget, generateToml(BLOCKEND_SERVER), "utf-8");
|
|
1053
1007
|
}
|
|
1054
1008
|
s.stop(
|
|
1055
|
-
|
|
1009
|
+
pc6.green(`\u2714 Local workspace integration fully complete! File target: ${meta.relativePath}`)
|
|
1056
1010
|
);
|
|
1057
1011
|
} catch (error) {
|
|
1058
|
-
s.stop(
|
|
1059
|
-
console.error(
|
|
1012
|
+
s.stop(pc6.red("\u2716 Fatal crash reading or creating local configuration maps."));
|
|
1013
|
+
console.error(pc6.dim(String(error)));
|
|
1060
1014
|
}
|
|
1061
1015
|
}
|
|
1062
1016
|
|
package/package.json
CHANGED