create-fornix 0.0.8 → 0.0.10
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 +604 -153
- 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,10 +349,43 @@ 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>
|
|
356
|
+
|
|
357
|
+
<style is:global>
|
|
358
|
+
*, *::before, *::after {
|
|
359
|
+
box-sizing: border-box;
|
|
360
|
+
margin: 0;
|
|
361
|
+
padding: 0;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
html {
|
|
365
|
+
scroll-behavior: smooth;
|
|
366
|
+
-webkit-font-smoothing: antialiased;
|
|
367
|
+
-moz-osx-font-smoothing: grayscale;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
body {
|
|
371
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
372
|
+
background: var(--color-background, #0f172a);
|
|
373
|
+
color: var(--color-foreground, #f8fafc);
|
|
374
|
+
line-height: 1.6;
|
|
375
|
+
min-height: 100vh;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
img, video {
|
|
379
|
+
max-width: 100%;
|
|
380
|
+
height: auto;
|
|
381
|
+
display: block;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
a {
|
|
385
|
+
color: inherit;
|
|
386
|
+
text-decoration: none;
|
|
387
|
+
}
|
|
388
|
+
</style>
|
|
332
389
|
`.trim() + "\n";
|
|
333
390
|
if (config.deployTarget === "cloudflare") {
|
|
334
391
|
const wrangler = {
|
|
@@ -345,6 +402,15 @@ const { title } = Astro.props;${tailwindImport}
|
|
|
345
402
|
}
|
|
346
403
|
return files;
|
|
347
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
|
+
}
|
|
348
414
|
function getAdapterDependencies(config) {
|
|
349
415
|
if (config.renderMode === "static" && config.deployTarget === "static") {
|
|
350
416
|
return {};
|
|
@@ -373,7 +439,7 @@ function generateAstroConfig(config, blocks = []) {
|
|
|
373
439
|
local: "defineConfig"
|
|
374
440
|
});
|
|
375
441
|
const configObject = module.exports.default.$args[0];
|
|
376
|
-
if (config.renderMode === "server") {
|
|
442
|
+
if (config.renderMode === "server" || config.renderMode === "hybrid") {
|
|
377
443
|
configObject.output = "server";
|
|
378
444
|
}
|
|
379
445
|
const adapter = ADAPTER_MAP[config.deployTarget];
|
|
@@ -427,22 +493,11 @@ function generateTailwindConfig(config) {
|
|
|
427
493
|
if (config.cssEngine !== "tailwind") {
|
|
428
494
|
return ok(null);
|
|
429
495
|
}
|
|
430
|
-
const themeBlock = [
|
|
431
|
-
"@theme {",
|
|
432
|
-
" --color-primary: var(--color-primary);",
|
|
433
|
-
" --color-secondary: var(--color-secondary);",
|
|
434
|
-
" --color-accent: var(--color-accent);",
|
|
435
|
-
" --color-background: var(--color-background);",
|
|
436
|
-
" --color-foreground: var(--color-foreground);",
|
|
437
|
-
"}"
|
|
438
|
-
].join("\n");
|
|
439
496
|
const lines = [
|
|
440
497
|
'@import "tailwindcss";',
|
|
441
498
|
'@import "./src/styles/palettes/_current.css";',
|
|
442
499
|
"",
|
|
443
500
|
`@source "./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}";`,
|
|
444
|
-
"",
|
|
445
|
-
themeBlock,
|
|
446
501
|
""
|
|
447
502
|
];
|
|
448
503
|
return ok(lines.join("\n"));
|
|
@@ -480,11 +535,35 @@ function buildPaletteFile(colors) {
|
|
|
480
535
|
const properties = COLOR_TOKENS.map(
|
|
481
536
|
(token) => ` --color-${token}: ${colors[token]};`
|
|
482
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");
|
|
483
544
|
return `:root {
|
|
484
545
|
${properties}
|
|
546
|
+
${derived}
|
|
485
547
|
}
|
|
486
548
|
`;
|
|
487
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
|
+
}
|
|
488
567
|
function buildSwitcherScript(paletteNames) {
|
|
489
568
|
return `(function () {
|
|
490
569
|
const PALETTES = ${JSON.stringify(paletteNames)};
|
|
@@ -615,7 +694,7 @@ function generateContentConfig(blocks) {
|
|
|
615
694
|
'import { defineCollection, z } from "astro:content";'
|
|
616
695
|
];
|
|
617
696
|
const collections = [];
|
|
618
|
-
const
|
|
697
|
+
const dataCollectionNames = /* @__PURE__ */ new Set();
|
|
619
698
|
for (const block of blocks) {
|
|
620
699
|
if (block.collections && block.collections.length > 0) {
|
|
621
700
|
for (const col of block.collections) {
|
|
@@ -632,27 +711,15 @@ function generateContentConfig(blocks) {
|
|
|
632
711
|
}
|
|
633
712
|
const slots = block.ai?.contentSlots;
|
|
634
713
|
if (slots && Object.keys(slots).length > 0) {
|
|
635
|
-
const schemaFields = Object.entries(slots).map(([name, slot]) => ` ${name}: ${zodTypeForSlot(slot)}.optional(),`).join("\n");
|
|
636
714
|
const subdirectory = TYPE_DIRECTORY[block.type] ?? block.type;
|
|
637
|
-
|
|
638
|
-
dataCollections.set(subdirectory, []);
|
|
639
|
-
}
|
|
640
|
-
dataCollections.get(subdirectory).push(
|
|
641
|
-
` // ${block.name}
|
|
642
|
-
z.object({
|
|
643
|
-
${schemaFields}
|
|
644
|
-
})`
|
|
645
|
-
);
|
|
715
|
+
dataCollectionNames.add(subdirectory);
|
|
646
716
|
}
|
|
647
717
|
}
|
|
648
|
-
for (const
|
|
649
|
-
const schemaStr = schemas.length === 1 ? schemas[0] : `z.union([
|
|
650
|
-
${schemas.join(",\n")}
|
|
651
|
-
])`;
|
|
718
|
+
for (const colName of dataCollectionNames) {
|
|
652
719
|
collections.push(
|
|
653
720
|
` "${colName}": defineCollection({
|
|
654
721
|
type: "data",
|
|
655
|
-
schema:
|
|
722
|
+
schema: z.record(z.unknown()),
|
|
656
723
|
})`
|
|
657
724
|
);
|
|
658
725
|
}
|
|
@@ -666,20 +733,6 @@ ${schemas.join(",\n")}
|
|
|
666
733
|
];
|
|
667
734
|
return lines.join("\n");
|
|
668
735
|
}
|
|
669
|
-
function zodTypeForSlot(slot) {
|
|
670
|
-
switch (slot.type) {
|
|
671
|
-
case "string":
|
|
672
|
-
return "z.string()";
|
|
673
|
-
case "number":
|
|
674
|
-
return "z.number()";
|
|
675
|
-
case "boolean":
|
|
676
|
-
return "z.boolean()";
|
|
677
|
-
case "array":
|
|
678
|
-
return "z.array(z.unknown())";
|
|
679
|
-
case "object":
|
|
680
|
-
return "z.record(z.unknown())";
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
736
|
function buildDefaultFromSlots(slots) {
|
|
684
737
|
const content = {};
|
|
685
738
|
for (const [name, slot] of Object.entries(slots)) {
|
|
@@ -703,15 +756,24 @@ function defaultValueForType(type) {
|
|
|
703
756
|
}
|
|
704
757
|
|
|
705
758
|
// src/scaffold/i18n-wiring.ts
|
|
706
|
-
function wireI18n(config) {
|
|
759
|
+
function wireI18n(config, manifests) {
|
|
707
760
|
const files = {};
|
|
708
761
|
if (config.locales.length < 2) {
|
|
709
762
|
return ok(files);
|
|
710
763
|
}
|
|
711
764
|
files["src/i18n/utils.ts"] = generateI18nUtils(config);
|
|
712
|
-
files["src/pages/[locale]/index.astro"] = generateLocaleIndexPage(config);
|
|
765
|
+
files["src/pages/[locale]/index.astro"] = generateLocaleIndexPage(config, manifests ?? []);
|
|
766
|
+
files["src/pages/index.astro"] = generateRootRedirect(config);
|
|
713
767
|
return ok(files);
|
|
714
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
|
+
}
|
|
715
777
|
function generateI18nUtils(config) {
|
|
716
778
|
const localesArray = config.locales.map((locale) => `"${locale}"`).join(", ");
|
|
717
779
|
return `export const locales = [${localesArray}] as const;
|
|
@@ -744,22 +806,34 @@ export function t<T>(
|
|
|
744
806
|
}
|
|
745
807
|
`;
|
|
746
808
|
}
|
|
747
|
-
function generateLocaleIndexPage(config) {
|
|
809
|
+
function generateLocaleIndexPage(config, manifests) {
|
|
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
|
+
);
|
|
814
|
+
const imports = [];
|
|
815
|
+
const tags = [];
|
|
816
|
+
for (const block of contentBlocks) {
|
|
817
|
+
const componentName = block.name.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
818
|
+
imports.push(`import ${componentName} from '../../components/sections/${block.name}.astro';`);
|
|
819
|
+
tags.push(` <${componentName} />`);
|
|
820
|
+
}
|
|
821
|
+
const importSection = imports.length > 0 ? imports.join("\n") + "\n" : "";
|
|
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" : "";
|
|
748
825
|
return `---
|
|
749
826
|
import { locales } from "../../i18n/utils";
|
|
750
827
|
import Layout from "../../layouts/Layout.astro";
|
|
751
|
-
|
|
828
|
+
${importSection}${prerenderLine}
|
|
752
829
|
export function getStaticPaths() {
|
|
753
|
-
return locales.map((locale) => ({ params: { locale } }));
|
|
830
|
+
return locales.map((locale: string) => ({ params: { locale } }));
|
|
754
831
|
}
|
|
755
832
|
|
|
756
833
|
const { locale } = Astro.params;
|
|
757
834
|
---
|
|
758
835
|
<Layout title="${config.projectName}">
|
|
759
|
-
<main>
|
|
760
|
-
<h1>${config.projectName}</h1>
|
|
761
|
-
<p>Locale: {locale}</p>
|
|
762
|
-
</main>
|
|
836
|
+
<main>${blockSection}</main>
|
|
763
837
|
</Layout>
|
|
764
838
|
`;
|
|
765
839
|
}
|
|
@@ -875,7 +949,7 @@ function scaffold(input) {
|
|
|
875
949
|
return err(contentResult.error);
|
|
876
950
|
}
|
|
877
951
|
Object.assign(files, contentResult.value);
|
|
878
|
-
const i18nResult = wireI18n(config);
|
|
952
|
+
const i18nResult = wireI18n(config, resolvedManifests);
|
|
879
953
|
if (!isOk(i18nResult)) {
|
|
880
954
|
return err(i18nResult.error);
|
|
881
955
|
}
|
|
@@ -1503,7 +1577,7 @@ function manifest(name, overrides = {}) {
|
|
|
1503
1577
|
var FIXTURE_MANIFESTS = {
|
|
1504
1578
|
"hero-gradient": manifest("hero-gradient", {
|
|
1505
1579
|
category: "hero",
|
|
1506
|
-
conflicts: ["hero-video"],
|
|
1580
|
+
conflicts: ["hero-video", "hero-split"],
|
|
1507
1581
|
ai: {
|
|
1508
1582
|
whenToUse: "Landing page hero with gradient background",
|
|
1509
1583
|
whenNotToUse: "Internal pages",
|
|
@@ -1515,7 +1589,8 @@ var FIXTURE_MANIFESTS = {
|
|
|
1515
1589
|
}
|
|
1516
1590
|
}),
|
|
1517
1591
|
"footer-minimal": manifest("footer-minimal", {
|
|
1518
|
-
category: "footer"
|
|
1592
|
+
category: "footer",
|
|
1593
|
+
conflicts: ["footer-rich"]
|
|
1519
1594
|
}),
|
|
1520
1595
|
"cta-simple": manifest("cta-simple", {
|
|
1521
1596
|
category: "cta"
|
|
@@ -1626,17 +1701,96 @@ var FIXTURE_MANIFESTS = {
|
|
|
1626
1701
|
}
|
|
1627
1702
|
}
|
|
1628
1703
|
}),
|
|
1629
|
-
"hero-video": manifest("hero-video"
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1704
|
+
"hero-video": manifest("hero-video", {
|
|
1705
|
+
category: "hero",
|
|
1706
|
+
conflicts: ["hero-gradient", "hero-split"],
|
|
1707
|
+
files: [
|
|
1708
|
+
{ source: "hero-video.astro", destination: "src/components/sections/hero-video.astro" },
|
|
1709
|
+
{ source: "hero-video.css", destination: "src/styles/sections/hero-video.css" }
|
|
1710
|
+
],
|
|
1711
|
+
ai: {
|
|
1712
|
+
whenToUse: "Hero with background video",
|
|
1713
|
+
whenNotToUse: "Static sites",
|
|
1714
|
+
pairsWith: [],
|
|
1715
|
+
contentSlots: {
|
|
1716
|
+
headline: { type: "string" },
|
|
1717
|
+
subheadline: { type: "string" },
|
|
1718
|
+
ctaText: { type: "string" },
|
|
1719
|
+
ctaHref: { type: "string" },
|
|
1720
|
+
videoUrl: { type: "string" },
|
|
1721
|
+
posterUrl: { type: "string" }
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
}),
|
|
1725
|
+
"features-bento": manifest("features-bento", {
|
|
1726
|
+
category: "features",
|
|
1727
|
+
ai: {
|
|
1728
|
+
whenToUse: "Feature showcase in bento grid",
|
|
1729
|
+
whenNotToUse: "Simple pages",
|
|
1730
|
+
pairsWith: [],
|
|
1731
|
+
contentSlots: {
|
|
1732
|
+
headline: { type: "string" },
|
|
1733
|
+
subheadline: { type: "string" },
|
|
1734
|
+
items: { type: "array" }
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
}),
|
|
1738
|
+
"pricing-table": manifest("pricing-table", {
|
|
1739
|
+
category: "pricing",
|
|
1740
|
+
conflicts: ["pricing-comparison"],
|
|
1741
|
+
ai: {
|
|
1742
|
+
whenToUse: "Pricing plans display",
|
|
1743
|
+
whenNotToUse: "Free products",
|
|
1744
|
+
pairsWith: [],
|
|
1745
|
+
contentSlots: {
|
|
1746
|
+
headline: { type: "string" },
|
|
1747
|
+
subheadline: { type: "string" },
|
|
1748
|
+
plans: { type: "array" }
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
}),
|
|
1752
|
+
"faq-accordion": manifest("faq-accordion", {
|
|
1753
|
+
category: "faq",
|
|
1754
|
+
ai: {
|
|
1755
|
+
whenToUse: "Frequently asked questions",
|
|
1756
|
+
whenNotToUse: "Simple pages",
|
|
1757
|
+
pairsWith: [],
|
|
1758
|
+
contentSlots: {
|
|
1759
|
+
headline: { type: "string" },
|
|
1760
|
+
items: { type: "array" }
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
}),
|
|
1764
|
+
"footer-rich": manifest("footer-rich", {
|
|
1765
|
+
category: "footer",
|
|
1766
|
+
conflicts: ["footer-minimal"],
|
|
1767
|
+
ai: {
|
|
1768
|
+
whenToUse: "Multi-column footer with links",
|
|
1769
|
+
whenNotToUse: "Minimal sites",
|
|
1770
|
+
pairsWith: [],
|
|
1771
|
+
contentSlots: {
|
|
1772
|
+
brand: { type: "string" },
|
|
1773
|
+
description: { type: "string" },
|
|
1774
|
+
columns: { type: "array" },
|
|
1775
|
+
copyright: { type: "string" }
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
}),
|
|
1634
1779
|
"testimonials-carousel": manifest("testimonials-carousel"),
|
|
1635
1780
|
"contact-form": manifest("contact-form"),
|
|
1636
|
-
"hero-split": manifest("hero-split"
|
|
1637
|
-
|
|
1781
|
+
"hero-split": manifest("hero-split", {
|
|
1782
|
+
category: "hero",
|
|
1783
|
+
conflicts: ["hero-gradient", "hero-video"]
|
|
1784
|
+
}),
|
|
1785
|
+
"header-transparent": manifest("header-transparent", {
|
|
1786
|
+
category: "header",
|
|
1787
|
+
conflicts: ["header-sticky"]
|
|
1788
|
+
}),
|
|
1638
1789
|
"cta-newsletter": manifest("cta-newsletter"),
|
|
1639
|
-
"header-sticky": manifest("header-sticky"
|
|
1790
|
+
"header-sticky": manifest("header-sticky", {
|
|
1791
|
+
category: "header",
|
|
1792
|
+
conflicts: ["header-transparent"]
|
|
1793
|
+
})
|
|
1640
1794
|
};
|
|
1641
1795
|
var FIXTURE_BLOCK_SOURCES = {
|
|
1642
1796
|
"hero-gradient": {
|
|
@@ -1769,11 +1923,31 @@ export function getStaticPaths() { return [{ params: { slug: '1' } }]; }
|
|
|
1769
1923
|
<slot />`,
|
|
1770
1924
|
"default-content.json": `{ "sidebarLinks": [], "logoutText": "" }`
|
|
1771
1925
|
},
|
|
1772
|
-
"hero-video": {
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1926
|
+
"hero-video": {
|
|
1927
|
+
"hero-video.astro": "<section>Hero Video</section>\n",
|
|
1928
|
+
"hero-video.css": ".hero-video { min-height: 80vh; }\n",
|
|
1929
|
+
"default-content.json": '{"headline":"Experience the Future","subheadline":"Immersive experiences.","ctaText":"Get Started","ctaHref":"#","videoUrl":"","posterUrl":""}'
|
|
1930
|
+
},
|
|
1931
|
+
"features-bento": {
|
|
1932
|
+
"features-bento.astro": "<section>Features Bento</section>\n",
|
|
1933
|
+
"features-bento.css": ".features-bento { padding: 4rem 2rem; }\n",
|
|
1934
|
+
"default-content.json": '{"headline":"Features","subheadline":"Everything you need.","items":[{"title":"Fast","description":"Lightning speed."}]}'
|
|
1935
|
+
},
|
|
1936
|
+
"pricing-table": {
|
|
1937
|
+
"pricing-table.astro": "<section>Pricing</section>\n",
|
|
1938
|
+
"pricing-table.css": ".pricing-table { padding: 4rem 2rem; }\n",
|
|
1939
|
+
"default-content.json": '{"headline":"Pricing","subheadline":"Simple pricing.","plans":[{"name":"Free","price":"$0","features":["Basic"]}]}'
|
|
1940
|
+
},
|
|
1941
|
+
"faq-accordion": {
|
|
1942
|
+
"faq-accordion.astro": "<section>FAQ</section>\n",
|
|
1943
|
+
"faq-accordion.css": ".faq-accordion { padding: 4rem 2rem; }\n",
|
|
1944
|
+
"default-content.json": '{"headline":"FAQ","items":[{"question":"How does it work?","answer":"It just works."}]}'
|
|
1945
|
+
},
|
|
1946
|
+
"footer-rich": {
|
|
1947
|
+
"footer-rich.astro": "<section>Footer</section>\n",
|
|
1948
|
+
"footer-rich.css": ".footer-rich { padding: 3rem 2rem; }\n",
|
|
1949
|
+
"default-content.json": '{"brand":"Acme","description":"Building the future.","columns":[],"copyright":"\xA9 2025"}'
|
|
1950
|
+
},
|
|
1777
1951
|
"testimonials-carousel": { "testimonials-carousel.astro": "<section>Testimonials</section>\n" },
|
|
1778
1952
|
"contact-form": { "contact-form.astro": "<section>Contact</section>\n" },
|
|
1779
1953
|
"hero-split": { "hero-split.astro": "<section>Hero Split</section>\n" },
|
|
@@ -2067,7 +2241,7 @@ async function runManualFlow(input) {
|
|
|
2067
2241
|
message: "Choose a render mode",
|
|
2068
2242
|
options: [
|
|
2069
2243
|
{ value: "static", label: "Static (SSG)", hint: "Pre-built HTML, fastest" },
|
|
2070
|
-
{ value: "hybrid", label: "Hybrid", hint: "Static
|
|
2244
|
+
{ value: "hybrid", label: "Hybrid", hint: "Static + per-page SSR opt-in" },
|
|
2071
2245
|
{ value: "server", label: "Server (SSR)", hint: "Server-rendered on every request" }
|
|
2072
2246
|
]
|
|
2073
2247
|
});
|
|
@@ -2090,16 +2264,38 @@ async function runManualFlow(input) {
|
|
|
2090
2264
|
]
|
|
2091
2265
|
});
|
|
2092
2266
|
if (p.isCancel(cssEngine)) return handleCancel();
|
|
2093
|
-
const
|
|
2094
|
-
|
|
2095
|
-
|
|
2267
|
+
const headerOptions = buildCategoryOptions(input.manifests, "header");
|
|
2268
|
+
const footerOptions = buildCategoryOptions(input.manifests, "footer");
|
|
2269
|
+
const contentOptions = buildContentBlockOptions(input.manifests);
|
|
2270
|
+
let selectedHeader;
|
|
2271
|
+
let selectedFooter;
|
|
2272
|
+
let selectedContentBlocks = [];
|
|
2273
|
+
if (headerOptions.length > 0) {
|
|
2274
|
+
const noneOption = { value: "__none__", label: "None", hint: "No header" };
|
|
2275
|
+
const headerChoice = await p.select({
|
|
2276
|
+
message: "Choose a header (appears on every page)",
|
|
2277
|
+
options: [noneOption, ...headerOptions]
|
|
2278
|
+
});
|
|
2279
|
+
if (p.isCancel(headerChoice)) return handleCancel();
|
|
2280
|
+
if (headerChoice !== "__none__") selectedHeader = headerChoice;
|
|
2281
|
+
}
|
|
2282
|
+
if (contentOptions.length > 0) {
|
|
2096
2283
|
const blocks = await p.multiselect({
|
|
2097
|
-
message: "Select blocks
|
|
2098
|
-
options:
|
|
2284
|
+
message: "Select content blocks (space to toggle, enter to confirm)",
|
|
2285
|
+
options: contentOptions,
|
|
2099
2286
|
required: false
|
|
2100
2287
|
});
|
|
2101
2288
|
if (p.isCancel(blocks)) return handleCancel();
|
|
2102
|
-
|
|
2289
|
+
selectedContentBlocks = blocks;
|
|
2290
|
+
}
|
|
2291
|
+
if (footerOptions.length > 0) {
|
|
2292
|
+
const noneOption = { value: "__none__", label: "None", hint: "No footer" };
|
|
2293
|
+
const footerChoice = await p.select({
|
|
2294
|
+
message: "Choose a footer (appears on every page)",
|
|
2295
|
+
options: [noneOption, ...footerOptions]
|
|
2296
|
+
});
|
|
2297
|
+
if (p.isCancel(footerChoice)) return handleCancel();
|
|
2298
|
+
if (footerChoice !== "__none__") selectedFooter = footerChoice;
|
|
2103
2299
|
}
|
|
2104
2300
|
const localesInput = await p.text({
|
|
2105
2301
|
message: "Locales (comma-separated, e.g. en,es,ar)",
|
|
@@ -2129,6 +2325,24 @@ async function runManualFlow(input) {
|
|
|
2129
2325
|
if (p.isCancel(switcherChoice)) return handleCancel();
|
|
2130
2326
|
themeSwitcher = switcherChoice;
|
|
2131
2327
|
}
|
|
2328
|
+
if (!selectedHeader && headerOptions.length > 0) {
|
|
2329
|
+
const needsNav = locales.length >= 2 || themeSwitcher;
|
|
2330
|
+
if (needsNav) {
|
|
2331
|
+
const autoHeader = await p.confirm({
|
|
2332
|
+
message: `You enabled ${locales.length >= 2 ? "multiple locales" : "theme switching"} \u2014 add a header for navigation?`,
|
|
2333
|
+
initialValue: true
|
|
2334
|
+
});
|
|
2335
|
+
if (p.isCancel(autoHeader)) return handleCancel();
|
|
2336
|
+
if (autoHeader) {
|
|
2337
|
+
selectedHeader = headerOptions[0].value;
|
|
2338
|
+
console.log(pc.dim(` Adding ${selectedHeader} for navigation.`));
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
const selectedBlocks = [];
|
|
2343
|
+
if (selectedHeader) selectedBlocks.push(selectedHeader);
|
|
2344
|
+
selectedBlocks.push(...selectedContentBlocks);
|
|
2345
|
+
if (selectedFooter) selectedBlocks.push(selectedFooter);
|
|
2132
2346
|
const config = {
|
|
2133
2347
|
projectName: projectName.trim(),
|
|
2134
2348
|
projectDir: `./${projectName.trim()}`,
|
|
@@ -2168,8 +2382,18 @@ function handleCancel() {
|
|
|
2168
2382
|
p.cancel("Operation cancelled.");
|
|
2169
2383
|
return null;
|
|
2170
2384
|
}
|
|
2171
|
-
function
|
|
2172
|
-
|
|
2385
|
+
function buildCategoryOptions(manifests, category) {
|
|
2386
|
+
return Object.values(manifests).filter((block) => (block.category ?? "other") === category).map((block) => ({
|
|
2387
|
+
value: block.name,
|
|
2388
|
+
label: block.name,
|
|
2389
|
+
hint: block.description
|
|
2390
|
+
}));
|
|
2391
|
+
}
|
|
2392
|
+
function buildContentBlockOptions(manifests) {
|
|
2393
|
+
const LAYOUT_CATEGORIES2 = /* @__PURE__ */ new Set(["header", "footer"]);
|
|
2394
|
+
const blocks = Object.values(manifests).filter(
|
|
2395
|
+
(block) => !LAYOUT_CATEGORIES2.has(block.category ?? "other")
|
|
2396
|
+
);
|
|
2173
2397
|
const categories = /* @__PURE__ */ new Map();
|
|
2174
2398
|
for (const block of blocks) {
|
|
2175
2399
|
const category = block.category ?? "other";
|
|
@@ -2183,7 +2407,7 @@ function buildBlockOptions(manifests) {
|
|
|
2183
2407
|
for (const block of categoryBlocks) {
|
|
2184
2408
|
options.push({
|
|
2185
2409
|
value: block.name,
|
|
2186
|
-
label:
|
|
2410
|
+
label: block.name,
|
|
2187
2411
|
hint: `${category} \u2014 ${block.description}`
|
|
2188
2412
|
});
|
|
2189
2413
|
}
|
|
@@ -2285,7 +2509,7 @@ function runPostScaffold(input, callbacks) {
|
|
|
2285
2509
|
log(pc2.dim(` ${config.packageManager} install`));
|
|
2286
2510
|
}
|
|
2287
2511
|
log(pc2.dim(` ${config.packageManager} dev`));
|
|
2288
|
-
log(pc2.dim(` fornix add <block>`));
|
|
2512
|
+
log(pc2.dim(` npx create-fornix add <block>`));
|
|
2289
2513
|
log("");
|
|
2290
2514
|
}
|
|
2291
2515
|
function installDependencies(projectDir, packageManager, verbose, log, warn) {
|
|
@@ -2378,9 +2602,9 @@ function generateClaudeMd(projectDir, config, blockNames) {
|
|
|
2378
2602
|
lines.push("");
|
|
2379
2603
|
lines.push(`- \`${config.packageManager} dev\` \u2014 start development server`);
|
|
2380
2604
|
lines.push(`- \`${config.packageManager} build\` \u2014 build for production`);
|
|
2381
|
-
lines.push("- `fornix add <block>` \u2014 add a new block");
|
|
2382
|
-
lines.push("- `fornix remove <block>` \u2014 remove a block");
|
|
2383
|
-
lines.push("- `fornix status` \u2014 show project configuration");
|
|
2605
|
+
lines.push("- `npx create-fornix add <block>` \u2014 add a new block");
|
|
2606
|
+
lines.push("- `npx create-fornix remove <block>` \u2014 remove a block");
|
|
2607
|
+
lines.push("- `npx create-fornix status` \u2014 show project configuration");
|
|
2384
2608
|
lines.push("");
|
|
2385
2609
|
lines.push("## File Structure");
|
|
2386
2610
|
lines.push("");
|
|
@@ -3829,8 +4053,9 @@ async function runFlagDrivenMode(args2, manifests, allPalettes) {
|
|
|
3829
4053
|
async function runScaffold(config, manifests, allPalettes, dryRun, verbose, skipInstall, skipGit) {
|
|
3830
4054
|
const spinner2 = p2.spinner();
|
|
3831
4055
|
spinner2.start("Fetching blocks from registry...");
|
|
3832
|
-
const
|
|
3833
|
-
const
|
|
4056
|
+
const selectedBlockNames = config.blocks.map((b) => b.name);
|
|
4057
|
+
const allBlockNames = preResolveDependencies(selectedBlockNames, manifests);
|
|
4058
|
+
const blockResults = await fetchBlocks(allBlockNames);
|
|
3834
4059
|
const blockSources = {};
|
|
3835
4060
|
const blockDefaultContent = {};
|
|
3836
4061
|
for (const result2 of blockResults) {
|
|
@@ -3977,12 +4202,178 @@ function showNoProviderGuide() {
|
|
|
3977
4202
|
console.error(" export CLOUDFLARE_ACCOUNT_ID=... CLOUDFLARE_API_TOKEN=...\n");
|
|
3978
4203
|
console.error(pc3.dim(" Or use manual mode: npx create-fornix --manual\n"));
|
|
3979
4204
|
}
|
|
4205
|
+
function preResolveDependencies(selected, manifests) {
|
|
4206
|
+
const result = /* @__PURE__ */ new Set();
|
|
4207
|
+
function walk(name) {
|
|
4208
|
+
if (result.has(name)) return;
|
|
4209
|
+
const manifest2 = manifests[name];
|
|
4210
|
+
if (!manifest2) {
|
|
4211
|
+
result.add(name);
|
|
4212
|
+
return;
|
|
4213
|
+
}
|
|
4214
|
+
for (const dep of manifest2.requires) {
|
|
4215
|
+
walk(dep);
|
|
4216
|
+
}
|
|
4217
|
+
result.add(name);
|
|
4218
|
+
}
|
|
4219
|
+
for (const name of selected) {
|
|
4220
|
+
walk(name);
|
|
4221
|
+
}
|
|
4222
|
+
return [...result];
|
|
4223
|
+
}
|
|
3980
4224
|
|
|
3981
4225
|
// src/cli/commands/add.ts
|
|
3982
4226
|
import { defineCommand as defineCommand2 } from "citty";
|
|
3983
4227
|
import pc4 from "picocolors";
|
|
3984
|
-
import { readFileSync as
|
|
3985
|
-
import { join as
|
|
4228
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
|
|
4229
|
+
import { join as join8, dirname as dirname3 } from "path";
|
|
4230
|
+
|
|
4231
|
+
// src/scaffold/page-updater.ts
|
|
4232
|
+
import { readFileSync as readFileSync5, existsSync as existsSync4 } from "fs";
|
|
4233
|
+
import { join as join7 } from "path";
|
|
4234
|
+
var CATEGORY_ORDER = {
|
|
4235
|
+
header: 0,
|
|
4236
|
+
hero: 1,
|
|
4237
|
+
features: 2,
|
|
4238
|
+
pricing: 3,
|
|
4239
|
+
testimonials: 4,
|
|
4240
|
+
faq: 5,
|
|
4241
|
+
cta: 6,
|
|
4242
|
+
contact: 7,
|
|
4243
|
+
theme: 8,
|
|
4244
|
+
footer: 9
|
|
4245
|
+
};
|
|
4246
|
+
var DEFAULT_PRIORITY = 5;
|
|
4247
|
+
var LAYOUT_CATEGORIES = /* @__PURE__ */ new Set(["header", "footer"]);
|
|
4248
|
+
function blockNameToComponentName(blockName) {
|
|
4249
|
+
return blockName.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
4250
|
+
}
|
|
4251
|
+
function blockNameToImportPath(blockName) {
|
|
4252
|
+
return `../components/sections/${blockName}.astro`;
|
|
4253
|
+
}
|
|
4254
|
+
function blockNameToLayoutImportPath(blockName) {
|
|
4255
|
+
return `../components/sections/${blockName}.astro`;
|
|
4256
|
+
}
|
|
4257
|
+
function getBlockCategory(blockName, projectDir) {
|
|
4258
|
+
if (projectDir) {
|
|
4259
|
+
try {
|
|
4260
|
+
const fornixPath = join7(projectDir, "fornix.json");
|
|
4261
|
+
if (existsSync4(fornixPath)) {
|
|
4262
|
+
const fornix = JSON.parse(readFileSync5(fornixPath, "utf-8"));
|
|
4263
|
+
const block = fornix.blocks?.find((b) => b.name === blockName);
|
|
4264
|
+
if (block?.category) return block.category;
|
|
4265
|
+
}
|
|
4266
|
+
} catch {
|
|
4267
|
+
}
|
|
4268
|
+
}
|
|
4269
|
+
for (const prefix of Object.keys(CATEGORY_ORDER)) {
|
|
4270
|
+
if (blockName.startsWith(prefix)) return prefix;
|
|
4271
|
+
}
|
|
4272
|
+
return "other";
|
|
4273
|
+
}
|
|
4274
|
+
function getPriority(category) {
|
|
4275
|
+
return CATEGORY_ORDER[category] ?? DEFAULT_PRIORITY;
|
|
4276
|
+
}
|
|
4277
|
+
function isLayoutBlock(blockName, projectDir) {
|
|
4278
|
+
const category = getBlockCategory(blockName, projectDir);
|
|
4279
|
+
return LAYOUT_CATEGORIES.has(category);
|
|
4280
|
+
}
|
|
4281
|
+
function addBlockToPage(pageContent, blockName, projectDir) {
|
|
4282
|
+
const componentName = blockNameToComponentName(blockName);
|
|
4283
|
+
const importPath = blockNameToImportPath(blockName);
|
|
4284
|
+
if (pageContent.includes(importPath) || pageContent.includes(`import ${componentName}`)) {
|
|
4285
|
+
return pageContent;
|
|
4286
|
+
}
|
|
4287
|
+
const importLine = `import ${componentName} from '${importPath}';`;
|
|
4288
|
+
const componentTag = ` <${componentName} />`;
|
|
4289
|
+
const newCategory = getBlockCategory(blockName, projectDir);
|
|
4290
|
+
const newPriority = getPriority(newCategory);
|
|
4291
|
+
const frontmatterEnd = pageContent.indexOf("---", pageContent.indexOf("---") + 3);
|
|
4292
|
+
if (frontmatterEnd === -1) {
|
|
4293
|
+
return pageContent;
|
|
4294
|
+
}
|
|
4295
|
+
let updated = pageContent.slice(0, frontmatterEnd) + importLine + "\n" + pageContent.slice(frontmatterEnd);
|
|
4296
|
+
const mainOpenMatch = updated.match(/<main[^>]*>/);
|
|
4297
|
+
const mainCloseIndex = updated.lastIndexOf("</main>");
|
|
4298
|
+
if (!mainOpenMatch || mainCloseIndex === -1) {
|
|
4299
|
+
return updated;
|
|
4300
|
+
}
|
|
4301
|
+
const mainOpenEnd = mainOpenMatch.index + mainOpenMatch[0].length;
|
|
4302
|
+
const mainContent = updated.slice(mainOpenEnd, mainCloseIndex);
|
|
4303
|
+
const tagPattern = /^(\s*<([A-Z][A-Za-z]*)\s*\/>)\s*$/gm;
|
|
4304
|
+
let insertOffset = mainCloseIndex;
|
|
4305
|
+
let match;
|
|
4306
|
+
while ((match = tagPattern.exec(mainContent)) !== null) {
|
|
4307
|
+
const existingComponentName = match[2];
|
|
4308
|
+
const existingBlockName = componentNameToBlockName(existingComponentName);
|
|
4309
|
+
const existingCategory = getBlockCategory(existingBlockName, projectDir);
|
|
4310
|
+
const existingPriority = getPriority(existingCategory);
|
|
4311
|
+
if (existingPriority > newPriority) {
|
|
4312
|
+
insertOffset = mainOpenEnd + match.index;
|
|
4313
|
+
break;
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
updated = updated.slice(0, insertOffset) + componentTag + "\n" + updated.slice(insertOffset);
|
|
4317
|
+
return updated;
|
|
4318
|
+
}
|
|
4319
|
+
function addBlockToLayout(layoutContent, blockName, projectDir) {
|
|
4320
|
+
const componentName = blockNameToComponentName(blockName);
|
|
4321
|
+
const importPath = blockNameToLayoutImportPath(blockName);
|
|
4322
|
+
if (layoutContent.includes(importPath) || layoutContent.includes(`import ${componentName}`)) {
|
|
4323
|
+
return layoutContent;
|
|
4324
|
+
}
|
|
4325
|
+
const importLine = `import ${componentName} from '${importPath}';`;
|
|
4326
|
+
const componentTag = ` <${componentName} />`;
|
|
4327
|
+
const category = getBlockCategory(blockName, projectDir);
|
|
4328
|
+
const frontmatterEnd = layoutContent.indexOf("---", layoutContent.indexOf("---") + 3);
|
|
4329
|
+
if (frontmatterEnd === -1) {
|
|
4330
|
+
return layoutContent;
|
|
4331
|
+
}
|
|
4332
|
+
let updated = layoutContent.slice(0, frontmatterEnd) + importLine + "\n" + layoutContent.slice(frontmatterEnd);
|
|
4333
|
+
if (category === "header") {
|
|
4334
|
+
const slotIndex = updated.indexOf("<slot");
|
|
4335
|
+
const mainIndex = updated.indexOf("<main");
|
|
4336
|
+
const insertBefore = mainIndex !== -1 ? mainIndex : slotIndex;
|
|
4337
|
+
if (insertBefore !== -1) {
|
|
4338
|
+
updated = updated.slice(0, insertBefore) + componentTag + "\n" + updated.slice(insertBefore);
|
|
4339
|
+
}
|
|
4340
|
+
} else if (category === "footer") {
|
|
4341
|
+
const mainCloseIndex = updated.indexOf("</main>");
|
|
4342
|
+
const slotMatch = updated.match(/<slot\s*\/>/);
|
|
4343
|
+
if (mainCloseIndex !== -1) {
|
|
4344
|
+
const afterMain = mainCloseIndex + "</main>".length;
|
|
4345
|
+
updated = updated.slice(0, afterMain) + "\n" + componentTag + updated.slice(afterMain);
|
|
4346
|
+
} else if (slotMatch) {
|
|
4347
|
+
const afterSlot = slotMatch.index + slotMatch[0].length;
|
|
4348
|
+
updated = updated.slice(0, afterSlot) + "\n" + componentTag + updated.slice(afterSlot);
|
|
4349
|
+
}
|
|
4350
|
+
}
|
|
4351
|
+
return updated;
|
|
4352
|
+
}
|
|
4353
|
+
function removeBlockFromPage(pageContent, blockName) {
|
|
4354
|
+
const componentName = blockNameToComponentName(blockName);
|
|
4355
|
+
const importPath = blockNameToImportPath(blockName);
|
|
4356
|
+
let updated = pageContent;
|
|
4357
|
+
const importRegex = new RegExp(
|
|
4358
|
+
`^\\s*import\\s+${componentName}\\s+from\\s+['"]${escapeRegex(importPath)}['"];?\\s*\\n?`,
|
|
4359
|
+
"m"
|
|
4360
|
+
);
|
|
4361
|
+
updated = updated.replace(importRegex, "");
|
|
4362
|
+
const tagRegex = new RegExp(
|
|
4363
|
+
`^\\s*<${componentName}\\s*/?>\\s*\\n?`,
|
|
4364
|
+
"m"
|
|
4365
|
+
);
|
|
4366
|
+
updated = updated.replace(tagRegex, "");
|
|
4367
|
+
return updated;
|
|
4368
|
+
}
|
|
4369
|
+
function escapeRegex(str) {
|
|
4370
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4371
|
+
}
|
|
4372
|
+
function componentNameToBlockName(componentName) {
|
|
4373
|
+
return componentName.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
|
|
4374
|
+
}
|
|
4375
|
+
|
|
4376
|
+
// src/cli/commands/add.ts
|
|
3986
4377
|
var addCommand = defineCommand2({
|
|
3987
4378
|
meta: {
|
|
3988
4379
|
name: "add",
|
|
@@ -4013,14 +4404,14 @@ var addCommand = defineCommand2({
|
|
|
4013
4404
|
async run({ args: args2 }) {
|
|
4014
4405
|
const typedArgs = args2;
|
|
4015
4406
|
const cwd = process.cwd();
|
|
4016
|
-
const manifestPath =
|
|
4017
|
-
if (!
|
|
4407
|
+
const manifestPath = join8(cwd, "fornix.json");
|
|
4408
|
+
if (!existsSync5(manifestPath)) {
|
|
4018
4409
|
console.error(
|
|
4019
4410
|
pc4.red("\u2717 No fornix.json found. Are you in a Fornix project?")
|
|
4020
4411
|
);
|
|
4021
4412
|
process.exit(1);
|
|
4022
4413
|
}
|
|
4023
|
-
const manifestRaw =
|
|
4414
|
+
const manifestRaw = readFileSync6(manifestPath, "utf-8");
|
|
4024
4415
|
const manifest2 = JSON.parse(manifestRaw);
|
|
4025
4416
|
const registryResult = await fetchRegistryIndex();
|
|
4026
4417
|
if (!isOk(registryResult)) {
|
|
@@ -4049,6 +4440,27 @@ var addCommand = defineCommand2({
|
|
|
4049
4440
|
return;
|
|
4050
4441
|
}
|
|
4051
4442
|
const blocksToAdd = resolveDependencies2(blockName, installedNames, manifests);
|
|
4443
|
+
for (const name of blocksToAdd) {
|
|
4444
|
+
const m = manifests[name];
|
|
4445
|
+
if (m?.conflicts && m.conflicts.length > 0) {
|
|
4446
|
+
for (const conflictName of m.conflicts) {
|
|
4447
|
+
if (installedNames.has(conflictName)) {
|
|
4448
|
+
console.error(
|
|
4449
|
+
pc4.red(
|
|
4450
|
+
`\u2717 Block '${name}' conflicts with installed block '${conflictName}'.`
|
|
4451
|
+
)
|
|
4452
|
+
);
|
|
4453
|
+
console.log(
|
|
4454
|
+
pc4.dim(
|
|
4455
|
+
` Remove '${conflictName}' first: npx create-fornix remove ${conflictName}`
|
|
4456
|
+
)
|
|
4457
|
+
);
|
|
4458
|
+
process.exitCode = 1;
|
|
4459
|
+
return;
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4052
4464
|
for (const name of blocksToAdd) {
|
|
4053
4465
|
const m = manifests[name];
|
|
4054
4466
|
if (m?.requiredMode && manifest2.renderMode !== m.requiredMode) {
|
|
@@ -4086,7 +4498,7 @@ var addCommand = defineCommand2({
|
|
|
4086
4498
|
return;
|
|
4087
4499
|
}
|
|
4088
4500
|
filesToWrite.push({
|
|
4089
|
-
path:
|
|
4501
|
+
path: join8(cwd, file.destination),
|
|
4090
4502
|
content
|
|
4091
4503
|
});
|
|
4092
4504
|
}
|
|
@@ -4125,6 +4537,33 @@ var addCommand = defineCommand2({
|
|
|
4125
4537
|
});
|
|
4126
4538
|
}
|
|
4127
4539
|
writeFileSync5(manifestPath, JSON.stringify(manifest2, null, 2) + "\n");
|
|
4540
|
+
const indexPath = join8(cwd, "src/pages/index.astro");
|
|
4541
|
+
const layoutPath = join8(cwd, "src/layouts/Layout.astro");
|
|
4542
|
+
for (const name of blocksToAdd) {
|
|
4543
|
+
const bManifest = manifests[name];
|
|
4544
|
+
if (!bManifest || bManifest.type !== "section") continue;
|
|
4545
|
+
if (isLayoutBlock(name, cwd)) {
|
|
4546
|
+
if (existsSync5(layoutPath)) {
|
|
4547
|
+
const layoutContent = readFileSync6(layoutPath, "utf-8");
|
|
4548
|
+
const updated = addBlockToLayout(layoutContent, name, cwd);
|
|
4549
|
+
if (updated !== layoutContent) {
|
|
4550
|
+
writeFileSync5(layoutPath, updated);
|
|
4551
|
+
if (typedArgs.verbose) {
|
|
4552
|
+
console.log(` ${pc4.dim("\u270E")} updated Layout.astro (${name})`);
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
} else {
|
|
4557
|
+
if (existsSync5(indexPath)) {
|
|
4558
|
+
let pageContent = readFileSync6(indexPath, "utf-8");
|
|
4559
|
+
pageContent = addBlockToPage(pageContent, name, cwd);
|
|
4560
|
+
writeFileSync5(indexPath, pageContent);
|
|
4561
|
+
if (typedArgs.verbose) {
|
|
4562
|
+
console.log(` ${pc4.dim("\u270E")} updated index.astro (${name})`);
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4128
4567
|
console.log();
|
|
4129
4568
|
for (const name of blocksToAdd) {
|
|
4130
4569
|
const isDep = name !== blockName;
|
|
@@ -4165,8 +4604,8 @@ function resolveDependencies2(blockName, installedNames, manifests) {
|
|
|
4165
4604
|
// src/cli/commands/remove.ts
|
|
4166
4605
|
import { defineCommand as defineCommand3 } from "citty";
|
|
4167
4606
|
import pc5 from "picocolors";
|
|
4168
|
-
import { readFileSync as
|
|
4169
|
-
import { join as
|
|
4607
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync6, unlinkSync, readdirSync as readdirSync3, rmdirSync } from "fs";
|
|
4608
|
+
import { join as join9, dirname as dirname4 } from "path";
|
|
4170
4609
|
var removeCommand = defineCommand3({
|
|
4171
4610
|
meta: {
|
|
4172
4611
|
name: "remove",
|
|
@@ -4197,14 +4636,14 @@ var removeCommand = defineCommand3({
|
|
|
4197
4636
|
async run({ args: args2 }) {
|
|
4198
4637
|
const typedArgs = args2;
|
|
4199
4638
|
const cwd = process.cwd();
|
|
4200
|
-
const manifestPath =
|
|
4201
|
-
if (!
|
|
4639
|
+
const manifestPath = join9(cwd, "fornix.json");
|
|
4640
|
+
if (!existsSync6(manifestPath)) {
|
|
4202
4641
|
console.error(
|
|
4203
4642
|
pc5.red("\u2717 No fornix.json found. Are you in a Fornix project?")
|
|
4204
4643
|
);
|
|
4205
4644
|
process.exit(1);
|
|
4206
4645
|
}
|
|
4207
|
-
const manifestRaw =
|
|
4646
|
+
const manifestRaw = readFileSync7(manifestPath, "utf-8");
|
|
4208
4647
|
const manifest2 = JSON.parse(manifestRaw);
|
|
4209
4648
|
const registryResult = await fetchRegistryIndex();
|
|
4210
4649
|
if (!isOk(registryResult)) {
|
|
@@ -4237,8 +4676,8 @@ var removeCommand = defineCommand3({
|
|
|
4237
4676
|
const filesToRemove = [];
|
|
4238
4677
|
if (blockManifest) {
|
|
4239
4678
|
for (const file of blockManifest.files) {
|
|
4240
|
-
const filePath =
|
|
4241
|
-
if (
|
|
4679
|
+
const filePath = join9(cwd, file.destination);
|
|
4680
|
+
if (existsSync6(filePath)) {
|
|
4242
4681
|
filesToRemove.push(filePath);
|
|
4243
4682
|
}
|
|
4244
4683
|
}
|
|
@@ -4261,6 +4700,18 @@ var removeCommand = defineCommand3({
|
|
|
4261
4700
|
}
|
|
4262
4701
|
manifest2.blocks = manifest2.blocks.filter((b) => b.name !== blockName);
|
|
4263
4702
|
writeFileSync6(manifestPath, JSON.stringify(manifest2, null, 2) + "\n");
|
|
4703
|
+
const targetFile = isLayoutBlock(blockName, cwd) ? join9(cwd, "src/layouts/Layout.astro") : join9(cwd, "src/pages/index.astro");
|
|
4704
|
+
const targetLabel = isLayoutBlock(blockName, cwd) ? "Layout.astro" : "index.astro";
|
|
4705
|
+
if (existsSync6(targetFile)) {
|
|
4706
|
+
const original = readFileSync7(targetFile, "utf-8");
|
|
4707
|
+
const updated = removeBlockFromPage(original, blockName);
|
|
4708
|
+
if (updated !== original) {
|
|
4709
|
+
writeFileSync6(targetFile, updated);
|
|
4710
|
+
if (typedArgs.verbose) {
|
|
4711
|
+
console.log(` ${pc5.dim("\u270E")} updated ${targetLabel}`);
|
|
4712
|
+
}
|
|
4713
|
+
}
|
|
4714
|
+
}
|
|
4264
4715
|
console.log();
|
|
4265
4716
|
console.log(` ${pc5.red("-")} ${pc5.bold(blockName)} removed`);
|
|
4266
4717
|
if (dependents.length > 0) {
|
|
@@ -4432,8 +4883,8 @@ function printFormatted(blocks, verbose) {
|
|
|
4432
4883
|
// src/cli/commands/status.ts
|
|
4433
4884
|
import { defineCommand as defineCommand5 } from "citty";
|
|
4434
4885
|
import pc7 from "picocolors";
|
|
4435
|
-
import { readFileSync as
|
|
4436
|
-
import { join as
|
|
4886
|
+
import { readFileSync as readFileSync8, existsSync as existsSync7 } from "fs";
|
|
4887
|
+
import { join as join10 } from "path";
|
|
4437
4888
|
var statusCommand = defineCommand5({
|
|
4438
4889
|
meta: {
|
|
4439
4890
|
name: "status",
|
|
@@ -4454,8 +4905,8 @@ var statusCommand = defineCommand5({
|
|
|
4454
4905
|
run({ args: args2 }) {
|
|
4455
4906
|
const typedArgs = args2;
|
|
4456
4907
|
const cwd = process.cwd();
|
|
4457
|
-
const manifestPath =
|
|
4458
|
-
if (!
|
|
4908
|
+
const manifestPath = join10(cwd, "fornix.json");
|
|
4909
|
+
if (!existsSync7(manifestPath)) {
|
|
4459
4910
|
console.error(
|
|
4460
4911
|
pc7.red("\u2717 No fornix.json found in the current directory.")
|
|
4461
4912
|
);
|
|
@@ -4468,7 +4919,7 @@ var statusCommand = defineCommand5({
|
|
|
4468
4919
|
}
|
|
4469
4920
|
let manifest2;
|
|
4470
4921
|
try {
|
|
4471
|
-
const raw =
|
|
4922
|
+
const raw = readFileSync8(manifestPath, "utf-8");
|
|
4472
4923
|
manifest2 = JSON.parse(raw);
|
|
4473
4924
|
} catch {
|
|
4474
4925
|
console.error(pc7.red("\u2717 Failed to parse fornix.json."));
|
|
@@ -4549,8 +5000,8 @@ function printStatus(manifest2, verbose) {
|
|
|
4549
5000
|
// src/cli/commands/doctor.ts
|
|
4550
5001
|
import { defineCommand as defineCommand6 } from "citty";
|
|
4551
5002
|
import pc8 from "picocolors";
|
|
4552
|
-
import { readFileSync as
|
|
4553
|
-
import { join as
|
|
5003
|
+
import { readFileSync as readFileSync9, existsSync as existsSync8 } from "fs";
|
|
5004
|
+
import { join as join11 } from "path";
|
|
4554
5005
|
var doctorCommand = defineCommand6({
|
|
4555
5006
|
meta: {
|
|
4556
5007
|
name: "doctor",
|
|
@@ -4565,7 +5016,7 @@ var doctorCommand = defineCommand6({
|
|
|
4565
5016
|
},
|
|
4566
5017
|
async run({ args: args2 }) {
|
|
4567
5018
|
const cwd = process.cwd();
|
|
4568
|
-
const manifestPath =
|
|
5019
|
+
const manifestPath = join11(cwd, "fornix.json");
|
|
4569
5020
|
let hasErrors = false;
|
|
4570
5021
|
const errors = [];
|
|
4571
5022
|
function reportError(msg) {
|
|
@@ -4575,7 +5026,7 @@ var doctorCommand = defineCommand6({
|
|
|
4575
5026
|
console.error(pc8.red(`\u2717 ${msg}`));
|
|
4576
5027
|
}
|
|
4577
5028
|
}
|
|
4578
|
-
if (!
|
|
5029
|
+
if (!existsSync8(manifestPath)) {
|
|
4579
5030
|
reportError("No fornix.json found in the current directory.");
|
|
4580
5031
|
if (args2.json) {
|
|
4581
5032
|
console.log(JSON.stringify({ healthy: false, errors }));
|
|
@@ -4584,7 +5035,7 @@ var doctorCommand = defineCommand6({
|
|
|
4584
5035
|
}
|
|
4585
5036
|
let manifest2;
|
|
4586
5037
|
try {
|
|
4587
|
-
const raw =
|
|
5038
|
+
const raw = readFileSync9(manifestPath, "utf-8");
|
|
4588
5039
|
manifest2 = JSON.parse(raw);
|
|
4589
5040
|
} catch {
|
|
4590
5041
|
reportError("Failed to parse fornix.json.");
|
|
@@ -4610,8 +5061,8 @@ var doctorCommand = defineCommand6({
|
|
|
4610
5061
|
const bManifest = manifests[block.name];
|
|
4611
5062
|
if (bManifest) {
|
|
4612
5063
|
for (const file of bManifest.files) {
|
|
4613
|
-
const filePath =
|
|
4614
|
-
if (!
|
|
5064
|
+
const filePath = join11(cwd, file.destination);
|
|
5065
|
+
if (!existsSync8(filePath)) {
|
|
4615
5066
|
reportError(`Missing expected file for installed block '${block.name}': ${file.destination}`);
|
|
4616
5067
|
}
|
|
4617
5068
|
}
|
|
@@ -4620,7 +5071,7 @@ var doctorCommand = defineCommand6({
|
|
|
4620
5071
|
for (const [name, bManifest] of Object.entries(manifests)) {
|
|
4621
5072
|
if (!installedBlocks.has(name)) {
|
|
4622
5073
|
const foundOrphaned = bManifest.files.some((file) => {
|
|
4623
|
-
return
|
|
5074
|
+
return existsSync8(join11(cwd, file.destination));
|
|
4624
5075
|
});
|
|
4625
5076
|
if (foundOrphaned) {
|
|
4626
5077
|
reportError(`Orphaned block files detected for '${name}'. The block is not in fornix.json.`);
|
|
@@ -4644,14 +5095,14 @@ var doctorCommand = defineCommand6({
|
|
|
4644
5095
|
if (locale !== "") {
|
|
4645
5096
|
pathFragment = `src/content/${locale}/${subdirectory}/${bManifest.name}.json`;
|
|
4646
5097
|
}
|
|
4647
|
-
if (!
|
|
5098
|
+
if (!existsSync8(join11(cwd, pathFragment))) {
|
|
4648
5099
|
missingContentFiles.push(pathFragment);
|
|
4649
5100
|
}
|
|
4650
5101
|
}
|
|
4651
5102
|
}
|
|
4652
5103
|
}
|
|
4653
5104
|
if (requiresContentConfig) {
|
|
4654
|
-
if (!
|
|
5105
|
+
if (!existsSync8(join11(cwd, "src/content/config.ts"))) {
|
|
4655
5106
|
reportError("Missing expected file: src/content/config.ts");
|
|
4656
5107
|
}
|
|
4657
5108
|
}
|
|
@@ -4742,19 +5193,19 @@ async function listBlocksHandler(args2) {
|
|
|
4742
5193
|
}
|
|
4743
5194
|
|
|
4744
5195
|
// src/mcp/tools/add-block.ts
|
|
4745
|
-
import { readFileSync as
|
|
4746
|
-
import { join as
|
|
5196
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
|
|
5197
|
+
import { join as join12, dirname as dirname5 } from "path";
|
|
4747
5198
|
async function addBlock2(input) {
|
|
4748
5199
|
const { name, variant = "default", projectDirectory } = input;
|
|
4749
|
-
const manifestPath =
|
|
4750
|
-
if (!
|
|
5200
|
+
const manifestPath = join12(projectDirectory, "fornix.json");
|
|
5201
|
+
if (!existsSync9(manifestPath)) {
|
|
4751
5202
|
return err(
|
|
4752
5203
|
new Error("No fornix.json found. Not a Fornix project directory.")
|
|
4753
5204
|
);
|
|
4754
5205
|
}
|
|
4755
5206
|
let manifest2;
|
|
4756
5207
|
try {
|
|
4757
|
-
const raw =
|
|
5208
|
+
const raw = readFileSync10(manifestPath, "utf-8");
|
|
4758
5209
|
manifest2 = JSON.parse(raw);
|
|
4759
5210
|
} catch {
|
|
4760
5211
|
return err(new Error("Failed to parse fornix.json."));
|
|
@@ -4806,7 +5257,7 @@ async function addBlock2(input) {
|
|
|
4806
5257
|
)
|
|
4807
5258
|
);
|
|
4808
5259
|
}
|
|
4809
|
-
const filePath =
|
|
5260
|
+
const filePath = join12(projectDirectory, file.destination);
|
|
4810
5261
|
mkdirSync6(dirname5(filePath), { recursive: true });
|
|
4811
5262
|
writeFileSync7(filePath, content);
|
|
4812
5263
|
filesCreated++;
|
|
@@ -4845,25 +5296,25 @@ function resolveDependencies3(blockName, installedNames, manifests) {
|
|
|
4845
5296
|
|
|
4846
5297
|
// src/mcp/tools/remove-block.ts
|
|
4847
5298
|
import {
|
|
4848
|
-
readFileSync as
|
|
5299
|
+
readFileSync as readFileSync11,
|
|
4849
5300
|
writeFileSync as writeFileSync8,
|
|
4850
|
-
existsSync as
|
|
5301
|
+
existsSync as existsSync10,
|
|
4851
5302
|
unlinkSync as unlinkSync2,
|
|
4852
5303
|
readdirSync as readdirSync4,
|
|
4853
5304
|
rmdirSync as rmdirSync2
|
|
4854
5305
|
} from "fs";
|
|
4855
|
-
import { join as
|
|
5306
|
+
import { join as join13, dirname as dirname6 } from "path";
|
|
4856
5307
|
async function removeBlock(input) {
|
|
4857
5308
|
const { name, force = false, projectDirectory } = input;
|
|
4858
|
-
const manifestPath =
|
|
4859
|
-
if (!
|
|
5309
|
+
const manifestPath = join13(projectDirectory, "fornix.json");
|
|
5310
|
+
if (!existsSync10(manifestPath)) {
|
|
4860
5311
|
return err(
|
|
4861
5312
|
new Error("No fornix.json found. Not a Fornix project directory.")
|
|
4862
5313
|
);
|
|
4863
5314
|
}
|
|
4864
5315
|
let manifest2;
|
|
4865
5316
|
try {
|
|
4866
|
-
const raw =
|
|
5317
|
+
const raw = readFileSync11(manifestPath, "utf-8");
|
|
4867
5318
|
manifest2 = JSON.parse(raw);
|
|
4868
5319
|
} catch {
|
|
4869
5320
|
return err(new Error("Failed to parse fornix.json."));
|
|
@@ -4889,8 +5340,8 @@ async function removeBlock(input) {
|
|
|
4889
5340
|
const filesToRemove = [];
|
|
4890
5341
|
if (blockManifest) {
|
|
4891
5342
|
for (const file of blockManifest.files) {
|
|
4892
|
-
const filePath =
|
|
4893
|
-
if (
|
|
5343
|
+
const filePath = join13(projectDirectory, file.destination);
|
|
5344
|
+
if (existsSync10(filePath)) {
|
|
4894
5345
|
filesToRemove.push(filePath);
|
|
4895
5346
|
}
|
|
4896
5347
|
}
|
|
@@ -4992,7 +5443,7 @@ async function validateContent(input) {
|
|
|
4992
5443
|
}
|
|
4993
5444
|
const schemaShape = {};
|
|
4994
5445
|
for (const [slotName, slot] of Object.entries(contentSlots)) {
|
|
4995
|
-
schemaShape[slotName] =
|
|
5446
|
+
schemaShape[slotName] = zodTypeForSlot(slot.type);
|
|
4996
5447
|
}
|
|
4997
5448
|
const schema = z5.object(schemaShape);
|
|
4998
5449
|
const parseResult = schema.safeParse(data);
|
|
@@ -5004,7 +5455,7 @@ async function validateContent(input) {
|
|
|
5004
5455
|
);
|
|
5005
5456
|
return ok({ valid: false, errors });
|
|
5006
5457
|
}
|
|
5007
|
-
function
|
|
5458
|
+
function zodTypeForSlot(slotType) {
|
|
5008
5459
|
switch (slotType) {
|
|
5009
5460
|
case "string":
|
|
5010
5461
|
return z5.string();
|
|
@@ -5022,17 +5473,17 @@ function zodTypeForSlot2(slotType) {
|
|
|
5022
5473
|
}
|
|
5023
5474
|
|
|
5024
5475
|
// src/mcp/tools/get-project-status.ts
|
|
5025
|
-
import { readFileSync as
|
|
5026
|
-
import { join as
|
|
5476
|
+
import { readFileSync as readFileSync12, existsSync as existsSync11 } from "fs";
|
|
5477
|
+
import { join as join14 } from "path";
|
|
5027
5478
|
function getProjectStatus(input) {
|
|
5028
|
-
const manifestPath =
|
|
5029
|
-
if (!
|
|
5479
|
+
const manifestPath = join14(input.projectDirectory, "fornix.json");
|
|
5480
|
+
if (!existsSync11(manifestPath)) {
|
|
5030
5481
|
return err(
|
|
5031
5482
|
new Error("No fornix.json found. Not a Fornix project directory.")
|
|
5032
5483
|
);
|
|
5033
5484
|
}
|
|
5034
5485
|
try {
|
|
5035
|
-
const raw =
|
|
5486
|
+
const raw = readFileSync12(manifestPath, "utf-8");
|
|
5036
5487
|
const manifest2 = JSON.parse(raw);
|
|
5037
5488
|
const blocks = manifest2.blocks;
|
|
5038
5489
|
return ok({
|
|
@@ -5055,7 +5506,7 @@ function getProjectStatus(input) {
|
|
|
5055
5506
|
|
|
5056
5507
|
// src/mcp/tools/scaffold-project.ts
|
|
5057
5508
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync9 } from "fs";
|
|
5058
|
-
import { join as
|
|
5509
|
+
import { join as join15, basename as basename3 } from "path";
|
|
5059
5510
|
var DEFAULT_COLORS2 = {
|
|
5060
5511
|
primary: "#6366f1",
|
|
5061
5512
|
secondary: "#818cf8",
|
|
@@ -5126,8 +5577,8 @@ async function scaffoldProject(input) {
|
|
|
5126
5577
|
const files = result.value.files;
|
|
5127
5578
|
let filesCreated = 0;
|
|
5128
5579
|
for (const [relativePath, content] of Object.entries(files)) {
|
|
5129
|
-
const fullPath =
|
|
5130
|
-
const parentDirectory =
|
|
5580
|
+
const fullPath = join15(projectDirectory, relativePath);
|
|
5581
|
+
const parentDirectory = join15(fullPath, "..");
|
|
5131
5582
|
mkdirSync7(parentDirectory, { recursive: true });
|
|
5132
5583
|
writeFileSync9(fullPath, content, "utf-8");
|
|
5133
5584
|
filesCreated++;
|