create-avalon 0.1.14 → 0.1.16

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.
Files changed (3) hide show
  1. package/README.md +59 -59
  2. package/dist/cli.js +359 -20
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,59 +1,59 @@
1
- # create-avalon
2
-
3
- Scaffold a new [Avalon](https://useavalon.dev) project in seconds.
4
-
5
- ## Usage
6
-
7
- ```bash
8
- npm create avalon@latest
9
- ```
10
-
11
- Or with other package managers:
12
-
13
- ```bash
14
- pnpm create avalon@latest
15
- yarn create avalon
16
- bun create avalon
17
- ```
18
-
19
- The CLI walks you through:
20
-
21
- - Project name and directory
22
- - Framework selection (React, Preact, Vue, Svelte, Solid, Lit, Qwik — or multiple)
23
- - Styling approach (CSS Modules, Tailwind, vanilla CSS)
24
- - Optional features (API routes, middleware, layouts, MDX)
25
- - Package manager preference
26
-
27
- ## What you get
28
-
29
- A ready-to-run Avalon project with file-system routing, islands architecture, and zero JavaScript by default.
30
-
31
- ```
32
- my-project/
33
- ├── app/
34
- │ ├── modules/
35
- │ │ └── home/
36
- │ │ ├── pages/ # File-system routes
37
- │ │ ├── components/ # Interactive components
38
- │ │ └── layouts/ # Module layouts
39
- │ └── shared/
40
- │ ├── layouts/ # Root layout
41
- │ ├── components/ # Shared components
42
- │ └── styles/ # Global styles & tokens
43
- ├── middleware/ # Server middleware
44
- ├── routes/
45
- │ └── api/ # API routes
46
- ├── server/ # Server config & env
47
- ├── public/ # Static assets
48
- ├── vite.config.ts
49
- └── package.json
50
- ```
51
-
52
- ## Links
53
-
54
- - [Documentation](https://useavalon.dev/docs/introduction)
55
- - [GitHub](https://github.com/useAvalon/Avalon)
56
-
57
- ## License
58
-
59
- MIT
1
+ # create-avalon
2
+
3
+ Scaffold a new [Avalon](https://useavalon.dev) project in seconds.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npm create avalon@latest
9
+ ```
10
+
11
+ Or with other package managers:
12
+
13
+ ```bash
14
+ pnpm create avalon@latest
15
+ yarn create avalon
16
+ bun create avalon
17
+ ```
18
+
19
+ The CLI walks you through:
20
+
21
+ - Project name and directory
22
+ - Framework selection (React, Preact, Vue, Svelte, Solid, Lit, Qwik — or multiple)
23
+ - Styling approach (CSS Modules, Tailwind, vanilla CSS)
24
+ - Optional features (API routes, middleware, layouts, MDX)
25
+ - Package manager preference
26
+
27
+ ## What you get
28
+
29
+ A ready-to-run Avalon project with file-system routing, islands architecture, and zero JavaScript by default.
30
+
31
+ ```
32
+ my-project/
33
+ ├── app/
34
+ │ ├── modules/
35
+ │ │ └── home/
36
+ │ │ ├── pages/ # File-system routes
37
+ │ │ ├── components/ # Interactive components
38
+ │ │ └── layouts/ # Module layouts
39
+ │ └── shared/
40
+ │ ├── layouts/ # Root layout
41
+ │ ├── components/ # Shared components
42
+ │ └── styles/ # Global styles & tokens
43
+ ├── middleware/ # Server middleware
44
+ ├── routes/
45
+ │ └── api/ # API routes
46
+ ├── server/ # Server config & env
47
+ ├── public/ # Static assets
48
+ ├── vite.config.ts
49
+ └── package.json
50
+ ```
51
+
52
+ ## Links
53
+
54
+ - [Documentation](https://useavalon.dev/docs/introduction)
55
+ - [GitHub](https://github.com/useAvalon/Avalon)
56
+
57
+ ## License
58
+
59
+ MIT
package/dist/cli.js CHANGED
@@ -1181,12 +1181,24 @@ async function collectProjectConfig(initialName) {
1181
1181
  Nt("Operation cancelled.");
1182
1182
  process.exit(1);
1183
1183
  }
1184
+ const deployResult = await Jt({
1185
+ message: "Where will you deploy?",
1186
+ options: [
1187
+ { value: "netlify", label: "Netlify", hint: "Generates netlify.toml, build.mjs, post-build.mjs" },
1188
+ { value: "none", label: "None / Other", hint: "Node server preset, no deploy config" }
1189
+ ]
1190
+ });
1191
+ if (Ct(deployResult)) {
1192
+ Nt("Operation cancelled.");
1193
+ process.exit(1);
1194
+ }
1184
1195
  return {
1185
1196
  projectName,
1186
1197
  integrations: integrationsResult,
1187
1198
  styling: stylingResult,
1188
1199
  plugins: pluginsResult,
1189
- middleware: middlewareResult
1200
+ middleware: middlewareResult,
1201
+ deploy: deployResult
1190
1202
  };
1191
1203
  }
1192
1204
 
@@ -1264,12 +1276,17 @@ function generatePackageJson(config) {
1264
1276
  private: true,
1265
1277
  scripts: {
1266
1278
  dev: "bunx --bun vite dev",
1267
- build: "bunx --bun vite build",
1268
- preview: "bunx --bun vite preview"
1279
+ build: config.deploy === "netlify" ? "bun build.mjs" : "bunx --bun vite build",
1280
+ "post-build": config.deploy === "netlify" ? "node post-build.mjs" : undefined,
1281
+ preview: "node .output/server/index.mjs"
1269
1282
  },
1270
1283
  dependencies,
1271
1284
  devDependencies
1272
1285
  };
1286
+ for (const key of Object.keys(pkg.scripts)) {
1287
+ if (pkg.scripts[key] === undefined)
1288
+ delete pkg.scripts[key];
1289
+ }
1273
1290
  return JSON.stringify(pkg, null, 2);
1274
1291
  }
1275
1292
 
@@ -1352,8 +1369,13 @@ function generateViteConfig(config) {
1352
1369
  ` layoutsDir: 'app/shared/layouts',`,
1353
1370
  ` image: true,`,
1354
1371
  ` nitro: {`,
1355
- ` preset: 'node_server',`,
1372
+ ` preset: process.env.NITRO_PRESET || 'node_server',`,
1356
1373
  ` streaming: true,`,
1374
+ ` prerender: {`,
1375
+ ` routes: ['/'],`,
1376
+ ` crawlLinks: true,`,
1377
+ ` ignore: [],`,
1378
+ ` },`,
1357
1379
  ` },`,
1358
1380
  ` });`,
1359
1381
  "",
@@ -1699,6 +1721,328 @@ function getFaviconBuffer() {
1699
1721
  return Buffer.from(FAVICON_BASE64, "base64");
1700
1722
  }
1701
1723
 
1724
+ // src/templates/deploy.ts
1725
+ function generateNetlifyToml(config) {
1726
+ return `[build]
1727
+ base = "."
1728
+ command = "bun install && bun build.mjs"
1729
+ publish = "dist"
1730
+
1731
+ [build.environment]
1732
+ NODE_VERSION = "22"
1733
+ BUN_VERSION = "latest"
1734
+ NITRO_PRESET = "netlify"
1735
+
1736
+ [functions]
1737
+ directory = "netlify/functions"
1738
+ `;
1739
+ }
1740
+ function generateBuildMjs() {
1741
+ return `/**
1742
+ * Netlify build wrapper.
1743
+ *
1744
+ * Vite/Nitro leaves open handles after the build completes, preventing
1745
+ * the Node process from exiting. This wrapper detects when the build
1746
+ * output is ready, kills the entire process group, then runs post-build.
1747
+ */
1748
+
1749
+ import { spawn, execSync } from 'node:child_process';
1750
+ import { existsSync, rmSync } from 'node:fs';
1751
+ import { join } from 'node:path';
1752
+
1753
+ const CWD = process.cwd();
1754
+ const NITRO_JSON = join(CWD, '.netlify', 'functions-internal', 'nitro.json');
1755
+ const SERVER_MJS = join(CWD, '.netlify', 'functions-internal', 'server', 'server.mjs');
1756
+ const OUTPUT_SSR = join(CWD, '.output', 'server', '_ssr', 'ssr.mjs');
1757
+
1758
+ console.log('[build] Starting vite build...');
1759
+
1760
+ for (const dir of ['.netlify', '.output', 'netlify']) {
1761
+ const full = join(CWD, dir);
1762
+ if (existsSync(full)) {
1763
+ rmSync(full, { recursive: true, force: true });
1764
+ console.log(\`[build] Cleaned stale \${dir}/\`);
1765
+ }
1766
+ }
1767
+
1768
+ const child = spawn('bunx', ['--bun', 'vite', 'build'], {
1769
+ cwd: CWD,
1770
+ stdio: 'inherit',
1771
+ detached: true,
1772
+ });
1773
+
1774
+ const childPid = child.pid;
1775
+ let done = false;
1776
+
1777
+ function killTree() {
1778
+ try { process.kill(-childPid, 'SIGKILL'); } catch {}
1779
+ try { child.kill('SIGKILL'); } catch {}
1780
+ }
1781
+
1782
+ function finish() {
1783
+ if (done) return;
1784
+ done = true;
1785
+ clearInterval(poll);
1786
+ clearTimeout(absoluteTimeout);
1787
+ killTree();
1788
+
1789
+ setTimeout(() => {
1790
+ console.log('[build] Running post-build...');
1791
+ try {
1792
+ execSync('node post-build.mjs', { cwd: CWD, stdio: 'inherit', timeout: 120_000 });
1793
+ } catch (err) {
1794
+ console.error('[build] post-build warning:', err.message);
1795
+ }
1796
+
1797
+ const V1_SERVER = join(CWD, '.netlify', 'v1', 'functions', 'server', 'server.mjs');
1798
+ if (existsSync(V1_SERVER)) console.log('[build] ✅ Server function found (v1 API)');
1799
+ else if (existsSync(SERVER_MJS)) console.log('[build] ✅ Server function found (legacy)');
1800
+ else if (existsSync(OUTPUT_SSR)) console.log('[build] ✅ SSR bundle found');
1801
+ else console.error('[build] ❌ No server output found');
1802
+
1803
+ console.log('[build] ✅ Complete');
1804
+ process.exit(0);
1805
+ }, 500);
1806
+ }
1807
+
1808
+ child.on('exit', (code) => {
1809
+ console.log(\`[build] vite build exited with code \${code}\`);
1810
+ finish();
1811
+ });
1812
+
1813
+ child.on('error', (err) => {
1814
+ console.error('[build] spawn error:', err);
1815
+ process.exit(1);
1816
+ });
1817
+
1818
+ const poll = setInterval(() => {
1819
+ const netlifyReady = existsSync(NITRO_JSON) && existsSync(SERVER_MJS);
1820
+ const nodeServerReady = existsSync(OUTPUT_SSR);
1821
+ if (netlifyReady || nodeServerReady) {
1822
+ console.log(\`[build] Output detected (\${netlifyReady ? 'netlify' : 'node-server'}), waiting 3s for final writes...\`);
1823
+ clearInterval(poll);
1824
+ setTimeout(finish, 3_000);
1825
+ }
1826
+ }, 1_000);
1827
+
1828
+ const absoluteTimeout = setTimeout(() => {
1829
+ console.error('[build] Timeout — killing build');
1830
+ finish();
1831
+ }, 240_000);
1832
+ `;
1833
+ }
1834
+
1835
+ // src/templates/post-build.ts
1836
+ function generatePostBuildMjs() {
1837
+ const lines = [
1838
+ `/**`,
1839
+ ` * Post-build script for Netlify deployment.`,
1840
+ ` *`,
1841
+ ` * - Removes stale index.html that would shadow SSR routes`,
1842
+ ` * - Generates _redirects for island JS path mapping`,
1843
+ ` * - Copies island JS to clean paths for local preview`,
1844
+ ` * - Copies server function to all Netlify function paths`,
1845
+ ` * - Ensures SSR catch-all redirect exists`,
1846
+ ` */`,
1847
+ ``,
1848
+ `import {`,
1849
+ ` existsSync, unlinkSync, readFileSync, writeFileSync,`,
1850
+ ` readdirSync, copyFileSync, mkdirSync, cpSync,`,
1851
+ `} from 'node:fs';`,
1852
+ `import { join, dirname, relative } from 'node:path';`,
1853
+ ``,
1854
+ `const CWD = process.cwd();`,
1855
+ `const DIST_DIR = join(CWD, 'dist');`,
1856
+ `const ASSETS_DIR = join(CWD, 'dist', 'assets');`,
1857
+ ``,
1858
+ `function collectFiles(dir, predicate, result = []) {`,
1859
+ ` if (!existsSync(dir)) return result;`,
1860
+ ` for (const entry of readdirSync(dir, { withFileTypes: true })) {`,
1861
+ ` const full = join(dir, entry.name);`,
1862
+ ` if (entry.isDirectory()) collectFiles(full, predicate, result);`,
1863
+ ` else if (predicate(entry.name)) result.push(full);`,
1864
+ ` }`,
1865
+ ` return result;`,
1866
+ `}`,
1867
+ ``,
1868
+ `function toServePath(absPath) {`,
1869
+ ` return '/' + relative(DIST_DIR, absPath).replaceAll('\\\\', '/');`,
1870
+ `}`,
1871
+ ``,
1872
+ `// ── Cleanup stale index.html ─────────────────────────────────────`,
1873
+ `for (const htmlPath of ['dist/index.html', '.netlify/functions-internal/server/public/index.html']) {`,
1874
+ ` const full = join(CWD, htmlPath);`,
1875
+ ` if (existsSync(full)) { unlinkSync(full); console.log('[cleanup] Removed ' + htmlPath); }`,
1876
+ `}`,
1877
+ ``,
1878
+ `// ── Generate island redirects + local copies ─────────────────────`,
1879
+ `function generateIslandRedirects() {`,
1880
+ ` const islandsDir = join(ASSETS_DIR, 'islands');`,
1881
+ ` if (!existsSync(islandsDir)) return;`,
1882
+ ` const islandFiles = collectFiles(islandsDir, n => n.endsWith('.js') && !n.endsWith('.js.map'));`,
1883
+ ` if (islandFiles.length === 0) return;`,
1884
+ ` const redirectLines = [];`,
1885
+ ` for (const absPath of islandFiles) {`,
1886
+ ` const servePath = toServePath(absPath);`,
1887
+ ` const cleanPath = servePath.replace('/assets/', '/').replace(/-[A-Za-z0-9_-]{6,12}\\.js$/, '.js');`,
1888
+ ` redirectLines.push(cleanPath + ' ' + servePath + ' 200');`,
1889
+ ` const cleanAbsPath = join(DIST_DIR, cleanPath.slice(1));`,
1890
+ ` mkdirSync(dirname(cleanAbsPath), { recursive: true });`,
1891
+ ` copyFileSync(absPath, cleanAbsPath);`,
1892
+ ` }`,
1893
+ ` const redirectsPath = join(DIST_DIR, '_redirects');`,
1894
+ ` let existing = existsSync(redirectsPath) ? readFileSync(redirectsPath, 'utf-8') : '';`,
1895
+ ` existing = existing.replaceAll(/# Island JS path rewrites[^\\n]*\\n(?:\\/islands\\/[^\\n]*\\n)*/g, '').trim();`,
1896
+ ` const header = '# Island JS path rewrites (generated by post-build.mjs)\\n';`,
1897
+ ` const content = existing`,
1898
+ ` ? existing + '\\n\\n' + header + redirectLines.join('\\n') + '\\n'`,
1899
+ ` : header + redirectLines.join('\\n') + '\\n';`,
1900
+ ` writeFileSync(redirectsPath, content);`,
1901
+ ` console.log('[redirects] Wrote ' + redirectLines.length + ' island redirects');`,
1902
+ `}`,
1903
+ ``,
1904
+ `// ── Copy framework adapters ───────────────────────────────────────`,
1905
+ `function copyAdapters() {`,
1906
+ ` const sources = [`,
1907
+ ` join(CWD, '.output', 'public', '_adapters'),`,
1908
+ ` join(CWD, 'dist', '_adapters'),`,
1909
+ ` ];`,
1910
+ ` for (const srcDir of sources) {`,
1911
+ ` if (!existsSync(srcDir)) continue;`,
1912
+ ` const files = readdirSync(srcDir).filter(f => f.endsWith('.js'));`,
1913
+ ` if (files.length === 0) continue;`,
1914
+ ` const destDir = join(DIST_DIR, '_adapters');`,
1915
+ ` mkdirSync(destDir, { recursive: true });`,
1916
+ ` for (const file of files) {`,
1917
+ ` const src = join(srcDir, file);`,
1918
+ ` const dest = join(destDir, file);`,
1919
+ ` if (src !== dest) copyFileSync(src, dest);`,
1920
+ ` }`,
1921
+ ` console.log('[adapters] Copied ' + files.length + ' framework adapters');`,
1922
+ ` return;`,
1923
+ ` }`,
1924
+ `}`,
1925
+ ``,
1926
+ `// ── Copy function to all Netlify paths ────────────────────────────`,
1927
+ `function copyToNetlifyPaths() {`,
1928
+ ` const legacyDir = join(CWD, '.netlify', 'functions-internal', 'server');`,
1929
+ ` if (!existsSync(legacyDir)) return;`,
1930
+ ` const targets = [`,
1931
+ ` join(CWD, '.netlify', 'v1', 'functions', 'server'),`,
1932
+ ` join(CWD, 'netlify', 'functions', 'server'),`,
1933
+ ` ];`,
1934
+ ` for (const target of targets) {`,
1935
+ ` cpSync(legacyDir, target, { recursive: true, force: true });`,
1936
+ ` }`,
1937
+ ` console.log('[netlify-fn] Copied server function to all Netlify paths');`,
1938
+ `}`,
1939
+ ``,
1940
+ `// ── Ensure SSR catch-all redirect ─────────────────────────────────`,
1941
+ `function ensureNetlifyRedirects() {`,
1942
+ ` const redirectsPath = join(DIST_DIR, '_redirects');`,
1943
+ ` let content = existsSync(redirectsPath) ? readFileSync(redirectsPath, 'utf-8') : '';`,
1944
+ ` if (content.includes('/.netlify/functions/server')) return;`,
1945
+ ` const catchAll = '\\n# SSR catch-all (Nitro server function)\\n/* /.netlify/functions/server 200\\n';`,
1946
+ ` content = content.trimEnd() + '\\n' + catchAll;`,
1947
+ ` writeFileSync(redirectsPath, content);`,
1948
+ ` console.log('[redirects] Added SSR catch-all to _redirects');`,
1949
+ `}`,
1950
+ ``,
1951
+ `// ── Run ──────────────────────────────────────────────────────────`,
1952
+ `(async () => {`,
1953
+ ``,
1954
+ `generateIslandRedirects();`,
1955
+ `copyAdapters();`,
1956
+ `copyToNetlifyPaths();`,
1957
+ `ensureNetlifyRedirects();`,
1958
+ ``,
1959
+ `// ── Prerender (SSG) ──────────────────────────────────────────────`,
1960
+ `// Spawn the built server and fetch routes to generate static HTML.`,
1961
+ `const serverEntries = [`,
1962
+ ` join(CWD, '.output', 'server', 'index.mjs'),`,
1963
+ ` join(CWD, '.netlify', 'functions-internal', 'server', 'server.mjs'),`,
1964
+ ` join(CWD, '.netlify', 'v1', 'functions', 'server', 'server.mjs'),`,
1965
+ `];`,
1966
+ `const serverEntry = serverEntries.find(p => existsSync(p));`,
1967
+ `const outputDir = [join(CWD, '.output', 'public'), DIST_DIR].find(d => existsSync(d));`,
1968
+ ``,
1969
+ `if (serverEntry && outputDir) {`,
1970
+ ` const { spawn: spawnProcess } = await import('node:child_process');`,
1971
+ ` const PORT = 13172;`,
1972
+ ` const baseUrl = 'http://localhost:' + PORT;`,
1973
+ ` console.log('[prerender] Spawning server on port ' + PORT + '...');`,
1974
+ ` const srv = spawnProcess('node', [serverEntry], {`,
1975
+ ` env: { ...process.env, PORT: String(PORT), NITRO_PORT: String(PORT), HOST: '127.0.0.1', NITRO_HOST: '127.0.0.1', NODE_ENV: 'production' },`,
1976
+ ` stdio: ['ignore', 'pipe', 'pipe'],`,
1977
+ ` });`,
1978
+ ` srv.stdout?.on('data', d => { const m = d.toString().trim(); if (m) console.log('[prerender:server] ' + m); });`,
1979
+ ` srv.stderr?.on('data', d => { const m = d.toString().trim(); if (m) console.error('[prerender:server:err] ' + m); });`,
1980
+ ` let ready = false;`,
1981
+ ` const t0 = Date.now();`,
1982
+ ` while (Date.now() - t0 < 15000) {`,
1983
+ ` try { const r = await fetch(baseUrl + '/'); if (r.ok || r.status < 500) { ready = true; break; } } catch {}`,
1984
+ ` await new Promise(r => setTimeout(r, 200));`,
1985
+ ` }`,
1986
+ ` if (ready) {`,
1987
+ ` console.log('[prerender] Server ready');`,
1988
+ ` const visited = new Set();`,
1989
+ ` const queue = ['/'];`,
1990
+ ` const prerendered = [];`,
1991
+ ` while (queue.length > 0) {`,
1992
+ ` const batch = queue.splice(0, 4);`,
1993
+ ` await Promise.all(batch.map(async (route) => {`,
1994
+ ` const norm = route.endsWith('/') && route !== '/' ? route.slice(0, -1) : route;`,
1995
+ ` if (visited.has(norm)) return;`,
1996
+ ` visited.add(norm);`,
1997
+ ` try {`,
1998
+ ` const res = await fetch(baseUrl + norm);`,
1999
+ ` if (!res.ok) { console.error('[prerender] ' + norm + ' returned ' + res.status); return; }`,
2000
+ ` const html = await res.text();`,
2001
+ ` const fileName = join(norm, 'index.html');`,
2002
+ ` const out = join(outputDir, fileName);`,
2003
+ ` mkdirSync(dirname(out), { recursive: true });`,
2004
+ ` writeFileSync(out, html);`,
2005
+ ` prerendered.push(norm);`,
2006
+ ` console.log('[prerender] ' + norm);`,
2007
+ ` // Crawl links`,
2008
+ ` const re = /<a\\s[^>]*href=["']([^"'#?]+)/gi;`,
2009
+ ` let m;`,
2010
+ ` while ((m = re.exec(html)) !== null) {`,
2011
+ ` const href = m[1];`,
2012
+ ` if (href.startsWith('/') && !href.startsWith('//') && !href.startsWith('/assets/') && !href.startsWith('/islands/') && !href.match(/\\.\\w{2,5}$/)) {`,
2013
+ ` const n = href.endsWith('/') && href !== '/' ? href.slice(0, -1) : href;`,
2014
+ ` if (!visited.has(n)) queue.push(n);`,
2015
+ ` }`,
2016
+ ` }`,
2017
+ ` } catch (err) { console.error('[prerender] Error fetching ' + norm + ':', err.message); }`,
2018
+ ` }));`,
2019
+ ` }`,
2020
+ ` srv.kill('SIGKILL');`,
2021
+ ` console.log('[prerender] Done: ' + prerendered.length + ' page(s)');`,
2022
+ ` // Copy to alt output dirs`,
2023
+ ` for (const alt of [DIST_DIR, join(CWD, '.netlify', 'v1', 'functions', 'server', 'public')].filter(d => d !== outputDir && existsSync(dirname(d)))) {`,
2024
+ ` for (const route of prerendered) {`,
2025
+ ` const f = join(route, 'index.html');`,
2026
+ ` const src = join(outputDir, f);`,
2027
+ ` const dest = join(alt, f);`,
2028
+ ` if (existsSync(src)) { mkdirSync(dirname(dest), { recursive: true }); copyFileSync(src, dest); }`,
2029
+ ` }`,
2030
+ ` }`,
2031
+ ` } else {`,
2032
+ ` srv.kill('SIGKILL');`,
2033
+ ` console.error('[prerender] Server did not start, skipping prerender');`,
2034
+ ` }`,
2035
+ `}`,
2036
+ ``,
2037
+ `console.log('[post-build] Complete');`,
2038
+ ``,
2039
+ `})().catch(err => { console.error('[post-build] Fatal:', err); process.exit(1); });`,
2040
+ ``
2041
+ ];
2042
+ return lines.join(`
2043
+ `);
2044
+ }
2045
+
1702
2046
  // src/scaffold.ts
1703
2047
  function generateHonoServerEntry() {
1704
2048
  return `import { Hono } from 'hono';
@@ -1784,22 +2128,11 @@ async function scaffoldProject(config, targetDir) {
1784
2128
  ``
1785
2129
  ].join(`
1786
2130
  `));
1787
- await writeFile(join(targetDir, "index.html"), [
1788
- `<!DOCTYPE html>`,
1789
- `<html lang="en">`,
1790
- ` <head>`,
1791
- ` <meta charset="utf-8" />`,
1792
- ` <meta name="viewport" content="width=device-width, initial-scale=1" />`,
1793
- ` <title>Avalon</title>`,
1794
- ` </head>`,
1795
- ` <body>`,
1796
- ` <!--ssr-outlet-->`,
1797
- ` <script type="module" src="/src/client/main.js"></script>`,
1798
- ` </body>`,
1799
- `</html>`,
1800
- ``
1801
- ].join(`
1802
- `));
2131
+ if (config.deploy === "netlify") {
2132
+ await writeFile(join(targetDir, "netlify.toml"), generateNetlifyToml(config));
2133
+ await writeFile(join(targetDir, "build.mjs"), generateBuildMjs());
2134
+ await writeFile(join(targetDir, "post-build.mjs"), generatePostBuildMjs());
2135
+ }
1803
2136
  }
1804
2137
 
1805
2138
  // src/summary.ts
@@ -1808,10 +2141,15 @@ var STYLING_LABELS = {
1808
2141
  tailwind: "Tailwind CSS",
1809
2142
  shadcn: "shadcn"
1810
2143
  };
2144
+ var DEPLOY_LABELS = {
2145
+ netlify: "Netlify",
2146
+ none: "None"
2147
+ };
1811
2148
  function formatSummary(config, scaffoldedInPlace = false) {
1812
2149
  const integrations = config.integrations.length > 0 ? config.integrations.join(", ") : "none";
1813
2150
  const styling = STYLING_LABELS[config.styling] ?? config.styling;
1814
2151
  const plugins = config.plugins.length > 0 ? config.plugins.join(", ") : "none";
2152
+ const deploy = DEPLOY_LABELS[config.deploy] ?? config.deploy;
1815
2153
  const nextSteps = scaffoldedInPlace ? [" bun install", " bun run dev"] : [` cd ${config.projectName}`, " bun install", " bun run dev"];
1816
2154
  return [
1817
2155
  "",
@@ -1820,6 +2158,7 @@ function formatSummary(config, scaffoldedInPlace = false) {
1820
2158
  ` Styling: ${styling}`,
1821
2159
  ` Plugins: ${plugins}`,
1822
2160
  ` Middleware: ${config.middleware}`,
2161
+ ` Deploy: ${deploy}`,
1823
2162
  "",
1824
2163
  " Next steps:",
1825
2164
  ...nextSteps,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-avalon",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Scaffold a new Avalon project with multi-framework islands architecture",
5
5
  "license": "MIT",
6
6
  "type": "module",