zylaris 1.0.2
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/LICENSE +21 -0
- package/README.md +558 -0
- package/Zylaris.js.png +0 -0
- package/examples/default/index.html +13 -0
- package/examples/default/package.json +23 -0
- package/examples/default/src/app/about/page.tsx +18 -0
- package/examples/default/src/app/counter/page.tsx +22 -0
- package/examples/default/src/app/global.css +225 -0
- package/examples/default/src/app/layout.tsx +33 -0
- package/examples/default/src/app/page.tsx +14 -0
- package/examples/default/src/entry-client.tsx +87 -0
- package/examples/default/src/entry-server.tsx +52 -0
- package/examples/default/src/router.ts +60 -0
- package/examples/default/tsconfig.json +28 -0
- package/examples/default/zylaris.config.ts +24 -0
- package/package.json +34 -0
- package/packages/adapter/package.json +59 -0
- package/packages/adapter/src/adapters/bun.ts +215 -0
- package/packages/adapter/src/adapters/cloudflare.ts +278 -0
- package/packages/adapter/src/adapters/deno.ts +219 -0
- package/packages/adapter/src/adapters/netlify.ts +274 -0
- package/packages/adapter/src/adapters/node.ts +155 -0
- package/packages/adapter/src/adapters/static.ts +134 -0
- package/packages/adapter/src/adapters/vercel.ts +239 -0
- package/packages/adapter/src/index.ts +115 -0
- package/packages/adapter/src/lib/builder.ts +361 -0
- package/packages/adapter/src/types.ts +191 -0
- package/packages/adapter/tsconfig.json +8 -0
- package/packages/cli/package.json +43 -0
- package/packages/cli/src/bin.ts +107 -0
- package/packages/cli/src/commands/build.ts +197 -0
- package/packages/cli/src/commands/create.ts +222 -0
- package/packages/cli/src/commands/deploy.ts +90 -0
- package/packages/cli/src/commands/dev.ts +108 -0
- package/packages/cli/src/index.ts +6 -0
- package/packages/cli/tsconfig.json +9 -0
- package/packages/compiler/package.json +39 -0
- package/packages/compiler/src/index.ts +210 -0
- package/packages/compiler/src/jit.ts +187 -0
- package/packages/compiler/tsconfig.json +9 -0
- package/packages/core/package.json +55 -0
- package/packages/core/src/components.test.ts +125 -0
- package/packages/core/src/components.ts +181 -0
- package/packages/core/src/config.ts +204 -0
- package/packages/core/src/hooks.ts +142 -0
- package/packages/core/src/index.ts +59 -0
- package/packages/core/src/jsx-runtime.ts +46 -0
- package/packages/core/tsconfig.json +16 -0
- package/packages/dev-server/package.json +51 -0
- package/packages/dev-server/src/index.ts +306 -0
- package/packages/dev-server/src/jit-middleware.ts +78 -0
- package/packages/dev-server/tsconfig.json +9 -0
- package/packages/plugins/package.json +44 -0
- package/packages/plugins/src/cdn/loader.ts +275 -0
- package/packages/plugins/src/index.ts +238 -0
- package/packages/plugins/src/loaders/auto-import.ts +219 -0
- package/packages/plugins/src/loaders/external.ts +332 -0
- package/packages/plugins/src/transforms/index.ts +407 -0
- package/packages/plugins/src/types.ts +296 -0
- package/packages/plugins/tsconfig.json +8 -0
- package/packages/reactivity/package.json +36 -0
- package/packages/reactivity/src/computed.d.ts +3 -0
- package/packages/reactivity/src/computed.d.ts.map +1 -0
- package/packages/reactivity/src/computed.js +64 -0
- package/packages/reactivity/src/computed.js.map +1 -0
- package/packages/reactivity/src/computed.test.ts +83 -0
- package/packages/reactivity/src/computed.ts +69 -0
- package/packages/reactivity/src/index.d.ts +6 -0
- package/packages/reactivity/src/index.d.ts.map +1 -0
- package/packages/reactivity/src/index.js +7 -0
- package/packages/reactivity/src/index.js.map +1 -0
- package/packages/reactivity/src/index.ts +18 -0
- package/packages/reactivity/src/resource.d.ts +6 -0
- package/packages/reactivity/src/resource.d.ts.map +1 -0
- package/packages/reactivity/src/resource.js +43 -0
- package/packages/reactivity/src/resource.js.map +1 -0
- package/packages/reactivity/src/resource.test.ts +70 -0
- package/packages/reactivity/src/resource.ts +59 -0
- package/packages/reactivity/src/signal.d.ts +7 -0
- package/packages/reactivity/src/signal.d.ts.map +1 -0
- package/packages/reactivity/src/signal.js +145 -0
- package/packages/reactivity/src/signal.js.map +1 -0
- package/packages/reactivity/src/signal.test.ts +130 -0
- package/packages/reactivity/src/signal.ts +207 -0
- package/packages/reactivity/src/store.d.ts +4 -0
- package/packages/reactivity/src/store.d.ts.map +1 -0
- package/packages/reactivity/src/store.js +62 -0
- package/packages/reactivity/src/store.js.map +1 -0
- package/packages/reactivity/src/store.test.ts +38 -0
- package/packages/reactivity/src/store.ts +111 -0
- package/packages/reactivity/src/types.d.ts +43 -0
- package/packages/reactivity/src/types.d.ts.map +1 -0
- package/packages/reactivity/src/types.js +3 -0
- package/packages/reactivity/src/types.js.map +1 -0
- package/packages/reactivity/src/types.ts +43 -0
- package/packages/reactivity/tsconfig.json +9 -0
- package/packages/router/package.json +44 -0
- package/packages/router/src/components.tsx +150 -0
- package/packages/router/src/fs-router.ts +163 -0
- package/packages/router/src/index.ts +22 -0
- package/packages/router/src/router.test.ts +111 -0
- package/packages/router/src/router.ts +112 -0
- package/packages/router/src/types.ts +69 -0
- package/packages/router/tsconfig.json +10 -0
- package/packages/server/package.json +41 -0
- package/packages/server/src/action.test.ts +102 -0
- package/packages/server/src/action.ts +201 -0
- package/packages/server/src/api.ts +143 -0
- package/packages/server/src/index.ts +18 -0
- package/packages/server/src/types.ts +72 -0
- package/packages/server/tsconfig.json +9 -0
- package/pnpm-workspace.yaml +4 -0
- package/scripts/publish.ps1 +138 -0
- package/scripts/publish.sh +142 -0
- package/tsconfig.json +28 -0
- package/turbo.json +24 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Netlify Adapter (Functions + Edge)
|
|
3
|
+
* Supports: Netlify Functions & Edge Functions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import type { Adapter, AdapterConfig, BuildResult, RouteManifest } from '../types.js';
|
|
9
|
+
import {
|
|
10
|
+
buildClient,
|
|
11
|
+
generateManifest,
|
|
12
|
+
copyPublic,
|
|
13
|
+
analyzeBundle
|
|
14
|
+
} from '../lib/builder.js';
|
|
15
|
+
|
|
16
|
+
export const netlifyAdapter: Adapter = {
|
|
17
|
+
name: 'netlify',
|
|
18
|
+
target: 'netlify',
|
|
19
|
+
|
|
20
|
+
async build(config: AdapterConfig): Promise<BuildResult> {
|
|
21
|
+
const outDir = path.resolve(config.outDir || 'dist');
|
|
22
|
+
const isEdge = config.function?.edge ?? false;
|
|
23
|
+
const warnings: string[] = [];
|
|
24
|
+
const errors: string[] = [];
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// Clean output directory
|
|
28
|
+
await fs.rm(outDir, { recursive: true, force: true });
|
|
29
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
30
|
+
|
|
31
|
+
// Netlify output structure
|
|
32
|
+
const functionsDir = path.join(outDir, 'netlify', 'functions');
|
|
33
|
+
const edgeFunctionsDir = path.join(outDir, 'netlify', 'edge-functions');
|
|
34
|
+
const publishDir = outDir;
|
|
35
|
+
|
|
36
|
+
await fs.mkdir(functionsDir, { recursive: true });
|
|
37
|
+
await fs.mkdir(edgeFunctionsDir, { recursive: true });
|
|
38
|
+
|
|
39
|
+
// Build client bundle
|
|
40
|
+
const { files: clientFiles } = await buildClient(config, publishDir);
|
|
41
|
+
|
|
42
|
+
// Copy public assets
|
|
43
|
+
await copyPublic(publishDir);
|
|
44
|
+
|
|
45
|
+
// Build function
|
|
46
|
+
if (isEdge) {
|
|
47
|
+
await buildEdgeFunction(config, edgeFunctionsDir);
|
|
48
|
+
} else {
|
|
49
|
+
await buildNetlifyFunction(config, functionsDir);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Generate config files
|
|
53
|
+
await generateNetlifyConfig(config, outDir, isEdge);
|
|
54
|
+
|
|
55
|
+
// Generate manifest
|
|
56
|
+
const routes: RouteManifest[] = (config.static?.routes || ['/']).map(route => ({
|
|
57
|
+
path: route,
|
|
58
|
+
file: route === '/' ? 'index.html' : `${route}.html`,
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
const assets: Record<string, string> = {};
|
|
62
|
+
for (const file of clientFiles) {
|
|
63
|
+
const basename = path.basename(file);
|
|
64
|
+
assets[basename] = `/_assets/${basename}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
await generateManifest(config, outDir, routes, assets);
|
|
68
|
+
|
|
69
|
+
// Analyze bundle
|
|
70
|
+
const analysis = await analyzeBundle(outDir);
|
|
71
|
+
|
|
72
|
+
// List all output files
|
|
73
|
+
const allFiles = await listFiles(outDir);
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
success: true,
|
|
77
|
+
outDir,
|
|
78
|
+
files: allFiles,
|
|
79
|
+
warnings,
|
|
80
|
+
errors,
|
|
81
|
+
analysis,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
} catch (error) {
|
|
85
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
86
|
+
return {
|
|
87
|
+
success: false,
|
|
88
|
+
outDir,
|
|
89
|
+
files: [],
|
|
90
|
+
warnings,
|
|
91
|
+
errors,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
async preview(_config: AdapterConfig, port = 8888): Promise<void> {
|
|
97
|
+
const { execSync } = await import('child_process');
|
|
98
|
+
|
|
99
|
+
console.log('Starting Netlify dev server...');
|
|
100
|
+
execSync(`npx netlify dev --port ${port}`, { stdio: 'inherit' });
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
async deploy(_config: AdapterConfig): Promise<void> {
|
|
104
|
+
const { execSync } = await import('child_process');
|
|
105
|
+
|
|
106
|
+
console.log('Deploying to Netlify...');
|
|
107
|
+
execSync('npx netlify deploy --prod', { stdio: 'inherit' });
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/** Build Netlify Edge Function */
|
|
112
|
+
async function buildEdgeFunction(_config: AdapterConfig, edgeFunctionsDir: string): Promise<void> {
|
|
113
|
+
const entryContent = `
|
|
114
|
+
import type { Config } from "@netlify/edge-functions";
|
|
115
|
+
import { handleRequest } from '@zylaris/server/edge';
|
|
116
|
+
|
|
117
|
+
export default async function handler(request: Request) {
|
|
118
|
+
return handleRequest(request, {
|
|
119
|
+
// App configuration
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const config: Config = {
|
|
124
|
+
path: "/*",
|
|
125
|
+
cache: "manual",
|
|
126
|
+
};
|
|
127
|
+
`;
|
|
128
|
+
|
|
129
|
+
await fs.writeFile(path.join(edgeFunctionsDir, 'zylaris-app.ts'), entryContent);
|
|
130
|
+
|
|
131
|
+
// Build the edge function
|
|
132
|
+
const { build } = await import('esbuild');
|
|
133
|
+
await build({
|
|
134
|
+
entryPoints: [path.join(edgeFunctionsDir, 'zylaris-app.ts')],
|
|
135
|
+
bundle: true,
|
|
136
|
+
format: 'esm',
|
|
137
|
+
platform: 'neutral',
|
|
138
|
+
target: 'es2022',
|
|
139
|
+
outfile: path.join(edgeFunctionsDir, 'zylaris-app.js'),
|
|
140
|
+
external: ['@netlify/edge-functions'],
|
|
141
|
+
minify: true,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Build Netlify Function */
|
|
146
|
+
async function buildNetlifyFunction(config: AdapterConfig, functionsDir: string): Promise<void> {
|
|
147
|
+
const functionName = config.function?.name || 'zylaris-app';
|
|
148
|
+
|
|
149
|
+
// Build server bundle
|
|
150
|
+
const { build } = await import('esbuild');
|
|
151
|
+
|
|
152
|
+
const possibleEntries = ['./src/entry-server.ts', './src/entry-server.tsx'];
|
|
153
|
+
const entryPoints: string[] = [];
|
|
154
|
+
for (const e of possibleEntries) {
|
|
155
|
+
try {
|
|
156
|
+
await fs.access(e);
|
|
157
|
+
entryPoints.push(e);
|
|
158
|
+
} catch {
|
|
159
|
+
// skip
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
await build({
|
|
164
|
+
entryPoints,
|
|
165
|
+
bundle: true,
|
|
166
|
+
format: 'esm',
|
|
167
|
+
platform: 'node',
|
|
168
|
+
target: 'node18',
|
|
169
|
+
outfile: path.join(functionsDir, `${functionName}.js`),
|
|
170
|
+
external: ['@zylaris/*'],
|
|
171
|
+
minify: true,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Generate Netlify config files */
|
|
176
|
+
async function generateNetlifyConfig(
|
|
177
|
+
config: AdapterConfig,
|
|
178
|
+
outDir: string,
|
|
179
|
+
isEdge: boolean
|
|
180
|
+
): Promise<void> {
|
|
181
|
+
// netlify.toml
|
|
182
|
+
const redirects = config.redirects?.map(r => ({
|
|
183
|
+
from: r.from,
|
|
184
|
+
to: r.to,
|
|
185
|
+
status: r.status || 301,
|
|
186
|
+
})) || [];
|
|
187
|
+
|
|
188
|
+
const headers = Object.entries(config.headers || {}).map(([path, header]) => ({
|
|
189
|
+
for: path,
|
|
190
|
+
values: Object.fromEntries(header.split(':').map((h, i, arr) =>
|
|
191
|
+
i === 0 ? [] : [arr[0].trim(), h.trim()]
|
|
192
|
+
).filter(Boolean)),
|
|
193
|
+
}));
|
|
194
|
+
|
|
195
|
+
const netlifyConfig = {
|
|
196
|
+
build: {
|
|
197
|
+
publish: outDir,
|
|
198
|
+
command: 'echo "Build already done"',
|
|
199
|
+
},
|
|
200
|
+
functions: {
|
|
201
|
+
directory: 'netlify/functions',
|
|
202
|
+
},
|
|
203
|
+
'edge-functions': isEdge ? [
|
|
204
|
+
{
|
|
205
|
+
path: '/*',
|
|
206
|
+
function: 'zylaris-app',
|
|
207
|
+
}
|
|
208
|
+
] : undefined,
|
|
209
|
+
redirects: [
|
|
210
|
+
...redirects,
|
|
211
|
+
// SPA fallback
|
|
212
|
+
{ from: '/*', to: '/index.html', status: 200 },
|
|
213
|
+
],
|
|
214
|
+
headers,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Write as TOML-like format (simplified)
|
|
218
|
+
const tomlContent = generateToml(netlifyConfig);
|
|
219
|
+
await fs.writeFile('netlify.toml', tomlContent);
|
|
220
|
+
|
|
221
|
+
// _redirects file
|
|
222
|
+
const redirectsContent = [
|
|
223
|
+
...redirects.map(r => `${r.from} ${r.to} ${r.status}`),
|
|
224
|
+
'/* /index.html 200',
|
|
225
|
+
].join('\n');
|
|
226
|
+
|
|
227
|
+
await fs.writeFile(path.join(outDir, '_redirects'), redirectsContent);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Simple TOML generator */
|
|
231
|
+
function generateToml(obj: Record<string, unknown>, prefix = ''): string {
|
|
232
|
+
let result = '';
|
|
233
|
+
|
|
234
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
235
|
+
if (value === undefined) continue;
|
|
236
|
+
|
|
237
|
+
if (Array.isArray(value)) {
|
|
238
|
+
for (const item of value) {
|
|
239
|
+
if (typeof item === 'object') {
|
|
240
|
+
result += `[[${prefix}${key}]]\n`;
|
|
241
|
+
result += generateToml(item as Record<string, unknown>);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
} else if (typeof value === 'object') {
|
|
245
|
+
result += `[${prefix}${key}]\n`;
|
|
246
|
+
result += generateToml(value as Record<string, unknown>);
|
|
247
|
+
} else {
|
|
248
|
+
result += `${key} = ${JSON.stringify(value)}\n`;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** List all files recursively */
|
|
256
|
+
async function listFiles(dir: string, prefix = ''): Promise<string[]> {
|
|
257
|
+
const files: string[] = [];
|
|
258
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
259
|
+
|
|
260
|
+
for (const entry of entries) {
|
|
261
|
+
const fullPath = path.join(dir, entry.name);
|
|
262
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
263
|
+
|
|
264
|
+
if ((entry as { isDirectory(): boolean }).isDirectory()) {
|
|
265
|
+
files.push(...await listFiles(fullPath, relativePath));
|
|
266
|
+
} else {
|
|
267
|
+
files.push(relativePath);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return files;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export default netlifyAdapter;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js Server Adapter
|
|
3
|
+
* For: Traditional Node.js hosting, VPS, Docker, PM2
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import type { Adapter, AdapterConfig, BuildResult, RouteManifest } from '../types.js';
|
|
9
|
+
import {
|
|
10
|
+
buildClient,
|
|
11
|
+
buildServer,
|
|
12
|
+
generateManifest,
|
|
13
|
+
copyPublic,
|
|
14
|
+
analyzeBundle
|
|
15
|
+
} from '../lib/builder.js';
|
|
16
|
+
|
|
17
|
+
export const nodeAdapter: Adapter = {
|
|
18
|
+
name: 'node',
|
|
19
|
+
target: 'node',
|
|
20
|
+
|
|
21
|
+
async build(config: AdapterConfig): Promise<BuildResult> {
|
|
22
|
+
const outDir = path.resolve(config.outDir || 'dist');
|
|
23
|
+
const warnings: string[] = [];
|
|
24
|
+
const errors: string[] = [];
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// Clean output directory
|
|
28
|
+
await fs.rm(outDir, { recursive: true, force: true });
|
|
29
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
30
|
+
|
|
31
|
+
// Build client bundle
|
|
32
|
+
const { files: clientFiles } = await buildClient(config, outDir);
|
|
33
|
+
|
|
34
|
+
// Build server bundle
|
|
35
|
+
const { files: serverFiles, entry: serverEntry } = await buildServer(config, outDir, 'node');
|
|
36
|
+
|
|
37
|
+
// Copy public assets
|
|
38
|
+
await copyPublic(outDir);
|
|
39
|
+
|
|
40
|
+
// Generate server entry file
|
|
41
|
+
await generateServerEntry(config, outDir, serverEntry);
|
|
42
|
+
|
|
43
|
+
// Generate package.json for production
|
|
44
|
+
await generatePackageJson(outDir);
|
|
45
|
+
|
|
46
|
+
// Generate manifest
|
|
47
|
+
const routes: RouteManifest[] = (config.static?.routes || ['/']).map(route => ({
|
|
48
|
+
path: route,
|
|
49
|
+
file: route === '/' ? 'index.html' : `${route}.html`,
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
const assets: Record<string, string> = {};
|
|
53
|
+
for (const file of clientFiles) {
|
|
54
|
+
const basename = path.basename(file);
|
|
55
|
+
assets[basename] = `/_assets/${basename}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
await generateManifest(config, outDir, routes, assets);
|
|
59
|
+
|
|
60
|
+
// Analyze bundle
|
|
61
|
+
const analysis = await analyzeBundle(outDir);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
success: true,
|
|
65
|
+
outDir,
|
|
66
|
+
files: [...clientFiles, ...serverFiles],
|
|
67
|
+
warnings,
|
|
68
|
+
errors,
|
|
69
|
+
analysis,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
} catch (error) {
|
|
73
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
74
|
+
return {
|
|
75
|
+
success: false,
|
|
76
|
+
outDir,
|
|
77
|
+
files: [],
|
|
78
|
+
warnings,
|
|
79
|
+
errors,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
async preview(config: AdapterConfig, port = 3000): Promise<void> {
|
|
85
|
+
const outDir = path.resolve(config.outDir || 'dist');
|
|
86
|
+
const entryPath = path.join(outDir, 'index.js');
|
|
87
|
+
|
|
88
|
+
// Set port environment variable
|
|
89
|
+
process.env.PORT = String(port);
|
|
90
|
+
|
|
91
|
+
// Import and run server
|
|
92
|
+
await import(entryPath);
|
|
93
|
+
|
|
94
|
+
return new Promise(() => {}); // Keep running
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/** Generate server entry point */
|
|
99
|
+
async function generateServerEntry(
|
|
100
|
+
_config: AdapterConfig,
|
|
101
|
+
outDir: string,
|
|
102
|
+
serverEntry: string
|
|
103
|
+
): Promise<void> {
|
|
104
|
+
const port = process.env.PORT || 3000;
|
|
105
|
+
const host = process.env.HOST || '0.0.0.0';
|
|
106
|
+
|
|
107
|
+
const entryContent = `#!/usr/bin/env node
|
|
108
|
+
import { createServer } from '@zylaris/server';
|
|
109
|
+
import { handler } from './server/${path.basename(serverEntry)}';
|
|
110
|
+
|
|
111
|
+
const port = process.env.PORT || ${port};
|
|
112
|
+
const host = process.env.HOST || '${host}';
|
|
113
|
+
|
|
114
|
+
const server = createServer({
|
|
115
|
+
handler,
|
|
116
|
+
port: Number(port),
|
|
117
|
+
host,
|
|
118
|
+
static: './client',
|
|
119
|
+
compress: true,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
console.log(\`🚀 Server running at http://\${host}:\${port}\`);
|
|
123
|
+
`;
|
|
124
|
+
|
|
125
|
+
await fs.writeFile(path.join(outDir, 'index.js'), entryContent);
|
|
126
|
+
|
|
127
|
+
// Make executable (Unix)
|
|
128
|
+
try {
|
|
129
|
+
await fs.chmod(path.join(outDir, 'index.js'), 0o755);
|
|
130
|
+
} catch {
|
|
131
|
+
// Ignore on Windows
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Generate production package.json */
|
|
136
|
+
async function generatePackageJson(outDir: string): Promise<void> {
|
|
137
|
+
const pkg = {
|
|
138
|
+
name: 'zylaris-app',
|
|
139
|
+
version: '1.0.0',
|
|
140
|
+
type: 'module',
|
|
141
|
+
scripts: {
|
|
142
|
+
start: 'node index.js',
|
|
143
|
+
},
|
|
144
|
+
dependencies: {
|
|
145
|
+
'@zylaris/server': 'workspace:*',
|
|
146
|
+
},
|
|
147
|
+
engines: {
|
|
148
|
+
node: '>=18.0.0',
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
await fs.writeFile(path.join(outDir, 'package.json'), JSON.stringify(pkg, null, 2));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export default nodeAdapter;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static Site Generator Adapter
|
|
3
|
+
* For: GitHub Pages, Netlify Static, Vercel Static, Cloudflare Pages, Surge, etc.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import type { Adapter, AdapterConfig, BuildResult, RouteManifest } from '../types.js';
|
|
9
|
+
import {
|
|
10
|
+
buildClient,
|
|
11
|
+
generateStaticPages,
|
|
12
|
+
generateManifest,
|
|
13
|
+
copyPublic,
|
|
14
|
+
generateHeaders,
|
|
15
|
+
generateRedirects,
|
|
16
|
+
analyzeBundle
|
|
17
|
+
} from '../lib/builder.js';
|
|
18
|
+
|
|
19
|
+
export const staticAdapter: Adapter = {
|
|
20
|
+
name: 'static',
|
|
21
|
+
target: 'static',
|
|
22
|
+
|
|
23
|
+
async build(config: AdapterConfig): Promise<BuildResult> {
|
|
24
|
+
const outDir = path.resolve(config.outDir || 'dist');
|
|
25
|
+
const warnings: string[] = [];
|
|
26
|
+
const errors: string[] = [];
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// Clean output directory
|
|
30
|
+
await fs.rm(outDir, { recursive: true, force: true });
|
|
31
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
32
|
+
|
|
33
|
+
// Build client bundle
|
|
34
|
+
const { files: clientFiles } = await buildClient(config, outDir);
|
|
35
|
+
|
|
36
|
+
// Generate static pages
|
|
37
|
+
await generateStaticPages(config, outDir, clientFiles);
|
|
38
|
+
|
|
39
|
+
// Copy public assets
|
|
40
|
+
await copyPublic(outDir);
|
|
41
|
+
|
|
42
|
+
// Generate platform config files
|
|
43
|
+
await generatePlatformFiles(config, outDir);
|
|
44
|
+
|
|
45
|
+
// Generate manifest
|
|
46
|
+
const routes: RouteManifest[] = (config.static?.routes || ['/']).map(route => ({
|
|
47
|
+
path: route,
|
|
48
|
+
file: route === '/' ? 'index.html' : `${route}.html`,
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
const assets: Record<string, string> = {};
|
|
52
|
+
for (const file of clientFiles) {
|
|
53
|
+
const basename = path.basename(file);
|
|
54
|
+
assets[basename] = `/_assets/${basename}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await generateManifest(config, outDir, routes, assets);
|
|
58
|
+
|
|
59
|
+
// Analyze bundle
|
|
60
|
+
const analysis = await analyzeBundle(outDir);
|
|
61
|
+
|
|
62
|
+
// List output files
|
|
63
|
+
const allFiles = await listFiles(outDir);
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
success: true,
|
|
67
|
+
outDir,
|
|
68
|
+
files: allFiles,
|
|
69
|
+
warnings,
|
|
70
|
+
errors,
|
|
71
|
+
analysis,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
} catch (error) {
|
|
75
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
76
|
+
return {
|
|
77
|
+
success: false,
|
|
78
|
+
outDir,
|
|
79
|
+
files: [],
|
|
80
|
+
warnings,
|
|
81
|
+
errors,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
async preview(_config: AdapterConfig, _port = 4173): Promise<void> {
|
|
87
|
+
console.log('Preview is not implemented yet for static adapter.');
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/** Generate platform-specific files */
|
|
92
|
+
async function generatePlatformFiles(config: AdapterConfig, outDir: string): Promise<void> {
|
|
93
|
+
// Generate _headers for Netlify/Cloudflare
|
|
94
|
+
await generateHeaders(config, outDir);
|
|
95
|
+
|
|
96
|
+
// Generate _redirects
|
|
97
|
+
await generateRedirects(config, outDir);
|
|
98
|
+
|
|
99
|
+
// Generate SPA fallback for client-side routing
|
|
100
|
+
if (config.static?.fallback) {
|
|
101
|
+
const fallbackHtml = `<!DOCTYPE html>
|
|
102
|
+
<html>
|
|
103
|
+
<head>
|
|
104
|
+
<meta http-equiv="refresh" content="0;url=${config.static.fallback}">
|
|
105
|
+
</head>
|
|
106
|
+
<body></body>
|
|
107
|
+
</html>`;
|
|
108
|
+
await fs.writeFile(path.join(outDir, '404.html'), fallbackHtml);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Generate .nojekyll for GitHub Pages
|
|
112
|
+
await fs.writeFile(path.join(outDir, '.nojekyll'), '');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** List all files recursively */
|
|
116
|
+
async function listFiles(dir: string, prefix = ''): Promise<string[]> {
|
|
117
|
+
const files: string[] = [];
|
|
118
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
119
|
+
|
|
120
|
+
for (const entry of entries) {
|
|
121
|
+
const fullPath = path.join(dir, entry.name);
|
|
122
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
123
|
+
|
|
124
|
+
if ((entry as { isDirectory(): boolean }).isDirectory()) {
|
|
125
|
+
files.push(...await listFiles(fullPath, relativePath));
|
|
126
|
+
} else {
|
|
127
|
+
files.push(relativePath);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return files;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export default staticAdapter;
|