blockend-cli 1.4.4 → 1.4.6

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.
Files changed (2) hide show
  1. package/dist/index.js +38 -40
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14,6 +14,15 @@ 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 blockSupportsEnv(block, envKey) {
18
+ if (block.frameworks && block.frameworks.length > 0) {
19
+ return block.frameworks.includes("*") || block.frameworks.includes(envKey);
20
+ }
21
+ return block.adapters?.[envKey] !== void 0 || block.environments?.[envKey] !== void 0;
22
+ }
23
+ function resolveAdapterContext(block, envKey) {
24
+ return block.adapters?.[envKey] ?? block.adapters?.["*"] ?? block.environments?.[envKey] ?? block.environments?.["*"] ?? null;
25
+ }
17
26
  function rewriteFileImports(fileContent, writtenFilePath, blocksRoot, aliasBase) {
18
27
  if (!aliasBase || aliasBase.startsWith(".")) {
19
28
  return fileContent;
@@ -23,9 +32,7 @@ function rewriteFileImports(fileContent, writtenFilePath, blocksRoot, aliasBase)
23
32
  return fileContent.replace(importRegex, (match, keyword, openQuote, relativePath, closeQuote) => {
24
33
  const absoluteTarget = path.resolve(fileDir, relativePath);
25
34
  const fromBlocksRoot = path.relative(blocksRoot, absoluteTarget).replace(/\\/g, "/");
26
- if (fromBlocksRoot.startsWith("..")) {
27
- return match;
28
- }
35
+ if (fromBlocksRoot.startsWith("..")) return match;
29
36
  const aliased = `${aliasBase}/${fromBlocksRoot}`.replace(/\/+/g, "/");
30
37
  return `${keyword}${openQuote}${aliased}${closeQuote}`;
31
38
  });
@@ -41,11 +48,7 @@ function outputResult(json, result) {
41
48
  if (json) {
42
49
  process.stdout.write(JSON.stringify(result) + "\n");
43
50
  } else {
44
- if (result.success) {
45
- outro(pc.green(`\u2728 ${result.message}`));
46
- } else {
47
- outro(pc.yellow(`\u2139 ${result.message}`));
48
- }
51
+ outro(result.success ? pc.green(`\u2728 ${result.message}`) : pc.yellow(`\u2139 ${result.message}`));
49
52
  }
50
53
  }
51
54
  function handleCancel(value) {
@@ -92,7 +95,6 @@ async function addCommand(blockName, options = {}) {
92
95
  }
93
96
  if (config.language !== "typescript") {
94
97
  outputError(json, "Blockend currently only supports TypeScript projects.");
95
- if (!json) outro(pc.dim("Exiting."));
96
98
  return;
97
99
  }
98
100
  const physicalBlocksPath = config.paths.blocks;
@@ -111,9 +113,7 @@ async function addCommand(blockName, options = {}) {
111
113
  if (!json) {
112
114
  s.stop("Failed to fetch registry.");
113
115
  log.error(pc.dim(String(err)));
114
- } else {
115
- outputError(json, "Failed to fetch the component registry from GitHub.");
116
- }
116
+ } else outputError(json, "Failed to fetch registry from GitHub.");
117
117
  return;
118
118
  }
119
119
  const blockMap = registry.blocks && typeof registry.blocks === "object" && !Array.isArray(registry.blocks) ? registry.blocks : registry;
@@ -123,13 +123,11 @@ async function addCommand(blockName, options = {}) {
123
123
  }
124
124
  let targetBlock = blockName;
125
125
  if (!targetBlock) {
126
- const available = Object.entries(blockMap).filter(([, block]) => {
127
- if (!block) return false;
128
- return block.adapters?.[envKey] !== void 0 || block.environments?.[envKey] !== void 0;
129
- });
126
+ const available = Object.entries(blockMap).filter(
127
+ ([, block]) => block != null && blockSupportsEnv(block, envKey)
128
+ );
130
129
  if (available.length === 0) {
131
- const msg = `No blocks available for environment: ${envKey}`;
132
- outputError(json, msg);
130
+ outputError(json, `No blocks available for environment: ${envKey}`);
133
131
  if (!json) outro(pc.dim("Exiting."));
134
132
  return;
135
133
  }
@@ -148,17 +146,24 @@ async function addCommand(blockName, options = {}) {
148
146
  }
149
147
  const blockMeta = blockMap[targetBlock];
150
148
  if (!blockMeta) {
151
- outputError(json, `Block "${targetBlock}" does not exist in the registry.`);
149
+ outputError(json, `Block "${targetBlock}" not found in the registry.`);
152
150
  return;
153
151
  }
154
- const adapterContext = blockMeta.adapters?.[envKey] ?? blockMeta.environments?.[envKey];
155
- if (!adapterContext) {
152
+ if (!blockSupportsEnv(blockMeta, envKey)) {
156
153
  outputError(json, `Block "${targetBlock}" does not support environment: ${envKey}`);
157
154
  return;
158
155
  }
156
+ const adapterContext = resolveAdapterContext(blockMeta, envKey);
157
+ if (!adapterContext) {
158
+ outputError(
159
+ json,
160
+ `Block "${targetBlock}" has no adapter or environment entry for: ${envKey}. This is a registry configuration issue \u2014 please open an issue.`
161
+ );
162
+ return;
163
+ }
159
164
  const variantKeys = Object.keys(adapterContext.variants ?? {});
160
165
  if (variantKeys.length === 0) {
161
- outputError(json, `No variants found for block "${targetBlock}" in environment "${envKey}"`);
166
+ outputError(json, `No variants found for "${targetBlock}" / "${envKey}".`);
162
167
  return;
163
168
  }
164
169
  let selectedVariant;
@@ -197,17 +202,17 @@ async function addCommand(blockName, options = {}) {
197
202
  ...packageJson.devDependencies ?? {}
198
203
  };
199
204
  const missingProd = [
205
+ ...blockMeta.dependencies ?? [],
200
206
  ...adapterContext.dependencies ?? [],
201
207
  ...variantMeta?.dependencies ?? []
202
208
  ].filter((d) => !(d in installedDeps));
203
209
  const missingDev = [
210
+ ...blockMeta.dependencies ?? [],
204
211
  ...adapterContext.devDependencies ?? [],
205
212
  ...variantMeta?.devDependencies ?? []
206
213
  ].filter((d) => !(d in installedDeps));
207
214
  if (missingProd.length > 0 || missingDev.length > 0) {
208
- if (!json) {
209
- log.warn(`Missing dependencies: ${pc.cyan([...missingProd, ...missingDev].join(", "))}`);
210
- }
215
+ if (!json) log.warn(`Missing: ${pc.cyan([...missingProd, ...missingDev].join(", "))}`);
211
216
  const shouldInstall = yes ? true : await confirm({ message: "Install missing dependencies?", initialValue: true });
212
217
  if (!yes) handleCancel(shouldInstall);
213
218
  if (shouldInstall) {
@@ -246,15 +251,13 @@ async function addCommand(blockName, options = {}) {
246
251
  if (!json) {
247
252
  s.stop("Installation failed.");
248
253
  log.error(pc.dim(String(err)));
249
- } else {
250
- outputError(json, "Dependency installation failed.");
251
- }
254
+ } else outputError(json, "Dependency installation failed.");
252
255
  return;
253
256
  }
254
257
  }
255
258
  }
256
259
  const filesToDownload = [];
257
- if (blockMeta.baseFiles && Array.isArray(blockMeta.baseFiles)) {
260
+ if (blockMeta.baseFiles?.length) {
258
261
  filesToDownload.push(...blockMeta.baseFiles);
259
262
  }
260
263
  if (adapterContext.core) {
@@ -265,11 +268,9 @@ async function addCommand(blockName, options = {}) {
265
268
  }
266
269
  if (variantMeta && Array.isArray(variantMeta.files)) {
267
270
  for (const entry of variantMeta.files) {
268
- if (typeof entry === "string") {
269
- filesToDownload.push({ source: entry, target: path.basename(entry) });
270
- } else {
271
- filesToDownload.push(entry);
272
- }
271
+ filesToDownload.push(
272
+ typeof entry === "string" ? { source: entry, target: path.basename(entry) } : entry
273
+ );
273
274
  }
274
275
  }
275
276
  let hasConflict = false;
@@ -296,9 +297,8 @@ async function addCommand(blockName, options = {}) {
296
297
  const filesWritten = [];
297
298
  try {
298
299
  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})`);
300
+ const res = await fetch(`${RAW_CDN_BASE}/${fm.source}`);
301
+ if (!res.ok) throw new Error(`Download failed: ${fm.source} (HTTP ${res.status})`);
302
302
  let content = await res.text();
303
303
  const writtenFilePath = path.join(targetFolder, fm.target);
304
304
  content = rewriteFileImports(content, writtenFilePath, blocksRootAbsolute, aliasBase);
@@ -318,9 +318,7 @@ async function addCommand(blockName, options = {}) {
318
318
  if (!json) {
319
319
  s.stop("Failed.");
320
320
  log.error(pc.dim(String(err)));
321
- } else {
322
- outputError(json, "Fatal error while writing block files.");
323
- }
321
+ } else outputError(json, "Fatal error while writing block files.");
324
322
  }
325
323
  }
326
324
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blockend-cli",
3
- "version": "1.4.4",
3
+ "version": "1.4.6",
4
4
  "description": "CLI for installing production-ready backend blocks into Next.js, Express, Hono, NestJS, and other Node.js applications.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",