create-avalon 0.1.23 → 0.1.25
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 +2 -0
- package/dist/cli.js +430 -57
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ The CLI walks you through:
|
|
|
22
22
|
- Framework selection (React, Preact, Vue, Svelte, Solid, Lit, Qwik — or multiple)
|
|
23
23
|
- Styling approach (CSS Modules, Tailwind, vanilla CSS)
|
|
24
24
|
- Optional features (API routes, middleware, layouts, MDX)
|
|
25
|
+
- Scheduled jobs (cron) — scaffolds an example task and wires up `nitro.cron`
|
|
25
26
|
- Package manager preference
|
|
26
27
|
|
|
27
28
|
## What you get
|
|
@@ -43,6 +44,7 @@ my-project/
|
|
|
43
44
|
├── middleware/ # Server middleware
|
|
44
45
|
├── routes/
|
|
45
46
|
│ └── api/ # API routes
|
|
47
|
+
├── tasks/ # Scheduled jobs (cron) — optional
|
|
46
48
|
├── server/ # Server config & env
|
|
47
49
|
├── public/ # Static assets
|
|
48
50
|
├── vite.config.ts
|
package/dist/cli.js
CHANGED
|
@@ -76,12 +76,65 @@ var require_src = __commonJS((exports, module) => {
|
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
// src/cli.ts
|
|
79
|
-
import { basename, resolve } from "node:path";
|
|
80
79
|
import { createRequire } from "node:module";
|
|
80
|
+
import { basename, resolve } from "node:path";
|
|
81
81
|
|
|
82
82
|
// src/cli-utils.ts
|
|
83
|
-
import { parseArgs } from "node:util";
|
|
84
83
|
import { existsSync, readdirSync } from "node:fs";
|
|
84
|
+
import { parseArgs } from "node:util";
|
|
85
|
+
|
|
86
|
+
// src/types.ts
|
|
87
|
+
var RENDER_ENGINES = ["preact", "react"];
|
|
88
|
+
var INTEGRATIONS = [
|
|
89
|
+
"preact",
|
|
90
|
+
"react",
|
|
91
|
+
"vue",
|
|
92
|
+
"svelte",
|
|
93
|
+
"solid",
|
|
94
|
+
"lit",
|
|
95
|
+
"qwik"
|
|
96
|
+
];
|
|
97
|
+
var STYLING_OPTIONS = [
|
|
98
|
+
"css-modules",
|
|
99
|
+
"tailwind",
|
|
100
|
+
"shadcn"
|
|
101
|
+
];
|
|
102
|
+
var PLUGINS = [
|
|
103
|
+
"seo",
|
|
104
|
+
"agent-optimization",
|
|
105
|
+
"syntax-highlighting"
|
|
106
|
+
];
|
|
107
|
+
var MIDDLEWARE_OPTIONS = [
|
|
108
|
+
"h3",
|
|
109
|
+
"hono",
|
|
110
|
+
"elysia"
|
|
111
|
+
];
|
|
112
|
+
var DEPLOY_TARGETS = ["netlify", "none"];
|
|
113
|
+
var INTEGRATION_PACKAGES = {
|
|
114
|
+
preact: "@useavalon/preact",
|
|
115
|
+
react: "@useavalon/react",
|
|
116
|
+
vue: "@useavalon/vue",
|
|
117
|
+
svelte: "@useavalon/svelte",
|
|
118
|
+
solid: "@useavalon/solid",
|
|
119
|
+
lit: "@useavalon/lit",
|
|
120
|
+
qwik: "@useavalon/qwik"
|
|
121
|
+
};
|
|
122
|
+
var BASE_DIRS = [
|
|
123
|
+
"app/modules/main/pages",
|
|
124
|
+
"app/modules/main/components",
|
|
125
|
+
"app/modules/main/layouts",
|
|
126
|
+
"app/shared/layouts",
|
|
127
|
+
"app/shared/components",
|
|
128
|
+
"app/shared/styles",
|
|
129
|
+
"middleware",
|
|
130
|
+
"routes/api",
|
|
131
|
+
"public",
|
|
132
|
+
"server"
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
// src/cli-utils.ts
|
|
136
|
+
class CliArgError extends Error {
|
|
137
|
+
}
|
|
85
138
|
function validateDirectory(dir) {
|
|
86
139
|
if (!existsSync(dir)) {
|
|
87
140
|
return { valid: true };
|
|
@@ -100,7 +153,15 @@ function parseCliArgs(argv) {
|
|
|
100
153
|
args: argv,
|
|
101
154
|
options: {
|
|
102
155
|
help: { type: "boolean", default: false, short: "h" },
|
|
103
|
-
version: { type: "boolean", default: false, short: "v" }
|
|
156
|
+
version: { type: "boolean", default: false, short: "v" },
|
|
157
|
+
yes: { type: "boolean", default: false, short: "y" },
|
|
158
|
+
core: { type: "string" },
|
|
159
|
+
integrations: { type: "string" },
|
|
160
|
+
styling: { type: "string" },
|
|
161
|
+
plugins: { type: "string" },
|
|
162
|
+
middleware: { type: "string" },
|
|
163
|
+
deploy: { type: "string" },
|
|
164
|
+
cron: { type: "boolean", default: false }
|
|
104
165
|
},
|
|
105
166
|
strict: true,
|
|
106
167
|
allowPositionals: true
|
|
@@ -108,7 +169,58 @@ function parseCliArgs(argv) {
|
|
|
108
169
|
return {
|
|
109
170
|
projectName: positionals[0] ?? undefined,
|
|
110
171
|
help: values.help ?? false,
|
|
111
|
-
version: values.version ?? false
|
|
172
|
+
version: values.version ?? false,
|
|
173
|
+
yes: values.yes ?? false,
|
|
174
|
+
core: values.core,
|
|
175
|
+
integrations: values.integrations,
|
|
176
|
+
styling: values.styling,
|
|
177
|
+
plugins: values.plugins,
|
|
178
|
+
middleware: values.middleware,
|
|
179
|
+
deploy: values.deploy,
|
|
180
|
+
cron: values.cron ?? false
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
var csv = (value) => value ? value.split(",").map((v) => v.trim()).filter(Boolean) : undefined;
|
|
184
|
+
function assertOneOf(value, allowed, flag) {
|
|
185
|
+
if (value === undefined)
|
|
186
|
+
return;
|
|
187
|
+
if (!allowed.includes(value)) {
|
|
188
|
+
throw new CliArgError(`Invalid value "${value}" for --${flag}. Allowed: ${allowed.join(", ")}.`);
|
|
189
|
+
}
|
|
190
|
+
return value;
|
|
191
|
+
}
|
|
192
|
+
function assertAllOf(values, allowed, flag) {
|
|
193
|
+
if (values === undefined)
|
|
194
|
+
return;
|
|
195
|
+
for (const value of values) {
|
|
196
|
+
if (!allowed.includes(value)) {
|
|
197
|
+
throw new CliArgError(`Invalid value "${value}" for --${flag}. Allowed: ${allowed.join(", ")}.`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return values;
|
|
201
|
+
}
|
|
202
|
+
function resolveConfigNonInteractive(args) {
|
|
203
|
+
const core = assertOneOf(args.core, RENDER_ENGINES, "core") ?? "preact";
|
|
204
|
+
const integrations = assertAllOf(csv(args.integrations), INTEGRATIONS, "integrations") ?? [];
|
|
205
|
+
const styling = assertOneOf(args.styling, STYLING_OPTIONS, "styling") ?? "css-modules";
|
|
206
|
+
const plugins = assertAllOf(csv(args.plugins), PLUGINS, "plugins") ?? ["seo"];
|
|
207
|
+
const middleware = assertOneOf(args.middleware, MIDDLEWARE_OPTIONS, "middleware") ?? "h3";
|
|
208
|
+
const deploy = assertOneOf(args.deploy, DEPLOY_TARGETS, "deploy") ?? "none";
|
|
209
|
+
if (styling === "shadcn" && core !== "react") {
|
|
210
|
+
throw new CliArgError("--styling=shadcn requires --core=react (shadcn is Radix/React based).");
|
|
211
|
+
}
|
|
212
|
+
if (core === "react" && !integrations.includes("react")) {
|
|
213
|
+
integrations.push("react");
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
projectName: args.projectName ?? ".",
|
|
217
|
+
core,
|
|
218
|
+
integrations,
|
|
219
|
+
styling,
|
|
220
|
+
plugins,
|
|
221
|
+
middleware,
|
|
222
|
+
deploy,
|
|
223
|
+
cron: args.cron
|
|
112
224
|
};
|
|
113
225
|
}
|
|
114
226
|
|
|
@@ -543,6 +655,25 @@ class Vt extends B {
|
|
|
543
655
|
}
|
|
544
656
|
}
|
|
545
657
|
}
|
|
658
|
+
|
|
659
|
+
class kt extends B {
|
|
660
|
+
get cursor() {
|
|
661
|
+
return this.value ? 0 : 1;
|
|
662
|
+
}
|
|
663
|
+
get _value() {
|
|
664
|
+
return this.cursor === 0;
|
|
665
|
+
}
|
|
666
|
+
constructor(e) {
|
|
667
|
+
super(e, false), this.value = !!e.initialValue, this.on("userInput", () => {
|
|
668
|
+
this.value = this._value;
|
|
669
|
+
}), this.on("confirm", (s) => {
|
|
670
|
+
this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = s, this.state = "submit", this.close();
|
|
671
|
+
}), this.on("cursor", () => {
|
|
672
|
+
this.value = !this.value;
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
546
677
|
class yt extends B {
|
|
547
678
|
options;
|
|
548
679
|
cursor = 0;
|
|
@@ -961,6 +1092,33 @@ var X2 = ({ cursor: e, options: r, style: s, output: i = process.stdout, maxItem
|
|
|
961
1092
|
C.push(b);
|
|
962
1093
|
return $ && C.push(c), C;
|
|
963
1094
|
};
|
|
1095
|
+
var Rt = (e) => {
|
|
1096
|
+
const r = e.active ?? "Yes", s = e.inactive ?? "No";
|
|
1097
|
+
return new kt({ active: r, inactive: s, signal: e.signal, input: e.input, output: e.output, initialValue: e.initialValue ?? true, render() {
|
|
1098
|
+
const i = e.withGuide ?? _.withGuide, a = `${i ? `${t("gray", h)}
|
|
1099
|
+
` : ""}${W2(this.state)} ${e.message}
|
|
1100
|
+
`, o = this.value ? r : s;
|
|
1101
|
+
switch (this.state) {
|
|
1102
|
+
case "submit": {
|
|
1103
|
+
const u = i ? `${t("gray", h)} ` : "";
|
|
1104
|
+
return `${a}${u}${t("dim", o)}`;
|
|
1105
|
+
}
|
|
1106
|
+
case "cancel": {
|
|
1107
|
+
const u = i ? `${t("gray", h)} ` : "";
|
|
1108
|
+
return `${a}${u}${t(["strikethrough", "dim"], o)}${i ? `
|
|
1109
|
+
${t("gray", h)}` : ""}`;
|
|
1110
|
+
}
|
|
1111
|
+
default: {
|
|
1112
|
+
const u = i ? `${t("cyan", h)} ` : "", l = i ? t("cyan", x2) : "";
|
|
1113
|
+
return `${a}${u}${this.value ? `${t("green", z2)} ${r}` : `${t("dim", H2)} ${t("dim", r)}`}${e.vertical ? i ? `
|
|
1114
|
+
${t("cyan", h)} ` : `
|
|
1115
|
+
` : ` ${t("dim", "/")} `}${this.value ? `${t("dim", H2)} ${t("dim", s)}` : `${t("green", z2)} ${s}`}
|
|
1116
|
+
${l}
|
|
1117
|
+
`;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
} }).prompt();
|
|
1121
|
+
};
|
|
964
1122
|
var Nt = (e = "", r) => {
|
|
965
1123
|
const s = r?.output ?? process.stdout, i = r?.withGuide ?? _.withGuide ? `${t("gray", x2)} ` : "";
|
|
966
1124
|
s.write(`${i}${t("red", e)}
|
|
@@ -1124,6 +1282,26 @@ async function collectProjectConfig(initialName) {
|
|
|
1124
1282
|
}
|
|
1125
1283
|
projectName = nameResult;
|
|
1126
1284
|
}
|
|
1285
|
+
const coreResult = await Jt({
|
|
1286
|
+
message: "Which rendering engine should render your pages (the shell)?",
|
|
1287
|
+
options: [
|
|
1288
|
+
{
|
|
1289
|
+
value: "preact",
|
|
1290
|
+
label: "Preact",
|
|
1291
|
+
hint: "Smallest runtime (default). React libs run via preact/compat."
|
|
1292
|
+
},
|
|
1293
|
+
{
|
|
1294
|
+
value: "react",
|
|
1295
|
+
label: "React",
|
|
1296
|
+
hint: "Real react-dom/server. React libs like Radix/shadcn work natively."
|
|
1297
|
+
}
|
|
1298
|
+
],
|
|
1299
|
+
initialValue: "preact"
|
|
1300
|
+
});
|
|
1301
|
+
if (Ct(coreResult)) {
|
|
1302
|
+
Nt("Operation cancelled.");
|
|
1303
|
+
process.exit(1);
|
|
1304
|
+
}
|
|
1127
1305
|
const integrationsResult = await Lt2({
|
|
1128
1306
|
message: "Which integrations would you like to include? (use space to toggle, enter to confirm)",
|
|
1129
1307
|
options: [
|
|
@@ -1141,13 +1319,20 @@ async function collectProjectConfig(initialName) {
|
|
|
1141
1319
|
Nt("Operation cancelled.");
|
|
1142
1320
|
process.exit(1);
|
|
1143
1321
|
}
|
|
1322
|
+
const stylingOptions = [
|
|
1323
|
+
{ value: "css-modules", label: "CSS Modules" },
|
|
1324
|
+
{ value: "tailwind", label: "Tailwind CSS" }
|
|
1325
|
+
];
|
|
1326
|
+
if (coreResult === "react") {
|
|
1327
|
+
stylingOptions.push({
|
|
1328
|
+
value: "shadcn",
|
|
1329
|
+
label: "shadcn",
|
|
1330
|
+
hint: "Radix-based components — requires the React engine"
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1144
1333
|
const stylingResult = await Jt({
|
|
1145
1334
|
message: "Which styling approach would you like to use?",
|
|
1146
|
-
options:
|
|
1147
|
-
{ value: "css-modules", label: "CSS Modules" },
|
|
1148
|
-
{ value: "tailwind", label: "Tailwind CSS" },
|
|
1149
|
-
{ value: "shadcn", label: "shadcn" }
|
|
1150
|
-
]
|
|
1335
|
+
options: stylingOptions
|
|
1151
1336
|
});
|
|
1152
1337
|
if (Ct(stylingResult)) {
|
|
1153
1338
|
Nt("Operation cancelled.");
|
|
@@ -1156,13 +1341,23 @@ async function collectProjectConfig(initialName) {
|
|
|
1156
1341
|
const pluginsResult = await Lt2({
|
|
1157
1342
|
message: "Which plugins would you like to include? (use space to toggle, enter to confirm)",
|
|
1158
1343
|
options: [
|
|
1159
|
-
{
|
|
1344
|
+
{
|
|
1345
|
+
value: "seo",
|
|
1346
|
+
label: "seo",
|
|
1347
|
+
hint: "Auto-injects OG, Twitter cards, JSON-LD, canonical URLs"
|
|
1348
|
+
},
|
|
1349
|
+
{
|
|
1350
|
+
value: "agent-optimization",
|
|
1351
|
+
label: "agent-optimization",
|
|
1352
|
+
hint: "LLM/AI optimization (llms.txt, markdown, sitemap)"
|
|
1353
|
+
},
|
|
1160
1354
|
{
|
|
1161
1355
|
value: "syntax-highlighting",
|
|
1162
1356
|
label: "syntax-highlighting",
|
|
1163
1357
|
hint: "Code block highlighting for MDX (rehype-highlight)"
|
|
1164
1358
|
}
|
|
1165
1359
|
],
|
|
1360
|
+
initialValues: ["seo"],
|
|
1166
1361
|
required: false
|
|
1167
1362
|
});
|
|
1168
1363
|
if (Ct(pluginsResult)) {
|
|
@@ -1184,7 +1379,11 @@ async function collectProjectConfig(initialName) {
|
|
|
1184
1379
|
const deployResult = await Jt({
|
|
1185
1380
|
message: "Where will you deploy?",
|
|
1186
1381
|
options: [
|
|
1187
|
-
{
|
|
1382
|
+
{
|
|
1383
|
+
value: "netlify",
|
|
1384
|
+
label: "Netlify",
|
|
1385
|
+
hint: "Generates netlify.toml, build.mjs, post-build.mjs"
|
|
1386
|
+
},
|
|
1188
1387
|
{ value: "none", label: "None / Other", hint: "Node server preset, no deploy config" }
|
|
1189
1388
|
]
|
|
1190
1389
|
});
|
|
@@ -1192,13 +1391,28 @@ async function collectProjectConfig(initialName) {
|
|
|
1192
1391
|
Nt("Operation cancelled.");
|
|
1193
1392
|
process.exit(1);
|
|
1194
1393
|
}
|
|
1394
|
+
const cronResult = await Rt({
|
|
1395
|
+
message: "Set up scheduled jobs (cron)?",
|
|
1396
|
+
initialValue: false
|
|
1397
|
+
});
|
|
1398
|
+
if (Ct(cronResult)) {
|
|
1399
|
+
Nt("Operation cancelled.");
|
|
1400
|
+
process.exit(1);
|
|
1401
|
+
}
|
|
1402
|
+
const core = coreResult;
|
|
1403
|
+
const integrations = integrationsResult;
|
|
1404
|
+
if (core === "react" && !integrations.includes("react")) {
|
|
1405
|
+
integrations.push("react");
|
|
1406
|
+
}
|
|
1195
1407
|
return {
|
|
1196
1408
|
projectName,
|
|
1197
|
-
|
|
1409
|
+
core,
|
|
1410
|
+
integrations,
|
|
1198
1411
|
styling: stylingResult,
|
|
1199
1412
|
plugins: pluginsResult,
|
|
1200
1413
|
middleware: middlewareResult,
|
|
1201
|
-
deploy: deployResult
|
|
1414
|
+
deploy: deployResult,
|
|
1415
|
+
cron: cronResult
|
|
1202
1416
|
};
|
|
1203
1417
|
}
|
|
1204
1418
|
|
|
@@ -1216,6 +1430,31 @@ export default defineHandler(() => {
|
|
|
1216
1430
|
`;
|
|
1217
1431
|
}
|
|
1218
1432
|
|
|
1433
|
+
// src/templates/cron.ts
|
|
1434
|
+
var EXAMPLE_CRON_HANDLER = "tasks/cleanup.ts";
|
|
1435
|
+
var EXAMPLE_CRON_SCHEDULE = "0 * * * *";
|
|
1436
|
+
function generateExampleCronTask(_config) {
|
|
1437
|
+
return `import { defineCronJob } from '@useavalon/avalon/cron';
|
|
1438
|
+
|
|
1439
|
+
/**
|
|
1440
|
+
* Example scheduled job. Runs on the schedule defined in vite.config.ts
|
|
1441
|
+
* (\`nitro.cron\`). The task name is derived from this file path: "cleanup".
|
|
1442
|
+
*
|
|
1443
|
+
* In production this runs via your deployment preset's scheduler
|
|
1444
|
+
* (Vercel Cron, Cloudflare Triggers, or the Node server's in-process
|
|
1445
|
+
* scheduler). In development Avalon runs it inside the Vite dev server.
|
|
1446
|
+
*/
|
|
1447
|
+
export default defineCronJob({
|
|
1448
|
+
meta: { description: 'Example scheduled job' },
|
|
1449
|
+
async run() {
|
|
1450
|
+
console.log('[cron] cleanup ran at', new Date().toISOString());
|
|
1451
|
+
// TODO: replace with your scheduled work.
|
|
1452
|
+
return { result: 'ok' };
|
|
1453
|
+
},
|
|
1454
|
+
});
|
|
1455
|
+
`;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1219
1458
|
// src/templates/deploy.ts
|
|
1220
1459
|
function generateNetlifyToml(_config) {
|
|
1221
1460
|
return `[build]
|
|
@@ -1335,6 +1574,62 @@ const absoluteTimeout = setTimeout(() => {
|
|
|
1335
1574
|
}, 240_000);
|
|
1336
1575
|
`;
|
|
1337
1576
|
}
|
|
1577
|
+
function generateRobotsTxt(sitemapUrl = "https://YOUR_DOMAIN/sitemap.xml") {
|
|
1578
|
+
return `# robots.txt
|
|
1579
|
+
|
|
1580
|
+
User-agent: *
|
|
1581
|
+
Allow: /
|
|
1582
|
+
|
|
1583
|
+
# Sitemap
|
|
1584
|
+
Sitemap: ${sitemapUrl}
|
|
1585
|
+
|
|
1586
|
+
# AI Crawlers — explicitly allowed
|
|
1587
|
+
User-agent: GPTBot
|
|
1588
|
+
Allow: /
|
|
1589
|
+
|
|
1590
|
+
User-agent: ChatGPT-User
|
|
1591
|
+
Allow: /
|
|
1592
|
+
|
|
1593
|
+
User-agent: Google-Extended
|
|
1594
|
+
Allow: /
|
|
1595
|
+
|
|
1596
|
+
User-agent: PerplexityBot
|
|
1597
|
+
Allow: /
|
|
1598
|
+
|
|
1599
|
+
User-agent: OAI-SearchBot
|
|
1600
|
+
Allow: /
|
|
1601
|
+
|
|
1602
|
+
User-agent: Applebot-Extended
|
|
1603
|
+
Allow: /
|
|
1604
|
+
|
|
1605
|
+
User-agent: Amazonbot
|
|
1606
|
+
Allow: /
|
|
1607
|
+
|
|
1608
|
+
User-agent: ClaudeBot
|
|
1609
|
+
Allow: /
|
|
1610
|
+
|
|
1611
|
+
User-agent: Bytespider
|
|
1612
|
+
Allow: /
|
|
1613
|
+
|
|
1614
|
+
User-agent: cohere-ai
|
|
1615
|
+
Allow: /
|
|
1616
|
+
|
|
1617
|
+
User-agent: Diffbot
|
|
1618
|
+
Allow: /
|
|
1619
|
+
|
|
1620
|
+
User-agent: anthropic-ai
|
|
1621
|
+
Allow: /
|
|
1622
|
+
|
|
1623
|
+
User-agent: Claude-Web
|
|
1624
|
+
Allow: /
|
|
1625
|
+
|
|
1626
|
+
User-agent: CCBot
|
|
1627
|
+
Allow: /
|
|
1628
|
+
|
|
1629
|
+
User-agent: AI2Bot
|
|
1630
|
+
Allow: /
|
|
1631
|
+
`;
|
|
1632
|
+
}
|
|
1338
1633
|
|
|
1339
1634
|
// src/templates/favicon.ts
|
|
1340
1635
|
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=";
|
|
@@ -1351,16 +1646,21 @@ function generateRootLayout(config) {
|
|
|
1351
1646
|
} else {
|
|
1352
1647
|
imports.push(`import '../styles/main.css';`);
|
|
1353
1648
|
}
|
|
1649
|
+
const safeName = config.projectName.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
1354
1650
|
return `${imports.join(`
|
|
1355
1651
|
`)}
|
|
1356
1652
|
|
|
1357
|
-
export default async function RootLayout({ children }: Readonly<LayoutProps>) {
|
|
1653
|
+
export default async function RootLayout({ children, frontmatter }: Readonly<LayoutProps>) {
|
|
1654
|
+
const title = frontmatter?.title ?? '${safeName}';
|
|
1655
|
+
const description = frontmatter?.description ?? '';
|
|
1656
|
+
|
|
1358
1657
|
return (
|
|
1359
1658
|
<html lang="en">
|
|
1360
1659
|
<head>
|
|
1361
1660
|
<meta charset="UTF-8" />
|
|
1362
1661
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1363
|
-
<title
|
|
1662
|
+
<title>{title}</title>
|
|
1663
|
+
{description && <meta name="description" content={description} />}
|
|
1364
1664
|
<link rel="icon" href="/favicon.ico" />
|
|
1365
1665
|
</head>
|
|
1366
1666
|
<body style={{ margin: 0 }}>
|
|
@@ -1371,10 +1671,10 @@ export default async function RootLayout({ children }: Readonly<LayoutProps>) {
|
|
|
1371
1671
|
}
|
|
1372
1672
|
`;
|
|
1373
1673
|
}
|
|
1374
|
-
function
|
|
1674
|
+
function generateMainLayout(config) {
|
|
1375
1675
|
return `import type { LayoutProps } from '@useavalon/avalon';
|
|
1376
1676
|
|
|
1377
|
-
export default async function
|
|
1677
|
+
export default async function MainLayout({ children }: Readonly<LayoutProps>) {
|
|
1378
1678
|
return <>{children}</>;
|
|
1379
1679
|
}
|
|
1380
1680
|
`;
|
|
@@ -1390,36 +1690,31 @@ export default defineHandler((event) => {
|
|
|
1390
1690
|
`;
|
|
1391
1691
|
}
|
|
1392
1692
|
|
|
1393
|
-
// src/types.ts
|
|
1394
|
-
var INTEGRATION_PACKAGES = {
|
|
1395
|
-
preact: "@useavalon/preact",
|
|
1396
|
-
react: "@useavalon/react",
|
|
1397
|
-
vue: "@useavalon/vue",
|
|
1398
|
-
svelte: "@useavalon/svelte",
|
|
1399
|
-
solid: "@useavalon/solid",
|
|
1400
|
-
lit: "@useavalon/lit",
|
|
1401
|
-
qwik: "@useavalon/qwik"
|
|
1402
|
-
};
|
|
1403
|
-
var BASE_DIRS = [
|
|
1404
|
-
"app/modules/home/pages",
|
|
1405
|
-
"app/modules/home/components",
|
|
1406
|
-
"app/modules/home/layouts",
|
|
1407
|
-
"app/shared/layouts",
|
|
1408
|
-
"app/shared/components",
|
|
1409
|
-
"app/shared/styles",
|
|
1410
|
-
"middleware",
|
|
1411
|
-
"routes/api",
|
|
1412
|
-
"public",
|
|
1413
|
-
"server"
|
|
1414
|
-
];
|
|
1415
|
-
|
|
1416
1693
|
// src/templates/package-json.ts
|
|
1694
|
+
var INTEGRATION_RUNTIME_DEPS = {
|
|
1695
|
+
preact: { preact: "^10.0.0", "preact-render-to-string": "^6.0.0" },
|
|
1696
|
+
react: { react: "^19.0.0", "react-dom": "^19.0.0" },
|
|
1697
|
+
vue: { vue: "^3.4.0" },
|
|
1698
|
+
svelte: { svelte: "^5.0.0" },
|
|
1699
|
+
solid: { "solid-js": "^1.8.0" },
|
|
1700
|
+
lit: {
|
|
1701
|
+
lit: "^3.0.0",
|
|
1702
|
+
"@lit-labs/ssr": "^4.0.0",
|
|
1703
|
+
"@lit-labs/ssr-client": "^1.0.0",
|
|
1704
|
+
"@lit-labs/ssr-dom-shim": "^1.0.0"
|
|
1705
|
+
},
|
|
1706
|
+
qwik: { "@builder.io/qwik": "^1.5.0" }
|
|
1707
|
+
};
|
|
1417
1708
|
function generatePackageJson(config) {
|
|
1418
1709
|
const dependencies = {
|
|
1419
1710
|
"@useavalon/avalon": "latest"
|
|
1420
1711
|
};
|
|
1421
1712
|
for (const integration of config.integrations) {
|
|
1422
1713
|
dependencies[INTEGRATION_PACKAGES[integration]] = "latest";
|
|
1714
|
+
Object.assign(dependencies, INTEGRATION_RUNTIME_DEPS[integration]);
|
|
1715
|
+
}
|
|
1716
|
+
if (config.plugins.includes("seo")) {
|
|
1717
|
+
dependencies["@useavalon/seo"] = "latest";
|
|
1423
1718
|
}
|
|
1424
1719
|
if (config.plugins.includes("agent-optimization")) {
|
|
1425
1720
|
dependencies["@useavalon/agent-optimization"] = "latest";
|
|
@@ -1470,7 +1765,7 @@ function generatePackageJson(config) {
|
|
|
1470
1765
|
}
|
|
1471
1766
|
|
|
1472
1767
|
// src/templates/pages.ts
|
|
1473
|
-
function
|
|
1768
|
+
function generateMainPage(config) {
|
|
1474
1769
|
return `export const metadata = {
|
|
1475
1770
|
title: 'Avalon — Islands Architecture',
|
|
1476
1771
|
description: 'A multi-framework islands architecture project powered by Avalon.',
|
|
@@ -1508,7 +1803,7 @@ export default async function HomePage() {
|
|
|
1508
1803
|
<div style={{ position: 'relative', zIndex: 1, marginTop: '4rem', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '0.75rem' }}>
|
|
1509
1804
|
<p style={{ fontSize: '0.75rem', color: '#64748b', letterSpacing: '0.1em', textTransform: 'uppercase' }}>Get started</p>
|
|
1510
1805
|
<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' }}>
|
|
1511
|
-
Edit app/modules/
|
|
1806
|
+
Edit app/modules/main/pages/index.tsx
|
|
1512
1807
|
</code>
|
|
1513
1808
|
</div>
|
|
1514
1809
|
|
|
@@ -1523,6 +1818,24 @@ export default async function HomePage() {
|
|
|
1523
1818
|
}
|
|
1524
1819
|
`;
|
|
1525
1820
|
}
|
|
1821
|
+
function generate404Page() {
|
|
1822
|
+
return `export const metadata = {
|
|
1823
|
+
title: '404 - Page Not Found',
|
|
1824
|
+
description: 'The page you are looking for does not exist.',
|
|
1825
|
+
robots: 'noindex, nofollow',
|
|
1826
|
+
};
|
|
1827
|
+
|
|
1828
|
+
export default function NotFoundPage() {
|
|
1829
|
+
return (
|
|
1830
|
+
<div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', fontFamily: 'system-ui, -apple-system, sans-serif', textAlign: 'center', padding: '2rem' }}>
|
|
1831
|
+
<h1 style={{ fontSize: '4rem', margin: '0 0 1rem' }}>404</h1>
|
|
1832
|
+
<p style={{ fontSize: '1.25rem', color: '#64748b', margin: '0 0 2rem' }}>Page not found</p>
|
|
1833
|
+
<a href="/" style={{ color: '#6366f1', textDecoration: 'none', fontWeight: 500 }}>Go Home</a>
|
|
1834
|
+
</div>
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1837
|
+
`;
|
|
1838
|
+
}
|
|
1526
1839
|
|
|
1527
1840
|
// src/templates/post-build.ts
|
|
1528
1841
|
function generatePostBuildMjs() {
|
|
@@ -1558,8 +1871,8 @@ function generateStylingFiles(config) {
|
|
|
1558
1871
|
case "css-modules":
|
|
1559
1872
|
files.set("app/shared/styles/tokens.css", generateTokensCss());
|
|
1560
1873
|
files.set("app/shared/layouts/_layout.module.css", generateLayoutModuleCss());
|
|
1561
|
-
files.set("app/modules/
|
|
1562
|
-
files.set("app/modules/
|
|
1874
|
+
files.set("app/modules/main/pages/index.module.css", generatePageModuleCss());
|
|
1875
|
+
files.set("app/modules/main/layouts/_layout.module.css", generateLayoutModuleCss());
|
|
1563
1876
|
break;
|
|
1564
1877
|
case "tailwind":
|
|
1565
1878
|
files.set("tailwind.config.js", generateTailwindConfig());
|
|
@@ -1746,7 +2059,7 @@ function generateShadcnComponentsJson(config) {
|
|
|
1746
2059
|
}
|
|
1747
2060
|
|
|
1748
2061
|
// src/templates/tsconfig.ts
|
|
1749
|
-
function generateTsConfig() {
|
|
2062
|
+
function generateTsConfig(core = "preact") {
|
|
1750
2063
|
const tsconfig = {
|
|
1751
2064
|
compilerOptions: {
|
|
1752
2065
|
target: "ESNext",
|
|
@@ -1759,6 +2072,7 @@ function generateTsConfig() {
|
|
|
1759
2072
|
allowImportingTsExtensions: true,
|
|
1760
2073
|
noEmit: true,
|
|
1761
2074
|
jsx: "react-jsx",
|
|
2075
|
+
jsxImportSource: core,
|
|
1762
2076
|
paths: {
|
|
1763
2077
|
"@shared/*": ["./app/shared/*"],
|
|
1764
2078
|
"@modules/*": ["./app/modules/*"]
|
|
@@ -1819,20 +2133,37 @@ function generateViteConfig(config) {
|
|
|
1819
2133
|
if (needsTailwind) {
|
|
1820
2134
|
imports.push(`import tailwindcss from '@tailwindcss/vite';`);
|
|
1821
2135
|
}
|
|
2136
|
+
const hasSeo = config.plugins.includes("seo");
|
|
2137
|
+
if (hasSeo) {
|
|
2138
|
+
imports.push(`import { seo } from '@useavalon/seo';`);
|
|
2139
|
+
}
|
|
1822
2140
|
const hasAgentOptimization = config.plugins.includes("agent-optimization");
|
|
1823
2141
|
if (hasAgentOptimization) {
|
|
1824
2142
|
imports.push(`import { agentOptimization } from '@useavalon/agent-optimization';`);
|
|
1825
2143
|
}
|
|
1826
2144
|
const integrationsList = config.integrations.map((i) => `'${i}'`).join(", ");
|
|
1827
2145
|
const pluginEntries = [];
|
|
2146
|
+
if (hasSeo) {
|
|
2147
|
+
pluginEntries.push(` seo({
|
|
2148
|
+
siteUrl: 'http://localhost:3000',
|
|
2149
|
+
siteName: '${config.projectName.replace(/'/g, "\\'")}',
|
|
2150
|
+
defaultDescription: 'Built with Avalon',
|
|
2151
|
+
defaultOgImage: {
|
|
2152
|
+
url: '/og-image.png',
|
|
2153
|
+
width: 1200,
|
|
2154
|
+
height: 630,
|
|
2155
|
+
},
|
|
2156
|
+
breadcrumbs: true,
|
|
2157
|
+
speakable: true,
|
|
2158
|
+
}),`);
|
|
2159
|
+
}
|
|
1828
2160
|
if (hasAgentOptimization) {
|
|
1829
2161
|
pluginEntries.push(` agentOptimization({
|
|
1830
2162
|
sitemap: { siteUrl: 'http://localhost:3000' },
|
|
1831
2163
|
markdown: true,
|
|
1832
|
-
structuredData: true,
|
|
1833
2164
|
llms: {
|
|
1834
2165
|
siteUrl: 'http://localhost:3000',
|
|
1835
|
-
siteName: '
|
|
2166
|
+
siteName: '${config.projectName.replace(/'/g, "\\'")}',
|
|
1836
2167
|
siteDescription: 'Built with Avalon',
|
|
1837
2168
|
sections: { 'Pages': ['/'] },
|
|
1838
2169
|
},
|
|
@@ -1842,12 +2173,20 @@ function generateViteConfig(config) {
|
|
|
1842
2173
|
if (needsTailwind) {
|
|
1843
2174
|
pluginEntries.push(` tailwindcss(),`);
|
|
1844
2175
|
}
|
|
2176
|
+
const cronLines = config.cron ? [
|
|
2177
|
+
` // Scheduled jobs (cron). Each entry maps a schedule to a task file`,
|
|
2178
|
+
` // in tasks/. See https://useavalon.dev/docs/cron-jobs`,
|
|
2179
|
+
` cron: [`,
|
|
2180
|
+
` { schedule: '${EXAMPLE_CRON_SCHEDULE}', handler: '${EXAMPLE_CRON_HANDLER}' },`,
|
|
2181
|
+
` ],`
|
|
2182
|
+
] : [];
|
|
1845
2183
|
const lines = [
|
|
1846
2184
|
imports.join(`
|
|
1847
2185
|
`),
|
|
1848
2186
|
"",
|
|
1849
2187
|
`export default defineConfig(async (): Promise<UserConfig> => {`,
|
|
1850
2188
|
` const avalonPlugins = await avalon({`,
|
|
2189
|
+
` core: '${config.core}',`,
|
|
1851
2190
|
` integrations: [${integrationsList}],`,
|
|
1852
2191
|
` modules: 'app/modules',`,
|
|
1853
2192
|
` layoutsDir: 'app/shared/layouts',`,
|
|
@@ -1862,6 +2201,7 @@ function generateViteConfig(config) {
|
|
|
1862
2201
|
` crawlLinks: true,`,
|
|
1863
2202
|
` ignore: [],`,
|
|
1864
2203
|
` },`,
|
|
2204
|
+
...cronLines,
|
|
1865
2205
|
` },`,
|
|
1866
2206
|
` });`,
|
|
1867
2207
|
"",
|
|
@@ -1952,13 +2292,19 @@ async function scaffoldProject(config, targetDir) {
|
|
|
1952
2292
|
await mkdir(join(targetDir, dir), { recursive: true });
|
|
1953
2293
|
}
|
|
1954
2294
|
await writeFile(join(targetDir, "package.json"), generatePackageJson(config));
|
|
1955
|
-
await writeFile(join(targetDir, "tsconfig.json"), generateTsConfig());
|
|
2295
|
+
await writeFile(join(targetDir, "tsconfig.json"), generateTsConfig(config.core));
|
|
1956
2296
|
await writeFile(join(targetDir, "vite.config.ts"), generateViteConfig(config));
|
|
1957
2297
|
await writeFile(join(targetDir, "app/shared/layouts/_layout.tsx"), generateRootLayout(config));
|
|
1958
|
-
await writeFile(join(targetDir, "app/modules/
|
|
1959
|
-
await writeFile(join(targetDir, "app/modules/
|
|
2298
|
+
await writeFile(join(targetDir, "app/modules/main/layouts/_layout.tsx"), generateMainLayout(config));
|
|
2299
|
+
await writeFile(join(targetDir, "app/modules/main/pages/index.tsx"), generateMainPage(config));
|
|
2300
|
+
await writeFile(join(targetDir, "app/modules/main/pages/404.tsx"), generate404Page());
|
|
1960
2301
|
await writeFile(join(targetDir, "middleware/01.logger.ts"), generateSampleMiddleware(config));
|
|
1961
2302
|
await writeFile(join(targetDir, "routes/api/hello.ts"), generateHelloRoute(config));
|
|
2303
|
+
if (config.cron) {
|
|
2304
|
+
const cronPath = join(targetDir, EXAMPLE_CRON_HANDLER);
|
|
2305
|
+
await mkdir(dirname(cronPath), { recursive: true });
|
|
2306
|
+
await writeFile(cronPath, generateExampleCronTask(config));
|
|
2307
|
+
}
|
|
1962
2308
|
if (config.middleware === "hono") {
|
|
1963
2309
|
await writeFile(join(targetDir, "server.ts"), generateHonoServerEntry());
|
|
1964
2310
|
} else if (config.middleware === "elysia") {
|
|
@@ -1970,9 +2316,12 @@ async function scaffoldProject(config, targetDir) {
|
|
|
1970
2316
|
await writeFile(join(targetDir, filePath), content);
|
|
1971
2317
|
}
|
|
1972
2318
|
await writeFile(join(targetDir, "public/favicon.ico"), getFaviconBuffer());
|
|
2319
|
+
await writeFile(join(targetDir, "public/robots.txt"), generateRobotsTxt());
|
|
1973
2320
|
await writeFile(join(targetDir, "server/env.d.ts"), `/// <reference types="nitro" />
|
|
1974
2321
|
`);
|
|
1975
2322
|
await writeFile(join(targetDir, "app/env.d.ts"), generateEnvDts(config.integrations));
|
|
2323
|
+
await writeFile(join(targetDir, "app/entry-client.ts"), `import "virtual:avalon/client-entry";
|
|
2324
|
+
`);
|
|
1976
2325
|
await writeFile(join(targetDir, "server/renderer.ts"), [
|
|
1977
2326
|
`/**`,
|
|
1978
2327
|
` * SSR Renderer — provided by Avalon's virtual module system.`,
|
|
@@ -2018,6 +2367,7 @@ function formatSummary(config, scaffoldedInPlace = false) {
|
|
|
2018
2367
|
` Plugins: ${plugins}`,
|
|
2019
2368
|
` Middleware: ${config.middleware}`,
|
|
2020
2369
|
` Deploy: ${deploy}`,
|
|
2370
|
+
` Cron: ${config.cron ? "yes" : "no"}`,
|
|
2021
2371
|
"",
|
|
2022
2372
|
" Next steps:",
|
|
2023
2373
|
...nextSteps,
|
|
@@ -2039,11 +2389,28 @@ async function main() {
|
|
|
2039
2389
|
process.exit(0);
|
|
2040
2390
|
}
|
|
2041
2391
|
if (args.help) {
|
|
2042
|
-
console.log(
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2392
|
+
console.log([
|
|
2393
|
+
"Usage: create-avalon [project-name] [options]",
|
|
2394
|
+
"",
|
|
2395
|
+
"Runs interactively by default. Pass --yes (or run without a TTY, e.g. in",
|
|
2396
|
+
"CI/Docker) to scaffold non-interactively from flags + defaults.",
|
|
2397
|
+
"",
|
|
2398
|
+
"Options:",
|
|
2399
|
+
" -v, --version Show version number",
|
|
2400
|
+
" -h, --help Show help",
|
|
2401
|
+
" -y, --yes Skip prompts; use flags and defaults",
|
|
2402
|
+
" --core Rendering engine: preact (default) | react",
|
|
2403
|
+
" --integrations Comma list: preact,react,vue,svelte,solid,lit,qwik",
|
|
2404
|
+
" --styling css-modules (default) | tailwind | shadcn",
|
|
2405
|
+
" --plugins Comma list: seo (default),agent-optimization,syntax-highlighting",
|
|
2406
|
+
" --middleware h3 (default) | hono | elysia",
|
|
2407
|
+
" --deploy netlify | none (default)",
|
|
2408
|
+
" --cron Scaffold an example cron task + config",
|
|
2409
|
+
"",
|
|
2410
|
+
"Example:",
|
|
2411
|
+
" create-avalon my-app --yes --core react --integrations react,vue --styling shadcn"
|
|
2412
|
+
].join(`
|
|
2413
|
+
`));
|
|
2047
2414
|
process.exit(0);
|
|
2048
2415
|
}
|
|
2049
2416
|
if (args.projectName && args.projectName !== ".") {
|
|
@@ -2053,7 +2420,8 @@ Options:
|
|
|
2053
2420
|
process.exit(1);
|
|
2054
2421
|
}
|
|
2055
2422
|
}
|
|
2056
|
-
const
|
|
2423
|
+
const nonInteractive = args.yes || !process.stdin.isTTY;
|
|
2424
|
+
const config = nonInteractive ? resolveConfigNonInteractive(args) : await collectProjectConfig(args.projectName);
|
|
2057
2425
|
if (!args.projectName && config.projectName !== ".") {
|
|
2058
2426
|
const dirResult = validateDirectory(resolve(config.projectName));
|
|
2059
2427
|
if (!dirResult.valid) {
|
|
@@ -2070,4 +2438,9 @@ Options:
|
|
|
2070
2438
|
printSummary(config, scaffoldedInPlace);
|
|
2071
2439
|
process.exit(0);
|
|
2072
2440
|
}
|
|
2073
|
-
|
|
2441
|
+
try {
|
|
2442
|
+
await main();
|
|
2443
|
+
} catch (error) {
|
|
2444
|
+
console.error(error instanceof CliArgError ? error.message : error);
|
|
2445
|
+
process.exit(1);
|
|
2446
|
+
}
|