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.
Files changed (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +558 -0
  3. package/Zylaris.js.png +0 -0
  4. package/examples/default/index.html +13 -0
  5. package/examples/default/package.json +23 -0
  6. package/examples/default/src/app/about/page.tsx +18 -0
  7. package/examples/default/src/app/counter/page.tsx +22 -0
  8. package/examples/default/src/app/global.css +225 -0
  9. package/examples/default/src/app/layout.tsx +33 -0
  10. package/examples/default/src/app/page.tsx +14 -0
  11. package/examples/default/src/entry-client.tsx +87 -0
  12. package/examples/default/src/entry-server.tsx +52 -0
  13. package/examples/default/src/router.ts +60 -0
  14. package/examples/default/tsconfig.json +28 -0
  15. package/examples/default/zylaris.config.ts +24 -0
  16. package/package.json +34 -0
  17. package/packages/adapter/package.json +59 -0
  18. package/packages/adapter/src/adapters/bun.ts +215 -0
  19. package/packages/adapter/src/adapters/cloudflare.ts +278 -0
  20. package/packages/adapter/src/adapters/deno.ts +219 -0
  21. package/packages/adapter/src/adapters/netlify.ts +274 -0
  22. package/packages/adapter/src/adapters/node.ts +155 -0
  23. package/packages/adapter/src/adapters/static.ts +134 -0
  24. package/packages/adapter/src/adapters/vercel.ts +239 -0
  25. package/packages/adapter/src/index.ts +115 -0
  26. package/packages/adapter/src/lib/builder.ts +361 -0
  27. package/packages/adapter/src/types.ts +191 -0
  28. package/packages/adapter/tsconfig.json +8 -0
  29. package/packages/cli/package.json +43 -0
  30. package/packages/cli/src/bin.ts +107 -0
  31. package/packages/cli/src/commands/build.ts +197 -0
  32. package/packages/cli/src/commands/create.ts +222 -0
  33. package/packages/cli/src/commands/deploy.ts +90 -0
  34. package/packages/cli/src/commands/dev.ts +108 -0
  35. package/packages/cli/src/index.ts +6 -0
  36. package/packages/cli/tsconfig.json +9 -0
  37. package/packages/compiler/package.json +39 -0
  38. package/packages/compiler/src/index.ts +210 -0
  39. package/packages/compiler/src/jit.ts +187 -0
  40. package/packages/compiler/tsconfig.json +9 -0
  41. package/packages/core/package.json +55 -0
  42. package/packages/core/src/components.test.ts +125 -0
  43. package/packages/core/src/components.ts +181 -0
  44. package/packages/core/src/config.ts +204 -0
  45. package/packages/core/src/hooks.ts +142 -0
  46. package/packages/core/src/index.ts +59 -0
  47. package/packages/core/src/jsx-runtime.ts +46 -0
  48. package/packages/core/tsconfig.json +16 -0
  49. package/packages/dev-server/package.json +51 -0
  50. package/packages/dev-server/src/index.ts +306 -0
  51. package/packages/dev-server/src/jit-middleware.ts +78 -0
  52. package/packages/dev-server/tsconfig.json +9 -0
  53. package/packages/plugins/package.json +44 -0
  54. package/packages/plugins/src/cdn/loader.ts +275 -0
  55. package/packages/plugins/src/index.ts +238 -0
  56. package/packages/plugins/src/loaders/auto-import.ts +219 -0
  57. package/packages/plugins/src/loaders/external.ts +332 -0
  58. package/packages/plugins/src/transforms/index.ts +407 -0
  59. package/packages/plugins/src/types.ts +296 -0
  60. package/packages/plugins/tsconfig.json +8 -0
  61. package/packages/reactivity/package.json +36 -0
  62. package/packages/reactivity/src/computed.d.ts +3 -0
  63. package/packages/reactivity/src/computed.d.ts.map +1 -0
  64. package/packages/reactivity/src/computed.js +64 -0
  65. package/packages/reactivity/src/computed.js.map +1 -0
  66. package/packages/reactivity/src/computed.test.ts +83 -0
  67. package/packages/reactivity/src/computed.ts +69 -0
  68. package/packages/reactivity/src/index.d.ts +6 -0
  69. package/packages/reactivity/src/index.d.ts.map +1 -0
  70. package/packages/reactivity/src/index.js +7 -0
  71. package/packages/reactivity/src/index.js.map +1 -0
  72. package/packages/reactivity/src/index.ts +18 -0
  73. package/packages/reactivity/src/resource.d.ts +6 -0
  74. package/packages/reactivity/src/resource.d.ts.map +1 -0
  75. package/packages/reactivity/src/resource.js +43 -0
  76. package/packages/reactivity/src/resource.js.map +1 -0
  77. package/packages/reactivity/src/resource.test.ts +70 -0
  78. package/packages/reactivity/src/resource.ts +59 -0
  79. package/packages/reactivity/src/signal.d.ts +7 -0
  80. package/packages/reactivity/src/signal.d.ts.map +1 -0
  81. package/packages/reactivity/src/signal.js +145 -0
  82. package/packages/reactivity/src/signal.js.map +1 -0
  83. package/packages/reactivity/src/signal.test.ts +130 -0
  84. package/packages/reactivity/src/signal.ts +207 -0
  85. package/packages/reactivity/src/store.d.ts +4 -0
  86. package/packages/reactivity/src/store.d.ts.map +1 -0
  87. package/packages/reactivity/src/store.js +62 -0
  88. package/packages/reactivity/src/store.js.map +1 -0
  89. package/packages/reactivity/src/store.test.ts +38 -0
  90. package/packages/reactivity/src/store.ts +111 -0
  91. package/packages/reactivity/src/types.d.ts +43 -0
  92. package/packages/reactivity/src/types.d.ts.map +1 -0
  93. package/packages/reactivity/src/types.js +3 -0
  94. package/packages/reactivity/src/types.js.map +1 -0
  95. package/packages/reactivity/src/types.ts +43 -0
  96. package/packages/reactivity/tsconfig.json +9 -0
  97. package/packages/router/package.json +44 -0
  98. package/packages/router/src/components.tsx +150 -0
  99. package/packages/router/src/fs-router.ts +163 -0
  100. package/packages/router/src/index.ts +22 -0
  101. package/packages/router/src/router.test.ts +111 -0
  102. package/packages/router/src/router.ts +112 -0
  103. package/packages/router/src/types.ts +69 -0
  104. package/packages/router/tsconfig.json +10 -0
  105. package/packages/server/package.json +41 -0
  106. package/packages/server/src/action.test.ts +102 -0
  107. package/packages/server/src/action.ts +201 -0
  108. package/packages/server/src/api.ts +143 -0
  109. package/packages/server/src/index.ts +18 -0
  110. package/packages/server/src/types.ts +72 -0
  111. package/packages/server/tsconfig.json +9 -0
  112. package/pnpm-workspace.yaml +4 -0
  113. package/scripts/publish.ps1 +138 -0
  114. package/scripts/publish.sh +142 -0
  115. package/tsconfig.json +28 -0
  116. 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;