create-fornix 0.0.7 → 0.0.9
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 +319 -94
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { defineCommand as defineCommand8 } from "citty";
|
|
|
9
9
|
// src/cli/commands/create.ts
|
|
10
10
|
import { defineCommand } from "citty";
|
|
11
11
|
import { resolve as resolve2, basename as basename2 } from "path";
|
|
12
|
-
import { mkdirSync as mkdirSync4, writeFileSync as
|
|
12
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
13
13
|
import { join as join6 } from "path";
|
|
14
14
|
import * as p2 from "@clack/prompts";
|
|
15
15
|
import pc3 from "picocolors";
|
|
@@ -329,6 +329,39 @@ const { title } = Astro.props;${tailwindImport}
|
|
|
329
329
|
<slot />
|
|
330
330
|
</body>
|
|
331
331
|
</html>
|
|
332
|
+
|
|
333
|
+
<style is:global>
|
|
334
|
+
*, *::before, *::after {
|
|
335
|
+
box-sizing: border-box;
|
|
336
|
+
margin: 0;
|
|
337
|
+
padding: 0;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
html {
|
|
341
|
+
scroll-behavior: smooth;
|
|
342
|
+
-webkit-font-smoothing: antialiased;
|
|
343
|
+
-moz-osx-font-smoothing: grayscale;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
body {
|
|
347
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
348
|
+
background: var(--color-background, #0f172a);
|
|
349
|
+
color: var(--color-foreground, #f8fafc);
|
|
350
|
+
line-height: 1.6;
|
|
351
|
+
min-height: 100vh;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
img, video {
|
|
355
|
+
max-width: 100%;
|
|
356
|
+
height: auto;
|
|
357
|
+
display: block;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
a {
|
|
361
|
+
color: inherit;
|
|
362
|
+
text-decoration: none;
|
|
363
|
+
}
|
|
364
|
+
</style>
|
|
332
365
|
`.trim() + "\n";
|
|
333
366
|
if (config.deployTarget === "cloudflare") {
|
|
334
367
|
const wrangler = {
|
|
@@ -373,7 +406,7 @@ function generateAstroConfig(config, blocks = []) {
|
|
|
373
406
|
local: "defineConfig"
|
|
374
407
|
});
|
|
375
408
|
const configObject = module.exports.default.$args[0];
|
|
376
|
-
if (config.renderMode === "server") {
|
|
409
|
+
if (config.renderMode === "server" || config.renderMode === "hybrid") {
|
|
377
410
|
configObject.output = "server";
|
|
378
411
|
}
|
|
379
412
|
const adapter = ADAPTER_MAP[config.deployTarget];
|
|
@@ -427,22 +460,11 @@ function generateTailwindConfig(config) {
|
|
|
427
460
|
if (config.cssEngine !== "tailwind") {
|
|
428
461
|
return ok(null);
|
|
429
462
|
}
|
|
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
463
|
const lines = [
|
|
440
464
|
'@import "tailwindcss";',
|
|
441
465
|
'@import "./src/styles/palettes/_current.css";',
|
|
442
466
|
"",
|
|
443
467
|
`@source "./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}";`,
|
|
444
|
-
"",
|
|
445
|
-
themeBlock,
|
|
446
468
|
""
|
|
447
469
|
];
|
|
448
470
|
return ok(lines.join("\n"));
|
|
@@ -615,7 +637,7 @@ function generateContentConfig(blocks) {
|
|
|
615
637
|
'import { defineCollection, z } from "astro:content";'
|
|
616
638
|
];
|
|
617
639
|
const collections = [];
|
|
618
|
-
const
|
|
640
|
+
const dataCollectionNames = /* @__PURE__ */ new Set();
|
|
619
641
|
for (const block of blocks) {
|
|
620
642
|
if (block.collections && block.collections.length > 0) {
|
|
621
643
|
for (const col of block.collections) {
|
|
@@ -632,27 +654,15 @@ function generateContentConfig(blocks) {
|
|
|
632
654
|
}
|
|
633
655
|
const slots = block.ai?.contentSlots;
|
|
634
656
|
if (slots && Object.keys(slots).length > 0) {
|
|
635
|
-
const schemaFields = Object.entries(slots).map(([name, slot]) => ` ${name}: ${zodTypeForSlot(slot)}.optional(),`).join("\n");
|
|
636
657
|
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
|
-
);
|
|
658
|
+
dataCollectionNames.add(subdirectory);
|
|
646
659
|
}
|
|
647
660
|
}
|
|
648
|
-
for (const
|
|
649
|
-
const schemaStr = schemas.length === 1 ? schemas[0] : `z.union([
|
|
650
|
-
${schemas.join(",\n")}
|
|
651
|
-
])`;
|
|
661
|
+
for (const colName of dataCollectionNames) {
|
|
652
662
|
collections.push(
|
|
653
663
|
` "${colName}": defineCollection({
|
|
654
664
|
type: "data",
|
|
655
|
-
schema:
|
|
665
|
+
schema: z.record(z.unknown()),
|
|
656
666
|
})`
|
|
657
667
|
);
|
|
658
668
|
}
|
|
@@ -666,20 +676,6 @@ ${schemas.join(",\n")}
|
|
|
666
676
|
];
|
|
667
677
|
return lines.join("\n");
|
|
668
678
|
}
|
|
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
679
|
function buildDefaultFromSlots(slots) {
|
|
684
680
|
const content = {};
|
|
685
681
|
for (const [name, slot] of Object.entries(slots)) {
|
|
@@ -703,13 +699,13 @@ function defaultValueForType(type) {
|
|
|
703
699
|
}
|
|
704
700
|
|
|
705
701
|
// src/scaffold/i18n-wiring.ts
|
|
706
|
-
function wireI18n(config) {
|
|
702
|
+
function wireI18n(config, manifests) {
|
|
707
703
|
const files = {};
|
|
708
704
|
if (config.locales.length < 2) {
|
|
709
705
|
return ok(files);
|
|
710
706
|
}
|
|
711
707
|
files["src/i18n/utils.ts"] = generateI18nUtils(config);
|
|
712
|
-
files["src/pages/[locale]/index.astro"] = generateLocaleIndexPage(config);
|
|
708
|
+
files["src/pages/[locale]/index.astro"] = generateLocaleIndexPage(config, manifests ?? []);
|
|
713
709
|
return ok(files);
|
|
714
710
|
}
|
|
715
711
|
function generateI18nUtils(config) {
|
|
@@ -744,11 +740,21 @@ export function t<T>(
|
|
|
744
740
|
}
|
|
745
741
|
`;
|
|
746
742
|
}
|
|
747
|
-
function generateLocaleIndexPage(config) {
|
|
743
|
+
function generateLocaleIndexPage(config, manifests) {
|
|
744
|
+
const sectionBlocks = manifests.filter((m) => m.type === "section");
|
|
745
|
+
const imports = [];
|
|
746
|
+
const tags = [];
|
|
747
|
+
for (const block of sectionBlocks) {
|
|
748
|
+
const componentName = block.name.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
749
|
+
imports.push(`import ${componentName} from '../../../components/sections/${block.name}.astro';`);
|
|
750
|
+
tags.push(` <${componentName} />`);
|
|
751
|
+
}
|
|
752
|
+
const importSection = imports.length > 0 ? imports.join("\n") + "\n" : "";
|
|
753
|
+
const blockSection = tags.length > 0 ? "\n" + tags.join("\n") + "\n " : "\n <h1>" + config.projectName + "</h1>\n <p>Locale: {locale}</p>\n ";
|
|
748
754
|
return `---
|
|
749
|
-
import { locales } from "
|
|
750
|
-
import Layout from "
|
|
751
|
-
|
|
755
|
+
import { locales } from "../../../i18n/utils";
|
|
756
|
+
import Layout from "../../../layouts/Layout.astro";
|
|
757
|
+
${importSection}
|
|
752
758
|
export function getStaticPaths() {
|
|
753
759
|
return locales.map((locale) => ({ params: { locale } }));
|
|
754
760
|
}
|
|
@@ -756,10 +762,7 @@ export function getStaticPaths() {
|
|
|
756
762
|
const { locale } = Astro.params;
|
|
757
763
|
---
|
|
758
764
|
<Layout title="${config.projectName}">
|
|
759
|
-
<main>
|
|
760
|
-
<h1>${config.projectName}</h1>
|
|
761
|
-
<p>Locale: {locale}</p>
|
|
762
|
-
</main>
|
|
765
|
+
<main>${blockSection}</main>
|
|
763
766
|
</Layout>
|
|
764
767
|
`;
|
|
765
768
|
}
|
|
@@ -875,7 +878,7 @@ function scaffold(input) {
|
|
|
875
878
|
return err(contentResult.error);
|
|
876
879
|
}
|
|
877
880
|
Object.assign(files, contentResult.value);
|
|
878
|
-
const i18nResult = wireI18n(config);
|
|
881
|
+
const i18nResult = wireI18n(config, resolvedManifests);
|
|
879
882
|
if (!isOk(i18nResult)) {
|
|
880
883
|
return err(i18nResult.error);
|
|
881
884
|
}
|
|
@@ -1503,7 +1506,7 @@ function manifest(name, overrides = {}) {
|
|
|
1503
1506
|
var FIXTURE_MANIFESTS = {
|
|
1504
1507
|
"hero-gradient": manifest("hero-gradient", {
|
|
1505
1508
|
category: "hero",
|
|
1506
|
-
conflicts: ["hero-video"],
|
|
1509
|
+
conflicts: ["hero-video", "hero-split"],
|
|
1507
1510
|
ai: {
|
|
1508
1511
|
whenToUse: "Landing page hero with gradient background",
|
|
1509
1512
|
whenNotToUse: "Internal pages",
|
|
@@ -1515,7 +1518,8 @@ var FIXTURE_MANIFESTS = {
|
|
|
1515
1518
|
}
|
|
1516
1519
|
}),
|
|
1517
1520
|
"footer-minimal": manifest("footer-minimal", {
|
|
1518
|
-
category: "footer"
|
|
1521
|
+
category: "footer",
|
|
1522
|
+
conflicts: ["footer-rich"]
|
|
1519
1523
|
}),
|
|
1520
1524
|
"cta-simple": manifest("cta-simple", {
|
|
1521
1525
|
category: "cta"
|
|
@@ -1626,17 +1630,96 @@ var FIXTURE_MANIFESTS = {
|
|
|
1626
1630
|
}
|
|
1627
1631
|
}
|
|
1628
1632
|
}),
|
|
1629
|
-
"hero-video": manifest("hero-video"
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1633
|
+
"hero-video": manifest("hero-video", {
|
|
1634
|
+
category: "hero",
|
|
1635
|
+
conflicts: ["hero-gradient", "hero-split"],
|
|
1636
|
+
files: [
|
|
1637
|
+
{ source: "hero-video.astro", destination: "src/components/sections/hero-video.astro" },
|
|
1638
|
+
{ source: "hero-video.css", destination: "src/styles/sections/hero-video.css" }
|
|
1639
|
+
],
|
|
1640
|
+
ai: {
|
|
1641
|
+
whenToUse: "Hero with background video",
|
|
1642
|
+
whenNotToUse: "Static sites",
|
|
1643
|
+
pairsWith: [],
|
|
1644
|
+
contentSlots: {
|
|
1645
|
+
headline: { type: "string" },
|
|
1646
|
+
subheadline: { type: "string" },
|
|
1647
|
+
ctaText: { type: "string" },
|
|
1648
|
+
ctaHref: { type: "string" },
|
|
1649
|
+
videoUrl: { type: "string" },
|
|
1650
|
+
posterUrl: { type: "string" }
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
}),
|
|
1654
|
+
"features-bento": manifest("features-bento", {
|
|
1655
|
+
category: "features",
|
|
1656
|
+
ai: {
|
|
1657
|
+
whenToUse: "Feature showcase in bento grid",
|
|
1658
|
+
whenNotToUse: "Simple pages",
|
|
1659
|
+
pairsWith: [],
|
|
1660
|
+
contentSlots: {
|
|
1661
|
+
headline: { type: "string" },
|
|
1662
|
+
subheadline: { type: "string" },
|
|
1663
|
+
items: { type: "array" }
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
}),
|
|
1667
|
+
"pricing-table": manifest("pricing-table", {
|
|
1668
|
+
category: "pricing",
|
|
1669
|
+
conflicts: ["pricing-comparison"],
|
|
1670
|
+
ai: {
|
|
1671
|
+
whenToUse: "Pricing plans display",
|
|
1672
|
+
whenNotToUse: "Free products",
|
|
1673
|
+
pairsWith: [],
|
|
1674
|
+
contentSlots: {
|
|
1675
|
+
headline: { type: "string" },
|
|
1676
|
+
subheadline: { type: "string" },
|
|
1677
|
+
plans: { type: "array" }
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
}),
|
|
1681
|
+
"faq-accordion": manifest("faq-accordion", {
|
|
1682
|
+
category: "faq",
|
|
1683
|
+
ai: {
|
|
1684
|
+
whenToUse: "Frequently asked questions",
|
|
1685
|
+
whenNotToUse: "Simple pages",
|
|
1686
|
+
pairsWith: [],
|
|
1687
|
+
contentSlots: {
|
|
1688
|
+
headline: { type: "string" },
|
|
1689
|
+
items: { type: "array" }
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
}),
|
|
1693
|
+
"footer-rich": manifest("footer-rich", {
|
|
1694
|
+
category: "footer",
|
|
1695
|
+
conflicts: ["footer-minimal"],
|
|
1696
|
+
ai: {
|
|
1697
|
+
whenToUse: "Multi-column footer with links",
|
|
1698
|
+
whenNotToUse: "Minimal sites",
|
|
1699
|
+
pairsWith: [],
|
|
1700
|
+
contentSlots: {
|
|
1701
|
+
brand: { type: "string" },
|
|
1702
|
+
description: { type: "string" },
|
|
1703
|
+
columns: { type: "array" },
|
|
1704
|
+
copyright: { type: "string" }
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
}),
|
|
1634
1708
|
"testimonials-carousel": manifest("testimonials-carousel"),
|
|
1635
1709
|
"contact-form": manifest("contact-form"),
|
|
1636
|
-
"hero-split": manifest("hero-split"
|
|
1637
|
-
|
|
1710
|
+
"hero-split": manifest("hero-split", {
|
|
1711
|
+
category: "hero",
|
|
1712
|
+
conflicts: ["hero-gradient", "hero-video"]
|
|
1713
|
+
}),
|
|
1714
|
+
"header-transparent": manifest("header-transparent", {
|
|
1715
|
+
category: "header",
|
|
1716
|
+
conflicts: ["header-sticky"]
|
|
1717
|
+
}),
|
|
1638
1718
|
"cta-newsletter": manifest("cta-newsletter"),
|
|
1639
|
-
"header-sticky": manifest("header-sticky"
|
|
1719
|
+
"header-sticky": manifest("header-sticky", {
|
|
1720
|
+
category: "header",
|
|
1721
|
+
conflicts: ["header-transparent"]
|
|
1722
|
+
})
|
|
1640
1723
|
};
|
|
1641
1724
|
var FIXTURE_BLOCK_SOURCES = {
|
|
1642
1725
|
"hero-gradient": {
|
|
@@ -1769,11 +1852,31 @@ export function getStaticPaths() { return [{ params: { slug: '1' } }]; }
|
|
|
1769
1852
|
<slot />`,
|
|
1770
1853
|
"default-content.json": `{ "sidebarLinks": [], "logoutText": "" }`
|
|
1771
1854
|
},
|
|
1772
|
-
"hero-video": {
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1855
|
+
"hero-video": {
|
|
1856
|
+
"hero-video.astro": "<section>Hero Video</section>\n",
|
|
1857
|
+
"hero-video.css": ".hero-video { min-height: 80vh; }\n",
|
|
1858
|
+
"default-content.json": '{"headline":"Experience the Future","subheadline":"Immersive experiences.","ctaText":"Get Started","ctaHref":"#","videoUrl":"","posterUrl":""}'
|
|
1859
|
+
},
|
|
1860
|
+
"features-bento": {
|
|
1861
|
+
"features-bento.astro": "<section>Features Bento</section>\n",
|
|
1862
|
+
"features-bento.css": ".features-bento { padding: 4rem 2rem; }\n",
|
|
1863
|
+
"default-content.json": '{"headline":"Features","subheadline":"Everything you need.","items":[{"title":"Fast","description":"Lightning speed."}]}'
|
|
1864
|
+
},
|
|
1865
|
+
"pricing-table": {
|
|
1866
|
+
"pricing-table.astro": "<section>Pricing</section>\n",
|
|
1867
|
+
"pricing-table.css": ".pricing-table { padding: 4rem 2rem; }\n",
|
|
1868
|
+
"default-content.json": '{"headline":"Pricing","subheadline":"Simple pricing.","plans":[{"name":"Free","price":"$0","features":["Basic"]}]}'
|
|
1869
|
+
},
|
|
1870
|
+
"faq-accordion": {
|
|
1871
|
+
"faq-accordion.astro": "<section>FAQ</section>\n",
|
|
1872
|
+
"faq-accordion.css": ".faq-accordion { padding: 4rem 2rem; }\n",
|
|
1873
|
+
"default-content.json": '{"headline":"FAQ","items":[{"question":"How does it work?","answer":"It just works."}]}'
|
|
1874
|
+
},
|
|
1875
|
+
"footer-rich": {
|
|
1876
|
+
"footer-rich.astro": "<section>Footer</section>\n",
|
|
1877
|
+
"footer-rich.css": ".footer-rich { padding: 3rem 2rem; }\n",
|
|
1878
|
+
"default-content.json": '{"brand":"Acme","description":"Building the future.","columns":[],"copyright":"\xA9 2025"}'
|
|
1879
|
+
},
|
|
1777
1880
|
"testimonials-carousel": { "testimonials-carousel.astro": "<section>Testimonials</section>\n" },
|
|
1778
1881
|
"contact-form": { "contact-form.astro": "<section>Contact</section>\n" },
|
|
1779
1882
|
"hero-split": { "hero-split.astro": "<section>Hero Split</section>\n" },
|
|
@@ -1898,10 +2001,13 @@ import {
|
|
|
1898
2001
|
readFileSync as readFileSync3,
|
|
1899
2002
|
readdirSync as readdirSync2,
|
|
1900
2003
|
mkdirSync as mkdirSync2,
|
|
1901
|
-
statSync as statSync2
|
|
2004
|
+
statSync as statSync2,
|
|
2005
|
+
writeFileSync as writeFileSync2,
|
|
2006
|
+
rmSync
|
|
1902
2007
|
} from "fs";
|
|
1903
2008
|
import { join as join3 } from "path";
|
|
1904
2009
|
import { homedir as homedir2 } from "os";
|
|
2010
|
+
import { createRequire } from "module";
|
|
1905
2011
|
var DEFAULT_CONFIG2 = {
|
|
1906
2012
|
repo: "kamsqe/fornix",
|
|
1907
2013
|
ref: "main",
|
|
@@ -1911,6 +2017,31 @@ var DEFAULT_CONFIG2 = {
|
|
|
1911
2017
|
maxCacheAge: 24 * 60 * 60 * 1e3
|
|
1912
2018
|
// 24 hours
|
|
1913
2019
|
};
|
|
2020
|
+
var cacheVersionChecked = false;
|
|
2021
|
+
function ensureCacheVersion(cacheDir) {
|
|
2022
|
+
if (cacheVersionChecked) return;
|
|
2023
|
+
cacheVersionChecked = true;
|
|
2024
|
+
let cliVersion = "unknown";
|
|
2025
|
+
try {
|
|
2026
|
+
const require2 = createRequire(import.meta.url);
|
|
2027
|
+
const pkg = require2("../../package.json");
|
|
2028
|
+
cliVersion = pkg.version ?? "unknown";
|
|
2029
|
+
} catch {
|
|
2030
|
+
}
|
|
2031
|
+
const versionFile = join3(cacheDir, ".version");
|
|
2032
|
+
try {
|
|
2033
|
+
if (existsSync2(versionFile)) {
|
|
2034
|
+
const cached = readFileSync3(versionFile, "utf-8").trim();
|
|
2035
|
+
if (cached === cliVersion) return;
|
|
2036
|
+
}
|
|
2037
|
+
if (existsSync2(cacheDir)) {
|
|
2038
|
+
rmSync(cacheDir, { recursive: true, force: true });
|
|
2039
|
+
}
|
|
2040
|
+
mkdirSync2(cacheDir, { recursive: true });
|
|
2041
|
+
writeFileSync2(versionFile, cliVersion);
|
|
2042
|
+
} catch {
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
1914
2045
|
async function fetchBlock(blockName, config = {}) {
|
|
1915
2046
|
if (process.env.FORNIX_E2E_MOCK === "true") {
|
|
1916
2047
|
const manifest2 = FIXTURE_MANIFESTS[blockName];
|
|
@@ -1925,6 +2056,7 @@ async function fetchBlock(blockName, config = {}) {
|
|
|
1925
2056
|
return ok({ manifest: manifest2, files, fromCache: true });
|
|
1926
2057
|
}
|
|
1927
2058
|
const cfg = { ...DEFAULT_CONFIG2, ...config };
|
|
2059
|
+
ensureCacheVersion(cfg.cacheDir);
|
|
1928
2060
|
const blockCacheDir = join3(cfg.cacheDir, blockName);
|
|
1929
2061
|
if (!cfg.force && isCacheValid2(blockCacheDir, cfg.maxCacheAge)) {
|
|
1930
2062
|
return loadFromCache2(blockName, blockCacheDir);
|
|
@@ -2038,7 +2170,7 @@ async function runManualFlow(input) {
|
|
|
2038
2170
|
message: "Choose a render mode",
|
|
2039
2171
|
options: [
|
|
2040
2172
|
{ value: "static", label: "Static (SSG)", hint: "Pre-built HTML, fastest" },
|
|
2041
|
-
{ value: "hybrid", label: "Hybrid", hint: "Static
|
|
2173
|
+
{ value: "hybrid", label: "Hybrid", hint: "Static + per-page SSR opt-in" },
|
|
2042
2174
|
{ value: "server", label: "Server (SSR)", hint: "Server-rendered on every request" }
|
|
2043
2175
|
]
|
|
2044
2176
|
});
|
|
@@ -2206,7 +2338,7 @@ function buildSummary(config, blockNames, palette) {
|
|
|
2206
2338
|
|
|
2207
2339
|
// src/scaffold/post-scaffold.ts
|
|
2208
2340
|
import { execSync } from "child_process";
|
|
2209
|
-
import { writeFileSync as
|
|
2341
|
+
import { writeFileSync as writeFileSync3 } from "fs";
|
|
2210
2342
|
import { join as join4, basename } from "path";
|
|
2211
2343
|
import pc2 from "picocolors";
|
|
2212
2344
|
function runPostScaffold(input, callbacks) {
|
|
@@ -2256,7 +2388,7 @@ function runPostScaffold(input, callbacks) {
|
|
|
2256
2388
|
log(pc2.dim(` ${config.packageManager} install`));
|
|
2257
2389
|
}
|
|
2258
2390
|
log(pc2.dim(` ${config.packageManager} dev`));
|
|
2259
|
-
log(pc2.dim(` fornix add <block>`));
|
|
2391
|
+
log(pc2.dim(` npx create-fornix add <block>`));
|
|
2260
2392
|
log("");
|
|
2261
2393
|
}
|
|
2262
2394
|
function installDependencies(projectDir, packageManager, verbose, log, warn) {
|
|
@@ -2349,9 +2481,9 @@ function generateClaudeMd(projectDir, config, blockNames) {
|
|
|
2349
2481
|
lines.push("");
|
|
2350
2482
|
lines.push(`- \`${config.packageManager} dev\` \u2014 start development server`);
|
|
2351
2483
|
lines.push(`- \`${config.packageManager} build\` \u2014 build for production`);
|
|
2352
|
-
lines.push("- `fornix add <block>` \u2014 add a new block");
|
|
2353
|
-
lines.push("- `fornix remove <block>` \u2014 remove a block");
|
|
2354
|
-
lines.push("- `fornix status` \u2014 show project configuration");
|
|
2484
|
+
lines.push("- `npx create-fornix add <block>` \u2014 add a new block");
|
|
2485
|
+
lines.push("- `npx create-fornix remove <block>` \u2014 remove a block");
|
|
2486
|
+
lines.push("- `npx create-fornix status` \u2014 show project configuration");
|
|
2355
2487
|
lines.push("");
|
|
2356
2488
|
lines.push("## File Structure");
|
|
2357
2489
|
lines.push("");
|
|
@@ -2373,7 +2505,7 @@ function generateClaudeMd(projectDir, config, blockNames) {
|
|
|
2373
2505
|
lines.push("```");
|
|
2374
2506
|
lines.push("");
|
|
2375
2507
|
const content = lines.join("\n");
|
|
2376
|
-
|
|
2508
|
+
writeFileSync3(join4(projectDir, "CLAUDE.md"), content, "utf-8");
|
|
2377
2509
|
}
|
|
2378
2510
|
|
|
2379
2511
|
// src/ai/prompt-builder.ts
|
|
@@ -3800,8 +3932,9 @@ async function runFlagDrivenMode(args2, manifests, allPalettes) {
|
|
|
3800
3932
|
async function runScaffold(config, manifests, allPalettes, dryRun, verbose, skipInstall, skipGit) {
|
|
3801
3933
|
const spinner2 = p2.spinner();
|
|
3802
3934
|
spinner2.start("Fetching blocks from registry...");
|
|
3803
|
-
const
|
|
3804
|
-
const
|
|
3935
|
+
const selectedBlockNames = config.blocks.map((b) => b.name);
|
|
3936
|
+
const allBlockNames = preResolveDependencies(selectedBlockNames, manifests);
|
|
3937
|
+
const blockResults = await fetchBlocks(allBlockNames);
|
|
3805
3938
|
const blockSources = {};
|
|
3806
3939
|
const blockDefaultContent = {};
|
|
3807
3940
|
for (const result2 of blockResults) {
|
|
@@ -3863,7 +3996,7 @@ async function runScaffold(config, manifests, allPalettes, dryRun, verbose, skip
|
|
|
3863
3996
|
const fullPath = join6(config.projectDir, relativePath);
|
|
3864
3997
|
const parentDir = join6(fullPath, "..");
|
|
3865
3998
|
mkdirSync4(parentDir, { recursive: true });
|
|
3866
|
-
|
|
3999
|
+
writeFileSync4(fullPath, content, "utf-8");
|
|
3867
4000
|
filesWritten++;
|
|
3868
4001
|
if (verbose) {
|
|
3869
4002
|
console.log(pc3.dim(` created ${relativePath}`));
|
|
@@ -3948,12 +4081,79 @@ function showNoProviderGuide() {
|
|
|
3948
4081
|
console.error(" export CLOUDFLARE_ACCOUNT_ID=... CLOUDFLARE_API_TOKEN=...\n");
|
|
3949
4082
|
console.error(pc3.dim(" Or use manual mode: npx create-fornix --manual\n"));
|
|
3950
4083
|
}
|
|
4084
|
+
function preResolveDependencies(selected, manifests) {
|
|
4085
|
+
const result = /* @__PURE__ */ new Set();
|
|
4086
|
+
function walk(name) {
|
|
4087
|
+
if (result.has(name)) return;
|
|
4088
|
+
const manifest2 = manifests[name];
|
|
4089
|
+
if (!manifest2) {
|
|
4090
|
+
result.add(name);
|
|
4091
|
+
return;
|
|
4092
|
+
}
|
|
4093
|
+
for (const dep of manifest2.requires) {
|
|
4094
|
+
walk(dep);
|
|
4095
|
+
}
|
|
4096
|
+
result.add(name);
|
|
4097
|
+
}
|
|
4098
|
+
for (const name of selected) {
|
|
4099
|
+
walk(name);
|
|
4100
|
+
}
|
|
4101
|
+
return [...result];
|
|
4102
|
+
}
|
|
3951
4103
|
|
|
3952
4104
|
// src/cli/commands/add.ts
|
|
3953
4105
|
import { defineCommand as defineCommand2 } from "citty";
|
|
3954
4106
|
import pc4 from "picocolors";
|
|
3955
|
-
import { readFileSync as readFileSync5, writeFileSync as
|
|
4107
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync4, mkdirSync as mkdirSync5 } from "fs";
|
|
3956
4108
|
import { join as join7, dirname as dirname3 } from "path";
|
|
4109
|
+
|
|
4110
|
+
// src/scaffold/page-updater.ts
|
|
4111
|
+
function blockNameToComponentName(blockName) {
|
|
4112
|
+
return blockName.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
4113
|
+
}
|
|
4114
|
+
function blockNameToImportPath(blockName) {
|
|
4115
|
+
return `../components/sections/${blockName}.astro`;
|
|
4116
|
+
}
|
|
4117
|
+
function addBlockToPage(pageContent, blockName) {
|
|
4118
|
+
const componentName = blockNameToComponentName(blockName);
|
|
4119
|
+
const importPath = blockNameToImportPath(blockName);
|
|
4120
|
+
if (pageContent.includes(importPath) || pageContent.includes(`import ${componentName}`)) {
|
|
4121
|
+
return pageContent;
|
|
4122
|
+
}
|
|
4123
|
+
const importLine = `import ${componentName} from '${importPath}';`;
|
|
4124
|
+
const componentTag = ` <${componentName} />`;
|
|
4125
|
+
const frontmatterEnd = pageContent.indexOf("---", pageContent.indexOf("---") + 3);
|
|
4126
|
+
if (frontmatterEnd === -1) {
|
|
4127
|
+
return pageContent;
|
|
4128
|
+
}
|
|
4129
|
+
let updated = pageContent.slice(0, frontmatterEnd) + importLine + "\n" + pageContent.slice(frontmatterEnd);
|
|
4130
|
+
const mainCloseIndex = updated.lastIndexOf("</main>");
|
|
4131
|
+
if (mainCloseIndex !== -1) {
|
|
4132
|
+
updated = updated.slice(0, mainCloseIndex) + componentTag + "\n " + updated.slice(mainCloseIndex);
|
|
4133
|
+
}
|
|
4134
|
+
return updated;
|
|
4135
|
+
}
|
|
4136
|
+
function removeBlockFromPage(pageContent, blockName) {
|
|
4137
|
+
const componentName = blockNameToComponentName(blockName);
|
|
4138
|
+
const importPath = blockNameToImportPath(blockName);
|
|
4139
|
+
let updated = pageContent;
|
|
4140
|
+
const importRegex = new RegExp(
|
|
4141
|
+
`^\\s*import\\s+${componentName}\\s+from\\s+['"]${escapeRegex(importPath)}['"];?\\s*\\n?`,
|
|
4142
|
+
"m"
|
|
4143
|
+
);
|
|
4144
|
+
updated = updated.replace(importRegex, "");
|
|
4145
|
+
const tagRegex = new RegExp(
|
|
4146
|
+
`^\\s*<${componentName}\\s*/?>\\s*\\n?`,
|
|
4147
|
+
"m"
|
|
4148
|
+
);
|
|
4149
|
+
updated = updated.replace(tagRegex, "");
|
|
4150
|
+
return updated;
|
|
4151
|
+
}
|
|
4152
|
+
function escapeRegex(str) {
|
|
4153
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4154
|
+
}
|
|
4155
|
+
|
|
4156
|
+
// src/cli/commands/add.ts
|
|
3957
4157
|
var addCommand = defineCommand2({
|
|
3958
4158
|
meta: {
|
|
3959
4159
|
name: "add",
|
|
@@ -4079,7 +4279,7 @@ var addCommand = defineCommand2({
|
|
|
4079
4279
|
}
|
|
4080
4280
|
for (const file of filesToWrite) {
|
|
4081
4281
|
mkdirSync5(dirname3(file.path), { recursive: true });
|
|
4082
|
-
|
|
4282
|
+
writeFileSync5(file.path, file.content);
|
|
4083
4283
|
if (typedArgs.verbose) {
|
|
4084
4284
|
console.log(` ${pc4.dim("\u2192")} ${file.path}`);
|
|
4085
4285
|
}
|
|
@@ -4095,7 +4295,21 @@ var addCommand = defineCommand2({
|
|
|
4095
4295
|
installedAt: now
|
|
4096
4296
|
});
|
|
4097
4297
|
}
|
|
4098
|
-
|
|
4298
|
+
writeFileSync5(manifestPath, JSON.stringify(manifest2, null, 2) + "\n");
|
|
4299
|
+
const indexPath = join7(cwd, "src/pages/index.astro");
|
|
4300
|
+
if (existsSync4(indexPath)) {
|
|
4301
|
+
let pageContent = readFileSync5(indexPath, "utf-8");
|
|
4302
|
+
for (const name of blocksToAdd) {
|
|
4303
|
+
const bManifest = manifests[name];
|
|
4304
|
+
if (bManifest && bManifest.type === "section") {
|
|
4305
|
+
pageContent = addBlockToPage(pageContent, name);
|
|
4306
|
+
}
|
|
4307
|
+
}
|
|
4308
|
+
writeFileSync5(indexPath, pageContent);
|
|
4309
|
+
if (typedArgs.verbose) {
|
|
4310
|
+
console.log(` ${pc4.dim("\u270E")} updated index.astro`);
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4099
4313
|
console.log();
|
|
4100
4314
|
for (const name of blocksToAdd) {
|
|
4101
4315
|
const isDep = name !== blockName;
|
|
@@ -4136,7 +4350,7 @@ function resolveDependencies2(blockName, installedNames, manifests) {
|
|
|
4136
4350
|
// src/cli/commands/remove.ts
|
|
4137
4351
|
import { defineCommand as defineCommand3 } from "citty";
|
|
4138
4352
|
import pc5 from "picocolors";
|
|
4139
|
-
import { readFileSync as readFileSync6, writeFileSync as
|
|
4353
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, existsSync as existsSync5, unlinkSync, readdirSync as readdirSync3, rmdirSync } from "fs";
|
|
4140
4354
|
import { join as join8, dirname as dirname4 } from "path";
|
|
4141
4355
|
var removeCommand = defineCommand3({
|
|
4142
4356
|
meta: {
|
|
@@ -4231,7 +4445,18 @@ var removeCommand = defineCommand3({
|
|
|
4231
4445
|
tryRemoveEmptyDir(dirname4(filePath), cwd);
|
|
4232
4446
|
}
|
|
4233
4447
|
manifest2.blocks = manifest2.blocks.filter((b) => b.name !== blockName);
|
|
4234
|
-
|
|
4448
|
+
writeFileSync6(manifestPath, JSON.stringify(manifest2, null, 2) + "\n");
|
|
4449
|
+
const indexPath = join8(cwd, "src/pages/index.astro");
|
|
4450
|
+
if (existsSync5(indexPath)) {
|
|
4451
|
+
const original = readFileSync6(indexPath, "utf-8");
|
|
4452
|
+
const updated = removeBlockFromPage(original, blockName);
|
|
4453
|
+
if (updated !== original) {
|
|
4454
|
+
writeFileSync6(indexPath, updated);
|
|
4455
|
+
if (typedArgs.verbose) {
|
|
4456
|
+
console.log(` ${pc5.dim("\u270E")} updated index.astro`);
|
|
4457
|
+
}
|
|
4458
|
+
}
|
|
4459
|
+
}
|
|
4235
4460
|
console.log();
|
|
4236
4461
|
console.log(` ${pc5.red("-")} ${pc5.bold(blockName)} removed`);
|
|
4237
4462
|
if (dependents.length > 0) {
|
|
@@ -4713,7 +4938,7 @@ async function listBlocksHandler(args2) {
|
|
|
4713
4938
|
}
|
|
4714
4939
|
|
|
4715
4940
|
// src/mcp/tools/add-block.ts
|
|
4716
|
-
import { readFileSync as readFileSync9, writeFileSync as
|
|
4941
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, existsSync as existsSync8, mkdirSync as mkdirSync6 } from "fs";
|
|
4717
4942
|
import { join as join11, dirname as dirname5 } from "path";
|
|
4718
4943
|
async function addBlock2(input) {
|
|
4719
4944
|
const { name, variant = "default", projectDirectory } = input;
|
|
@@ -4779,7 +5004,7 @@ async function addBlock2(input) {
|
|
|
4779
5004
|
}
|
|
4780
5005
|
const filePath = join11(projectDirectory, file.destination);
|
|
4781
5006
|
mkdirSync6(dirname5(filePath), { recursive: true });
|
|
4782
|
-
|
|
5007
|
+
writeFileSync7(filePath, content);
|
|
4783
5008
|
filesCreated++;
|
|
4784
5009
|
}
|
|
4785
5010
|
}
|
|
@@ -4794,7 +5019,7 @@ async function addBlock2(input) {
|
|
|
4794
5019
|
installedAt: now
|
|
4795
5020
|
});
|
|
4796
5021
|
}
|
|
4797
|
-
|
|
5022
|
+
writeFileSync7(manifestPath, JSON.stringify(manifest2, null, 2) + "\n");
|
|
4798
5023
|
return ok({ addedBlocks: blocksToAdd, filesCreated });
|
|
4799
5024
|
}
|
|
4800
5025
|
function resolveDependencies3(blockName, installedNames, manifests) {
|
|
@@ -4817,7 +5042,7 @@ function resolveDependencies3(blockName, installedNames, manifests) {
|
|
|
4817
5042
|
// src/mcp/tools/remove-block.ts
|
|
4818
5043
|
import {
|
|
4819
5044
|
readFileSync as readFileSync10,
|
|
4820
|
-
writeFileSync as
|
|
5045
|
+
writeFileSync as writeFileSync8,
|
|
4821
5046
|
existsSync as existsSync9,
|
|
4822
5047
|
unlinkSync as unlinkSync2,
|
|
4823
5048
|
readdirSync as readdirSync4,
|
|
@@ -4871,7 +5096,7 @@ async function removeBlock(input) {
|
|
|
4871
5096
|
tryRemoveEmptyDirectory(dirname6(filePath), projectDirectory);
|
|
4872
5097
|
}
|
|
4873
5098
|
manifest2.blocks = manifest2.blocks.filter((block) => block.name !== name);
|
|
4874
|
-
|
|
5099
|
+
writeFileSync8(manifestPath, JSON.stringify(manifest2, null, 2) + "\n");
|
|
4875
5100
|
return ok({
|
|
4876
5101
|
removedBlock: name,
|
|
4877
5102
|
filesRemoved: filesToRemove.length,
|
|
@@ -4963,7 +5188,7 @@ async function validateContent(input) {
|
|
|
4963
5188
|
}
|
|
4964
5189
|
const schemaShape = {};
|
|
4965
5190
|
for (const [slotName, slot] of Object.entries(contentSlots)) {
|
|
4966
|
-
schemaShape[slotName] =
|
|
5191
|
+
schemaShape[slotName] = zodTypeForSlot(slot.type);
|
|
4967
5192
|
}
|
|
4968
5193
|
const schema = z5.object(schemaShape);
|
|
4969
5194
|
const parseResult = schema.safeParse(data);
|
|
@@ -4975,7 +5200,7 @@ async function validateContent(input) {
|
|
|
4975
5200
|
);
|
|
4976
5201
|
return ok({ valid: false, errors });
|
|
4977
5202
|
}
|
|
4978
|
-
function
|
|
5203
|
+
function zodTypeForSlot(slotType) {
|
|
4979
5204
|
switch (slotType) {
|
|
4980
5205
|
case "string":
|
|
4981
5206
|
return z5.string();
|
|
@@ -5025,7 +5250,7 @@ function getProjectStatus(input) {
|
|
|
5025
5250
|
}
|
|
5026
5251
|
|
|
5027
5252
|
// src/mcp/tools/scaffold-project.ts
|
|
5028
|
-
import { mkdirSync as mkdirSync7, writeFileSync as
|
|
5253
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync9 } from "fs";
|
|
5029
5254
|
import { join as join14, basename as basename3 } from "path";
|
|
5030
5255
|
var DEFAULT_COLORS2 = {
|
|
5031
5256
|
primary: "#6366f1",
|
|
@@ -5100,7 +5325,7 @@ async function scaffoldProject(input) {
|
|
|
5100
5325
|
const fullPath = join14(projectDirectory, relativePath);
|
|
5101
5326
|
const parentDirectory = join14(fullPath, "..");
|
|
5102
5327
|
mkdirSync7(parentDirectory, { recursive: true });
|
|
5103
|
-
|
|
5328
|
+
writeFileSync9(fullPath, content, "utf-8");
|
|
5104
5329
|
filesCreated++;
|
|
5105
5330
|
}
|
|
5106
5331
|
return ok({
|