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,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bun Adapter
|
|
3
|
+
* For: Bun runtime and Bun deploy
|
|
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 bunAdapter: Adapter = {
|
|
17
|
+
name: 'bun',
|
|
18
|
+
target: 'bun',
|
|
19
|
+
|
|
20
|
+
async build(config: AdapterConfig): Promise<BuildResult> {
|
|
21
|
+
const outDir = path.resolve(config.outDir || 'dist');
|
|
22
|
+
const warnings: string[] = [];
|
|
23
|
+
const errors: string[] = [];
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// Clean output directory
|
|
27
|
+
await fs.rm(outDir, { recursive: true, force: true });
|
|
28
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
29
|
+
|
|
30
|
+
// Build client bundle
|
|
31
|
+
const { files: clientFiles } = await buildClient(config, outDir);
|
|
32
|
+
|
|
33
|
+
// Copy public assets
|
|
34
|
+
await copyPublic(outDir);
|
|
35
|
+
|
|
36
|
+
// Build Bun server entry
|
|
37
|
+
await buildBunEntry(config, outDir);
|
|
38
|
+
|
|
39
|
+
// Generate config files
|
|
40
|
+
await generateBunConfig(config, outDir);
|
|
41
|
+
|
|
42
|
+
// Generate manifest
|
|
43
|
+
const routes: RouteManifest[] = (config.static?.routes || ['/']).map(route => ({
|
|
44
|
+
path: route,
|
|
45
|
+
file: route === '/' ? 'index.html' : `${route}.html`,
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
const assets: Record<string, string> = {};
|
|
49
|
+
for (const file of clientFiles) {
|
|
50
|
+
const basename = path.basename(file);
|
|
51
|
+
assets[basename] = `/${basename}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
await generateManifest(config, outDir, routes, assets);
|
|
55
|
+
|
|
56
|
+
// Analyze bundle
|
|
57
|
+
const analysis = await analyzeBundle(outDir);
|
|
58
|
+
|
|
59
|
+
// List all output files
|
|
60
|
+
const allFiles = await listFiles(outDir);
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
outDir,
|
|
65
|
+
files: allFiles,
|
|
66
|
+
warnings,
|
|
67
|
+
errors,
|
|
68
|
+
analysis,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
} catch (error) {
|
|
72
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
73
|
+
return {
|
|
74
|
+
success: false,
|
|
75
|
+
outDir,
|
|
76
|
+
files: [],
|
|
77
|
+
warnings,
|
|
78
|
+
errors,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
async preview(config: AdapterConfig, port = 3000): Promise<void> {
|
|
84
|
+
const { spawn } = await import('child_process');
|
|
85
|
+
const outDir = path.resolve(config.outDir || 'dist');
|
|
86
|
+
|
|
87
|
+
console.log('Starting Bun server...');
|
|
88
|
+
const proc = spawn('bun', [path.join(outDir, 'server.js')], {
|
|
89
|
+
stdio: 'inherit',
|
|
90
|
+
env: { ...process.env, PORT: String(port) }
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
proc.on('error', reject);
|
|
95
|
+
proc.on('exit', resolve);
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
async deploy(config: AdapterConfig): Promise<void> {
|
|
100
|
+
console.log('Deploying with Bun...');
|
|
101
|
+
// Bun doesn't have a native deploy yet, but we can use Docker or other methods
|
|
102
|
+
console.log('For Bun Deploy, consider using Docker or a VPS with Bun installed.');
|
|
103
|
+
console.log('Build output is in:', config.outDir || 'dist');
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/** Build Bun server entry */
|
|
108
|
+
async function buildBunEntry(_config: AdapterConfig, outDir: string): Promise<void> {
|
|
109
|
+
const serverContent = `
|
|
110
|
+
import { serve } from "bun";
|
|
111
|
+
|
|
112
|
+
const port = parseInt(process.env.PORT || "3000");
|
|
113
|
+
const hostname = process.env.HOST || "0.0.0.0";
|
|
114
|
+
|
|
115
|
+
serve({
|
|
116
|
+
port,
|
|
117
|
+
hostname,
|
|
118
|
+
async fetch(request) {
|
|
119
|
+
const url = new URL(request.url);
|
|
120
|
+
const pathname = url.pathname;
|
|
121
|
+
|
|
122
|
+
// Static assets
|
|
123
|
+
if (pathname.startsWith("/_assets/")) {
|
|
124
|
+
const file = Bun.file(pathname.slice(1));
|
|
125
|
+
if (await file.exists()) {
|
|
126
|
+
return new Response(file, {
|
|
127
|
+
headers: {
|
|
128
|
+
"cache-control": "public, max-age=31536000, immutable",
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Try to serve static file directly
|
|
135
|
+
const filePath = pathname === "/" ? "index.html" : pathname.slice(1);
|
|
136
|
+
const file = Bun.file(filePath);
|
|
137
|
+
|
|
138
|
+
if (await file.exists()) {
|
|
139
|
+
return new Response(file);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// SPA fallback - serve index.html
|
|
143
|
+
const indexFile = Bun.file("index.html");
|
|
144
|
+
if (await indexFile.exists()) {
|
|
145
|
+
return new Response(indexFile, {
|
|
146
|
+
headers: { "content-type": "text/html" },
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return new Response("Not Found", { status: 404 });
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
console.log(\`🚀 Bun server running at http://\${hostname}:\${port}\`);
|
|
155
|
+
`;
|
|
156
|
+
|
|
157
|
+
await fs.writeFile(path.join(outDir, 'server.js'), serverContent);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Generate Bun config files */
|
|
161
|
+
async function generateBunConfig(_config: AdapterConfig, outDir: string): Promise<void> {
|
|
162
|
+
// bunfig.toml
|
|
163
|
+
const bunfigToml = `
|
|
164
|
+
[install]
|
|
165
|
+
# Use exact versions
|
|
166
|
+
exact = true
|
|
167
|
+
|
|
168
|
+
[run]
|
|
169
|
+
# Auto-install dependencies
|
|
170
|
+
auto-install = true
|
|
171
|
+
|
|
172
|
+
[deploy]
|
|
173
|
+
# Deployment configuration
|
|
174
|
+
main = "${path.join(outDir, 'server.js')}"
|
|
175
|
+
`;
|
|
176
|
+
|
|
177
|
+
await fs.writeFile('bunfig.toml', bunfigToml.trim());
|
|
178
|
+
|
|
179
|
+
// package.json for Bun
|
|
180
|
+
const pkg = {
|
|
181
|
+
name: 'zylaris-bun-app',
|
|
182
|
+
version: '1.0.0',
|
|
183
|
+
type: 'module',
|
|
184
|
+
scripts: {
|
|
185
|
+
start: `bun ${path.join(outDir, 'server.js')}`,
|
|
186
|
+
build: 'zylaris build --adapter bun',
|
|
187
|
+
},
|
|
188
|
+
dependencies: {
|
|
189
|
+
// Bun has built-in server support
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
await fs.writeFile(path.join(outDir, 'package.json'), JSON.stringify(pkg, null, 2));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** List all files recursively */
|
|
197
|
+
async function listFiles(dir: string, prefix = ''): Promise<string[]> {
|
|
198
|
+
const files: string[] = [];
|
|
199
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
200
|
+
|
|
201
|
+
for (const entry of entries) {
|
|
202
|
+
const fullPath = path.join(dir, entry.name);
|
|
203
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
204
|
+
|
|
205
|
+
if ((entry as { isDirectory(): boolean }).isDirectory()) {
|
|
206
|
+
files.push(...await listFiles(fullPath, relativePath));
|
|
207
|
+
} else {
|
|
208
|
+
files.push(relativePath);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return files;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export default bunAdapter;
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Adapter (Pages + Workers)
|
|
3
|
+
* Supports: Cloudflare Pages (Static + Functions) & Cloudflare Workers
|
|
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 cloudflareAdapter: Adapter = {
|
|
17
|
+
name: 'cloudflare',
|
|
18
|
+
target: 'cloudflare',
|
|
19
|
+
|
|
20
|
+
async build(config: AdapterConfig): Promise<BuildResult> {
|
|
21
|
+
const outDir = path.resolve(config.outDir || 'dist');
|
|
22
|
+
const isWorkers = config.function?.name !== undefined;
|
|
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
|
+
// Copy public assets
|
|
35
|
+
await copyPublic(outDir);
|
|
36
|
+
|
|
37
|
+
// Build worker/function
|
|
38
|
+
if (isWorkers) {
|
|
39
|
+
await buildWorker(config, outDir);
|
|
40
|
+
} else {
|
|
41
|
+
await buildPagesFunction(config, outDir);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Generate config files
|
|
45
|
+
await generateCloudflareConfig(config, outDir, isWorkers);
|
|
46
|
+
|
|
47
|
+
// Generate manifest
|
|
48
|
+
const routes: RouteManifest[] = (config.static?.routes || ['/']).map(route => ({
|
|
49
|
+
path: route,
|
|
50
|
+
file: route === '/' ? 'index.html' : `${route}.html`,
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
const assets: Record<string, string> = {};
|
|
54
|
+
for (const file of clientFiles) {
|
|
55
|
+
const basename = path.basename(file);
|
|
56
|
+
assets[basename] = `/${basename}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
await generateManifest(config, outDir, routes, assets);
|
|
60
|
+
|
|
61
|
+
// Analyze bundle
|
|
62
|
+
const analysis = await analyzeBundle(outDir);
|
|
63
|
+
|
|
64
|
+
// List all output files
|
|
65
|
+
const allFiles = await listFiles(outDir);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
success: true,
|
|
69
|
+
outDir,
|
|
70
|
+
files: allFiles,
|
|
71
|
+
warnings,
|
|
72
|
+
errors,
|
|
73
|
+
analysis,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
} catch (error) {
|
|
77
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
outDir,
|
|
81
|
+
files: [],
|
|
82
|
+
warnings,
|
|
83
|
+
errors,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
async preview(config: AdapterConfig, port = 8788): Promise<void> {
|
|
89
|
+
const { execSync } = await import('child_process');
|
|
90
|
+
|
|
91
|
+
console.log('Starting Cloudflare Pages dev server...');
|
|
92
|
+
execSync(`npx wrangler pages dev ${config.outDir || 'dist'} --port ${port}`, { stdio: 'inherit' });
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
async deploy(config: AdapterConfig): Promise<void> {
|
|
96
|
+
const { execSync } = await import('child_process');
|
|
97
|
+
|
|
98
|
+
if (config.function?.name) {
|
|
99
|
+
console.log('Deploying to Cloudflare Workers...');
|
|
100
|
+
execSync('npx wrangler deploy', { stdio: 'inherit' });
|
|
101
|
+
} else {
|
|
102
|
+
console.log('Deploying to Cloudflare Pages...');
|
|
103
|
+
execSync('npx wrangler pages deploy dist', { stdio: 'inherit' });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/** Build Cloudflare Worker */
|
|
109
|
+
async function buildWorker(config: AdapterConfig, outDir: string): Promise<void> {
|
|
110
|
+
const entryContent = `
|
|
111
|
+
import { handleRequest } from '@zylaris/server/edge';
|
|
112
|
+
|
|
113
|
+
export interface Env {
|
|
114
|
+
// Environment bindings
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export default {
|
|
118
|
+
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
|
119
|
+
return handleRequest(request, {
|
|
120
|
+
env,
|
|
121
|
+
ctx,
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
`;
|
|
126
|
+
|
|
127
|
+
// Build worker
|
|
128
|
+
const { build } = await import('esbuild');
|
|
129
|
+
await build({
|
|
130
|
+
stdin: {
|
|
131
|
+
contents: entryContent,
|
|
132
|
+
resolveDir: process.cwd(),
|
|
133
|
+
},
|
|
134
|
+
bundle: true,
|
|
135
|
+
format: 'esm',
|
|
136
|
+
platform: 'neutral',
|
|
137
|
+
target: 'es2022',
|
|
138
|
+
outfile: path.join(outDir, 'worker.js'),
|
|
139
|
+
external: ['cloudflare:*'],
|
|
140
|
+
minify: true,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Generate wrangler.toml
|
|
144
|
+
const wranglerConfig = `
|
|
145
|
+
name = "${config.function?.name || 'zylaris-app'}"
|
|
146
|
+
main = "worker.js"
|
|
147
|
+
compatibility_date = "2024-01-01"
|
|
148
|
+
|
|
149
|
+
[build]
|
|
150
|
+
command = ""
|
|
151
|
+
|
|
152
|
+
[[routes]]
|
|
153
|
+
pattern = "${config.function?.route || '*/*'}"
|
|
154
|
+
custom_domain = false
|
|
155
|
+
`;
|
|
156
|
+
|
|
157
|
+
await fs.writeFile('wrangler.toml', wranglerConfig.trim());
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Build Cloudflare Pages Function */
|
|
161
|
+
async function buildPagesFunction(_config: AdapterConfig, outDir: string): Promise<void> {
|
|
162
|
+
const functionsDir = path.join(outDir, 'functions');
|
|
163
|
+
await fs.mkdir(functionsDir, { recursive: true });
|
|
164
|
+
|
|
165
|
+
const entryContent = `
|
|
166
|
+
import { handleRequest } from '@zylaris/server/edge';
|
|
167
|
+
|
|
168
|
+
export async function onRequest(context) {
|
|
169
|
+
const { request, env, params } = context;
|
|
170
|
+
|
|
171
|
+
return handleRequest(request, {
|
|
172
|
+
env,
|
|
173
|
+
params,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
`;
|
|
177
|
+
|
|
178
|
+
// Build function
|
|
179
|
+
const { build } = await import('esbuild');
|
|
180
|
+
await build({
|
|
181
|
+
stdin: {
|
|
182
|
+
contents: entryContent,
|
|
183
|
+
resolveDir: process.cwd(),
|
|
184
|
+
},
|
|
185
|
+
bundle: true,
|
|
186
|
+
format: 'esm',
|
|
187
|
+
platform: 'neutral',
|
|
188
|
+
target: 'es2022',
|
|
189
|
+
outfile: path.join(functionsDir, '[[path]].js'),
|
|
190
|
+
external: ['cloudflare:*'],
|
|
191
|
+
minify: true,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Generate Cloudflare config files */
|
|
196
|
+
async function generateCloudflareConfig(
|
|
197
|
+
config: AdapterConfig,
|
|
198
|
+
outDir: string,
|
|
199
|
+
isWorkers: boolean
|
|
200
|
+
): Promise<void> {
|
|
201
|
+
// _headers
|
|
202
|
+
const headers = [
|
|
203
|
+
'/*',
|
|
204
|
+
' X-Frame-Options: DENY',
|
|
205
|
+
' X-Content-Type-Options: nosniff',
|
|
206
|
+
' Referrer-Policy: strict-origin-when-cross-origin',
|
|
207
|
+
'',
|
|
208
|
+
'/_assets/*',
|
|
209
|
+
' Cache-Control: public, max-age=31536000, immutable',
|
|
210
|
+
...Object.entries(config.headers || {}).map(([path, header]) =>
|
|
211
|
+
`\n${path}\n ${header}`
|
|
212
|
+
),
|
|
213
|
+
].join('\n');
|
|
214
|
+
|
|
215
|
+
await fs.writeFile(path.join(outDir, '_headers'), headers);
|
|
216
|
+
|
|
217
|
+
// _redirects
|
|
218
|
+
const redirects = [
|
|
219
|
+
...(config.redirects || []).map(r =>
|
|
220
|
+
`${r.from} ${r.to} ${r.status || 301}`
|
|
221
|
+
),
|
|
222
|
+
].join('\n');
|
|
223
|
+
|
|
224
|
+
if (redirects) {
|
|
225
|
+
await fs.writeFile(path.join(outDir, '_redirects'), redirects);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// _routes.json for Pages
|
|
229
|
+
if (!isWorkers) {
|
|
230
|
+
const routesJson = {
|
|
231
|
+
version: 1,
|
|
232
|
+
include: ['/*'],
|
|
233
|
+
exclude: [
|
|
234
|
+
'/_assets/*',
|
|
235
|
+
'/*.ico',
|
|
236
|
+
'/*.png',
|
|
237
|
+
'/*.jpg',
|
|
238
|
+
'/*.svg',
|
|
239
|
+
'/*.css',
|
|
240
|
+
'/*.js',
|
|
241
|
+
],
|
|
242
|
+
};
|
|
243
|
+
await fs.writeFile(path.join(outDir, '_routes.json'), JSON.stringify(routesJson, null, 2));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// wrangler.toml for Workers
|
|
247
|
+
if (isWorkers) {
|
|
248
|
+
const wranglerToml = `
|
|
249
|
+
name = "${config.function?.name || 'zylaris-app'}"
|
|
250
|
+
compatibility_date = "2024-01-01"
|
|
251
|
+
|
|
252
|
+
[build]
|
|
253
|
+
command = ""
|
|
254
|
+
`;
|
|
255
|
+
await fs.writeFile('wrangler.toml', wranglerToml.trim());
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** List all files recursively */
|
|
260
|
+
async function listFiles(dir: string, prefix = ''): Promise<string[]> {
|
|
261
|
+
const files: string[] = [];
|
|
262
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
263
|
+
|
|
264
|
+
for (const entry of entries) {
|
|
265
|
+
const fullPath = path.join(dir, entry.name);
|
|
266
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
267
|
+
|
|
268
|
+
if ((entry as { isDirectory(): boolean }).isDirectory()) {
|
|
269
|
+
files.push(...await listFiles(fullPath, relativePath));
|
|
270
|
+
} else {
|
|
271
|
+
files.push(relativePath);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return files;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export default cloudflareAdapter;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deno Deploy Adapter
|
|
3
|
+
* For: Deno Deploy edge runtime
|
|
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 denoAdapter: Adapter = {
|
|
17
|
+
name: 'deno',
|
|
18
|
+
target: 'deno',
|
|
19
|
+
|
|
20
|
+
async build(config: AdapterConfig): Promise<BuildResult> {
|
|
21
|
+
const outDir = path.resolve(config.outDir || 'dist');
|
|
22
|
+
const warnings: string[] = [];
|
|
23
|
+
const errors: string[] = [];
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// Clean output directory
|
|
27
|
+
await fs.rm(outDir, { recursive: true, force: true });
|
|
28
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
29
|
+
|
|
30
|
+
// Build client bundle
|
|
31
|
+
const { files: clientFiles } = await buildClient(config, outDir);
|
|
32
|
+
|
|
33
|
+
// Copy public assets
|
|
34
|
+
await copyPublic(outDir);
|
|
35
|
+
|
|
36
|
+
// Build Deno server entry
|
|
37
|
+
await buildDenoEntry(config, outDir);
|
|
38
|
+
|
|
39
|
+
// Generate config files
|
|
40
|
+
await generateDenoConfig(config, outDir);
|
|
41
|
+
|
|
42
|
+
// Generate manifest
|
|
43
|
+
const routes: RouteManifest[] = (config.static?.routes || ['/']).map(route => ({
|
|
44
|
+
path: route,
|
|
45
|
+
file: route === '/' ? 'index.html' : `${route}.html`,
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
const assets: Record<string, string> = {};
|
|
49
|
+
for (const file of clientFiles) {
|
|
50
|
+
const basename = path.basename(file);
|
|
51
|
+
assets[basename] = `/${basename}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
await generateManifest(config, outDir, routes, assets);
|
|
55
|
+
|
|
56
|
+
// Analyze bundle
|
|
57
|
+
const analysis = await analyzeBundle(outDir);
|
|
58
|
+
|
|
59
|
+
// List all output files
|
|
60
|
+
const allFiles = await listFiles(outDir);
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
outDir,
|
|
65
|
+
files: allFiles,
|
|
66
|
+
warnings,
|
|
67
|
+
errors,
|
|
68
|
+
analysis,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
} catch (error) {
|
|
72
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
73
|
+
return {
|
|
74
|
+
success: false,
|
|
75
|
+
outDir,
|
|
76
|
+
files: [],
|
|
77
|
+
warnings,
|
|
78
|
+
errors,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
async preview(config: AdapterConfig, port = 8000): Promise<void> {
|
|
84
|
+
const { execSync } = await import('child_process');
|
|
85
|
+
const outDir = path.resolve(config.outDir || 'dist');
|
|
86
|
+
|
|
87
|
+
console.log('Starting Deno server...');
|
|
88
|
+
execSync(`deno run --allow-net --allow-read --allow-env ${path.join(outDir, 'server.ts')}`, {
|
|
89
|
+
stdio: 'inherit',
|
|
90
|
+
env: { ...process.env, PORT: String(port) }
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
async deploy(config: AdapterConfig): Promise<void> {
|
|
95
|
+
const { execSync } = await import('child_process');
|
|
96
|
+
const outDir = path.resolve(config.outDir || 'dist');
|
|
97
|
+
|
|
98
|
+
console.log('Deploying to Deno Deploy...');
|
|
99
|
+
execSync(`deno deploy --project=${config.function?.name || 'zylaris-app'} ${path.join(outDir, 'server.ts')}`, {
|
|
100
|
+
stdio: 'inherit'
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/** Build Deno server entry */
|
|
106
|
+
async function buildDenoEntry(_config: AdapterConfig, outDir: string): Promise<void> {
|
|
107
|
+
const serverContent = `
|
|
108
|
+
// @deno-types="https://deno.land/std/http/server.ts"
|
|
109
|
+
import { serve } from "https://deno.land/std@0.200.0/http/server.ts";
|
|
110
|
+
|
|
111
|
+
const port = parseInt(Deno.env.get("PORT") || "8000");
|
|
112
|
+
|
|
113
|
+
// Serve static files
|
|
114
|
+
async function serveStatic(request: Request): Promise<Response> {
|
|
115
|
+
const url = new URL(request.url);
|
|
116
|
+
const pathname = url.pathname;
|
|
117
|
+
|
|
118
|
+
// Static assets
|
|
119
|
+
if (pathname.startsWith("/_assets/")) {
|
|
120
|
+
try {
|
|
121
|
+
const filePath = pathname.slice(1);
|
|
122
|
+
const file = await Deno.readFile(filePath);
|
|
123
|
+
const ext = filePath.split('.').pop();
|
|
124
|
+
const contentType = getContentType(ext);
|
|
125
|
+
|
|
126
|
+
return new Response(file, {
|
|
127
|
+
headers: {
|
|
128
|
+
"content-type": contentType,
|
|
129
|
+
"cache-control": "public, max-age=31536000, immutable",
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
} catch {
|
|
133
|
+
return new Response("Not Found", { status: 404 });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Serve index.html for all routes (SPA)
|
|
138
|
+
try {
|
|
139
|
+
const html = await Deno.readTextFile("./index.html");
|
|
140
|
+
return new Response(html, {
|
|
141
|
+
headers: { "content-type": "text/html" },
|
|
142
|
+
});
|
|
143
|
+
} catch {
|
|
144
|
+
return new Response("Not Found", { status: 404 });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function getContentType(ext: string | undefined): string {
|
|
149
|
+
const types: Record<string, string> = {
|
|
150
|
+
js: "application/javascript",
|
|
151
|
+
css: "text/css",
|
|
152
|
+
html: "text/html",
|
|
153
|
+
json: "application/json",
|
|
154
|
+
png: "image/png",
|
|
155
|
+
jpg: "image/jpeg",
|
|
156
|
+
jpeg: "image/jpeg",
|
|
157
|
+
gif: "image/gif",
|
|
158
|
+
svg: "image/svg+xml",
|
|
159
|
+
ico: "image/x-icon",
|
|
160
|
+
};
|
|
161
|
+
return types[ext || ""] || "application/octet-stream";
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log(\`🚀 Deno server running at http://localhost:\${port}\`);
|
|
165
|
+
|
|
166
|
+
await serve(serveStatic, { port });
|
|
167
|
+
`;
|
|
168
|
+
|
|
169
|
+
await fs.writeFile(path.join(outDir, 'server.ts'), serverContent);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Generate Deno config files */
|
|
173
|
+
async function generateDenoConfig(config: AdapterConfig, outDir: string): Promise<void> {
|
|
174
|
+
// deno.json
|
|
175
|
+
const denoJson = {
|
|
176
|
+
tasks: {
|
|
177
|
+
start: `deno run --allow-net --allow-read --allow-env ${path.join(outDir, 'server.ts')}`,
|
|
178
|
+
deploy: `deno deploy --project=${config.function?.name || 'zylaris-app'} ${path.join(outDir, 'server.ts')}`,
|
|
179
|
+
},
|
|
180
|
+
deploy: {
|
|
181
|
+
project: config.function?.name || 'zylaris-app',
|
|
182
|
+
include: ['dist/**/*'],
|
|
183
|
+
exclude: [],
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
await fs.writeFile('deno.json', JSON.stringify(denoJson, null, 2));
|
|
188
|
+
|
|
189
|
+
// Generate import map for Deno Deploy
|
|
190
|
+
const importMap = {
|
|
191
|
+
imports: {
|
|
192
|
+
"@zylaris/core": "https://esm.sh/@zylaris/core",
|
|
193
|
+
"@zylaris/server": "https://esm.sh/@zylaris/server",
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
await fs.writeFile('import_map.json', JSON.stringify(importMap, null, 2));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** List all files recursively */
|
|
201
|
+
async function listFiles(dir: string, prefix = ''): Promise<string[]> {
|
|
202
|
+
const files: string[] = [];
|
|
203
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
204
|
+
|
|
205
|
+
for (const entry of entries) {
|
|
206
|
+
const fullPath = path.join(dir, entry.name);
|
|
207
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
208
|
+
|
|
209
|
+
if ((entry as { isDirectory(): boolean }).isDirectory()) {
|
|
210
|
+
files.push(...await listFiles(fullPath, relativePath));
|
|
211
|
+
} else {
|
|
212
|
+
files.push(relativePath);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return files;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export default denoAdapter;
|