create-avalon 0.1.25 → 0.2.0
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 +23 -7
- package/dist/cli.js +311 -197
- 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, syntax highlighting)
|
|
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,7 +44,7 @@ A ready-to-run Avalon project with file-system routing, islands architecture, an
|
|
|
33
44
|
my-project/
|
|
34
45
|
├── app/
|
|
35
46
|
│ ├── modules/
|
|
36
|
-
│ │ └──
|
|
47
|
+
│ │ └── main/
|
|
37
48
|
│ │ ├── pages/ # File-system routes
|
|
38
49
|
│ │ ├── components/ # Interactive components
|
|
39
50
|
│ │ └── layouts/ # Module layouts
|
|
@@ -46,7 +57,12 @@ my-project/
|
|
|
46
57
|
│ └── api/ # API routes
|
|
47
58
|
├── tasks/ # Scheduled jobs (cron) — optional
|
|
48
59
|
├── server/ # Server config & env
|
|
49
|
-
├── public/ # Static assets
|
|
60
|
+
├── public/ # Static assets (+ _headers for Cloudflare)
|
|
61
|
+
├── build.mjs # Vite hang workaround + post-build
|
|
62
|
+
├── post-build.mjs # Avalon production patches / prerender
|
|
63
|
+
├── DEPLOY.md # Platform-specific deploy steps
|
|
64
|
+
├── wrangler.toml # Cloudflare Pages (optional)
|
|
65
|
+
├── netlify.toml # Netlify (optional)
|
|
50
66
|
├── vite.config.ts
|
|
51
67
|
└── package.json
|
|
52
68
|
```
|
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";
|
|
@@ -109,7 +125,11 @@ var MIDDLEWARE_OPTIONS = [
|
|
|
109
125
|
"hono",
|
|
110
126
|
"elysia"
|
|
111
127
|
];
|
|
112
|
-
var DEPLOY_TARGETS = [
|
|
128
|
+
var DEPLOY_TARGETS = [
|
|
129
|
+
"cloudflare",
|
|
130
|
+
"netlify",
|
|
131
|
+
"none"
|
|
132
|
+
];
|
|
113
133
|
var INTEGRATION_PACKAGES = {
|
|
114
134
|
preact: "@useavalon/preact",
|
|
115
135
|
react: "@useavalon/react",
|
|
@@ -209,8 +229,8 @@ function resolveConfigNonInteractive(args) {
|
|
|
209
229
|
if (styling === "shadcn" && core !== "react") {
|
|
210
230
|
throw new CliArgError("--styling=shadcn requires --core=react (shadcn is Radix/React based).");
|
|
211
231
|
}
|
|
212
|
-
if (
|
|
213
|
-
integrations.push(
|
|
232
|
+
if (!integrations.includes(core)) {
|
|
233
|
+
integrations.push(core);
|
|
214
234
|
}
|
|
215
235
|
return {
|
|
216
236
|
projectName: args.projectName ?? ".",
|
|
@@ -580,82 +600,6 @@ class B {
|
|
|
580
600
|
}
|
|
581
601
|
}
|
|
582
602
|
}
|
|
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
603
|
class kt extends B {
|
|
660
604
|
get cursor() {
|
|
661
605
|
return this.value ? 0 : 1;
|
|
@@ -673,54 +617,6 @@ class kt extends B {
|
|
|
673
617
|
});
|
|
674
618
|
}
|
|
675
619
|
}
|
|
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
620
|
var Lt = class extends B {
|
|
725
621
|
options;
|
|
726
622
|
cursor = 0;
|
|
@@ -889,59 +785,59 @@ var Ft2 = /\p{M}+/gu;
|
|
|
889
785
|
var yt2 = { limit: 1 / 0, ellipsis: "" };
|
|
890
786
|
var Le = (e, r = {}, s = {}) => {
|
|
891
787
|
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,
|
|
788
|
+
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
789
|
e:
|
|
894
790
|
for (;; ) {
|
|
895
791
|
if (b > A || m >= d && m > $) {
|
|
896
|
-
const
|
|
792
|
+
const T = e.slice(A, b) || e.slice($, m);
|
|
897
793
|
F = 0;
|
|
898
|
-
for (const
|
|
899
|
-
const
|
|
900
|
-
if (gt2(
|
|
901
|
-
|
|
794
|
+
for (const M of T.replaceAll(Ft2, "")) {
|
|
795
|
+
const O = M.codePointAt(0) || 0;
|
|
796
|
+
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) {
|
|
797
|
+
y = true;
|
|
902
798
|
break e;
|
|
903
799
|
}
|
|
904
|
-
F +=
|
|
800
|
+
F += M.length, w += S;
|
|
905
801
|
}
|
|
906
802
|
A = b = 0;
|
|
907
803
|
}
|
|
908
804
|
if (m >= d)
|
|
909
805
|
break;
|
|
910
806
|
if (ne.lastIndex = m, ne.test(e)) {
|
|
911
|
-
if (F = ne.lastIndex - m,
|
|
912
|
-
|
|
807
|
+
if (F = ne.lastIndex - m, S = F * g, w + S > C && (v = Math.min(v, m + Math.floor((C - w) / g))), w + S > i) {
|
|
808
|
+
y = true;
|
|
913
809
|
break;
|
|
914
810
|
}
|
|
915
|
-
w +=
|
|
811
|
+
w += S, A = $, b = m, m = $ = ne.lastIndex;
|
|
916
812
|
continue;
|
|
917
813
|
}
|
|
918
814
|
if (we.lastIndex = m, we.test(e)) {
|
|
919
815
|
if (w + u > C && (v = Math.min(v, m)), w + u > i) {
|
|
920
|
-
|
|
816
|
+
y = true;
|
|
921
817
|
break;
|
|
922
818
|
}
|
|
923
819
|
w += u, A = $, b = m, m = $ = we.lastIndex;
|
|
924
820
|
continue;
|
|
925
821
|
}
|
|
926
822
|
if (re.lastIndex = m, re.test(e)) {
|
|
927
|
-
if (F = re.lastIndex - m,
|
|
928
|
-
|
|
823
|
+
if (F = re.lastIndex - m, S = F * l, w + S > C && (v = Math.min(v, m + Math.floor((C - w) / l))), w + S > i) {
|
|
824
|
+
y = true;
|
|
929
825
|
break;
|
|
930
826
|
}
|
|
931
|
-
w +=
|
|
827
|
+
w += S, A = $, b = m, m = $ = re.lastIndex;
|
|
932
828
|
continue;
|
|
933
829
|
}
|
|
934
830
|
if (ie.lastIndex = m, ie.test(e)) {
|
|
935
|
-
if (F = ie.lastIndex - m,
|
|
936
|
-
|
|
831
|
+
if (F = ie.lastIndex - m, S = F * n, w + S > C && (v = Math.min(v, m + Math.floor((C - w) / n))), w + S > i) {
|
|
832
|
+
y = true;
|
|
937
833
|
break;
|
|
938
834
|
}
|
|
939
|
-
w +=
|
|
835
|
+
w += S, A = $, b = m, m = $ = ie.lastIndex;
|
|
940
836
|
continue;
|
|
941
837
|
}
|
|
942
838
|
if (Ae.lastIndex = m, Ae.test(e)) {
|
|
943
839
|
if (w + p > C && (v = Math.min(v, m)), w + p > i) {
|
|
944
|
-
|
|
840
|
+
y = true;
|
|
945
841
|
break;
|
|
946
842
|
}
|
|
947
843
|
w += p, A = $, b = m, m = $ = Ae.lastIndex;
|
|
@@ -949,7 +845,7 @@ var Le = (e, r = {}, s = {}) => {
|
|
|
949
845
|
}
|
|
950
846
|
m += 1;
|
|
951
847
|
}
|
|
952
|
-
return { width:
|
|
848
|
+
return { width: y ? C : w, index: y ? v : d, truncated: y, ellipsed: y && i >= o };
|
|
953
849
|
};
|
|
954
850
|
var Et2 = { limit: 1 / 0, ellipsis: "", ellipsisWidth: 0 };
|
|
955
851
|
var D2 = (e, r = {}) => Le(e, Et2, r).width;
|
|
@@ -958,9 +854,9 @@ var je = "";
|
|
|
958
854
|
var vt2 = 39;
|
|
959
855
|
var Ce = "\x07";
|
|
960
856
|
var ke = "[";
|
|
961
|
-
var
|
|
857
|
+
var wt = "]";
|
|
962
858
|
var Ve = "m";
|
|
963
|
-
var Se = `${
|
|
859
|
+
var Se = `${wt}8;;`;
|
|
964
860
|
var He = new RegExp(`(?:\\${ke}(?<code>\\d+)m|\\${Se}(?<uri>.*)${Ce})`, "y");
|
|
965
861
|
var At2 = (e) => {
|
|
966
862
|
if (e >= 30 && e <= 37 || e >= 90 && e <= 97)
|
|
@@ -994,7 +890,7 @@ var Ie = (e, r, s) => {
|
|
|
994
890
|
}
|
|
995
891
|
u = e.at(-1), !l && u !== undefined && u.length > 0 && e.length > 1 && (e[e.length - 2] += e.pop());
|
|
996
892
|
};
|
|
997
|
-
var
|
|
893
|
+
var St = (e) => {
|
|
998
894
|
const r = e.split(" ");
|
|
999
895
|
let s = r.length;
|
|
1000
896
|
for (;s > 0 && !(D2(r[s - 1]) > 0); )
|
|
@@ -1011,8 +907,8 @@ var It2 = (e, r, s = {}) => {
|
|
|
1011
907
|
s.trim !== false && (n[n.length - 1] = (n.at(-1) ?? "").trimStart());
|
|
1012
908
|
let d = D2(n.at(-1) ?? "");
|
|
1013
909
|
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) <
|
|
910
|
+
const F = r - d, y = 1 + Math.floor((l[$] - F - 1) / r);
|
|
911
|
+
Math.floor((l[$] - 1) / r) < y && n.push(""), Ie(n, m, r);
|
|
1016
912
|
continue;
|
|
1017
913
|
}
|
|
1018
914
|
if (d + l[$] > r && d > 0 && l[$] > 0) {
|
|
@@ -1028,7 +924,7 @@ var It2 = (e, r, s = {}) => {
|
|
|
1028
924
|
}
|
|
1029
925
|
n[n.length - 1] += m;
|
|
1030
926
|
}
|
|
1031
|
-
s.trim !== false && (n = n.map(($) =>
|
|
927
|
+
s.trim !== false && (n = n.map(($) => St($)));
|
|
1032
928
|
const c = n.join(`
|
|
1033
929
|
`), p = c[Symbol.iterator]();
|
|
1034
930
|
let f = p.next(), g = p.next(), E = 0;
|
|
@@ -1036,12 +932,12 @@ var It2 = (e, r, s = {}) => {
|
|
|
1036
932
|
const $ = f.value, m = g.value;
|
|
1037
933
|
if (i += $, $ === ae || $ === je) {
|
|
1038
934
|
He.lastIndex = E + 1;
|
|
1039
|
-
const
|
|
1040
|
-
if (
|
|
1041
|
-
const v = Number.parseFloat(
|
|
935
|
+
const y = He.exec(c)?.groups;
|
|
936
|
+
if (y?.code !== undefined) {
|
|
937
|
+
const v = Number.parseFloat(y.code);
|
|
1042
938
|
a = v === vt2 ? undefined : v;
|
|
1043
939
|
} else
|
|
1044
|
-
|
|
940
|
+
y?.uri !== undefined && (o = y.uri.length === 0 ? undefined : y.uri);
|
|
1045
941
|
}
|
|
1046
942
|
const d = a ? At2(a) : undefined;
|
|
1047
943
|
m === `
|
|
@@ -1074,16 +970,16 @@ var X2 = ({ cursor: e, options: r, style: s, output: i = process.stdout, maxItem
|
|
|
1074
970
|
const m = Math.min(g + f, r.length), d = [];
|
|
1075
971
|
let F = 0;
|
|
1076
972
|
E && F++, $ && F++;
|
|
1077
|
-
const
|
|
1078
|
-
for (let A =
|
|
973
|
+
const y = g + (E ? 1 : 0), v = m - ($ ? 1 : 0);
|
|
974
|
+
for (let A = y;A < v; A++) {
|
|
1079
975
|
const b = J(s(r[A], A === e), l, { hard: true, trim: false }).split(`
|
|
1080
976
|
`);
|
|
1081
977
|
d.push(b), F += b.length;
|
|
1082
978
|
}
|
|
1083
979
|
if (F > p) {
|
|
1084
980
|
let A = 0, b = 0, w = F;
|
|
1085
|
-
const
|
|
1086
|
-
E ? ({ lineCount: w, removals: A } =
|
|
981
|
+
const S = e - y, T = (M, O) => bt2(d, w, M, O, p);
|
|
982
|
+
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
983
|
}
|
|
1088
984
|
const C = [];
|
|
1089
985
|
E && C.push(c);
|
|
@@ -1379,12 +1275,17 @@ async function collectProjectConfig(initialName) {
|
|
|
1379
1275
|
const deployResult = await Jt({
|
|
1380
1276
|
message: "Where will you deploy?",
|
|
1381
1277
|
options: [
|
|
1278
|
+
{
|
|
1279
|
+
value: "cloudflare",
|
|
1280
|
+
label: "Cloudflare Pages",
|
|
1281
|
+
hint: "wrangler.toml, public/_headers, build + deploy scripts"
|
|
1282
|
+
},
|
|
1382
1283
|
{
|
|
1383
1284
|
value: "netlify",
|
|
1384
1285
|
label: "Netlify",
|
|
1385
|
-
hint: "
|
|
1286
|
+
hint: "netlify.toml, build.mjs, post-build.mjs"
|
|
1386
1287
|
},
|
|
1387
|
-
{ value: "none", label: "None / Other", hint: "Node server preset, no
|
|
1288
|
+
{ value: "none", label: "None / Other", hint: "Node server preset, no platform config" }
|
|
1388
1289
|
]
|
|
1389
1290
|
});
|
|
1390
1291
|
if (Ct(deployResult)) {
|
|
@@ -1401,8 +1302,8 @@ async function collectProjectConfig(initialName) {
|
|
|
1401
1302
|
}
|
|
1402
1303
|
const core = coreResult;
|
|
1403
1304
|
const integrations = integrationsResult;
|
|
1404
|
-
if (
|
|
1405
|
-
integrations.push(
|
|
1305
|
+
if (!integrations.includes(core)) {
|
|
1306
|
+
integrations.push(core);
|
|
1406
1307
|
}
|
|
1407
1308
|
return {
|
|
1408
1309
|
projectName,
|
|
@@ -1456,6 +1357,10 @@ export default defineCronJob({
|
|
|
1456
1357
|
}
|
|
1457
1358
|
|
|
1458
1359
|
// src/templates/deploy.ts
|
|
1360
|
+
function cloudflareProjectName(projectName) {
|
|
1361
|
+
const slug = projectName.toLowerCase().replaceAll(/[^a-z0-9-]+/g, "-").replaceAll(/-+/g, "-").replaceAll(/^-|-$/g, "");
|
|
1362
|
+
return slug || "avalon-app";
|
|
1363
|
+
}
|
|
1459
1364
|
function generateNetlifyToml(_config) {
|
|
1460
1365
|
return `[build]
|
|
1461
1366
|
base = "."
|
|
@@ -1480,6 +1385,117 @@ function generateNetlifyToml(_config) {
|
|
|
1480
1385
|
status = 200
|
|
1481
1386
|
`;
|
|
1482
1387
|
}
|
|
1388
|
+
function generateWranglerToml(config) {
|
|
1389
|
+
const name = cloudflareProjectName(config.projectName);
|
|
1390
|
+
return `# Cloudflare Pages — production output is dist/_worker.js + static assets.
|
|
1391
|
+
# Date must be >= 2025-09-15 so nodejs_compat includes \`node:fs\` (needed by the Nitro worker).
|
|
1392
|
+
name = "${name}"
|
|
1393
|
+
compatibility_date = "2026-09-04"
|
|
1394
|
+
compatibility_flags = ["nodejs_compat", "enable_nodejs_fs_module"]
|
|
1395
|
+
pages_build_output_dir = "./dist"
|
|
1396
|
+
`;
|
|
1397
|
+
}
|
|
1398
|
+
function generateCloudflareHeaders() {
|
|
1399
|
+
return `# Cloudflare Pages static headers (Worker routeRules still apply to SSR).
|
|
1400
|
+
/*.html
|
|
1401
|
+
Cache-Control: public, max-age=3600, s-maxage=31536000
|
|
1402
|
+
|
|
1403
|
+
/
|
|
1404
|
+
Cache-Control: public, max-age=3600, s-maxage=31536000
|
|
1405
|
+
|
|
1406
|
+
/islands/*
|
|
1407
|
+
Cache-Control: public, max-age=0, must-revalidate
|
|
1408
|
+
`;
|
|
1409
|
+
}
|
|
1410
|
+
function generateDeployReadme(config) {
|
|
1411
|
+
if (config.deploy === "cloudflare") {
|
|
1412
|
+
const name = cloudflareProjectName(config.projectName);
|
|
1413
|
+
return `# Deploying to Cloudflare Pages
|
|
1414
|
+
|
|
1415
|
+
This project is set up for \`NITRO_PRESET=cloudflare_pages\`.
|
|
1416
|
+
|
|
1417
|
+
## Build
|
|
1418
|
+
|
|
1419
|
+
\`\`\`bash
|
|
1420
|
+
bun run build
|
|
1421
|
+
\`\`\`
|
|
1422
|
+
|
|
1423
|
+
\`build.mjs\` detects \`wrangler.toml\` and sets the Nitro preset when \`NITRO_PRESET\` is unset.
|
|
1424
|
+
Avalon's post-build patches the Cloudflare worker (DOM stub, CSS manifest, \`_routes.json\`).
|
|
1425
|
+
|
|
1426
|
+
## Preview locally
|
|
1427
|
+
|
|
1428
|
+
\`\`\`bash
|
|
1429
|
+
bun run preview
|
|
1430
|
+
\`\`\`
|
|
1431
|
+
|
|
1432
|
+
## Deploy
|
|
1433
|
+
|
|
1434
|
+
1. Create an API token with **Account → Cloudflare Pages → Edit** and **Account Settings → Read**.
|
|
1435
|
+
2. Set \`CLOUDFLARE_API_TOKEN\` and \`CLOUDFLARE_ACCOUNT_ID\` in your environment (or CI secrets).
|
|
1436
|
+
3. Create the Pages project once (Wrangler 4 does not auto-create):
|
|
1437
|
+
|
|
1438
|
+
\`\`\`bash
|
|
1439
|
+
bunx wrangler@4 pages project create ${name} \\
|
|
1440
|
+
--production-branch=main \\
|
|
1441
|
+
--compatibility-flags=nodejs_compat \\
|
|
1442
|
+
--compatibility-date=2026-09-04
|
|
1443
|
+
\`\`\`
|
|
1444
|
+
|
|
1445
|
+
4. Deploy:
|
|
1446
|
+
|
|
1447
|
+
\`\`\`bash
|
|
1448
|
+
bun run deploy
|
|
1449
|
+
\`\`\`
|
|
1450
|
+
|
|
1451
|
+
Prefer **one** deployer (e.g. GitHub Actions with Wrangler). If you also connect the
|
|
1452
|
+
GitHub repo in the Cloudflare dashboard, disable Cloudflare's own build to avoid
|
|
1453
|
+
double deploys.
|
|
1454
|
+
|
|
1455
|
+
Do not rely on Cloudflare's Git integration to run \`vite build\` without Avalon's
|
|
1456
|
+
\`post-build.mjs\` — prerendered CSS and worker patches will be missing.
|
|
1457
|
+
`;
|
|
1458
|
+
}
|
|
1459
|
+
if (config.deploy === "netlify") {
|
|
1460
|
+
return `# Deploying to Netlify
|
|
1461
|
+
|
|
1462
|
+
This project is set up for \`NITRO_PRESET=netlify\` via \`netlify.toml\`.
|
|
1463
|
+
|
|
1464
|
+
## Build
|
|
1465
|
+
|
|
1466
|
+
\`\`\`bash
|
|
1467
|
+
bun run build
|
|
1468
|
+
\`\`\`
|
|
1469
|
+
|
|
1470
|
+
Or connect the repo in Netlify — \`netlify.toml\` runs \`bun install && bun build.mjs\`
|
|
1471
|
+
and publishes \`dist/\`. Avalon's post-build copies the server function and prerenders routes.
|
|
1472
|
+
|
|
1473
|
+
## Preview locally
|
|
1474
|
+
|
|
1475
|
+
\`\`\`bash
|
|
1476
|
+
bun run preview
|
|
1477
|
+
\`\`\`
|
|
1478
|
+
|
|
1479
|
+
## Notes
|
|
1480
|
+
|
|
1481
|
+
- Functions live under \`.netlify/functions-internal\` (\`node_bundler = "none"\`).
|
|
1482
|
+
- The \`/*\` → \`/.netlify/functions/server\` redirect is a soft SSR fallback: static
|
|
1483
|
+
prerendered HTML in \`dist/\` is served first when present.
|
|
1484
|
+
`;
|
|
1485
|
+
}
|
|
1486
|
+
return `# Deploy
|
|
1487
|
+
|
|
1488
|
+
No platform config was generated. Build with:
|
|
1489
|
+
|
|
1490
|
+
\`\`\`bash
|
|
1491
|
+
bun run build
|
|
1492
|
+
\`\`\`
|
|
1493
|
+
|
|
1494
|
+
Default Nitro preset is \`node_server\` (\`.output/server\`). Set \`NITRO_PRESET\` for
|
|
1495
|
+
other targets (\`netlify\`, \`cloudflare_pages\`, \`vercel\`, …) and add the matching
|
|
1496
|
+
platform config — or re-run \`create-avalon\` with a deploy target.
|
|
1497
|
+
`;
|
|
1498
|
+
}
|
|
1483
1499
|
function generateBuildMjs() {
|
|
1484
1500
|
return `/**
|
|
1485
1501
|
* Build wrapper.
|
|
@@ -1487,20 +1503,49 @@ function generateBuildMjs() {
|
|
|
1487
1503
|
* Vite/Nitro leaves open handles after the build completes, preventing
|
|
1488
1504
|
* the Node process from exiting. This wrapper detects when the build
|
|
1489
1505
|
* output is ready, kills the entire process group, then runs post-build.
|
|
1506
|
+
*
|
|
1507
|
+
* When NITRO_PRESET is unset, defaults from wrangler.toml / netlify.toml
|
|
1508
|
+
* so local \`bun run build\` matches the chosen create-avalon deploy target.
|
|
1490
1509
|
*/
|
|
1491
1510
|
|
|
1492
1511
|
import { spawn, execSync } from 'node:child_process';
|
|
1493
|
-
import { existsSync, rmSync } from 'node:fs';
|
|
1512
|
+
import { existsSync, rmSync, statSync } from 'node:fs';
|
|
1494
1513
|
import { join } from 'node:path';
|
|
1495
1514
|
|
|
1496
1515
|
const CWD = process.cwd();
|
|
1497
1516
|
const NITRO_JSON = join(CWD, '.netlify', 'functions-internal', 'nitro.json');
|
|
1498
1517
|
const SERVER_MJS = join(CWD, '.netlify', 'functions-internal', 'server', 'server.mjs');
|
|
1499
1518
|
const OUTPUT_SSR = join(CWD, '.output', 'server', '_ssr', 'ssr.mjs');
|
|
1519
|
+
const CF_WORKER = join(CWD, 'dist', '_worker.js');
|
|
1520
|
+
|
|
1521
|
+
if (!process.env.NITRO_PRESET) {
|
|
1522
|
+
if (existsSync(join(CWD, 'wrangler.toml'))) {
|
|
1523
|
+
process.env.NITRO_PRESET = 'cloudflare_pages';
|
|
1524
|
+
} else if (existsSync(join(CWD, 'netlify.toml'))) {
|
|
1525
|
+
process.env.NITRO_PRESET = 'netlify';
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/** True when Nitro finished a Cloudflare worker file, not an empty client-build directory. */
|
|
1530
|
+
function isCloudflareWorkerReady() {
|
|
1531
|
+
try {
|
|
1532
|
+
const st = statSync(CF_WORKER);
|
|
1533
|
+
if (st.isFile()) return true;
|
|
1534
|
+
if (st.isDirectory()) {
|
|
1535
|
+
return existsSync(join(CF_WORKER, 'index.js')) || existsSync(join(CF_WORKER, 'index.mjs'));
|
|
1536
|
+
}
|
|
1537
|
+
} catch {
|
|
1538
|
+
return false;
|
|
1539
|
+
}
|
|
1540
|
+
return false;
|
|
1541
|
+
}
|
|
1500
1542
|
|
|
1501
1543
|
console.log('[build] Starting vite build...');
|
|
1544
|
+
if (process.env.NITRO_PRESET) {
|
|
1545
|
+
console.log(\`[build] NITRO_PRESET=\${process.env.NITRO_PRESET}\`);
|
|
1546
|
+
}
|
|
1502
1547
|
|
|
1503
|
-
for (const dir of ['.netlify', '.output', 'netlify']) {
|
|
1548
|
+
for (const dir of ['.netlify', '.output', 'netlify', 'dist']) {
|
|
1504
1549
|
const full = join(CWD, dir);
|
|
1505
1550
|
if (existsSync(full)) {
|
|
1506
1551
|
rmSync(full, { recursive: true, force: true });
|
|
@@ -1512,10 +1557,13 @@ const child = spawn('bunx', ['--bun', 'vite', 'build'], {
|
|
|
1512
1557
|
cwd: CWD,
|
|
1513
1558
|
stdio: 'inherit',
|
|
1514
1559
|
detached: true,
|
|
1560
|
+
env: process.env,
|
|
1515
1561
|
});
|
|
1516
1562
|
|
|
1517
1563
|
const childPid = child.pid;
|
|
1518
1564
|
let done = false;
|
|
1565
|
+
let killedEarly = false;
|
|
1566
|
+
let viteExitCode = 0;
|
|
1519
1567
|
|
|
1520
1568
|
function killTree() {
|
|
1521
1569
|
try { process.kill(-childPid, 'SIGKILL'); } catch {}
|
|
@@ -1527,29 +1575,40 @@ function finish() {
|
|
|
1527
1575
|
done = true;
|
|
1528
1576
|
clearInterval(poll);
|
|
1529
1577
|
clearTimeout(absoluteTimeout);
|
|
1530
|
-
|
|
1578
|
+
|
|
1579
|
+
if (killedEarly) killTree();
|
|
1531
1580
|
|
|
1532
1581
|
setTimeout(() => {
|
|
1533
1582
|
console.log('[build] Running post-build...');
|
|
1534
1583
|
try {
|
|
1535
|
-
execSync('node post-build.mjs', { cwd: CWD, stdio: 'inherit', timeout: 120_000 });
|
|
1584
|
+
execSync('node post-build.mjs', { cwd: CWD, stdio: 'inherit', timeout: 120_000, env: process.env });
|
|
1536
1585
|
} catch (err) {
|
|
1537
|
-
console.error('[build] post-build
|
|
1586
|
+
console.error('[build] post-build failed:', err.message);
|
|
1587
|
+
process.exit(1);
|
|
1538
1588
|
}
|
|
1539
1589
|
|
|
1540
1590
|
const V1_SERVER = join(CWD, '.netlify', 'v1', 'functions', 'server', 'server.mjs');
|
|
1541
|
-
|
|
1591
|
+
const cloudflareReady = isCloudflareWorkerReady();
|
|
1592
|
+
const ok =
|
|
1593
|
+
cloudflareReady ||
|
|
1594
|
+
existsSync(V1_SERVER) ||
|
|
1595
|
+
existsSync(SERVER_MJS) ||
|
|
1596
|
+
existsSync(OUTPUT_SSR);
|
|
1597
|
+
if (cloudflareReady) console.log('[build] ✅ Cloudflare worker found (dist/_worker.js)');
|
|
1598
|
+
else if (existsSync(V1_SERVER)) console.log('[build] ✅ Server function found (v1 API)');
|
|
1542
1599
|
else if (existsSync(SERVER_MJS)) console.log('[build] ✅ Server function found (legacy)');
|
|
1543
1600
|
else if (existsSync(OUTPUT_SSR)) console.log('[build] ✅ SSR bundle found');
|
|
1544
1601
|
else console.error('[build] ❌ No server output found');
|
|
1545
1602
|
|
|
1603
|
+
if (!ok) process.exit(1);
|
|
1546
1604
|
console.log('[build] ✅ Complete');
|
|
1547
|
-
process.exit(0);
|
|
1605
|
+
process.exit(killedEarly || viteExitCode === 0 ? 0 : viteExitCode);
|
|
1548
1606
|
}, 500);
|
|
1549
1607
|
}
|
|
1550
1608
|
|
|
1551
1609
|
child.on('exit', (code) => {
|
|
1552
1610
|
console.log(\`[build] vite build exited with code \${code}\`);
|
|
1611
|
+
viteExitCode = code ?? (killedEarly ? 0 : 1);
|
|
1553
1612
|
finish();
|
|
1554
1613
|
});
|
|
1555
1614
|
|
|
@@ -1559,10 +1618,14 @@ child.on('error', (err) => {
|
|
|
1559
1618
|
});
|
|
1560
1619
|
|
|
1561
1620
|
const poll = setInterval(() => {
|
|
1621
|
+
// Cloudflare \`_worker.js\` appears during the client build; killing then
|
|
1622
|
+
// aborts SSR. Only force-stop the Netlify/Node hang.
|
|
1562
1623
|
const netlifyReady = existsSync(NITRO_JSON) && existsSync(SERVER_MJS);
|
|
1563
1624
|
const nodeServerReady = existsSync(OUTPUT_SSR);
|
|
1564
1625
|
if (netlifyReady || nodeServerReady) {
|
|
1565
|
-
|
|
1626
|
+
killedEarly = true;
|
|
1627
|
+
const kind = netlifyReady ? 'netlify' : 'node-server';
|
|
1628
|
+
console.log(\`[build] Output detected (\${kind}), waiting 3s for final writes...\`);
|
|
1566
1629
|
clearInterval(poll);
|
|
1567
1630
|
setTimeout(finish, 3_000);
|
|
1568
1631
|
}
|
|
@@ -1570,6 +1633,7 @@ const poll = setInterval(() => {
|
|
|
1570
1633
|
|
|
1571
1634
|
const absoluteTimeout = setTimeout(() => {
|
|
1572
1635
|
console.error('[build] Timeout — killing build');
|
|
1636
|
+
killedEarly = true;
|
|
1573
1637
|
finish();
|
|
1574
1638
|
}, 240_000);
|
|
1575
1639
|
`;
|
|
@@ -1630,6 +1694,22 @@ User-agent: AI2Bot
|
|
|
1630
1694
|
Allow: /
|
|
1631
1695
|
`;
|
|
1632
1696
|
}
|
|
1697
|
+
function generateGitignore(_config) {
|
|
1698
|
+
return [
|
|
1699
|
+
"node_modules/",
|
|
1700
|
+
"dist/",
|
|
1701
|
+
".output/",
|
|
1702
|
+
".netlify/",
|
|
1703
|
+
".wrangler/",
|
|
1704
|
+
"*.log",
|
|
1705
|
+
".DS_Store",
|
|
1706
|
+
".env",
|
|
1707
|
+
".env.*",
|
|
1708
|
+
"!.env.example",
|
|
1709
|
+
""
|
|
1710
|
+
].join(`
|
|
1711
|
+
`);
|
|
1712
|
+
}
|
|
1633
1713
|
|
|
1634
1714
|
// src/templates/favicon.ts
|
|
1635
1715
|
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=";
|
|
@@ -1709,7 +1789,9 @@ function generatePackageJson(config) {
|
|
|
1709
1789
|
const dependencies = {
|
|
1710
1790
|
"@useavalon/avalon": "latest"
|
|
1711
1791
|
};
|
|
1712
|
-
|
|
1792
|
+
const selectedIntegrations = new Set(config.integrations);
|
|
1793
|
+
selectedIntegrations.add(config.core);
|
|
1794
|
+
for (const integration of selectedIntegrations) {
|
|
1713
1795
|
dependencies[INTEGRATION_PACKAGES[integration]] = "latest";
|
|
1714
1796
|
Object.assign(dependencies, INTEGRATION_RUNTIME_DEPS[integration]);
|
|
1715
1797
|
}
|
|
@@ -1719,9 +1801,7 @@ function generatePackageJson(config) {
|
|
|
1719
1801
|
if (config.plugins.includes("agent-optimization")) {
|
|
1720
1802
|
dependencies["@useavalon/agent-optimization"] = "latest";
|
|
1721
1803
|
}
|
|
1722
|
-
|
|
1723
|
-
dependencies["rehype-highlight"] = "latest";
|
|
1724
|
-
}
|
|
1804
|
+
dependencies["rehype-highlight"] = "latest";
|
|
1725
1805
|
switch (config.middleware) {
|
|
1726
1806
|
case "hono":
|
|
1727
1807
|
dependencies.hono = "latest";
|
|
@@ -1731,10 +1811,10 @@ function generatePackageJson(config) {
|
|
|
1731
1811
|
break;
|
|
1732
1812
|
}
|
|
1733
1813
|
const devDependencies = {
|
|
1734
|
-
vite: "
|
|
1735
|
-
typescript: "
|
|
1736
|
-
nitro: "
|
|
1737
|
-
"vite-imagetools": "
|
|
1814
|
+
vite: "^8.0.0",
|
|
1815
|
+
typescript: "^5.0.0",
|
|
1816
|
+
nitro: "^3.0.260311-beta",
|
|
1817
|
+
"vite-imagetools": "^7.0.0"
|
|
1738
1818
|
};
|
|
1739
1819
|
switch (config.styling) {
|
|
1740
1820
|
case "tailwind":
|
|
@@ -1749,15 +1829,21 @@ function generatePackageJson(config) {
|
|
|
1749
1829
|
dependencies.clsx = "^2.1.1";
|
|
1750
1830
|
break;
|
|
1751
1831
|
}
|
|
1832
|
+
const scripts = {
|
|
1833
|
+
dev: "bunx --bun vite dev",
|
|
1834
|
+
build: "node build.mjs",
|
|
1835
|
+
preview: "node .output/server/index.mjs"
|
|
1836
|
+
};
|
|
1837
|
+
if (config.deploy === "cloudflare") {
|
|
1838
|
+
const name = cloudflareProjectName(config.projectName);
|
|
1839
|
+
scripts.preview = "bunx wrangler@4 pages dev dist";
|
|
1840
|
+
scripts.deploy = `bunx wrangler@4 pages deploy --project-name=${name}`;
|
|
1841
|
+
}
|
|
1752
1842
|
const pkg = {
|
|
1753
1843
|
name: config.projectName,
|
|
1754
1844
|
type: "module",
|
|
1755
1845
|
private: true,
|
|
1756
|
-
scripts
|
|
1757
|
-
dev: "bunx --bun vite dev",
|
|
1758
|
-
build: "node build.mjs",
|
|
1759
|
-
preview: "node .output/server/index.mjs"
|
|
1760
|
-
},
|
|
1846
|
+
scripts,
|
|
1761
1847
|
dependencies,
|
|
1762
1848
|
devDependencies
|
|
1763
1849
|
};
|
|
@@ -1844,7 +1930,8 @@ function generatePostBuildMjs() {
|
|
|
1844
1930
|
` * Post-build script — delegates to Avalon's built-in post-build.`,
|
|
1845
1931
|
` *`,
|
|
1846
1932
|
` * All the heavy lifting (CSS patching, island redirects, prerendering,`,
|
|
1847
|
-
` * Netlify function copying) is handled by
|
|
1933
|
+
` * Cloudflare worker patches, Netlify function copying) is handled by`,
|
|
1934
|
+
` * the framework.`,
|
|
1848
1935
|
` */`,
|
|
1849
1936
|
`import { runPostBuild } from '@useavalon/avalon/post-build';`,
|
|
1850
1937
|
``,
|
|
@@ -2180,6 +2267,7 @@ function generateViteConfig(config) {
|
|
|
2180
2267
|
` { schedule: '${EXAMPLE_CRON_SCHEDULE}', handler: '${EXAMPLE_CRON_HANDLER}' },`,
|
|
2181
2268
|
` ],`
|
|
2182
2269
|
] : [];
|
|
2270
|
+
const compatLines = config.deploy === "cloudflare" ? [` compatibilityDate: '2026-09-04',`] : [];
|
|
2183
2271
|
const lines = [
|
|
2184
2272
|
imports.join(`
|
|
2185
2273
|
`),
|
|
@@ -2194,6 +2282,7 @@ function generateViteConfig(config) {
|
|
|
2194
2282
|
` nitro: {`,
|
|
2195
2283
|
` preset: process.env.NITRO_PRESET || 'node_server',`,
|
|
2196
2284
|
` streaming: true,`,
|
|
2285
|
+
...compatLines,
|
|
2197
2286
|
` clientEntry: 'app/entry-client',`,
|
|
2198
2287
|
` globalCSS: ['app/shared/styles/main.css'],`,
|
|
2199
2288
|
` prerender: {`,
|
|
@@ -2338,9 +2427,15 @@ async function scaffoldProject(config, targetDir) {
|
|
|
2338
2427
|
`));
|
|
2339
2428
|
await writeFile(join(targetDir, "build.mjs"), generateBuildMjs());
|
|
2340
2429
|
await writeFile(join(targetDir, "post-build.mjs"), generatePostBuildMjs());
|
|
2430
|
+
await writeFile(join(targetDir, ".gitignore"), generateGitignore(config));
|
|
2431
|
+
await writeFile(join(targetDir, "DEPLOY.md"), generateDeployReadme(config));
|
|
2341
2432
|
if (config.deploy === "netlify") {
|
|
2342
2433
|
await writeFile(join(targetDir, "netlify.toml"), generateNetlifyToml(config));
|
|
2343
2434
|
}
|
|
2435
|
+
if (config.deploy === "cloudflare") {
|
|
2436
|
+
await writeFile(join(targetDir, "wrangler.toml"), generateWranglerToml(config));
|
|
2437
|
+
await writeFile(join(targetDir, "public/_headers"), generateCloudflareHeaders());
|
|
2438
|
+
}
|
|
2344
2439
|
}
|
|
2345
2440
|
|
|
2346
2441
|
// src/summary.ts
|
|
@@ -2350,15 +2445,34 @@ var STYLING_LABELS = {
|
|
|
2350
2445
|
shadcn: "shadcn"
|
|
2351
2446
|
};
|
|
2352
2447
|
var DEPLOY_LABELS = {
|
|
2448
|
+
cloudflare: "Cloudflare Pages",
|
|
2353
2449
|
netlify: "Netlify",
|
|
2354
2450
|
none: "None"
|
|
2355
2451
|
};
|
|
2452
|
+
function deployNextSteps(config) {
|
|
2453
|
+
if (config.deploy === "cloudflare") {
|
|
2454
|
+
return [
|
|
2455
|
+
" bun run build",
|
|
2456
|
+
" bun run preview # wrangler pages dev",
|
|
2457
|
+
" # See DEPLOY.md for Cloudflare Pages deploy steps"
|
|
2458
|
+
];
|
|
2459
|
+
}
|
|
2460
|
+
if (config.deploy === "netlify") {
|
|
2461
|
+
return [" bun run build", " # Connect the repo in Netlify, or see DEPLOY.md"];
|
|
2462
|
+
}
|
|
2463
|
+
return [" bun run build"];
|
|
2464
|
+
}
|
|
2356
2465
|
function formatSummary(config, scaffoldedInPlace = false) {
|
|
2357
2466
|
const integrations = config.integrations.length > 0 ? config.integrations.join(", ") : "none";
|
|
2358
2467
|
const styling = STYLING_LABELS[config.styling] ?? config.styling;
|
|
2359
2468
|
const plugins = config.plugins.length > 0 ? config.plugins.join(", ") : "none";
|
|
2360
2469
|
const deploy = DEPLOY_LABELS[config.deploy] ?? config.deploy;
|
|
2361
|
-
const nextSteps = scaffoldedInPlace ? [" bun install", " bun run dev"] : [
|
|
2470
|
+
const nextSteps = scaffoldedInPlace ? [" bun install", " bun run dev", ...deployNextSteps(config)] : [
|
|
2471
|
+
` cd ${config.projectName}`,
|
|
2472
|
+
" bun install",
|
|
2473
|
+
" bun run dev",
|
|
2474
|
+
...deployNextSteps(config)
|
|
2475
|
+
];
|
|
2362
2476
|
return [
|
|
2363
2477
|
"",
|
|
2364
2478
|
` Project: ${config.projectName}`,
|
|
@@ -2366,7 +2480,7 @@ function formatSummary(config, scaffoldedInPlace = false) {
|
|
|
2366
2480
|
` Styling: ${styling}`,
|
|
2367
2481
|
` Plugins: ${plugins}`,
|
|
2368
2482
|
` Middleware: ${config.middleware}`,
|
|
2369
|
-
` Deploy:
|
|
2483
|
+
` Deploy: ${deploy}`,
|
|
2370
2484
|
` Cron: ${config.cron ? "yes" : "no"}`,
|
|
2371
2485
|
"",
|
|
2372
2486
|
" Next steps:",
|
|
@@ -2404,7 +2518,7 @@ async function main() {
|
|
|
2404
2518
|
" --styling css-modules (default) | tailwind | shadcn",
|
|
2405
2519
|
" --plugins Comma list: seo (default),agent-optimization,syntax-highlighting",
|
|
2406
2520
|
" --middleware h3 (default) | hono | elysia",
|
|
2407
|
-
" --deploy netlify | none (default)",
|
|
2521
|
+
" --deploy cloudflare | netlify | none (default)",
|
|
2408
2522
|
" --cron Scaffold an example cron task + config",
|
|
2409
2523
|
"",
|
|
2410
2524
|
"Example:",
|