create-fornix 0.0.9 → 0.0.11
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 +382 -106
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -216,6 +216,7 @@ function topologicalSort(blocks, manifests) {
|
|
|
216
216
|
function generateStructure(config, manifests = []) {
|
|
217
217
|
const files = {};
|
|
218
218
|
const adapterDeps = getAdapterDependencies(config);
|
|
219
|
+
const blockDeps = getBlockDependencies(manifests);
|
|
219
220
|
const pkg = {
|
|
220
221
|
name: config.projectName,
|
|
221
222
|
type: "module",
|
|
@@ -234,10 +235,14 @@ function generateStructure(config, manifests = []) {
|
|
|
234
235
|
tailwindcss: "^4.0.0",
|
|
235
236
|
"@tailwindcss/vite": "^4.0.0"
|
|
236
237
|
},
|
|
237
|
-
...adapterDeps
|
|
238
|
+
...adapterDeps,
|
|
239
|
+
...blockDeps
|
|
238
240
|
},
|
|
239
241
|
devDependencies: {
|
|
240
|
-
typescript: "^5.7.0"
|
|
242
|
+
typescript: "^5.7.0",
|
|
243
|
+
...config.deployTarget === "cloudflare" && {
|
|
244
|
+
"@cloudflare/workers-types": "^4.0.0"
|
|
245
|
+
}
|
|
241
246
|
}
|
|
242
247
|
};
|
|
243
248
|
files["package.json"] = JSON.stringify(pkg, null, 2) + "\n";
|
|
@@ -246,9 +251,11 @@ function generateStructure(config, manifests = []) {
|
|
|
246
251
|
compilerOptions: {
|
|
247
252
|
jsx: "preserve",
|
|
248
253
|
jsxImportSource: "react",
|
|
249
|
-
// Even if not using React yet, good default for UI frameworks
|
|
250
254
|
strictNullChecks: true,
|
|
251
255
|
baseUrl: ".",
|
|
256
|
+
...config.deployTarget === "cloudflare" && {
|
|
257
|
+
types: ["@cloudflare/workers-types"]
|
|
258
|
+
},
|
|
252
259
|
paths: {
|
|
253
260
|
"@/*": ["src/*"]
|
|
254
261
|
}
|
|
@@ -278,42 +285,59 @@ pnpm-debug.log*
|
|
|
278
285
|
.DS_Store
|
|
279
286
|
Thumbs.db
|
|
280
287
|
`.trim() + "\n";
|
|
281
|
-
const
|
|
282
|
-
const
|
|
288
|
+
const LAYOUT_CATEGORIES2 = /* @__PURE__ */ new Set(["header", "footer"]);
|
|
289
|
+
const headerImports = [];
|
|
290
|
+
const headerComponents = [];
|
|
291
|
+
const contentImports = [];
|
|
292
|
+
const contentComponents = [];
|
|
293
|
+
const footerImports = [];
|
|
294
|
+
const footerComponents = [];
|
|
283
295
|
if (manifests.length > 0) {
|
|
284
296
|
for (const manifest2 of manifests) {
|
|
285
297
|
if (manifest2.type !== "section") continue;
|
|
286
298
|
const mainFile = manifest2.files.find((f) => f.destination.endsWith(".astro") || f.destination.endsWith(".tsx"));
|
|
287
|
-
if (mainFile)
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
299
|
+
if (!mainFile) continue;
|
|
300
|
+
const componentName = manifest2.name.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
301
|
+
let importPath = mainFile.destination;
|
|
302
|
+
if (importPath.startsWith("src/")) {
|
|
303
|
+
importPath = "../" + importPath.substring(4);
|
|
304
|
+
}
|
|
305
|
+
const category = manifest2.category ?? "other";
|
|
306
|
+
if (category === "header") {
|
|
307
|
+
headerImports.push(`import ${componentName} from '${importPath}';`);
|
|
308
|
+
headerComponents.push(` <${componentName} />`);
|
|
309
|
+
} else if (category === "footer") {
|
|
310
|
+
footerImports.push(`import ${componentName} from '${importPath}';`);
|
|
311
|
+
footerComponents.push(` <${componentName} />`);
|
|
312
|
+
} else {
|
|
313
|
+
contentImports.push(`import ${componentName} from '${importPath}';`);
|
|
314
|
+
contentComponents.push(` <${componentName} />`);
|
|
295
315
|
}
|
|
296
316
|
}
|
|
297
317
|
}
|
|
298
318
|
const indexAstroContent = `
|
|
299
319
|
---
|
|
300
320
|
import Layout from '../layouts/Layout.astro';
|
|
301
|
-
${
|
|
321
|
+
${contentImports.join("\n")}
|
|
302
322
|
---
|
|
303
323
|
<Layout title="Welcome to ${config.projectName}.">
|
|
304
324
|
<main>
|
|
305
|
-
${
|
|
325
|
+
${contentComponents.length > 0 ? contentComponents.join("\n") : ` <h1>Welcome to <span class="text-gradient">${config.projectName}</span></h1>`}
|
|
306
326
|
</main>
|
|
307
327
|
</Layout>
|
|
308
328
|
`.trim() + "\n";
|
|
309
329
|
files["src/pages/index.astro"] = indexAstroContent;
|
|
310
330
|
const tailwindImport = config.cssEngine === "tailwind" ? '\nimport "../../tailwind.css";' : "";
|
|
331
|
+
const layoutImportsStr = [...headerImports, ...footerImports].join("\n");
|
|
332
|
+
const layoutImportSection = layoutImportsStr ? "\n" + layoutImportsStr : "";
|
|
333
|
+
const headerSection = headerComponents.length > 0 ? "\n" + headerComponents.join("\n") + "\n" : "";
|
|
334
|
+
const footerSection = footerComponents.length > 0 ? "\n" + footerComponents.join("\n") : "";
|
|
311
335
|
files["src/layouts/Layout.astro"] = `
|
|
312
336
|
---
|
|
313
337
|
interface Props {
|
|
314
338
|
title: string;
|
|
315
339
|
}
|
|
316
|
-
const { title } = Astro.props;${tailwindImport}
|
|
340
|
+
const { title } = Astro.props;${tailwindImport}${layoutImportSection}
|
|
317
341
|
---
|
|
318
342
|
<!doctype html>
|
|
319
343
|
<html lang="en">
|
|
@@ -325,8 +349,8 @@ const { title } = Astro.props;${tailwindImport}
|
|
|
325
349
|
<meta name="generator" content={Astro.generator} />
|
|
326
350
|
<title>{title}</title>
|
|
327
351
|
</head>
|
|
328
|
-
<body
|
|
329
|
-
<slot
|
|
352
|
+
<body>${headerSection}
|
|
353
|
+
<slot />${footerSection}
|
|
330
354
|
</body>
|
|
331
355
|
</html>
|
|
332
356
|
|
|
@@ -378,6 +402,15 @@ const { title } = Astro.props;${tailwindImport}
|
|
|
378
402
|
}
|
|
379
403
|
return files;
|
|
380
404
|
}
|
|
405
|
+
function getBlockDependencies(manifests) {
|
|
406
|
+
const merged = {};
|
|
407
|
+
for (const manifest2 of manifests) {
|
|
408
|
+
if (manifest2.dependencies) {
|
|
409
|
+
Object.assign(merged, manifest2.dependencies);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return merged;
|
|
413
|
+
}
|
|
381
414
|
function getAdapterDependencies(config) {
|
|
382
415
|
if (config.renderMode === "static" && config.deployTarget === "static") {
|
|
383
416
|
return {};
|
|
@@ -502,11 +535,35 @@ function buildPaletteFile(colors) {
|
|
|
502
535
|
const properties = COLOR_TOKENS.map(
|
|
503
536
|
(token) => ` --color-${token}: ${colors[token]};`
|
|
504
537
|
).join("\n");
|
|
538
|
+
const surface = blendHex(colors.background, colors.foreground, 0.08);
|
|
539
|
+
const muted = blendHex(colors.foreground, colors.background, 0.4);
|
|
540
|
+
const derived = [
|
|
541
|
+
` --color-surface: ${surface};`,
|
|
542
|
+
` --color-muted: ${muted};`
|
|
543
|
+
].join("\n");
|
|
505
544
|
return `:root {
|
|
506
545
|
${properties}
|
|
546
|
+
${derived}
|
|
507
547
|
}
|
|
508
548
|
`;
|
|
509
549
|
}
|
|
550
|
+
function blendHex(colorA, colorB, ratio) {
|
|
551
|
+
const a = parseHex(colorA);
|
|
552
|
+
const b = parseHex(colorB);
|
|
553
|
+
const r = Math.round(a.r + (b.r - a.r) * ratio);
|
|
554
|
+
const g = Math.round(a.g + (b.g - a.g) * ratio);
|
|
555
|
+
const bl = Math.round(a.b + (b.b - a.b) * ratio);
|
|
556
|
+
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${bl.toString(16).padStart(2, "0")}`;
|
|
557
|
+
}
|
|
558
|
+
function parseHex(hex) {
|
|
559
|
+
const h = hex.replace("#", "");
|
|
560
|
+
const full = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
|
|
561
|
+
return {
|
|
562
|
+
r: parseInt(full.slice(0, 2), 16),
|
|
563
|
+
g: parseInt(full.slice(2, 4), 16),
|
|
564
|
+
b: parseInt(full.slice(4, 6), 16)
|
|
565
|
+
};
|
|
566
|
+
}
|
|
510
567
|
function buildSwitcherScript(paletteNames) {
|
|
511
568
|
return `(function () {
|
|
512
569
|
const PALETTES = ${JSON.stringify(paletteNames)};
|
|
@@ -706,8 +763,17 @@ function wireI18n(config, manifests) {
|
|
|
706
763
|
}
|
|
707
764
|
files["src/i18n/utils.ts"] = generateI18nUtils(config);
|
|
708
765
|
files["src/pages/[locale]/index.astro"] = generateLocaleIndexPage(config, manifests ?? []);
|
|
766
|
+
files["src/pages/index.astro"] = generateRootRedirect(config);
|
|
709
767
|
return ok(files);
|
|
710
768
|
}
|
|
769
|
+
function generateRootRedirect(config) {
|
|
770
|
+
const needsPrerender = config.renderMode === "server" || config.renderMode === "hybrid";
|
|
771
|
+
const prerenderLine = needsPrerender ? "export const prerender = true;\n" : "";
|
|
772
|
+
return `---
|
|
773
|
+
${prerenderLine}return Astro.redirect("/${config.defaultLocale}/");
|
|
774
|
+
---
|
|
775
|
+
`;
|
|
776
|
+
}
|
|
711
777
|
function generateI18nUtils(config) {
|
|
712
778
|
const localesArray = config.locales.map((locale) => `"${locale}"`).join(", ");
|
|
713
779
|
return `export const locales = [${localesArray}] as const;
|
|
@@ -741,22 +807,27 @@ export function t<T>(
|
|
|
741
807
|
`;
|
|
742
808
|
}
|
|
743
809
|
function generateLocaleIndexPage(config, manifests) {
|
|
744
|
-
const
|
|
810
|
+
const LAYOUT_CATEGORIES2 = /* @__PURE__ */ new Set(["header", "footer"]);
|
|
811
|
+
const contentBlocks = manifests.filter(
|
|
812
|
+
(m) => m.type === "section" && !LAYOUT_CATEGORIES2.has(m.category ?? "")
|
|
813
|
+
);
|
|
745
814
|
const imports = [];
|
|
746
815
|
const tags = [];
|
|
747
|
-
for (const block of
|
|
816
|
+
for (const block of contentBlocks) {
|
|
748
817
|
const componentName = block.name.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
749
|
-
imports.push(`import ${componentName} from '
|
|
818
|
+
imports.push(`import ${componentName} from '../../components/sections/${block.name}.astro';`);
|
|
750
819
|
tags.push(` <${componentName} />`);
|
|
751
820
|
}
|
|
752
821
|
const importSection = imports.length > 0 ? imports.join("\n") + "\n" : "";
|
|
753
822
|
const blockSection = tags.length > 0 ? "\n" + tags.join("\n") + "\n " : "\n <h1>" + config.projectName + "</h1>\n <p>Locale: {locale}</p>\n ";
|
|
823
|
+
const needsPrerender = config.renderMode === "server" || config.renderMode === "hybrid";
|
|
824
|
+
const prerenderLine = needsPrerender ? "export const prerender = true;\n" : "";
|
|
754
825
|
return `---
|
|
755
|
-
import { locales } from "
|
|
756
|
-
import Layout from "
|
|
757
|
-
${importSection}
|
|
826
|
+
import { locales } from "../../i18n/utils";
|
|
827
|
+
import Layout from "../../layouts/Layout.astro";
|
|
828
|
+
${importSection}${prerenderLine}
|
|
758
829
|
export function getStaticPaths() {
|
|
759
|
-
return locales.map((locale) => ({ params: { locale } }));
|
|
830
|
+
return locales.map((locale: string) => ({ params: { locale } }));
|
|
760
831
|
}
|
|
761
832
|
|
|
762
833
|
const { locale } = Astro.params;
|
|
@@ -2003,9 +2074,10 @@ import {
|
|
|
2003
2074
|
mkdirSync as mkdirSync2,
|
|
2004
2075
|
statSync as statSync2,
|
|
2005
2076
|
writeFileSync as writeFileSync2,
|
|
2006
|
-
rmSync
|
|
2077
|
+
rmSync,
|
|
2078
|
+
cpSync
|
|
2007
2079
|
} from "fs";
|
|
2008
|
-
import { join as join3 } from "path";
|
|
2080
|
+
import { join as join3, resolve } from "path";
|
|
2009
2081
|
import { homedir as homedir2 } from "os";
|
|
2010
2082
|
import { createRequire } from "module";
|
|
2011
2083
|
var DEFAULT_CONFIG2 = {
|
|
@@ -2061,6 +2133,15 @@ async function fetchBlock(blockName, config = {}) {
|
|
|
2061
2133
|
if (!cfg.force && isCacheValid2(blockCacheDir, cfg.maxCacheAge)) {
|
|
2062
2134
|
return loadFromCache2(blockName, blockCacheDir);
|
|
2063
2135
|
}
|
|
2136
|
+
const localBlocksDir = process.env.FORNIX_LOCAL_BLOCKS_DIR;
|
|
2137
|
+
if (localBlocksDir) {
|
|
2138
|
+
const localBlockPath = resolve(process.cwd(), localBlocksDir, blockName);
|
|
2139
|
+
if (existsSync2(localBlockPath)) {
|
|
2140
|
+
mkdirSync2(cfg.cacheDir, { recursive: true });
|
|
2141
|
+
cpSync(localBlockPath, blockCacheDir, { recursive: true, force: true });
|
|
2142
|
+
return loadFromCache2(blockName, blockCacheDir);
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2064
2145
|
try {
|
|
2065
2146
|
const source = `gh:${cfg.repo}/${cfg.blocksPrefix}/${blockName}#${cfg.ref}`;
|
|
2066
2147
|
mkdirSync2(cfg.cacheDir, { recursive: true });
|
|
@@ -2075,10 +2156,14 @@ async function fetchBlock(blockName, config = {}) {
|
|
|
2075
2156
|
return loadFromCache2(blockName, blockCacheDir);
|
|
2076
2157
|
}
|
|
2077
2158
|
const message = error instanceof Error ? error.message : String(error);
|
|
2159
|
+
let refinedMessage = message;
|
|
2160
|
+
if (message.includes("404") || message.includes("zlib: invalid distance code")) {
|
|
2161
|
+
refinedMessage += ` (This usually means the block doesn't exist on the remote branch. If developing locally, set FORNIX_LOCAL_BLOCKS_DIR or push your block to GitHub.)`;
|
|
2162
|
+
}
|
|
2078
2163
|
return err({
|
|
2079
2164
|
kind: "FetchError",
|
|
2080
2165
|
blockName,
|
|
2081
|
-
message: `Failed to fetch block '${blockName}': ${
|
|
2166
|
+
message: `Failed to fetch block '${blockName}': ${refinedMessage}`
|
|
2082
2167
|
});
|
|
2083
2168
|
}
|
|
2084
2169
|
}
|
|
@@ -2193,16 +2278,38 @@ async function runManualFlow(input) {
|
|
|
2193
2278
|
]
|
|
2194
2279
|
});
|
|
2195
2280
|
if (p.isCancel(cssEngine)) return handleCancel();
|
|
2196
|
-
const
|
|
2197
|
-
|
|
2198
|
-
|
|
2281
|
+
const headerOptions = buildCategoryOptions(input.manifests, "header");
|
|
2282
|
+
const footerOptions = buildCategoryOptions(input.manifests, "footer");
|
|
2283
|
+
const contentOptions = buildContentBlockOptions(input.manifests);
|
|
2284
|
+
let selectedHeader;
|
|
2285
|
+
let selectedFooter;
|
|
2286
|
+
let selectedContentBlocks = [];
|
|
2287
|
+
if (headerOptions.length > 0) {
|
|
2288
|
+
const noneOption = { value: "__none__", label: "None", hint: "No header" };
|
|
2289
|
+
const headerChoice = await p.select({
|
|
2290
|
+
message: "Choose a header (appears on every page)",
|
|
2291
|
+
options: [noneOption, ...headerOptions]
|
|
2292
|
+
});
|
|
2293
|
+
if (p.isCancel(headerChoice)) return handleCancel();
|
|
2294
|
+
if (headerChoice !== "__none__") selectedHeader = headerChoice;
|
|
2295
|
+
}
|
|
2296
|
+
if (contentOptions.length > 0) {
|
|
2199
2297
|
const blocks = await p.multiselect({
|
|
2200
|
-
message: "Select blocks
|
|
2201
|
-
options:
|
|
2298
|
+
message: "Select content blocks (space to toggle, enter to confirm)",
|
|
2299
|
+
options: contentOptions,
|
|
2202
2300
|
required: false
|
|
2203
2301
|
});
|
|
2204
2302
|
if (p.isCancel(blocks)) return handleCancel();
|
|
2205
|
-
|
|
2303
|
+
selectedContentBlocks = blocks;
|
|
2304
|
+
}
|
|
2305
|
+
if (footerOptions.length > 0) {
|
|
2306
|
+
const noneOption = { value: "__none__", label: "None", hint: "No footer" };
|
|
2307
|
+
const footerChoice = await p.select({
|
|
2308
|
+
message: "Choose a footer (appears on every page)",
|
|
2309
|
+
options: [noneOption, ...footerOptions]
|
|
2310
|
+
});
|
|
2311
|
+
if (p.isCancel(footerChoice)) return handleCancel();
|
|
2312
|
+
if (footerChoice !== "__none__") selectedFooter = footerChoice;
|
|
2206
2313
|
}
|
|
2207
2314
|
const localesInput = await p.text({
|
|
2208
2315
|
message: "Locales (comma-separated, e.g. en,es,ar)",
|
|
@@ -2232,6 +2339,24 @@ async function runManualFlow(input) {
|
|
|
2232
2339
|
if (p.isCancel(switcherChoice)) return handleCancel();
|
|
2233
2340
|
themeSwitcher = switcherChoice;
|
|
2234
2341
|
}
|
|
2342
|
+
if (!selectedHeader && headerOptions.length > 0) {
|
|
2343
|
+
const needsNav = locales.length >= 2 || themeSwitcher;
|
|
2344
|
+
if (needsNav) {
|
|
2345
|
+
const autoHeader = await p.confirm({
|
|
2346
|
+
message: `You enabled ${locales.length >= 2 ? "multiple locales" : "theme switching"} \u2014 add a header for navigation?`,
|
|
2347
|
+
initialValue: true
|
|
2348
|
+
});
|
|
2349
|
+
if (p.isCancel(autoHeader)) return handleCancel();
|
|
2350
|
+
if (autoHeader) {
|
|
2351
|
+
selectedHeader = headerOptions[0].value;
|
|
2352
|
+
console.log(pc.dim(` Adding ${selectedHeader} for navigation.`));
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
const selectedBlocks = [];
|
|
2357
|
+
if (selectedHeader) selectedBlocks.push(selectedHeader);
|
|
2358
|
+
selectedBlocks.push(...selectedContentBlocks);
|
|
2359
|
+
if (selectedFooter) selectedBlocks.push(selectedFooter);
|
|
2235
2360
|
const config = {
|
|
2236
2361
|
projectName: projectName.trim(),
|
|
2237
2362
|
projectDir: `./${projectName.trim()}`,
|
|
@@ -2271,8 +2396,18 @@ function handleCancel() {
|
|
|
2271
2396
|
p.cancel("Operation cancelled.");
|
|
2272
2397
|
return null;
|
|
2273
2398
|
}
|
|
2274
|
-
function
|
|
2275
|
-
|
|
2399
|
+
function buildCategoryOptions(manifests, category) {
|
|
2400
|
+
return Object.values(manifests).filter((block) => (block.category ?? "other") === category).map((block) => ({
|
|
2401
|
+
value: block.name,
|
|
2402
|
+
label: block.name,
|
|
2403
|
+
hint: block.description
|
|
2404
|
+
}));
|
|
2405
|
+
}
|
|
2406
|
+
function buildContentBlockOptions(manifests) {
|
|
2407
|
+
const LAYOUT_CATEGORIES2 = /* @__PURE__ */ new Set(["header", "footer"]);
|
|
2408
|
+
const blocks = Object.values(manifests).filter(
|
|
2409
|
+
(block) => !LAYOUT_CATEGORIES2.has(block.category ?? "other")
|
|
2410
|
+
);
|
|
2276
2411
|
const categories = /* @__PURE__ */ new Map();
|
|
2277
2412
|
for (const block of blocks) {
|
|
2278
2413
|
const category = block.category ?? "other";
|
|
@@ -2286,7 +2421,7 @@ function buildBlockOptions(manifests) {
|
|
|
2286
2421
|
for (const block of categoryBlocks) {
|
|
2287
2422
|
options.push({
|
|
2288
2423
|
value: block.name,
|
|
2289
|
-
label:
|
|
2424
|
+
label: block.name,
|
|
2290
2425
|
hint: `${category} \u2014 ${block.description}`
|
|
2291
2426
|
});
|
|
2292
2427
|
}
|
|
@@ -4104,17 +4239,60 @@ function preResolveDependencies(selected, manifests) {
|
|
|
4104
4239
|
// src/cli/commands/add.ts
|
|
4105
4240
|
import { defineCommand as defineCommand2 } from "citty";
|
|
4106
4241
|
import pc4 from "picocolors";
|
|
4107
|
-
import { readFileSync as
|
|
4108
|
-
import { join as
|
|
4242
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
|
|
4243
|
+
import { join as join8, dirname as dirname3 } from "path";
|
|
4109
4244
|
|
|
4110
4245
|
// src/scaffold/page-updater.ts
|
|
4246
|
+
import { readFileSync as readFileSync5, existsSync as existsSync4 } from "fs";
|
|
4247
|
+
import { join as join7 } from "path";
|
|
4248
|
+
var CATEGORY_ORDER = {
|
|
4249
|
+
header: 0,
|
|
4250
|
+
hero: 1,
|
|
4251
|
+
features: 2,
|
|
4252
|
+
pricing: 3,
|
|
4253
|
+
testimonials: 4,
|
|
4254
|
+
faq: 5,
|
|
4255
|
+
cta: 6,
|
|
4256
|
+
contact: 7,
|
|
4257
|
+
theme: 8,
|
|
4258
|
+
footer: 9
|
|
4259
|
+
};
|
|
4260
|
+
var DEFAULT_PRIORITY = 5;
|
|
4261
|
+
var LAYOUT_CATEGORIES = /* @__PURE__ */ new Set(["header", "footer"]);
|
|
4111
4262
|
function blockNameToComponentName(blockName) {
|
|
4112
4263
|
return blockName.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
4113
4264
|
}
|
|
4114
4265
|
function blockNameToImportPath(blockName) {
|
|
4115
4266
|
return `../components/sections/${blockName}.astro`;
|
|
4116
4267
|
}
|
|
4117
|
-
function
|
|
4268
|
+
function blockNameToLayoutImportPath(blockName) {
|
|
4269
|
+
return `../components/sections/${blockName}.astro`;
|
|
4270
|
+
}
|
|
4271
|
+
function getBlockCategory(blockName, projectDir) {
|
|
4272
|
+
if (projectDir) {
|
|
4273
|
+
try {
|
|
4274
|
+
const fornixPath = join7(projectDir, "fornix.json");
|
|
4275
|
+
if (existsSync4(fornixPath)) {
|
|
4276
|
+
const fornix = JSON.parse(readFileSync5(fornixPath, "utf-8"));
|
|
4277
|
+
const block = fornix.blocks?.find((b) => b.name === blockName);
|
|
4278
|
+
if (block?.category) return block.category;
|
|
4279
|
+
}
|
|
4280
|
+
} catch {
|
|
4281
|
+
}
|
|
4282
|
+
}
|
|
4283
|
+
for (const prefix of Object.keys(CATEGORY_ORDER)) {
|
|
4284
|
+
if (blockName.startsWith(prefix)) return prefix;
|
|
4285
|
+
}
|
|
4286
|
+
return "other";
|
|
4287
|
+
}
|
|
4288
|
+
function getPriority(category) {
|
|
4289
|
+
return CATEGORY_ORDER[category] ?? DEFAULT_PRIORITY;
|
|
4290
|
+
}
|
|
4291
|
+
function isLayoutBlock(blockName, projectDir) {
|
|
4292
|
+
const category = getBlockCategory(blockName, projectDir);
|
|
4293
|
+
return LAYOUT_CATEGORIES.has(category);
|
|
4294
|
+
}
|
|
4295
|
+
function addBlockToPage(pageContent, blockName, projectDir) {
|
|
4118
4296
|
const componentName = blockNameToComponentName(blockName);
|
|
4119
4297
|
const importPath = blockNameToImportPath(blockName);
|
|
4120
4298
|
if (pageContent.includes(importPath) || pageContent.includes(`import ${componentName}`)) {
|
|
@@ -4122,14 +4300,67 @@ function addBlockToPage(pageContent, blockName) {
|
|
|
4122
4300
|
}
|
|
4123
4301
|
const importLine = `import ${componentName} from '${importPath}';`;
|
|
4124
4302
|
const componentTag = ` <${componentName} />`;
|
|
4303
|
+
const newCategory = getBlockCategory(blockName, projectDir);
|
|
4304
|
+
const newPriority = getPriority(newCategory);
|
|
4125
4305
|
const frontmatterEnd = pageContent.indexOf("---", pageContent.indexOf("---") + 3);
|
|
4126
4306
|
if (frontmatterEnd === -1) {
|
|
4127
4307
|
return pageContent;
|
|
4128
4308
|
}
|
|
4129
4309
|
let updated = pageContent.slice(0, frontmatterEnd) + importLine + "\n" + pageContent.slice(frontmatterEnd);
|
|
4310
|
+
const mainOpenMatch = updated.match(/<main[^>]*>/);
|
|
4130
4311
|
const mainCloseIndex = updated.lastIndexOf("</main>");
|
|
4131
|
-
if (mainCloseIndex
|
|
4132
|
-
|
|
4312
|
+
if (!mainOpenMatch || mainCloseIndex === -1) {
|
|
4313
|
+
return updated;
|
|
4314
|
+
}
|
|
4315
|
+
const mainOpenEnd = mainOpenMatch.index + mainOpenMatch[0].length;
|
|
4316
|
+
const mainContent = updated.slice(mainOpenEnd, mainCloseIndex);
|
|
4317
|
+
const tagPattern = /^(\s*<([A-Z][A-Za-z]*)\s*\/>)\s*$/gm;
|
|
4318
|
+
let insertOffset = mainCloseIndex;
|
|
4319
|
+
let match;
|
|
4320
|
+
while ((match = tagPattern.exec(mainContent)) !== null) {
|
|
4321
|
+
const existingComponentName = match[2];
|
|
4322
|
+
const existingBlockName = componentNameToBlockName(existingComponentName);
|
|
4323
|
+
const existingCategory = getBlockCategory(existingBlockName, projectDir);
|
|
4324
|
+
const existingPriority = getPriority(existingCategory);
|
|
4325
|
+
if (existingPriority > newPriority) {
|
|
4326
|
+
insertOffset = mainOpenEnd + match.index;
|
|
4327
|
+
break;
|
|
4328
|
+
}
|
|
4329
|
+
}
|
|
4330
|
+
updated = updated.slice(0, insertOffset) + componentTag + "\n" + updated.slice(insertOffset);
|
|
4331
|
+
return updated;
|
|
4332
|
+
}
|
|
4333
|
+
function addBlockToLayout(layoutContent, blockName, projectDir) {
|
|
4334
|
+
const componentName = blockNameToComponentName(blockName);
|
|
4335
|
+
const importPath = blockNameToLayoutImportPath(blockName);
|
|
4336
|
+
if (layoutContent.includes(importPath) || layoutContent.includes(`import ${componentName}`)) {
|
|
4337
|
+
return layoutContent;
|
|
4338
|
+
}
|
|
4339
|
+
const importLine = `import ${componentName} from '${importPath}';`;
|
|
4340
|
+
const componentTag = ` <${componentName} />`;
|
|
4341
|
+
const category = getBlockCategory(blockName, projectDir);
|
|
4342
|
+
const frontmatterEnd = layoutContent.indexOf("---", layoutContent.indexOf("---") + 3);
|
|
4343
|
+
if (frontmatterEnd === -1) {
|
|
4344
|
+
return layoutContent;
|
|
4345
|
+
}
|
|
4346
|
+
let updated = layoutContent.slice(0, frontmatterEnd) + importLine + "\n" + layoutContent.slice(frontmatterEnd);
|
|
4347
|
+
if (category === "header") {
|
|
4348
|
+
const slotIndex = updated.indexOf("<slot");
|
|
4349
|
+
const mainIndex = updated.indexOf("<main");
|
|
4350
|
+
const insertBefore = mainIndex !== -1 ? mainIndex : slotIndex;
|
|
4351
|
+
if (insertBefore !== -1) {
|
|
4352
|
+
updated = updated.slice(0, insertBefore) + componentTag + "\n" + updated.slice(insertBefore);
|
|
4353
|
+
}
|
|
4354
|
+
} else if (category === "footer") {
|
|
4355
|
+
const mainCloseIndex = updated.indexOf("</main>");
|
|
4356
|
+
const slotMatch = updated.match(/<slot\s*\/>/);
|
|
4357
|
+
if (mainCloseIndex !== -1) {
|
|
4358
|
+
const afterMain = mainCloseIndex + "</main>".length;
|
|
4359
|
+
updated = updated.slice(0, afterMain) + "\n" + componentTag + updated.slice(afterMain);
|
|
4360
|
+
} else if (slotMatch) {
|
|
4361
|
+
const afterSlot = slotMatch.index + slotMatch[0].length;
|
|
4362
|
+
updated = updated.slice(0, afterSlot) + "\n" + componentTag + updated.slice(afterSlot);
|
|
4363
|
+
}
|
|
4133
4364
|
}
|
|
4134
4365
|
return updated;
|
|
4135
4366
|
}
|
|
@@ -4152,6 +4383,9 @@ function removeBlockFromPage(pageContent, blockName) {
|
|
|
4152
4383
|
function escapeRegex(str) {
|
|
4153
4384
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4154
4385
|
}
|
|
4386
|
+
function componentNameToBlockName(componentName) {
|
|
4387
|
+
return componentName.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
|
|
4388
|
+
}
|
|
4155
4389
|
|
|
4156
4390
|
// src/cli/commands/add.ts
|
|
4157
4391
|
var addCommand = defineCommand2({
|
|
@@ -4184,14 +4418,14 @@ var addCommand = defineCommand2({
|
|
|
4184
4418
|
async run({ args: args2 }) {
|
|
4185
4419
|
const typedArgs = args2;
|
|
4186
4420
|
const cwd = process.cwd();
|
|
4187
|
-
const manifestPath =
|
|
4188
|
-
if (!
|
|
4421
|
+
const manifestPath = join8(cwd, "fornix.json");
|
|
4422
|
+
if (!existsSync5(manifestPath)) {
|
|
4189
4423
|
console.error(
|
|
4190
4424
|
pc4.red("\u2717 No fornix.json found. Are you in a Fornix project?")
|
|
4191
4425
|
);
|
|
4192
4426
|
process.exit(1);
|
|
4193
4427
|
}
|
|
4194
|
-
const manifestRaw =
|
|
4428
|
+
const manifestRaw = readFileSync6(manifestPath, "utf-8");
|
|
4195
4429
|
const manifest2 = JSON.parse(manifestRaw);
|
|
4196
4430
|
const registryResult = await fetchRegistryIndex();
|
|
4197
4431
|
if (!isOk(registryResult)) {
|
|
@@ -4220,6 +4454,27 @@ var addCommand = defineCommand2({
|
|
|
4220
4454
|
return;
|
|
4221
4455
|
}
|
|
4222
4456
|
const blocksToAdd = resolveDependencies2(blockName, installedNames, manifests);
|
|
4457
|
+
for (const name of blocksToAdd) {
|
|
4458
|
+
const m = manifests[name];
|
|
4459
|
+
if (m?.conflicts && m.conflicts.length > 0) {
|
|
4460
|
+
for (const conflictName of m.conflicts) {
|
|
4461
|
+
if (installedNames.has(conflictName)) {
|
|
4462
|
+
console.error(
|
|
4463
|
+
pc4.red(
|
|
4464
|
+
`\u2717 Block '${name}' conflicts with installed block '${conflictName}'.`
|
|
4465
|
+
)
|
|
4466
|
+
);
|
|
4467
|
+
console.log(
|
|
4468
|
+
pc4.dim(
|
|
4469
|
+
` Remove '${conflictName}' first: npx create-fornix remove ${conflictName}`
|
|
4470
|
+
)
|
|
4471
|
+
);
|
|
4472
|
+
process.exitCode = 1;
|
|
4473
|
+
return;
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4223
4478
|
for (const name of blocksToAdd) {
|
|
4224
4479
|
const m = manifests[name];
|
|
4225
4480
|
if (m?.requiredMode && manifest2.renderMode !== m.requiredMode) {
|
|
@@ -4257,10 +4512,17 @@ var addCommand = defineCommand2({
|
|
|
4257
4512
|
return;
|
|
4258
4513
|
}
|
|
4259
4514
|
filesToWrite.push({
|
|
4260
|
-
path:
|
|
4515
|
+
path: join8(cwd, file.destination),
|
|
4261
4516
|
content
|
|
4262
4517
|
});
|
|
4263
4518
|
}
|
|
4519
|
+
if (sources["default-content.json"]) {
|
|
4520
|
+
const collection = bManifest.type === "section" ? "sections" : bManifest.type + "s";
|
|
4521
|
+
filesToWrite.push({
|
|
4522
|
+
path: join8(cwd, `src/content/${collection}/${name}.json`),
|
|
4523
|
+
content: sources["default-content.json"]
|
|
4524
|
+
});
|
|
4525
|
+
}
|
|
4264
4526
|
}
|
|
4265
4527
|
if (typedArgs["dry-run"]) {
|
|
4266
4528
|
console.log(pc4.bold("\n Dry run \u2014 no files written\n"));
|
|
@@ -4296,18 +4558,31 @@ var addCommand = defineCommand2({
|
|
|
4296
4558
|
});
|
|
4297
4559
|
}
|
|
4298
4560
|
writeFileSync5(manifestPath, JSON.stringify(manifest2, null, 2) + "\n");
|
|
4299
|
-
const indexPath =
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4561
|
+
const indexPath = join8(cwd, "src/pages/index.astro");
|
|
4562
|
+
const layoutPath = join8(cwd, "src/layouts/Layout.astro");
|
|
4563
|
+
for (const name of blocksToAdd) {
|
|
4564
|
+
const bManifest = manifests[name];
|
|
4565
|
+
if (!bManifest || bManifest.type !== "section") continue;
|
|
4566
|
+
if (isLayoutBlock(name, cwd)) {
|
|
4567
|
+
if (existsSync5(layoutPath)) {
|
|
4568
|
+
const layoutContent = readFileSync6(layoutPath, "utf-8");
|
|
4569
|
+
const updated = addBlockToLayout(layoutContent, name, cwd);
|
|
4570
|
+
if (updated !== layoutContent) {
|
|
4571
|
+
writeFileSync5(layoutPath, updated);
|
|
4572
|
+
if (typedArgs.verbose) {
|
|
4573
|
+
console.log(` ${pc4.dim("\u270E")} updated Layout.astro (${name})`);
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
}
|
|
4577
|
+
} else {
|
|
4578
|
+
if (existsSync5(indexPath)) {
|
|
4579
|
+
let pageContent = readFileSync6(indexPath, "utf-8");
|
|
4580
|
+
pageContent = addBlockToPage(pageContent, name, cwd);
|
|
4581
|
+
writeFileSync5(indexPath, pageContent);
|
|
4582
|
+
if (typedArgs.verbose) {
|
|
4583
|
+
console.log(` ${pc4.dim("\u270E")} updated index.astro (${name})`);
|
|
4584
|
+
}
|
|
4306
4585
|
}
|
|
4307
|
-
}
|
|
4308
|
-
writeFileSync5(indexPath, pageContent);
|
|
4309
|
-
if (typedArgs.verbose) {
|
|
4310
|
-
console.log(` ${pc4.dim("\u270E")} updated index.astro`);
|
|
4311
4586
|
}
|
|
4312
4587
|
}
|
|
4313
4588
|
console.log();
|
|
@@ -4350,8 +4625,8 @@ function resolveDependencies2(blockName, installedNames, manifests) {
|
|
|
4350
4625
|
// src/cli/commands/remove.ts
|
|
4351
4626
|
import { defineCommand as defineCommand3 } from "citty";
|
|
4352
4627
|
import pc5 from "picocolors";
|
|
4353
|
-
import { readFileSync as
|
|
4354
|
-
import { join as
|
|
4628
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync6, unlinkSync, readdirSync as readdirSync3, rmdirSync } from "fs";
|
|
4629
|
+
import { join as join9, dirname as dirname4 } from "path";
|
|
4355
4630
|
var removeCommand = defineCommand3({
|
|
4356
4631
|
meta: {
|
|
4357
4632
|
name: "remove",
|
|
@@ -4382,14 +4657,14 @@ var removeCommand = defineCommand3({
|
|
|
4382
4657
|
async run({ args: args2 }) {
|
|
4383
4658
|
const typedArgs = args2;
|
|
4384
4659
|
const cwd = process.cwd();
|
|
4385
|
-
const manifestPath =
|
|
4386
|
-
if (!
|
|
4660
|
+
const manifestPath = join9(cwd, "fornix.json");
|
|
4661
|
+
if (!existsSync6(manifestPath)) {
|
|
4387
4662
|
console.error(
|
|
4388
4663
|
pc5.red("\u2717 No fornix.json found. Are you in a Fornix project?")
|
|
4389
4664
|
);
|
|
4390
4665
|
process.exit(1);
|
|
4391
4666
|
}
|
|
4392
|
-
const manifestRaw =
|
|
4667
|
+
const manifestRaw = readFileSync7(manifestPath, "utf-8");
|
|
4393
4668
|
const manifest2 = JSON.parse(manifestRaw);
|
|
4394
4669
|
const registryResult = await fetchRegistryIndex();
|
|
4395
4670
|
if (!isOk(registryResult)) {
|
|
@@ -4422,8 +4697,8 @@ var removeCommand = defineCommand3({
|
|
|
4422
4697
|
const filesToRemove = [];
|
|
4423
4698
|
if (blockManifest) {
|
|
4424
4699
|
for (const file of blockManifest.files) {
|
|
4425
|
-
const filePath =
|
|
4426
|
-
if (
|
|
4700
|
+
const filePath = join9(cwd, file.destination);
|
|
4701
|
+
if (existsSync6(filePath)) {
|
|
4427
4702
|
filesToRemove.push(filePath);
|
|
4428
4703
|
}
|
|
4429
4704
|
}
|
|
@@ -4446,14 +4721,15 @@ var removeCommand = defineCommand3({
|
|
|
4446
4721
|
}
|
|
4447
4722
|
manifest2.blocks = manifest2.blocks.filter((b) => b.name !== blockName);
|
|
4448
4723
|
writeFileSync6(manifestPath, JSON.stringify(manifest2, null, 2) + "\n");
|
|
4449
|
-
const
|
|
4450
|
-
|
|
4451
|
-
|
|
4724
|
+
const targetFile = isLayoutBlock(blockName, cwd) ? join9(cwd, "src/layouts/Layout.astro") : join9(cwd, "src/pages/index.astro");
|
|
4725
|
+
const targetLabel = isLayoutBlock(blockName, cwd) ? "Layout.astro" : "index.astro";
|
|
4726
|
+
if (existsSync6(targetFile)) {
|
|
4727
|
+
const original = readFileSync7(targetFile, "utf-8");
|
|
4452
4728
|
const updated = removeBlockFromPage(original, blockName);
|
|
4453
4729
|
if (updated !== original) {
|
|
4454
|
-
writeFileSync6(
|
|
4730
|
+
writeFileSync6(targetFile, updated);
|
|
4455
4731
|
if (typedArgs.verbose) {
|
|
4456
|
-
console.log(` ${pc5.dim("\u270E")} updated
|
|
4732
|
+
console.log(` ${pc5.dim("\u270E")} updated ${targetLabel}`);
|
|
4457
4733
|
}
|
|
4458
4734
|
}
|
|
4459
4735
|
}
|
|
@@ -4628,8 +4904,8 @@ function printFormatted(blocks, verbose) {
|
|
|
4628
4904
|
// src/cli/commands/status.ts
|
|
4629
4905
|
import { defineCommand as defineCommand5 } from "citty";
|
|
4630
4906
|
import pc7 from "picocolors";
|
|
4631
|
-
import { readFileSync as
|
|
4632
|
-
import { join as
|
|
4907
|
+
import { readFileSync as readFileSync8, existsSync as existsSync7 } from "fs";
|
|
4908
|
+
import { join as join10 } from "path";
|
|
4633
4909
|
var statusCommand = defineCommand5({
|
|
4634
4910
|
meta: {
|
|
4635
4911
|
name: "status",
|
|
@@ -4650,8 +4926,8 @@ var statusCommand = defineCommand5({
|
|
|
4650
4926
|
run({ args: args2 }) {
|
|
4651
4927
|
const typedArgs = args2;
|
|
4652
4928
|
const cwd = process.cwd();
|
|
4653
|
-
const manifestPath =
|
|
4654
|
-
if (!
|
|
4929
|
+
const manifestPath = join10(cwd, "fornix.json");
|
|
4930
|
+
if (!existsSync7(manifestPath)) {
|
|
4655
4931
|
console.error(
|
|
4656
4932
|
pc7.red("\u2717 No fornix.json found in the current directory.")
|
|
4657
4933
|
);
|
|
@@ -4664,7 +4940,7 @@ var statusCommand = defineCommand5({
|
|
|
4664
4940
|
}
|
|
4665
4941
|
let manifest2;
|
|
4666
4942
|
try {
|
|
4667
|
-
const raw =
|
|
4943
|
+
const raw = readFileSync8(manifestPath, "utf-8");
|
|
4668
4944
|
manifest2 = JSON.parse(raw);
|
|
4669
4945
|
} catch {
|
|
4670
4946
|
console.error(pc7.red("\u2717 Failed to parse fornix.json."));
|
|
@@ -4745,8 +5021,8 @@ function printStatus(manifest2, verbose) {
|
|
|
4745
5021
|
// src/cli/commands/doctor.ts
|
|
4746
5022
|
import { defineCommand as defineCommand6 } from "citty";
|
|
4747
5023
|
import pc8 from "picocolors";
|
|
4748
|
-
import { readFileSync as
|
|
4749
|
-
import { join as
|
|
5024
|
+
import { readFileSync as readFileSync9, existsSync as existsSync8 } from "fs";
|
|
5025
|
+
import { join as join11 } from "path";
|
|
4750
5026
|
var doctorCommand = defineCommand6({
|
|
4751
5027
|
meta: {
|
|
4752
5028
|
name: "doctor",
|
|
@@ -4761,7 +5037,7 @@ var doctorCommand = defineCommand6({
|
|
|
4761
5037
|
},
|
|
4762
5038
|
async run({ args: args2 }) {
|
|
4763
5039
|
const cwd = process.cwd();
|
|
4764
|
-
const manifestPath =
|
|
5040
|
+
const manifestPath = join11(cwd, "fornix.json");
|
|
4765
5041
|
let hasErrors = false;
|
|
4766
5042
|
const errors = [];
|
|
4767
5043
|
function reportError(msg) {
|
|
@@ -4771,7 +5047,7 @@ var doctorCommand = defineCommand6({
|
|
|
4771
5047
|
console.error(pc8.red(`\u2717 ${msg}`));
|
|
4772
5048
|
}
|
|
4773
5049
|
}
|
|
4774
|
-
if (!
|
|
5050
|
+
if (!existsSync8(manifestPath)) {
|
|
4775
5051
|
reportError("No fornix.json found in the current directory.");
|
|
4776
5052
|
if (args2.json) {
|
|
4777
5053
|
console.log(JSON.stringify({ healthy: false, errors }));
|
|
@@ -4780,7 +5056,7 @@ var doctorCommand = defineCommand6({
|
|
|
4780
5056
|
}
|
|
4781
5057
|
let manifest2;
|
|
4782
5058
|
try {
|
|
4783
|
-
const raw =
|
|
5059
|
+
const raw = readFileSync9(manifestPath, "utf-8");
|
|
4784
5060
|
manifest2 = JSON.parse(raw);
|
|
4785
5061
|
} catch {
|
|
4786
5062
|
reportError("Failed to parse fornix.json.");
|
|
@@ -4806,8 +5082,8 @@ var doctorCommand = defineCommand6({
|
|
|
4806
5082
|
const bManifest = manifests[block.name];
|
|
4807
5083
|
if (bManifest) {
|
|
4808
5084
|
for (const file of bManifest.files) {
|
|
4809
|
-
const filePath =
|
|
4810
|
-
if (!
|
|
5085
|
+
const filePath = join11(cwd, file.destination);
|
|
5086
|
+
if (!existsSync8(filePath)) {
|
|
4811
5087
|
reportError(`Missing expected file for installed block '${block.name}': ${file.destination}`);
|
|
4812
5088
|
}
|
|
4813
5089
|
}
|
|
@@ -4816,7 +5092,7 @@ var doctorCommand = defineCommand6({
|
|
|
4816
5092
|
for (const [name, bManifest] of Object.entries(manifests)) {
|
|
4817
5093
|
if (!installedBlocks.has(name)) {
|
|
4818
5094
|
const foundOrphaned = bManifest.files.some((file) => {
|
|
4819
|
-
return
|
|
5095
|
+
return existsSync8(join11(cwd, file.destination));
|
|
4820
5096
|
});
|
|
4821
5097
|
if (foundOrphaned) {
|
|
4822
5098
|
reportError(`Orphaned block files detected for '${name}'. The block is not in fornix.json.`);
|
|
@@ -4840,14 +5116,14 @@ var doctorCommand = defineCommand6({
|
|
|
4840
5116
|
if (locale !== "") {
|
|
4841
5117
|
pathFragment = `src/content/${locale}/${subdirectory}/${bManifest.name}.json`;
|
|
4842
5118
|
}
|
|
4843
|
-
if (!
|
|
5119
|
+
if (!existsSync8(join11(cwd, pathFragment))) {
|
|
4844
5120
|
missingContentFiles.push(pathFragment);
|
|
4845
5121
|
}
|
|
4846
5122
|
}
|
|
4847
5123
|
}
|
|
4848
5124
|
}
|
|
4849
5125
|
if (requiresContentConfig) {
|
|
4850
|
-
if (!
|
|
5126
|
+
if (!existsSync8(join11(cwd, "src/content/config.ts"))) {
|
|
4851
5127
|
reportError("Missing expected file: src/content/config.ts");
|
|
4852
5128
|
}
|
|
4853
5129
|
}
|
|
@@ -4938,19 +5214,19 @@ async function listBlocksHandler(args2) {
|
|
|
4938
5214
|
}
|
|
4939
5215
|
|
|
4940
5216
|
// src/mcp/tools/add-block.ts
|
|
4941
|
-
import { readFileSync as
|
|
4942
|
-
import { join as
|
|
5217
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
|
|
5218
|
+
import { join as join12, dirname as dirname5 } from "path";
|
|
4943
5219
|
async function addBlock2(input) {
|
|
4944
5220
|
const { name, variant = "default", projectDirectory } = input;
|
|
4945
|
-
const manifestPath =
|
|
4946
|
-
if (!
|
|
5221
|
+
const manifestPath = join12(projectDirectory, "fornix.json");
|
|
5222
|
+
if (!existsSync9(manifestPath)) {
|
|
4947
5223
|
return err(
|
|
4948
5224
|
new Error("No fornix.json found. Not a Fornix project directory.")
|
|
4949
5225
|
);
|
|
4950
5226
|
}
|
|
4951
5227
|
let manifest2;
|
|
4952
5228
|
try {
|
|
4953
|
-
const raw =
|
|
5229
|
+
const raw = readFileSync10(manifestPath, "utf-8");
|
|
4954
5230
|
manifest2 = JSON.parse(raw);
|
|
4955
5231
|
} catch {
|
|
4956
5232
|
return err(new Error("Failed to parse fornix.json."));
|
|
@@ -5002,7 +5278,7 @@ async function addBlock2(input) {
|
|
|
5002
5278
|
)
|
|
5003
5279
|
);
|
|
5004
5280
|
}
|
|
5005
|
-
const filePath =
|
|
5281
|
+
const filePath = join12(projectDirectory, file.destination);
|
|
5006
5282
|
mkdirSync6(dirname5(filePath), { recursive: true });
|
|
5007
5283
|
writeFileSync7(filePath, content);
|
|
5008
5284
|
filesCreated++;
|
|
@@ -5041,25 +5317,25 @@ function resolveDependencies3(blockName, installedNames, manifests) {
|
|
|
5041
5317
|
|
|
5042
5318
|
// src/mcp/tools/remove-block.ts
|
|
5043
5319
|
import {
|
|
5044
|
-
readFileSync as
|
|
5320
|
+
readFileSync as readFileSync11,
|
|
5045
5321
|
writeFileSync as writeFileSync8,
|
|
5046
|
-
existsSync as
|
|
5322
|
+
existsSync as existsSync10,
|
|
5047
5323
|
unlinkSync as unlinkSync2,
|
|
5048
5324
|
readdirSync as readdirSync4,
|
|
5049
5325
|
rmdirSync as rmdirSync2
|
|
5050
5326
|
} from "fs";
|
|
5051
|
-
import { join as
|
|
5327
|
+
import { join as join13, dirname as dirname6 } from "path";
|
|
5052
5328
|
async function removeBlock(input) {
|
|
5053
5329
|
const { name, force = false, projectDirectory } = input;
|
|
5054
|
-
const manifestPath =
|
|
5055
|
-
if (!
|
|
5330
|
+
const manifestPath = join13(projectDirectory, "fornix.json");
|
|
5331
|
+
if (!existsSync10(manifestPath)) {
|
|
5056
5332
|
return err(
|
|
5057
5333
|
new Error("No fornix.json found. Not a Fornix project directory.")
|
|
5058
5334
|
);
|
|
5059
5335
|
}
|
|
5060
5336
|
let manifest2;
|
|
5061
5337
|
try {
|
|
5062
|
-
const raw =
|
|
5338
|
+
const raw = readFileSync11(manifestPath, "utf-8");
|
|
5063
5339
|
manifest2 = JSON.parse(raw);
|
|
5064
5340
|
} catch {
|
|
5065
5341
|
return err(new Error("Failed to parse fornix.json."));
|
|
@@ -5085,8 +5361,8 @@ async function removeBlock(input) {
|
|
|
5085
5361
|
const filesToRemove = [];
|
|
5086
5362
|
if (blockManifest) {
|
|
5087
5363
|
for (const file of blockManifest.files) {
|
|
5088
|
-
const filePath =
|
|
5089
|
-
if (
|
|
5364
|
+
const filePath = join13(projectDirectory, file.destination);
|
|
5365
|
+
if (existsSync10(filePath)) {
|
|
5090
5366
|
filesToRemove.push(filePath);
|
|
5091
5367
|
}
|
|
5092
5368
|
}
|
|
@@ -5218,17 +5494,17 @@ function zodTypeForSlot(slotType) {
|
|
|
5218
5494
|
}
|
|
5219
5495
|
|
|
5220
5496
|
// src/mcp/tools/get-project-status.ts
|
|
5221
|
-
import { readFileSync as
|
|
5222
|
-
import { join as
|
|
5497
|
+
import { readFileSync as readFileSync12, existsSync as existsSync11 } from "fs";
|
|
5498
|
+
import { join as join14 } from "path";
|
|
5223
5499
|
function getProjectStatus(input) {
|
|
5224
|
-
const manifestPath =
|
|
5225
|
-
if (!
|
|
5500
|
+
const manifestPath = join14(input.projectDirectory, "fornix.json");
|
|
5501
|
+
if (!existsSync11(manifestPath)) {
|
|
5226
5502
|
return err(
|
|
5227
5503
|
new Error("No fornix.json found. Not a Fornix project directory.")
|
|
5228
5504
|
);
|
|
5229
5505
|
}
|
|
5230
5506
|
try {
|
|
5231
|
-
const raw =
|
|
5507
|
+
const raw = readFileSync12(manifestPath, "utf-8");
|
|
5232
5508
|
const manifest2 = JSON.parse(raw);
|
|
5233
5509
|
const blocks = manifest2.blocks;
|
|
5234
5510
|
return ok({
|
|
@@ -5251,7 +5527,7 @@ function getProjectStatus(input) {
|
|
|
5251
5527
|
|
|
5252
5528
|
// src/mcp/tools/scaffold-project.ts
|
|
5253
5529
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync9 } from "fs";
|
|
5254
|
-
import { join as
|
|
5530
|
+
import { join as join15, basename as basename3 } from "path";
|
|
5255
5531
|
var DEFAULT_COLORS2 = {
|
|
5256
5532
|
primary: "#6366f1",
|
|
5257
5533
|
secondary: "#818cf8",
|
|
@@ -5322,8 +5598,8 @@ async function scaffoldProject(input) {
|
|
|
5322
5598
|
const files = result.value.files;
|
|
5323
5599
|
let filesCreated = 0;
|
|
5324
5600
|
for (const [relativePath, content] of Object.entries(files)) {
|
|
5325
|
-
const fullPath =
|
|
5326
|
-
const parentDirectory =
|
|
5601
|
+
const fullPath = join15(projectDirectory, relativePath);
|
|
5602
|
+
const parentDirectory = join15(fullPath, "..");
|
|
5327
5603
|
mkdirSync7(parentDirectory, { recursive: true });
|
|
5328
5604
|
writeFileSync9(fullPath, content, "utf-8");
|
|
5329
5605
|
filesCreated++;
|