create-avalon 0.1.25 → 0.2.1
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/README.md +27 -10
- package/dist/cli.js +385 -218
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,11 +19,22 @@ bun create avalon
|
|
|
19
19
|
The CLI walks you through:
|
|
20
20
|
|
|
21
21
|
- Project name and directory
|
|
22
|
-
-
|
|
23
|
-
- Styling approach (CSS Modules, Tailwind,
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
22
|
+
- Rendering engine (Preact or React) and island integrations
|
|
23
|
+
- Styling approach (CSS Modules, Tailwind, shadcn)
|
|
24
|
+
- Plugins (SEO, agent optimization). MDX syntax highlighting is always on.
|
|
25
|
+
- Middleware (h3, Hono, Elysia)
|
|
26
|
+
- Deploy target (**Cloudflare Pages**, Netlify, or none)
|
|
27
|
+
- Scheduled jobs (cron)
|
|
28
|
+
|
|
29
|
+
## Deploy targets
|
|
30
|
+
|
|
31
|
+
| Choice | What you get |
|
|
32
|
+
|--------|----------------|
|
|
33
|
+
| **Cloudflare Pages** | `wrangler.toml`, `public/_headers`, `DEPLOY.md`, `build` / `preview` / `deploy` scripts. `build.mjs` sets `NITRO_PRESET=cloudflare_pages` when unset. |
|
|
34
|
+
| **Netlify** | `netlify.toml` (`NITRO_PRESET=netlify`), soft SSR redirect, `DEPLOY.md`. |
|
|
35
|
+
| **None** | Node server preset; still gets `build.mjs` + `post-build.mjs`. |
|
|
36
|
+
|
|
37
|
+
Always run Avalon's post-build (`node post-build.mjs` via `bun run build`) — it patches the Cloudflare worker, CSS manifests, and prerenders HTML.
|
|
27
38
|
|
|
28
39
|
## What you get
|
|
29
40
|
|
|
@@ -33,10 +44,11 @@ A ready-to-run Avalon project with file-system routing, islands architecture, an
|
|
|
33
44
|
my-project/
|
|
34
45
|
├── app/
|
|
35
46
|
│ ├── modules/
|
|
36
|
-
│ │
|
|
37
|
-
│ │
|
|
38
|
-
│ │
|
|
39
|
-
│ │
|
|
47
|
+
│ │ ├── main/ # Site root (/)
|
|
48
|
+
│ │ │ ├── pages/ # File-system routes for this module
|
|
49
|
+
│ │ │ ├── components/
|
|
50
|
+
│ │ │ └── layouts/
|
|
51
|
+
│ │ └── about/ # /about — a module name is a URL prefix
|
|
40
52
|
│ └── shared/
|
|
41
53
|
│ ├── layouts/ # Root layout
|
|
42
54
|
│ ├── components/ # Shared components
|
|
@@ -46,7 +58,12 @@ my-project/
|
|
|
46
58
|
│ └── api/ # API routes
|
|
47
59
|
├── tasks/ # Scheduled jobs (cron) — optional
|
|
48
60
|
├── server/ # Server config & env
|
|
49
|
-
├── public/ # Static assets
|
|
61
|
+
├── public/ # Static assets (+ _headers for Cloudflare)
|
|
62
|
+
├── build.mjs # Vite hang workaround + post-build
|
|
63
|
+
├── post-build.mjs # Avalon production patches / prerender
|
|
64
|
+
├── DEPLOY.md # Platform-specific deploy steps
|
|
65
|
+
├── wrangler.toml # Cloudflare Pages (optional)
|
|
66
|
+
├── netlify.toml # Netlify (optional)
|
|
50
67
|
├── vite.config.ts
|
|
51
68
|
└── package.json
|
|
52
69
|
```
|
package/dist/cli.js
CHANGED
|
@@ -4,21 +4,37 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
7
12
|
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
8
20
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
-
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
if (mod && typeof mod === "object" || typeof mod === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(mod))
|
|
24
|
+
if (!__hasOwnProp.call(to, key))
|
|
25
|
+
__defProp(to, key, {
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
27
|
+
enumerable: true
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (canCache)
|
|
31
|
+
cache.set(mod, to);
|
|
16
32
|
return to;
|
|
17
33
|
};
|
|
18
34
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
19
35
|
|
|
20
36
|
// ../../node_modules/.bun/sisteransi@1.0.5/node_modules/sisteransi/src/index.js
|
|
21
|
-
var require_src = __commonJS((exports, module)
|
|
37
|
+
var require_src = __commonJS(function(exports, module) {
|
|
22
38
|
var ESC = "\x1B";
|
|
23
39
|
var CSI = `${ESC}[`;
|
|
24
40
|
var beep = "\x07";
|
|
@@ -99,17 +115,17 @@ var STYLING_OPTIONS = [
|
|
|
99
115
|
"tailwind",
|
|
100
116
|
"shadcn"
|
|
101
117
|
];
|
|
102
|
-
var PLUGINS = [
|
|
103
|
-
"seo",
|
|
104
|
-
"agent-optimization",
|
|
105
|
-
"syntax-highlighting"
|
|
106
|
-
];
|
|
118
|
+
var PLUGINS = ["seo", "agent-optimization"];
|
|
107
119
|
var MIDDLEWARE_OPTIONS = [
|
|
108
120
|
"h3",
|
|
109
121
|
"hono",
|
|
110
122
|
"elysia"
|
|
111
123
|
];
|
|
112
|
-
var DEPLOY_TARGETS = [
|
|
124
|
+
var DEPLOY_TARGETS = [
|
|
125
|
+
"cloudflare",
|
|
126
|
+
"netlify",
|
|
127
|
+
"none"
|
|
128
|
+
];
|
|
113
129
|
var INTEGRATION_PACKAGES = {
|
|
114
130
|
preact: "@useavalon/preact",
|
|
115
131
|
react: "@useavalon/react",
|
|
@@ -123,6 +139,9 @@ var BASE_DIRS = [
|
|
|
123
139
|
"app/modules/main/pages",
|
|
124
140
|
"app/modules/main/components",
|
|
125
141
|
"app/modules/main/layouts",
|
|
142
|
+
"app/modules/about/pages",
|
|
143
|
+
"app/modules/about/components",
|
|
144
|
+
"app/modules/about/layouts",
|
|
126
145
|
"app/shared/layouts",
|
|
127
146
|
"app/shared/components",
|
|
128
147
|
"app/shared/styles",
|
|
@@ -209,8 +228,8 @@ function resolveConfigNonInteractive(args) {
|
|
|
209
228
|
if (styling === "shadcn" && core !== "react") {
|
|
210
229
|
throw new CliArgError("--styling=shadcn requires --core=react (shadcn is Radix/React based).");
|
|
211
230
|
}
|
|
212
|
-
if (
|
|
213
|
-
integrations.push(
|
|
231
|
+
if (!integrations.includes(core)) {
|
|
232
|
+
integrations.push(core);
|
|
214
233
|
}
|
|
215
234
|
return {
|
|
216
235
|
projectName: args.projectName ?? ".",
|
|
@@ -580,82 +599,6 @@ class B {
|
|
|
580
599
|
}
|
|
581
600
|
}
|
|
582
601
|
}
|
|
583
|
-
function wt(t, e) {
|
|
584
|
-
if (t === undefined || e.length === 0)
|
|
585
|
-
return 0;
|
|
586
|
-
const s = e.findIndex((i) => i.value === t);
|
|
587
|
-
return s !== -1 ? s : 0;
|
|
588
|
-
}
|
|
589
|
-
function Dt(t, e) {
|
|
590
|
-
return (e.label ?? String(e.value)).toLowerCase().includes(t.toLowerCase());
|
|
591
|
-
}
|
|
592
|
-
function St(t, e) {
|
|
593
|
-
if (e)
|
|
594
|
-
return t ? e : e[0];
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
class Vt extends B {
|
|
598
|
-
filteredOptions;
|
|
599
|
-
multiple;
|
|
600
|
-
isNavigating = false;
|
|
601
|
-
selectedValues = [];
|
|
602
|
-
focusedValue;
|
|
603
|
-
#t = 0;
|
|
604
|
-
#s = "";
|
|
605
|
-
#i;
|
|
606
|
-
#e;
|
|
607
|
-
get cursor() {
|
|
608
|
-
return this.#t;
|
|
609
|
-
}
|
|
610
|
-
get userInputWithCursor() {
|
|
611
|
-
if (!this.userInput)
|
|
612
|
-
return D(["inverse", "hidden"], "_");
|
|
613
|
-
if (this._cursor >= this.userInput.length)
|
|
614
|
-
return `${this.userInput}█`;
|
|
615
|
-
const e = this.userInput.slice(0, this._cursor), [s, ...i] = this.userInput.slice(this._cursor);
|
|
616
|
-
return `${e}${D("inverse", s)}${i.join("")}`;
|
|
617
|
-
}
|
|
618
|
-
get options() {
|
|
619
|
-
return typeof this.#e == "function" ? this.#e() : this.#e;
|
|
620
|
-
}
|
|
621
|
-
constructor(e) {
|
|
622
|
-
super(e), this.#e = e.options;
|
|
623
|
-
const s = this.options;
|
|
624
|
-
this.filteredOptions = [...s], this.multiple = e.multiple === true, this.#i = e.filter ?? Dt;
|
|
625
|
-
let i;
|
|
626
|
-
if (e.initialValue && Array.isArray(e.initialValue) ? this.multiple ? i = e.initialValue : i = e.initialValue.slice(0, 1) : !this.multiple && this.options.length > 0 && (i = [this.options[0].value]), i)
|
|
627
|
-
for (const r of i) {
|
|
628
|
-
const n = s.findIndex((u) => u.value === r);
|
|
629
|
-
n !== -1 && (this.toggleSelected(r), this.#t = n);
|
|
630
|
-
}
|
|
631
|
-
this.focusedValue = this.options[this.#t]?.value, this.on("key", (r, n) => this.#r(r, n)), this.on("userInput", (r) => this.#n(r));
|
|
632
|
-
}
|
|
633
|
-
_isActionKey(e, s) {
|
|
634
|
-
return e === "\t" || this.multiple && this.isNavigating && s.name === "space" && e !== undefined && e !== "";
|
|
635
|
-
}
|
|
636
|
-
#r(e, s) {
|
|
637
|
-
const i = s.name === "up", r = s.name === "down", n = s.name === "return";
|
|
638
|
-
i || r ? (this.#t = x(this.#t, i ? -1 : 1, this.filteredOptions), this.focusedValue = this.filteredOptions[this.#t]?.value, this.multiple || (this.selectedValues = [this.focusedValue]), this.isNavigating = true) : n ? this.value = St(this.multiple, this.selectedValues) : this.multiple ? this.focusedValue !== undefined && (s.name === "tab" || this.isNavigating && s.name === "space") ? this.toggleSelected(this.focusedValue) : this.isNavigating = false : (this.focusedValue && (this.selectedValues = [this.focusedValue]), this.isNavigating = false);
|
|
639
|
-
}
|
|
640
|
-
deselectAll() {
|
|
641
|
-
this.selectedValues = [];
|
|
642
|
-
}
|
|
643
|
-
toggleSelected(e) {
|
|
644
|
-
this.filteredOptions.length !== 0 && (this.multiple ? this.selectedValues.includes(e) ? this.selectedValues = this.selectedValues.filter((s) => s !== e) : this.selectedValues = [...this.selectedValues, e] : this.selectedValues = [e]);
|
|
645
|
-
}
|
|
646
|
-
#n(e) {
|
|
647
|
-
if (e !== this.#s) {
|
|
648
|
-
this.#s = e;
|
|
649
|
-
const s = this.options;
|
|
650
|
-
e ? this.filteredOptions = s.filter((n) => this.#i(e, n)) : this.filteredOptions = [...s];
|
|
651
|
-
const i = wt(this.focusedValue, this.filteredOptions);
|
|
652
|
-
this.#t = x(i, 0, this.filteredOptions);
|
|
653
|
-
const r = this.filteredOptions[this.#t];
|
|
654
|
-
r && !r.disabled ? this.focusedValue = r.value : this.focusedValue = undefined, this.multiple || (this.focusedValue !== undefined ? this.toggleSelected(this.focusedValue) : this.deselectAll());
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
|
|
659
602
|
class kt extends B {
|
|
660
603
|
get cursor() {
|
|
661
604
|
return this.value ? 0 : 1;
|
|
@@ -673,54 +616,6 @@ class kt extends B {
|
|
|
673
616
|
});
|
|
674
617
|
}
|
|
675
618
|
}
|
|
676
|
-
|
|
677
|
-
class yt extends B {
|
|
678
|
-
options;
|
|
679
|
-
cursor = 0;
|
|
680
|
-
#t;
|
|
681
|
-
getGroupItems(e) {
|
|
682
|
-
return this.options.filter((s) => s.group === e);
|
|
683
|
-
}
|
|
684
|
-
isGroupSelected(e) {
|
|
685
|
-
const s = this.getGroupItems(e), i = this.value;
|
|
686
|
-
return i === undefined ? false : s.every((r) => i.includes(r.value));
|
|
687
|
-
}
|
|
688
|
-
toggleValue() {
|
|
689
|
-
const e = this.options[this.cursor];
|
|
690
|
-
if (this.value === undefined && (this.value = []), e.group === true) {
|
|
691
|
-
const s = e.value, i = this.getGroupItems(s);
|
|
692
|
-
this.isGroupSelected(s) ? this.value = this.value.filter((r) => i.findIndex((n) => n.value === r) === -1) : this.value = [...this.value, ...i.map((r) => r.value)], this.value = Array.from(new Set(this.value));
|
|
693
|
-
} else {
|
|
694
|
-
const s = this.value.includes(e.value);
|
|
695
|
-
this.value = s ? this.value.filter((i) => i !== e.value) : [...this.value, e.value];
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
constructor(e) {
|
|
699
|
-
super(e, false);
|
|
700
|
-
const { options: s } = e;
|
|
701
|
-
this.#t = e.selectableGroups !== false, this.options = Object.entries(s).flatMap(([i, r]) => [{ value: i, group: true, label: i }, ...r.map((n) => ({ ...n, group: i }))]), this.value = [...e.initialValues ?? []], this.cursor = Math.max(this.options.findIndex(({ value: i }) => i === e.cursorAt), this.#t ? 0 : 1), this.on("cursor", (i) => {
|
|
702
|
-
switch (i) {
|
|
703
|
-
case "left":
|
|
704
|
-
case "up": {
|
|
705
|
-
this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
|
|
706
|
-
const r = this.options[this.cursor]?.group === true;
|
|
707
|
-
!this.#t && r && (this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1);
|
|
708
|
-
break;
|
|
709
|
-
}
|
|
710
|
-
case "down":
|
|
711
|
-
case "right": {
|
|
712
|
-
this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
|
|
713
|
-
const r = this.options[this.cursor]?.group === true;
|
|
714
|
-
!this.#t && r && (this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1);
|
|
715
|
-
break;
|
|
716
|
-
}
|
|
717
|
-
case "space":
|
|
718
|
-
this.toggleValue();
|
|
719
|
-
break;
|
|
720
|
-
}
|
|
721
|
-
});
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
619
|
var Lt = class extends B {
|
|
725
620
|
options;
|
|
726
621
|
cursor = 0;
|
|
@@ -889,59 +784,59 @@ var Ft2 = /\p{M}+/gu;
|
|
|
889
784
|
var yt2 = { limit: 1 / 0, ellipsis: "" };
|
|
890
785
|
var Le = (e, r = {}, s = {}) => {
|
|
891
786
|
const i = r.limit ?? 1 / 0, a = r.ellipsis ?? "", o = r?.ellipsisWidth ?? (a ? Le(a, yt2, s).width : 0), u = s.ansiWidth ?? 0, l = s.controlWidth ?? 0, n = s.tabWidth ?? 8, c = s.ambiguousWidth ?? 1, p = s.emojiWidth ?? 2, f = s.fullWidthWidth ?? 2, g = s.regularWidth ?? 1, E = s.wideWidth ?? 2;
|
|
892
|
-
let $ = 0, m = 0, d = e.length, F = 0,
|
|
787
|
+
let $ = 0, m = 0, d = e.length, F = 0, y = false, v = d, C = Math.max(0, i - o), A = 0, b = 0, w = 0, S = 0;
|
|
893
788
|
e:
|
|
894
789
|
for (;; ) {
|
|
895
790
|
if (b > A || m >= d && m > $) {
|
|
896
|
-
const
|
|
791
|
+
const T = e.slice(A, b) || e.slice($, m);
|
|
897
792
|
F = 0;
|
|
898
|
-
for (const
|
|
899
|
-
const
|
|
900
|
-
if (gt2(
|
|
901
|
-
|
|
793
|
+
for (const M of T.replaceAll(Ft2, "")) {
|
|
794
|
+
const O = M.codePointAt(0) || 0;
|
|
795
|
+
if (gt2(O) ? S = f : ft2(O) ? S = E : c !== g && mt2(O) ? S = c : S = g, w + S > C && (v = Math.min(v, Math.max(A, $) + F)), w + S > i) {
|
|
796
|
+
y = true;
|
|
902
797
|
break e;
|
|
903
798
|
}
|
|
904
|
-
F +=
|
|
799
|
+
F += M.length, w += S;
|
|
905
800
|
}
|
|
906
801
|
A = b = 0;
|
|
907
802
|
}
|
|
908
803
|
if (m >= d)
|
|
909
804
|
break;
|
|
910
805
|
if (ne.lastIndex = m, ne.test(e)) {
|
|
911
|
-
if (F = ne.lastIndex - m,
|
|
912
|
-
|
|
806
|
+
if (F = ne.lastIndex - m, S = F * g, w + S > C && (v = Math.min(v, m + Math.floor((C - w) / g))), w + S > i) {
|
|
807
|
+
y = true;
|
|
913
808
|
break;
|
|
914
809
|
}
|
|
915
|
-
w +=
|
|
810
|
+
w += S, A = $, b = m, m = $ = ne.lastIndex;
|
|
916
811
|
continue;
|
|
917
812
|
}
|
|
918
813
|
if (we.lastIndex = m, we.test(e)) {
|
|
919
814
|
if (w + u > C && (v = Math.min(v, m)), w + u > i) {
|
|
920
|
-
|
|
815
|
+
y = true;
|
|
921
816
|
break;
|
|
922
817
|
}
|
|
923
818
|
w += u, A = $, b = m, m = $ = we.lastIndex;
|
|
924
819
|
continue;
|
|
925
820
|
}
|
|
926
821
|
if (re.lastIndex = m, re.test(e)) {
|
|
927
|
-
if (F = re.lastIndex - m,
|
|
928
|
-
|
|
822
|
+
if (F = re.lastIndex - m, S = F * l, w + S > C && (v = Math.min(v, m + Math.floor((C - w) / l))), w + S > i) {
|
|
823
|
+
y = true;
|
|
929
824
|
break;
|
|
930
825
|
}
|
|
931
|
-
w +=
|
|
826
|
+
w += S, A = $, b = m, m = $ = re.lastIndex;
|
|
932
827
|
continue;
|
|
933
828
|
}
|
|
934
829
|
if (ie.lastIndex = m, ie.test(e)) {
|
|
935
|
-
if (F = ie.lastIndex - m,
|
|
936
|
-
|
|
830
|
+
if (F = ie.lastIndex - m, S = F * n, w + S > C && (v = Math.min(v, m + Math.floor((C - w) / n))), w + S > i) {
|
|
831
|
+
y = true;
|
|
937
832
|
break;
|
|
938
833
|
}
|
|
939
|
-
w +=
|
|
834
|
+
w += S, A = $, b = m, m = $ = ie.lastIndex;
|
|
940
835
|
continue;
|
|
941
836
|
}
|
|
942
837
|
if (Ae.lastIndex = m, Ae.test(e)) {
|
|
943
838
|
if (w + p > C && (v = Math.min(v, m)), w + p > i) {
|
|
944
|
-
|
|
839
|
+
y = true;
|
|
945
840
|
break;
|
|
946
841
|
}
|
|
947
842
|
w += p, A = $, b = m, m = $ = Ae.lastIndex;
|
|
@@ -949,7 +844,7 @@ var Le = (e, r = {}, s = {}) => {
|
|
|
949
844
|
}
|
|
950
845
|
m += 1;
|
|
951
846
|
}
|
|
952
|
-
return { width:
|
|
847
|
+
return { width: y ? C : w, index: y ? v : d, truncated: y, ellipsed: y && i >= o };
|
|
953
848
|
};
|
|
954
849
|
var Et2 = { limit: 1 / 0, ellipsis: "", ellipsisWidth: 0 };
|
|
955
850
|
var D2 = (e, r = {}) => Le(e, Et2, r).width;
|
|
@@ -958,9 +853,9 @@ var je = "";
|
|
|
958
853
|
var vt2 = 39;
|
|
959
854
|
var Ce = "\x07";
|
|
960
855
|
var ke = "[";
|
|
961
|
-
var
|
|
856
|
+
var wt = "]";
|
|
962
857
|
var Ve = "m";
|
|
963
|
-
var Se = `${
|
|
858
|
+
var Se = `${wt}8;;`;
|
|
964
859
|
var He = new RegExp(`(?:\\${ke}(?<code>\\d+)m|\\${Se}(?<uri>.*)${Ce})`, "y");
|
|
965
860
|
var At2 = (e) => {
|
|
966
861
|
if (e >= 30 && e <= 37 || e >= 90 && e <= 97)
|
|
@@ -994,7 +889,7 @@ var Ie = (e, r, s) => {
|
|
|
994
889
|
}
|
|
995
890
|
u = e.at(-1), !l && u !== undefined && u.length > 0 && e.length > 1 && (e[e.length - 2] += e.pop());
|
|
996
891
|
};
|
|
997
|
-
var
|
|
892
|
+
var St = (e) => {
|
|
998
893
|
const r = e.split(" ");
|
|
999
894
|
let s = r.length;
|
|
1000
895
|
for (;s > 0 && !(D2(r[s - 1]) > 0); )
|
|
@@ -1011,8 +906,8 @@ var It2 = (e, r, s = {}) => {
|
|
|
1011
906
|
s.trim !== false && (n[n.length - 1] = (n.at(-1) ?? "").trimStart());
|
|
1012
907
|
let d = D2(n.at(-1) ?? "");
|
|
1013
908
|
if ($ !== 0 && (d >= r && (s.wordWrap === false || s.trim === false) && (n.push(""), d = 0), (d > 0 || s.trim === false) && (n[n.length - 1] += " ", d++)), s.hard && l[$] > r) {
|
|
1014
|
-
const F = r - d,
|
|
1015
|
-
Math.floor((l[$] - 1) / r) <
|
|
909
|
+
const F = r - d, y = 1 + Math.floor((l[$] - F - 1) / r);
|
|
910
|
+
Math.floor((l[$] - 1) / r) < y && n.push(""), Ie(n, m, r);
|
|
1016
911
|
continue;
|
|
1017
912
|
}
|
|
1018
913
|
if (d + l[$] > r && d > 0 && l[$] > 0) {
|
|
@@ -1028,7 +923,7 @@ var It2 = (e, r, s = {}) => {
|
|
|
1028
923
|
}
|
|
1029
924
|
n[n.length - 1] += m;
|
|
1030
925
|
}
|
|
1031
|
-
s.trim !== false && (n = n.map(($) =>
|
|
926
|
+
s.trim !== false && (n = n.map(($) => St($)));
|
|
1032
927
|
const c = n.join(`
|
|
1033
928
|
`), p = c[Symbol.iterator]();
|
|
1034
929
|
let f = p.next(), g = p.next(), E = 0;
|
|
@@ -1036,12 +931,12 @@ var It2 = (e, r, s = {}) => {
|
|
|
1036
931
|
const $ = f.value, m = g.value;
|
|
1037
932
|
if (i += $, $ === ae || $ === je) {
|
|
1038
933
|
He.lastIndex = E + 1;
|
|
1039
|
-
const
|
|
1040
|
-
if (
|
|
1041
|
-
const v = Number.parseFloat(
|
|
934
|
+
const y = He.exec(c)?.groups;
|
|
935
|
+
if (y?.code !== undefined) {
|
|
936
|
+
const v = Number.parseFloat(y.code);
|
|
1042
937
|
a = v === vt2 ? undefined : v;
|
|
1043
938
|
} else
|
|
1044
|
-
|
|
939
|
+
y?.uri !== undefined && (o = y.uri.length === 0 ? undefined : y.uri);
|
|
1045
940
|
}
|
|
1046
941
|
const d = a ? At2(a) : undefined;
|
|
1047
942
|
m === `
|
|
@@ -1074,16 +969,16 @@ var X2 = ({ cursor: e, options: r, style: s, output: i = process.stdout, maxItem
|
|
|
1074
969
|
const m = Math.min(g + f, r.length), d = [];
|
|
1075
970
|
let F = 0;
|
|
1076
971
|
E && F++, $ && F++;
|
|
1077
|
-
const
|
|
1078
|
-
for (let A =
|
|
972
|
+
const y = g + (E ? 1 : 0), v = m - ($ ? 1 : 0);
|
|
973
|
+
for (let A = y;A < v; A++) {
|
|
1079
974
|
const b = J(s(r[A], A === e), l, { hard: true, trim: false }).split(`
|
|
1080
975
|
`);
|
|
1081
976
|
d.push(b), F += b.length;
|
|
1082
977
|
}
|
|
1083
978
|
if (F > p) {
|
|
1084
979
|
let A = 0, b = 0, w = F;
|
|
1085
|
-
const
|
|
1086
|
-
E ? ({ lineCount: w, removals: A } =
|
|
980
|
+
const S = e - y, T = (M, O) => bt2(d, w, M, O, p);
|
|
981
|
+
E ? ({ lineCount: w, removals: A } = T(0, S), w > p && ({ lineCount: w, removals: b } = T(S + 1, d.length))) : ({ lineCount: w, removals: b } = T(S + 1, d.length), w > p && ({ lineCount: w, removals: A } = T(0, S))), A > 0 && (E = true, d.splice(0, A)), b > 0 && ($ = true, d.splice(d.length - b, b));
|
|
1087
982
|
}
|
|
1088
983
|
const C = [];
|
|
1089
984
|
E && C.push(c);
|
|
@@ -1350,11 +1245,6 @@ async function collectProjectConfig(initialName) {
|
|
|
1350
1245
|
value: "agent-optimization",
|
|
1351
1246
|
label: "agent-optimization",
|
|
1352
1247
|
hint: "LLM/AI optimization (llms.txt, markdown, sitemap)"
|
|
1353
|
-
},
|
|
1354
|
-
{
|
|
1355
|
-
value: "syntax-highlighting",
|
|
1356
|
-
label: "syntax-highlighting",
|
|
1357
|
-
hint: "Code block highlighting for MDX (rehype-highlight)"
|
|
1358
1248
|
}
|
|
1359
1249
|
],
|
|
1360
1250
|
initialValues: ["seo"],
|
|
@@ -1379,12 +1269,17 @@ async function collectProjectConfig(initialName) {
|
|
|
1379
1269
|
const deployResult = await Jt({
|
|
1380
1270
|
message: "Where will you deploy?",
|
|
1381
1271
|
options: [
|
|
1272
|
+
{
|
|
1273
|
+
value: "cloudflare",
|
|
1274
|
+
label: "Cloudflare Pages",
|
|
1275
|
+
hint: "wrangler.toml, public/_headers, build + deploy scripts"
|
|
1276
|
+
},
|
|
1382
1277
|
{
|
|
1383
1278
|
value: "netlify",
|
|
1384
1279
|
label: "Netlify",
|
|
1385
|
-
hint: "
|
|
1280
|
+
hint: "netlify.toml, build.mjs, post-build.mjs"
|
|
1386
1281
|
},
|
|
1387
|
-
{ value: "none", label: "None / Other", hint: "Node server preset, no
|
|
1282
|
+
{ value: "none", label: "None / Other", hint: "Node server preset, no platform config" }
|
|
1388
1283
|
]
|
|
1389
1284
|
});
|
|
1390
1285
|
if (Ct(deployResult)) {
|
|
@@ -1401,8 +1296,8 @@ async function collectProjectConfig(initialName) {
|
|
|
1401
1296
|
}
|
|
1402
1297
|
const core = coreResult;
|
|
1403
1298
|
const integrations = integrationsResult;
|
|
1404
|
-
if (
|
|
1405
|
-
integrations.push(
|
|
1299
|
+
if (!integrations.includes(core)) {
|
|
1300
|
+
integrations.push(core);
|
|
1406
1301
|
}
|
|
1407
1302
|
return {
|
|
1408
1303
|
projectName,
|
|
@@ -1456,6 +1351,10 @@ export default defineCronJob({
|
|
|
1456
1351
|
}
|
|
1457
1352
|
|
|
1458
1353
|
// src/templates/deploy.ts
|
|
1354
|
+
function cloudflareProjectName(projectName) {
|
|
1355
|
+
const slug = projectName.toLowerCase().replaceAll(/[^a-z0-9-]+/g, "-").replaceAll(/-+/g, "-").replaceAll(/^-|-$/g, "");
|
|
1356
|
+
return slug || "avalon-app";
|
|
1357
|
+
}
|
|
1459
1358
|
function generateNetlifyToml(_config) {
|
|
1460
1359
|
return `[build]
|
|
1461
1360
|
base = "."
|
|
@@ -1480,6 +1379,117 @@ function generateNetlifyToml(_config) {
|
|
|
1480
1379
|
status = 200
|
|
1481
1380
|
`;
|
|
1482
1381
|
}
|
|
1382
|
+
function generateWranglerToml(config) {
|
|
1383
|
+
const name = cloudflareProjectName(config.projectName);
|
|
1384
|
+
return `# Cloudflare Pages — production output is dist/_worker.js + static assets.
|
|
1385
|
+
# Date must be >= 2025-09-15 so nodejs_compat includes \`node:fs\` (needed by the Nitro worker).
|
|
1386
|
+
name = "${name}"
|
|
1387
|
+
compatibility_date = "2026-09-04"
|
|
1388
|
+
compatibility_flags = ["nodejs_compat", "enable_nodejs_fs_module"]
|
|
1389
|
+
pages_build_output_dir = "./dist"
|
|
1390
|
+
`;
|
|
1391
|
+
}
|
|
1392
|
+
function generateCloudflareHeaders() {
|
|
1393
|
+
return `# Cloudflare Pages static headers (Worker routeRules still apply to SSR).
|
|
1394
|
+
/*.html
|
|
1395
|
+
Cache-Control: public, max-age=3600, s-maxage=31536000
|
|
1396
|
+
|
|
1397
|
+
/
|
|
1398
|
+
Cache-Control: public, max-age=3600, s-maxage=31536000
|
|
1399
|
+
|
|
1400
|
+
/islands/*
|
|
1401
|
+
Cache-Control: public, max-age=0, must-revalidate
|
|
1402
|
+
`;
|
|
1403
|
+
}
|
|
1404
|
+
function generateDeployReadme(config) {
|
|
1405
|
+
if (config.deploy === "cloudflare") {
|
|
1406
|
+
const name = cloudflareProjectName(config.projectName);
|
|
1407
|
+
return `# Deploying to Cloudflare Pages
|
|
1408
|
+
|
|
1409
|
+
This project is set up for \`NITRO_PRESET=cloudflare_pages\`.
|
|
1410
|
+
|
|
1411
|
+
## Build
|
|
1412
|
+
|
|
1413
|
+
\`\`\`bash
|
|
1414
|
+
bun run build
|
|
1415
|
+
\`\`\`
|
|
1416
|
+
|
|
1417
|
+
\`build.mjs\` detects \`wrangler.toml\` and sets the Nitro preset when \`NITRO_PRESET\` is unset.
|
|
1418
|
+
Avalon's post-build patches the Cloudflare worker (DOM stub, CSS manifest, \`_routes.json\`).
|
|
1419
|
+
|
|
1420
|
+
## Preview locally
|
|
1421
|
+
|
|
1422
|
+
\`\`\`bash
|
|
1423
|
+
bun run preview
|
|
1424
|
+
\`\`\`
|
|
1425
|
+
|
|
1426
|
+
## Deploy
|
|
1427
|
+
|
|
1428
|
+
1. Create an API token with **Account → Cloudflare Pages → Edit** and **Account Settings → Read**.
|
|
1429
|
+
2. Set \`CLOUDFLARE_API_TOKEN\` and \`CLOUDFLARE_ACCOUNT_ID\` in your environment (or CI secrets).
|
|
1430
|
+
3. Create the Pages project once (Wrangler 4 does not auto-create):
|
|
1431
|
+
|
|
1432
|
+
\`\`\`bash
|
|
1433
|
+
bunx wrangler@4 pages project create ${name} \\
|
|
1434
|
+
--production-branch=main \\
|
|
1435
|
+
--compatibility-flags=nodejs_compat \\
|
|
1436
|
+
--compatibility-date=2026-09-04
|
|
1437
|
+
\`\`\`
|
|
1438
|
+
|
|
1439
|
+
4. Deploy:
|
|
1440
|
+
|
|
1441
|
+
\`\`\`bash
|
|
1442
|
+
bun run deploy
|
|
1443
|
+
\`\`\`
|
|
1444
|
+
|
|
1445
|
+
Prefer **one** deployer (e.g. GitHub Actions with Wrangler). If you also connect the
|
|
1446
|
+
GitHub repo in the Cloudflare dashboard, disable Cloudflare's own build to avoid
|
|
1447
|
+
double deploys.
|
|
1448
|
+
|
|
1449
|
+
Do not rely on Cloudflare's Git integration to run \`vite build\` without Avalon's
|
|
1450
|
+
\`post-build.mjs\` — prerendered CSS and worker patches will be missing.
|
|
1451
|
+
`;
|
|
1452
|
+
}
|
|
1453
|
+
if (config.deploy === "netlify") {
|
|
1454
|
+
return `# Deploying to Netlify
|
|
1455
|
+
|
|
1456
|
+
This project is set up for \`NITRO_PRESET=netlify\` via \`netlify.toml\`.
|
|
1457
|
+
|
|
1458
|
+
## Build
|
|
1459
|
+
|
|
1460
|
+
\`\`\`bash
|
|
1461
|
+
bun run build
|
|
1462
|
+
\`\`\`
|
|
1463
|
+
|
|
1464
|
+
Or connect the repo in Netlify — \`netlify.toml\` runs \`bun install && bun build.mjs\`
|
|
1465
|
+
and publishes \`dist/\`. Avalon's post-build copies the server function and prerenders routes.
|
|
1466
|
+
|
|
1467
|
+
## Preview locally
|
|
1468
|
+
|
|
1469
|
+
\`\`\`bash
|
|
1470
|
+
bun run preview
|
|
1471
|
+
\`\`\`
|
|
1472
|
+
|
|
1473
|
+
## Notes
|
|
1474
|
+
|
|
1475
|
+
- Functions live under \`.netlify/functions-internal\` (\`node_bundler = "none"\`).
|
|
1476
|
+
- The \`/*\` → \`/.netlify/functions/server\` redirect is a soft SSR fallback: static
|
|
1477
|
+
prerendered HTML in \`dist/\` is served first when present.
|
|
1478
|
+
`;
|
|
1479
|
+
}
|
|
1480
|
+
return `# Deploy
|
|
1481
|
+
|
|
1482
|
+
No platform config was generated. Build with:
|
|
1483
|
+
|
|
1484
|
+
\`\`\`bash
|
|
1485
|
+
bun run build
|
|
1486
|
+
\`\`\`
|
|
1487
|
+
|
|
1488
|
+
Default Nitro preset is \`node_server\` (\`.output/server\`). Set \`NITRO_PRESET\` for
|
|
1489
|
+
other targets (\`netlify\`, \`cloudflare_pages\`, \`vercel\`, …) and add the matching
|
|
1490
|
+
platform config — or re-run \`create-avalon\` with a deploy target.
|
|
1491
|
+
`;
|
|
1492
|
+
}
|
|
1483
1493
|
function generateBuildMjs() {
|
|
1484
1494
|
return `/**
|
|
1485
1495
|
* Build wrapper.
|
|
@@ -1487,20 +1497,49 @@ function generateBuildMjs() {
|
|
|
1487
1497
|
* Vite/Nitro leaves open handles after the build completes, preventing
|
|
1488
1498
|
* the Node process from exiting. This wrapper detects when the build
|
|
1489
1499
|
* output is ready, kills the entire process group, then runs post-build.
|
|
1500
|
+
*
|
|
1501
|
+
* When NITRO_PRESET is unset, defaults from wrangler.toml / netlify.toml
|
|
1502
|
+
* so local \`bun run build\` matches the chosen create-avalon deploy target.
|
|
1490
1503
|
*/
|
|
1491
1504
|
|
|
1492
1505
|
import { spawn, execSync } from 'node:child_process';
|
|
1493
|
-
import { existsSync, rmSync } from 'node:fs';
|
|
1506
|
+
import { existsSync, rmSync, statSync } from 'node:fs';
|
|
1494
1507
|
import { join } from 'node:path';
|
|
1495
1508
|
|
|
1496
1509
|
const CWD = process.cwd();
|
|
1497
1510
|
const NITRO_JSON = join(CWD, '.netlify', 'functions-internal', 'nitro.json');
|
|
1498
1511
|
const SERVER_MJS = join(CWD, '.netlify', 'functions-internal', 'server', 'server.mjs');
|
|
1499
1512
|
const OUTPUT_SSR = join(CWD, '.output', 'server', '_ssr', 'ssr.mjs');
|
|
1513
|
+
const CF_WORKER = join(CWD, 'dist', '_worker.js');
|
|
1514
|
+
|
|
1515
|
+
if (!process.env.NITRO_PRESET) {
|
|
1516
|
+
if (existsSync(join(CWD, 'wrangler.toml'))) {
|
|
1517
|
+
process.env.NITRO_PRESET = 'cloudflare_pages';
|
|
1518
|
+
} else if (existsSync(join(CWD, 'netlify.toml'))) {
|
|
1519
|
+
process.env.NITRO_PRESET = 'netlify';
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
/** True when Nitro finished a Cloudflare worker file, not an empty client-build directory. */
|
|
1524
|
+
function isCloudflareWorkerReady() {
|
|
1525
|
+
try {
|
|
1526
|
+
const st = statSync(CF_WORKER);
|
|
1527
|
+
if (st.isFile()) return true;
|
|
1528
|
+
if (st.isDirectory()) {
|
|
1529
|
+
return existsSync(join(CF_WORKER, 'index.js')) || existsSync(join(CF_WORKER, 'index.mjs'));
|
|
1530
|
+
}
|
|
1531
|
+
} catch {
|
|
1532
|
+
return false;
|
|
1533
|
+
}
|
|
1534
|
+
return false;
|
|
1535
|
+
}
|
|
1500
1536
|
|
|
1501
1537
|
console.log('[build] Starting vite build...');
|
|
1538
|
+
if (process.env.NITRO_PRESET) {
|
|
1539
|
+
console.log(\`[build] NITRO_PRESET=\${process.env.NITRO_PRESET}\`);
|
|
1540
|
+
}
|
|
1502
1541
|
|
|
1503
|
-
for (const dir of ['.netlify', '.output', 'netlify']) {
|
|
1542
|
+
for (const dir of ['.netlify', '.output', 'netlify', 'dist']) {
|
|
1504
1543
|
const full = join(CWD, dir);
|
|
1505
1544
|
if (existsSync(full)) {
|
|
1506
1545
|
rmSync(full, { recursive: true, force: true });
|
|
@@ -1512,10 +1551,13 @@ const child = spawn('bunx', ['--bun', 'vite', 'build'], {
|
|
|
1512
1551
|
cwd: CWD,
|
|
1513
1552
|
stdio: 'inherit',
|
|
1514
1553
|
detached: true,
|
|
1554
|
+
env: process.env,
|
|
1515
1555
|
});
|
|
1516
1556
|
|
|
1517
1557
|
const childPid = child.pid;
|
|
1518
1558
|
let done = false;
|
|
1559
|
+
let killedEarly = false;
|
|
1560
|
+
let viteExitCode = 0;
|
|
1519
1561
|
|
|
1520
1562
|
function killTree() {
|
|
1521
1563
|
try { process.kill(-childPid, 'SIGKILL'); } catch {}
|
|
@@ -1527,29 +1569,40 @@ function finish() {
|
|
|
1527
1569
|
done = true;
|
|
1528
1570
|
clearInterval(poll);
|
|
1529
1571
|
clearTimeout(absoluteTimeout);
|
|
1530
|
-
|
|
1572
|
+
|
|
1573
|
+
if (killedEarly) killTree();
|
|
1531
1574
|
|
|
1532
1575
|
setTimeout(() => {
|
|
1533
1576
|
console.log('[build] Running post-build...');
|
|
1534
1577
|
try {
|
|
1535
|
-
execSync('node post-build.mjs', { cwd: CWD, stdio: 'inherit', timeout: 120_000 });
|
|
1578
|
+
execSync('node post-build.mjs', { cwd: CWD, stdio: 'inherit', timeout: 120_000, env: process.env });
|
|
1536
1579
|
} catch (err) {
|
|
1537
|
-
console.error('[build] post-build
|
|
1580
|
+
console.error('[build] post-build failed:', err.message);
|
|
1581
|
+
process.exit(1);
|
|
1538
1582
|
}
|
|
1539
1583
|
|
|
1540
1584
|
const V1_SERVER = join(CWD, '.netlify', 'v1', 'functions', 'server', 'server.mjs');
|
|
1541
|
-
|
|
1585
|
+
const cloudflareReady = isCloudflareWorkerReady();
|
|
1586
|
+
const ok =
|
|
1587
|
+
cloudflareReady ||
|
|
1588
|
+
existsSync(V1_SERVER) ||
|
|
1589
|
+
existsSync(SERVER_MJS) ||
|
|
1590
|
+
existsSync(OUTPUT_SSR);
|
|
1591
|
+
if (cloudflareReady) console.log('[build] ✅ Cloudflare worker found (dist/_worker.js)');
|
|
1592
|
+
else if (existsSync(V1_SERVER)) console.log('[build] ✅ Server function found (v1 API)');
|
|
1542
1593
|
else if (existsSync(SERVER_MJS)) console.log('[build] ✅ Server function found (legacy)');
|
|
1543
1594
|
else if (existsSync(OUTPUT_SSR)) console.log('[build] ✅ SSR bundle found');
|
|
1544
1595
|
else console.error('[build] ❌ No server output found');
|
|
1545
1596
|
|
|
1597
|
+
if (!ok) process.exit(1);
|
|
1546
1598
|
console.log('[build] ✅ Complete');
|
|
1547
|
-
process.exit(0);
|
|
1599
|
+
process.exit(killedEarly || viteExitCode === 0 ? 0 : viteExitCode);
|
|
1548
1600
|
}, 500);
|
|
1549
1601
|
}
|
|
1550
1602
|
|
|
1551
1603
|
child.on('exit', (code) => {
|
|
1552
1604
|
console.log(\`[build] vite build exited with code \${code}\`);
|
|
1605
|
+
viteExitCode = code ?? (killedEarly ? 0 : 1);
|
|
1553
1606
|
finish();
|
|
1554
1607
|
});
|
|
1555
1608
|
|
|
@@ -1559,10 +1612,14 @@ child.on('error', (err) => {
|
|
|
1559
1612
|
});
|
|
1560
1613
|
|
|
1561
1614
|
const poll = setInterval(() => {
|
|
1615
|
+
// Cloudflare \`_worker.js\` appears during the client build; killing then
|
|
1616
|
+
// aborts SSR. Only force-stop the Netlify/Node hang.
|
|
1562
1617
|
const netlifyReady = existsSync(NITRO_JSON) && existsSync(SERVER_MJS);
|
|
1563
1618
|
const nodeServerReady = existsSync(OUTPUT_SSR);
|
|
1564
1619
|
if (netlifyReady || nodeServerReady) {
|
|
1565
|
-
|
|
1620
|
+
killedEarly = true;
|
|
1621
|
+
const kind = netlifyReady ? 'netlify' : 'node-server';
|
|
1622
|
+
console.log(\`[build] Output detected (\${kind}), waiting 3s for final writes...\`);
|
|
1566
1623
|
clearInterval(poll);
|
|
1567
1624
|
setTimeout(finish, 3_000);
|
|
1568
1625
|
}
|
|
@@ -1570,6 +1627,7 @@ const poll = setInterval(() => {
|
|
|
1570
1627
|
|
|
1571
1628
|
const absoluteTimeout = setTimeout(() => {
|
|
1572
1629
|
console.error('[build] Timeout — killing build');
|
|
1630
|
+
killedEarly = true;
|
|
1573
1631
|
finish();
|
|
1574
1632
|
}, 240_000);
|
|
1575
1633
|
`;
|
|
@@ -1630,6 +1688,22 @@ User-agent: AI2Bot
|
|
|
1630
1688
|
Allow: /
|
|
1631
1689
|
`;
|
|
1632
1690
|
}
|
|
1691
|
+
function generateGitignore(_config) {
|
|
1692
|
+
return [
|
|
1693
|
+
"node_modules/",
|
|
1694
|
+
"dist/",
|
|
1695
|
+
".output/",
|
|
1696
|
+
".netlify/",
|
|
1697
|
+
".wrangler/",
|
|
1698
|
+
"*.log",
|
|
1699
|
+
".DS_Store",
|
|
1700
|
+
".env",
|
|
1701
|
+
".env.*",
|
|
1702
|
+
"!.env.example",
|
|
1703
|
+
""
|
|
1704
|
+
].join(`
|
|
1705
|
+
`);
|
|
1706
|
+
}
|
|
1633
1707
|
|
|
1634
1708
|
// src/templates/favicon.ts
|
|
1635
1709
|
var FAVICON_BASE64 = "AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAACMuAAAjLgAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8BAAD/AQAA/wEAAP8BAAD/AQAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wIBAP9DIQr/f0AT/4BAE/+AQBP/f0AT/34/E/9+PxL/fj8T/39AE/+BQBP/cjkR/yAQBf8GAwH/Wy4N/4hEFP+HRBT/h0QU/4dEFP+GQxT/hUMU/4VDE/+EQhP/hEIT/2QzD/8PCAL/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/KxYG/8BgHP/YbSD/12wg/9dsIP/XbCD/12wg/9dsIP/XbCD/12wg/9dsIP/YbSD/iEUU/1AoDP/OaB7/2Gwg/9dsIP/XbCD/12wg/9dsIP/XbCD/12wg/9dsIP/XbCD/1Gsf/18wDv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8+Hwn/z2ge/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9RqH//FYx3/jUcV/9BpH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Vax//r1ga/xgMBP8AAAD/AAAA/wAAAP8AAAD/AAAA/xEIAv+lUxj/1msf/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9VrH/+iURj/tlsb/9VrH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//XzAO/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/1UrDP/RaR//02of/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//1Gof/8RiHf+ZTRb/0Wkf/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9VrH/+tVxn/FgsD/wAAAP8AAAD/AAAA/wAAAP8AAAD/EwoD/6lVGf/Wax//02of/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//1Gsf/6FRGP+6XRv/1Wsf/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9BoH/9BIQr/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/Wi0N/9JqH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Uax//wmId/51PF//Sah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Vax//ul4b/yQSBf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8WCwP/rVcZ/9VrH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Uah//olEY/71fHP/Uax//02of/9NqH//Tah//02of/9NqH//Tah//02of/9FpH/9eLw7/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP9fLw7/02of/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9RrH//BYRz/oFEX/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Wax//l0wW/w0HAv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xgMBP+wWRr/1Wsf/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9NqH/+iUhj/wWEc/9RrH//Tah//02of/9NqH//Tah//1Gsf/8BhHP8xGQf/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/2QyD//Uah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//1Gsf/8BgHP+kUxj/02of/9NqH//Tah//02of/9NqH//Tah//ZzQP/wEAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/HA4E/7RaGv/Vax//02of/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//02of/6NSGP/EYh3/1Gof/9NqH//Tah//1msf/59QF/8RCQP/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/aTUP/9RrH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Uax//v2Ac/6hVGf/Uah//02of/9RrH//FYx3/ORwI/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8fDwT/t1wb/9VrH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Sah//pVMY/8dkHf/Uax//1Gsf/3E5EP8CAQD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP9uNxD/1Wsf/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//02of/9RrH/+8Xhz/m04X/81nHv+dTxf/FgsD/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/yIRBf+6Xhv/1Wsf/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Tah//1Gsf/79gHP8yGQf/MxoI/xcMA/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/3Q6Ef/Vax//02of/9NqH//Tah//02of/9NqH//Tah//02of/9NqH//Uax//dDoR/wMBAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/JhMG/71fHP/Uax//02of/9NqH//Tah//02of/9NqH//Tah//1msf/6dUGP8XDAP/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8BAQD/eT0S/9ZrH//Tah//02of/9NqH//Tah//02of/9RqH//IZB3/QCAJ/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8qFQb/wGEc/9RrH//Tah//02of/9NqH//Tah//1Gsf/3c8Ev8DAgH/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wIBAP9+PxP/1msf/9NqH//Tah//02of/9ZrH/+nVBn/GAwE/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/y4XB//DYh3/1Gsf/9NqH//Uax//xWMd/zweCf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AwIA/4NCE//Wax//02of/9NqH/9rNhD/AgEA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/MhkH/8VjHf/XbCD/mk4X/xAIAv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8FAgH/iUUU/8BhHP8vGAf/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8yGQf/TygM/wEAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wEBAP8CAQD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
|
|
@@ -1651,8 +1725,8 @@ function generateRootLayout(config) {
|
|
|
1651
1725
|
`)}
|
|
1652
1726
|
|
|
1653
1727
|
export default async function RootLayout({ children, frontmatter }: Readonly<LayoutProps>) {
|
|
1654
|
-
const title = frontmatter?.title
|
|
1655
|
-
const description = frontmatter?.description
|
|
1728
|
+
const title = typeof frontmatter?.title === 'string' ? frontmatter.title : '${safeName}';
|
|
1729
|
+
const description = typeof frontmatter?.description === 'string' ? frontmatter.description : '';
|
|
1656
1730
|
|
|
1657
1731
|
return (
|
|
1658
1732
|
<html lang="en">
|
|
@@ -1660,8 +1734,9 @@ export default async function RootLayout({ children, frontmatter }: Readonly<Lay
|
|
|
1660
1734
|
<meta charset="UTF-8" />
|
|
1661
1735
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1662
1736
|
<title>{title}</title>
|
|
1663
|
-
{description
|
|
1737
|
+
{description ? <meta name="description" content={description} /> : null}
|
|
1664
1738
|
<link rel="icon" href="/favicon.ico" />
|
|
1739
|
+
<link rel="stylesheet" href="/syntax-highlighting.css" />
|
|
1665
1740
|
</head>
|
|
1666
1741
|
<body style={{ margin: 0 }}>
|
|
1667
1742
|
{children}
|
|
@@ -1679,6 +1754,14 @@ export default async function MainLayout({ children }: Readonly<LayoutProps>) {
|
|
|
1679
1754
|
}
|
|
1680
1755
|
`;
|
|
1681
1756
|
}
|
|
1757
|
+
function generateAboutLayout(_config) {
|
|
1758
|
+
return `import type { LayoutProps } from '@useavalon/avalon';
|
|
1759
|
+
|
|
1760
|
+
export default async function AboutLayout({ children }: Readonly<LayoutProps>) {
|
|
1761
|
+
return <>{children}</>;
|
|
1762
|
+
}
|
|
1763
|
+
`;
|
|
1764
|
+
}
|
|
1682
1765
|
|
|
1683
1766
|
// src/templates/middleware.ts
|
|
1684
1767
|
function generateSampleMiddleware(_config) {
|
|
@@ -1709,7 +1792,9 @@ function generatePackageJson(config) {
|
|
|
1709
1792
|
const dependencies = {
|
|
1710
1793
|
"@useavalon/avalon": "latest"
|
|
1711
1794
|
};
|
|
1712
|
-
|
|
1795
|
+
const selectedIntegrations = new Set(config.integrations);
|
|
1796
|
+
selectedIntegrations.add(config.core);
|
|
1797
|
+
for (const integration of selectedIntegrations) {
|
|
1713
1798
|
dependencies[INTEGRATION_PACKAGES[integration]] = "latest";
|
|
1714
1799
|
Object.assign(dependencies, INTEGRATION_RUNTIME_DEPS[integration]);
|
|
1715
1800
|
}
|
|
@@ -1719,9 +1804,7 @@ function generatePackageJson(config) {
|
|
|
1719
1804
|
if (config.plugins.includes("agent-optimization")) {
|
|
1720
1805
|
dependencies["@useavalon/agent-optimization"] = "latest";
|
|
1721
1806
|
}
|
|
1722
|
-
|
|
1723
|
-
dependencies["rehype-highlight"] = "latest";
|
|
1724
|
-
}
|
|
1807
|
+
dependencies["rehype-highlight"] = "latest";
|
|
1725
1808
|
switch (config.middleware) {
|
|
1726
1809
|
case "hono":
|
|
1727
1810
|
dependencies.hono = "latest";
|
|
@@ -1731,10 +1814,10 @@ function generatePackageJson(config) {
|
|
|
1731
1814
|
break;
|
|
1732
1815
|
}
|
|
1733
1816
|
const devDependencies = {
|
|
1734
|
-
vite: "
|
|
1735
|
-
typescript: "
|
|
1736
|
-
nitro: "
|
|
1737
|
-
"vite-imagetools": "
|
|
1817
|
+
vite: "^8.0.0",
|
|
1818
|
+
typescript: "^5.0.0",
|
|
1819
|
+
nitro: "^3.0.260311-beta",
|
|
1820
|
+
"vite-imagetools": "^7.0.0"
|
|
1738
1821
|
};
|
|
1739
1822
|
switch (config.styling) {
|
|
1740
1823
|
case "tailwind":
|
|
@@ -1749,15 +1832,21 @@ function generatePackageJson(config) {
|
|
|
1749
1832
|
dependencies.clsx = "^2.1.1";
|
|
1750
1833
|
break;
|
|
1751
1834
|
}
|
|
1835
|
+
const scripts = {
|
|
1836
|
+
dev: "bunx --bun vite dev",
|
|
1837
|
+
build: "node build.mjs",
|
|
1838
|
+
preview: "node .output/server/index.mjs"
|
|
1839
|
+
};
|
|
1840
|
+
if (config.deploy === "cloudflare") {
|
|
1841
|
+
const name = cloudflareProjectName(config.projectName);
|
|
1842
|
+
scripts.preview = "bunx wrangler@4 pages dev dist";
|
|
1843
|
+
scripts.deploy = `bunx wrangler@4 pages deploy --project-name=${name}`;
|
|
1844
|
+
}
|
|
1752
1845
|
const pkg = {
|
|
1753
1846
|
name: config.projectName,
|
|
1754
1847
|
type: "module",
|
|
1755
1848
|
private: true,
|
|
1756
|
-
scripts
|
|
1757
|
-
dev: "bunx --bun vite dev",
|
|
1758
|
-
build: "node build.mjs",
|
|
1759
|
-
preview: "node .output/server/index.mjs"
|
|
1760
|
-
},
|
|
1849
|
+
scripts,
|
|
1761
1850
|
dependencies,
|
|
1762
1851
|
devDependencies
|
|
1763
1852
|
};
|
|
@@ -1805,6 +1894,10 @@ export default async function HomePage() {
|
|
|
1805
1894
|
<code style={{ display: 'block', padding: '0.6rem 1.2rem', borderRadius: '6px', background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.08)', color: '#a5b4fc', fontSize: '0.85rem', fontFamily: 'ui-monospace, monospace' }}>
|
|
1806
1895
|
Edit app/modules/main/pages/index.tsx
|
|
1807
1896
|
</code>
|
|
1897
|
+
<p style={{ fontSize: '0.85rem', color: '#94a3b8', margin: '0.5rem 0 0' }}>
|
|
1898
|
+
<a href="/about" style={{ color: '#818cf8', textDecoration: 'none' }}>About</a>
|
|
1899
|
+
{' '}is the about module — app/modules/about/pages/index.tsx → /about
|
|
1900
|
+
</p>
|
|
1808
1901
|
</div>
|
|
1809
1902
|
|
|
1810
1903
|
<footer style={{ position: 'relative', zIndex: 1, marginTop: '4rem', paddingTop: '2rem', borderTop: '1px solid rgba(255,255,255,0.06)', width: '100%', maxWidth: '640px' }}>
|
|
@@ -1818,6 +1911,28 @@ export default async function HomePage() {
|
|
|
1818
1911
|
}
|
|
1819
1912
|
`;
|
|
1820
1913
|
}
|
|
1914
|
+
function generateAboutPage() {
|
|
1915
|
+
return `export const metadata = {
|
|
1916
|
+
title: 'About',
|
|
1917
|
+
description: 'The about module maps to the /about route.',
|
|
1918
|
+
};
|
|
1919
|
+
|
|
1920
|
+
export default function AboutPage() {
|
|
1921
|
+
return (
|
|
1922
|
+
<div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', background: 'linear-gradient(145deg, #0a0a12 0%, #0d1117 40%, #111827 100%)', color: '#e2e8f0', fontFamily: 'system-ui, -apple-system, sans-serif', padding: '2rem', textAlign: 'center' }}>
|
|
1923
|
+
<p style={{ fontSize: '0.8rem', letterSpacing: '0.15em', textTransform: 'uppercase', color: '#818cf8', marginBottom: '1.5rem', fontWeight: 500 }}>Module route</p>
|
|
1924
|
+
<h1 style={{ fontSize: 'clamp(2rem, 5vw, 3rem)', fontWeight: 700, lineHeight: 1.1, margin: '0 0 1.5rem' }}>About</h1>
|
|
1925
|
+
<p style={{ fontSize: '1.05rem', lineHeight: 1.7, color: '#94a3b8', marginBottom: '2rem', maxWidth: '480px' }}>
|
|
1926
|
+
This page is <code style={{ color: '#a5b4fc' }}>app/modules/about/pages/index.tsx</code>.
|
|
1927
|
+
A module name is a URL prefix, so the about module is <code style={{ color: '#a5b4fc' }}>/about</code>.
|
|
1928
|
+
The main module is the site root.
|
|
1929
|
+
</p>
|
|
1930
|
+
<a href="/" style={{ color: '#818cf8', textDecoration: 'none', fontWeight: 500 }}>Home</a>
|
|
1931
|
+
</div>
|
|
1932
|
+
);
|
|
1933
|
+
}
|
|
1934
|
+
`;
|
|
1935
|
+
}
|
|
1821
1936
|
function generate404Page() {
|
|
1822
1937
|
return `export const metadata = {
|
|
1823
1938
|
title: '404 - Page Not Found',
|
|
@@ -1844,7 +1959,8 @@ function generatePostBuildMjs() {
|
|
|
1844
1959
|
` * Post-build script — delegates to Avalon's built-in post-build.`,
|
|
1845
1960
|
` *`,
|
|
1846
1961
|
` * All the heavy lifting (CSS patching, island redirects, prerendering,`,
|
|
1847
|
-
` * Netlify function copying) is handled by
|
|
1962
|
+
` * Cloudflare worker patches, Netlify function copying) is handled by`,
|
|
1963
|
+
` * the framework.`,
|
|
1848
1964
|
` */`,
|
|
1849
1965
|
`import { runPostBuild } from '@useavalon/avalon/post-build';`,
|
|
1850
1966
|
``,
|
|
@@ -1864,6 +1980,7 @@ function generatePostBuildMjs() {
|
|
|
1864
1980
|
function generateStylingFiles(config) {
|
|
1865
1981
|
const files = new Map;
|
|
1866
1982
|
files.set("app/shared/styles/main.css", generateMainCss(config));
|
|
1983
|
+
files.set("public/syntax-highlighting.css", generateSyntaxHighlightingCss());
|
|
1867
1984
|
if (config.styling === "css-modules") {
|
|
1868
1985
|
files.set("app/shared/styles/reset.css", generateResetCss());
|
|
1869
1986
|
}
|
|
@@ -2057,6 +2174,30 @@ function generateShadcnComponentsJson(config) {
|
|
|
2057
2174
|
return JSON.stringify(componentsConfig, null, 2) + `
|
|
2058
2175
|
`;
|
|
2059
2176
|
}
|
|
2177
|
+
function generateSyntaxHighlightingCss() {
|
|
2178
|
+
return `code.hljs { padding: 3px 5px; }
|
|
2179
|
+
pre code.hljs { display: block; overflow-x: auto; padding: 1em; }
|
|
2180
|
+
.hljs { color: #c9d1d9; background: transparent; }
|
|
2181
|
+
.hljs-doctag, .hljs-keyword, .hljs-meta .hljs-keyword, .hljs-template-tag,
|
|
2182
|
+
.hljs-template-variable, .hljs-type, .hljs-variable.language_ { color: #ff7b72; }
|
|
2183
|
+
.hljs-title, .hljs-title.class_, .hljs-title.class_.inherited__,
|
|
2184
|
+
.hljs-title.function_ { color: #d2a8ff; }
|
|
2185
|
+
.hljs-attr, .hljs-attribute, .hljs-literal, .hljs-meta, .hljs-number,
|
|
2186
|
+
.hljs-operator, .hljs-variable, .hljs-selector-attr, .hljs-selector-class,
|
|
2187
|
+
.hljs-selector-id { color: #79c0ff; }
|
|
2188
|
+
.hljs-regexp, .hljs-string, .hljs-meta .hljs-string { color: #a5d6ff; }
|
|
2189
|
+
.hljs-built_in, .hljs-symbol { color: #ffa657; }
|
|
2190
|
+
.hljs-comment, .hljs-code, .hljs-formula { color: #8b949e; }
|
|
2191
|
+
.hljs-name, .hljs-quote, .hljs-selector-tag, .hljs-selector-pseudo { color: #7ee787; }
|
|
2192
|
+
.hljs-subst { color: #c9d1d9; }
|
|
2193
|
+
.hljs-section { color: #1f6feb; font-weight: bold; }
|
|
2194
|
+
.hljs-bullet { color: #f2cc60; }
|
|
2195
|
+
.hljs-emphasis { color: #c9d1d9; font-style: italic; }
|
|
2196
|
+
.hljs-strong { color: #c9d1d9; font-weight: bold; }
|
|
2197
|
+
.hljs-addition { color: #aff5b4; background-color: #033a16; }
|
|
2198
|
+
.hljs-deletion { color: #ffdcd7; background-color: #67060c; }
|
|
2199
|
+
`;
|
|
2200
|
+
}
|
|
2060
2201
|
|
|
2061
2202
|
// src/templates/tsconfig.ts
|
|
2062
2203
|
function generateTsConfig(core = "preact") {
|
|
@@ -2180,6 +2321,7 @@ function generateViteConfig(config) {
|
|
|
2180
2321
|
` { schedule: '${EXAMPLE_CRON_SCHEDULE}', handler: '${EXAMPLE_CRON_HANDLER}' },`,
|
|
2181
2322
|
` ],`
|
|
2182
2323
|
] : [];
|
|
2324
|
+
const compatLines = config.deploy === "cloudflare" ? [` compatibilityDate: '2026-09-04',`] : [];
|
|
2183
2325
|
const lines = [
|
|
2184
2326
|
imports.join(`
|
|
2185
2327
|
`),
|
|
@@ -2194,10 +2336,11 @@ function generateViteConfig(config) {
|
|
|
2194
2336
|
` nitro: {`,
|
|
2195
2337
|
` preset: process.env.NITRO_PRESET || 'node_server',`,
|
|
2196
2338
|
` streaming: true,`,
|
|
2339
|
+
...compatLines,
|
|
2197
2340
|
` clientEntry: 'app/entry-client',`,
|
|
2198
2341
|
` globalCSS: ['app/shared/styles/main.css'],`,
|
|
2199
2342
|
` prerender: {`,
|
|
2200
|
-
` routes: ['/'],`,
|
|
2343
|
+
` routes: ['/', '/about'],`,
|
|
2201
2344
|
` crawlLinks: true,`,
|
|
2202
2345
|
` ignore: [],`,
|
|
2203
2346
|
` },`,
|
|
@@ -2298,6 +2441,8 @@ async function scaffoldProject(config, targetDir) {
|
|
|
2298
2441
|
await writeFile(join(targetDir, "app/modules/main/layouts/_layout.tsx"), generateMainLayout(config));
|
|
2299
2442
|
await writeFile(join(targetDir, "app/modules/main/pages/index.tsx"), generateMainPage(config));
|
|
2300
2443
|
await writeFile(join(targetDir, "app/modules/main/pages/404.tsx"), generate404Page());
|
|
2444
|
+
await writeFile(join(targetDir, "app/modules/about/layouts/_layout.tsx"), generateAboutLayout(config));
|
|
2445
|
+
await writeFile(join(targetDir, "app/modules/about/pages/index.tsx"), generateAboutPage());
|
|
2301
2446
|
await writeFile(join(targetDir, "middleware/01.logger.ts"), generateSampleMiddleware(config));
|
|
2302
2447
|
await writeFile(join(targetDir, "routes/api/hello.ts"), generateHelloRoute(config));
|
|
2303
2448
|
if (config.cron) {
|
|
@@ -2324,13 +2469,10 @@ async function scaffoldProject(config, targetDir) {
|
|
|
2324
2469
|
`);
|
|
2325
2470
|
await writeFile(join(targetDir, "server/renderer.ts"), [
|
|
2326
2471
|
`/**`,
|
|
2327
|
-
` * SSR
|
|
2328
|
-
` *`,
|
|
2329
|
-
` * Avalon auto-discovers layouts, injects client assets, and handles`,
|
|
2330
|
-
` * layout wrapping. Import from the virtual modules directly to customize:`,
|
|
2472
|
+
` * Nitro SSR catch-all. Keep this re-export for the default renderer.`,
|
|
2331
2473
|
` *`,
|
|
2332
|
-
` *
|
|
2333
|
-
` *
|
|
2474
|
+
` * Customize only when you need a custom hydration directive or to wrap`,
|
|
2475
|
+
` * the handler. See https://useavalon.dev/docs/ssr-renderer`,
|
|
2334
2476
|
` */`,
|
|
2335
2477
|
`export { default } from 'virtual:avalon/renderer';`,
|
|
2336
2478
|
``
|
|
@@ -2338,9 +2480,15 @@ async function scaffoldProject(config, targetDir) {
|
|
|
2338
2480
|
`));
|
|
2339
2481
|
await writeFile(join(targetDir, "build.mjs"), generateBuildMjs());
|
|
2340
2482
|
await writeFile(join(targetDir, "post-build.mjs"), generatePostBuildMjs());
|
|
2483
|
+
await writeFile(join(targetDir, ".gitignore"), generateGitignore(config));
|
|
2484
|
+
await writeFile(join(targetDir, "DEPLOY.md"), generateDeployReadme(config));
|
|
2341
2485
|
if (config.deploy === "netlify") {
|
|
2342
2486
|
await writeFile(join(targetDir, "netlify.toml"), generateNetlifyToml(config));
|
|
2343
2487
|
}
|
|
2488
|
+
if (config.deploy === "cloudflare") {
|
|
2489
|
+
await writeFile(join(targetDir, "wrangler.toml"), generateWranglerToml(config));
|
|
2490
|
+
await writeFile(join(targetDir, "public/_headers"), generateCloudflareHeaders());
|
|
2491
|
+
}
|
|
2344
2492
|
}
|
|
2345
2493
|
|
|
2346
2494
|
// src/summary.ts
|
|
@@ -2350,15 +2498,34 @@ var STYLING_LABELS = {
|
|
|
2350
2498
|
shadcn: "shadcn"
|
|
2351
2499
|
};
|
|
2352
2500
|
var DEPLOY_LABELS = {
|
|
2501
|
+
cloudflare: "Cloudflare Pages",
|
|
2353
2502
|
netlify: "Netlify",
|
|
2354
2503
|
none: "None"
|
|
2355
2504
|
};
|
|
2505
|
+
function deployNextSteps(config) {
|
|
2506
|
+
if (config.deploy === "cloudflare") {
|
|
2507
|
+
return [
|
|
2508
|
+
" bun run build",
|
|
2509
|
+
" bun run preview # wrangler pages dev",
|
|
2510
|
+
" # See DEPLOY.md for Cloudflare Pages deploy steps"
|
|
2511
|
+
];
|
|
2512
|
+
}
|
|
2513
|
+
if (config.deploy === "netlify") {
|
|
2514
|
+
return [" bun run build", " # Connect the repo in Netlify, or see DEPLOY.md"];
|
|
2515
|
+
}
|
|
2516
|
+
return [" bun run build"];
|
|
2517
|
+
}
|
|
2356
2518
|
function formatSummary(config, scaffoldedInPlace = false) {
|
|
2357
2519
|
const integrations = config.integrations.length > 0 ? config.integrations.join(", ") : "none";
|
|
2358
2520
|
const styling = STYLING_LABELS[config.styling] ?? config.styling;
|
|
2359
2521
|
const plugins = config.plugins.length > 0 ? config.plugins.join(", ") : "none";
|
|
2360
2522
|
const deploy = DEPLOY_LABELS[config.deploy] ?? config.deploy;
|
|
2361
|
-
const nextSteps = scaffoldedInPlace ? [" bun install", " bun run dev"] : [
|
|
2523
|
+
const nextSteps = scaffoldedInPlace ? [" bun install", " bun run dev", ...deployNextSteps(config)] : [
|
|
2524
|
+
` cd ${config.projectName}`,
|
|
2525
|
+
" bun install",
|
|
2526
|
+
" bun run dev",
|
|
2527
|
+
...deployNextSteps(config)
|
|
2528
|
+
];
|
|
2362
2529
|
return [
|
|
2363
2530
|
"",
|
|
2364
2531
|
` Project: ${config.projectName}`,
|
|
@@ -2366,7 +2533,7 @@ function formatSummary(config, scaffoldedInPlace = false) {
|
|
|
2366
2533
|
` Styling: ${styling}`,
|
|
2367
2534
|
` Plugins: ${plugins}`,
|
|
2368
2535
|
` Middleware: ${config.middleware}`,
|
|
2369
|
-
` Deploy:
|
|
2536
|
+
` Deploy: ${deploy}`,
|
|
2370
2537
|
` Cron: ${config.cron ? "yes" : "no"}`,
|
|
2371
2538
|
"",
|
|
2372
2539
|
" Next steps:",
|
|
@@ -2402,9 +2569,9 @@ async function main() {
|
|
|
2402
2569
|
" --core Rendering engine: preact (default) | react",
|
|
2403
2570
|
" --integrations Comma list: preact,react,vue,svelte,solid,lit,qwik",
|
|
2404
2571
|
" --styling css-modules (default) | tailwind | shadcn",
|
|
2405
|
-
" --plugins Comma list: seo (default),agent-optimization
|
|
2572
|
+
" --plugins Comma list: seo (default),agent-optimization",
|
|
2406
2573
|
" --middleware h3 (default) | hono | elysia",
|
|
2407
|
-
" --deploy netlify | none (default)",
|
|
2574
|
+
" --deploy cloudflare | netlify | none (default)",
|
|
2408
2575
|
" --cron Scaffold an example cron task + config",
|
|
2409
2576
|
"",
|
|
2410
2577
|
"Example:",
|