bertui 1.2.2 → 1.2.4

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 (125) hide show
  1. package/index.js +1 -1
  2. package/package.json +43 -25
  3. package/src/build/compiler/file-transpiler.js +1 -0
  4. package/src/build/generators/html-generator.js +24 -9
  5. package/src/build.js +12 -3
  6. package/src/client/compiler.js +1 -0
  7. package/src/dev.js +49 -218
  8. package/src/server/dev-server-utils.js +2 -1
  9. package/types/bin/bertui.d.ts +3 -0
  10. package/types/bin/bertui.d.ts.map +1 -0
  11. package/types/error-overlay.d.ts +2 -0
  12. package/types/error-overlay.d.ts.map +1 -0
  13. package/types/index.d.ts +26 -177
  14. package/types/index.d.ts.map +1 -0
  15. package/types/scripts/fix-wasm-exports.d.ts +2 -0
  16. package/types/scripts/fix-wasm-exports.d.ts.map +1 -0
  17. package/types/src/analyzer/index.d.ts +8 -0
  18. package/types/src/analyzer/index.d.ts.map +1 -0
  19. package/types/src/build/compiler/file-transpiler.d.ts +5 -0
  20. package/types/src/build/compiler/file-transpiler.d.ts.map +1 -0
  21. package/types/src/build/compiler/index.d.ts +12 -0
  22. package/types/src/build/compiler/index.d.ts.map +1 -0
  23. package/types/src/build/compiler/route-discoverer.d.ts +2 -0
  24. package/types/src/build/compiler/route-discoverer.d.ts.map +1 -0
  25. package/types/src/build/compiler/router-generator.d.ts +2 -0
  26. package/types/src/build/compiler/router-generator.d.ts.map +1 -0
  27. package/types/src/build/css-builder.d.ts +18 -0
  28. package/types/src/build/css-builder.d.ts.map +1 -0
  29. package/types/src/build/generators/html-generator.d.ts +2 -0
  30. package/types/src/build/generators/html-generator.d.ts.map +1 -0
  31. package/types/src/build/generators/robots-generator.d.ts +11 -0
  32. package/types/src/build/generators/robots-generator.d.ts.map +1 -0
  33. package/types/src/build/generators/sitemap-generator.d.ts +5 -0
  34. package/types/src/build/generators/sitemap-generator.d.ts.map +1 -0
  35. package/types/src/build/image-optimizer.d.ts +11 -0
  36. package/types/src/build/image-optimizer.d.ts.map +1 -0
  37. package/types/src/build/processors/asset-processor.d.ts +2 -0
  38. package/types/src/build/processors/asset-processor.d.ts.map +1 -0
  39. package/types/src/build/processors/css-builder.d.ts +2 -0
  40. package/types/src/build/processors/css-builder.d.ts.map +1 -0
  41. package/types/src/build/server-island-validator.d.ts +27 -0
  42. package/types/src/build/server-island-validator.d.ts.map +1 -0
  43. package/types/src/build.d.ts +5 -0
  44. package/types/src/build.d.ts.map +1 -0
  45. package/types/src/cli.d.ts +2 -0
  46. package/types/src/cli.d.ts.map +1 -0
  47. package/types/src/client/compiler.d.ts +16 -0
  48. package/types/src/client/compiler.d.ts.map +1 -0
  49. package/types/src/client/fast-refresh.d.ts +3 -0
  50. package/types/src/client/fast-refresh.d.ts.map +1 -0
  51. package/types/src/client/hmr-runtime.d.ts +4 -0
  52. package/types/src/client/hmr-runtime.d.ts.map +1 -0
  53. package/types/src/compiler/index.d.ts +8 -0
  54. package/types/src/compiler/index.d.ts.map +1 -0
  55. package/types/src/compiler/router-generator-pure.d.ts +2 -0
  56. package/types/src/compiler/router-generator-pure.d.ts.map +1 -0
  57. package/types/src/compiler/transform.d.ts +36 -0
  58. package/types/src/compiler/transform.d.ts.map +1 -0
  59. package/types/src/config/defaultConfig.d.ts +26 -0
  60. package/types/src/config/defaultConfig.d.ts.map +1 -0
  61. package/types/src/config/index.d.ts +3 -0
  62. package/types/src/config/index.d.ts.map +1 -0
  63. package/types/src/config/loadConfig.d.ts +2 -0
  64. package/types/src/config/loadConfig.d.ts.map +1 -0
  65. package/types/src/css/index.d.ts +6 -0
  66. package/types/src/css/index.d.ts.map +1 -0
  67. package/types/src/css/processor.d.ts +23 -0
  68. package/types/src/css/processor.d.ts.map +1 -0
  69. package/types/src/dev.d.ts +2 -0
  70. package/types/src/dev.d.ts.map +1 -0
  71. package/types/src/hydration/index.d.ts +33 -0
  72. package/types/src/hydration/index.d.ts.map +1 -0
  73. package/types/src/image-optimizer/index.d.ts +24 -0
  74. package/types/src/image-optimizer/index.d.ts.map +1 -0
  75. package/types/src/images/index.d.ts +12 -0
  76. package/types/src/images/index.d.ts.map +1 -0
  77. package/types/src/images/processor.d.ts +30 -0
  78. package/types/src/images/processor.d.ts.map +1 -0
  79. package/types/src/layouts/index.d.ts +28 -0
  80. package/types/src/layouts/index.d.ts.map +1 -0
  81. package/types/src/loading/index.d.ts +28 -0
  82. package/types/src/loading/index.d.ts.map +1 -0
  83. package/types/src/logger/logger.d.ts +30 -0
  84. package/types/src/logger/logger.d.ts.map +1 -0
  85. package/types/src/middleware/index.d.ts +61 -0
  86. package/types/src/middleware/index.d.ts.map +1 -0
  87. package/types/src/router/Router.d.ts +16 -0
  88. package/types/src/router/Router.d.ts.map +1 -0
  89. package/types/src/router/SSRRouter.d.ts +20 -0
  90. package/types/src/router/SSRRouter.d.ts.map +1 -0
  91. package/types/src/router/index.d.ts +3 -0
  92. package/types/src/router/index.d.ts.map +1 -0
  93. package/types/src/scaffolder/index.d.ts +14 -0
  94. package/types/src/scaffolder/index.d.ts.map +1 -0
  95. package/types/src/serve.d.ts +3 -0
  96. package/types/src/serve.d.ts.map +1 -0
  97. package/types/src/server/dev-handler.d.ts +13 -0
  98. package/types/src/server/dev-handler.d.ts.map +1 -0
  99. package/types/src/server/dev-server-utils.d.ts +6 -0
  100. package/types/src/server/dev-server-utils.d.ts.map +1 -0
  101. package/types/src/server/dev-server.d.ts +18 -0
  102. package/types/src/server/dev-server.d.ts.map +1 -0
  103. package/types/src/server/hmr-handler.d.ts +19 -0
  104. package/types/src/server/hmr-handler.d.ts.map +1 -0
  105. package/types/src/server/index.d.ts +4 -0
  106. package/types/src/server/index.d.ts.map +1 -0
  107. package/types/src/server/request-handler.d.ts +19 -0
  108. package/types/src/server/request-handler.d.ts.map +1 -0
  109. package/types/src/server-islands/extractor.d.ts +16 -0
  110. package/types/src/server-islands/extractor.d.ts.map +1 -0
  111. package/types/src/server-islands/index.d.ts +3 -0
  112. package/types/src/server-islands/index.d.ts.map +1 -0
  113. package/types/src/utils/cache.d.ts +52 -0
  114. package/types/src/utils/cache.d.ts.map +1 -0
  115. package/types/src/utils/env.d.ts +20 -0
  116. package/types/src/utils/env.d.ts.map +1 -0
  117. package/types/src/utils/importhow.d.ts +15 -0
  118. package/types/src/utils/importhow.d.ts.map +1 -0
  119. package/types/src/utils/index.d.ts +3 -0
  120. package/types/src/utils/index.d.ts.map +1 -0
  121. package/types/src/utils/meta-extractor.d.ts +13 -0
  122. package/types/src/utils/meta-extractor.d.ts.map +1 -0
  123. package/types/config.d.ts +0 -33
  124. package/types/react.d.ts +0 -16
  125. package/types/router.d.ts +0 -79
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // bertui/index.js - v1.3.0
1
+ // bertui/index.js - v1.2.2
2
2
 
3
3
  // Compiler
4
4
  export { compileProject, compileFile } from './src/client/compiler.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bertui",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Lightning-fast React dev server powered by Bun - Now with Rust image optimization (WASM, no Rust required for users)",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -8,31 +8,49 @@
8
8
  "bin": {
9
9
  "bertui": "./bin/bertui.js"
10
10
  },
11
- "exports": {
12
- ".": {
13
- "types": "./types/index.d.ts",
14
- "import": "./index.js",
15
- "default": "./index.js"
16
- },
17
- "./hmr": {
18
- "import": "./src/client/hmr-runtime.js"
19
- },
20
- "./image-optimizer": {
21
- "import": "./src/image-optimizer/index.js"
22
- },
23
- "./router": {
24
- "types": "./types/router.d.ts",
25
- "import": "./src/router/index.js"
26
- },
27
- "./config": {
28
- "types": "./types/config.d.ts",
29
- "import": "./src/config/index.js"
30
- },
31
- "./logger": {
32
- "import": "./src/logger/logger.js"
33
- },
34
- "./styles": "./src/styles/bertui.css"
11
+ "exports": {
12
+ ".": {
13
+ "types": "./types/index.d.ts",
14
+ "import": "./index.js",
15
+ "default": "./index.js"
35
16
  },
17
+ "./hmr": {
18
+ "import": "./src/client/hmr-runtime.js"
19
+ },
20
+ "./image-optimizer": {
21
+ "import": "./src/image-optimizer/index.js"
22
+ },
23
+ "./router": {
24
+ "types": "./types/router.d.ts",
25
+ "import": "./src/router/index.js"
26
+ },
27
+ "./config": {
28
+ "types": "./types/config.d.ts",
29
+ "import": "./src/config/index.js"
30
+ },
31
+ "./logger": {
32
+ "import": "./src/logger/logger.js"
33
+ },
34
+ "./styles": "./src/styles/bertui.css",
35
+ "./compiler": {
36
+ "import": "./src/client/compiler.js"
37
+ },
38
+ "./layouts": {
39
+ "import": "./src/layouts/index.js"
40
+ },
41
+ "./loading": {
42
+ "import": "./src/loading/index.js"
43
+ },
44
+ "./hydration": {
45
+ "import": "./src/hydration/index.js"
46
+ },
47
+ "./dev": {
48
+ "import": "./src/server/dev-server-utils.js"
49
+ },
50
+ "./build": {
51
+ "import": "./src/build.js"
52
+ }
53
+ },
36
54
  "files": [
37
55
  "bin",
38
56
  "src",
@@ -49,6 +49,7 @@ async function _compileDir(srcDir, buildDir, root, envVars, aliasMap) {
49
49
  const stat = statSync(srcPath);
50
50
 
51
51
  if (stat.isDirectory()) {
52
+ if (file === 'api') { logger.debug('⏭️ Skipping api/'); continue; }
52
53
  const subBuildDir = join(buildDir, file);
53
54
  mkdirSync(subBuildDir, { recursive: true });
54
55
  await _compileDir(srcPath, subBuildDir, root, envVars, aliasMap);
@@ -17,7 +17,6 @@ export async function generateProductionHTML(root, outDir, buildResult, routes,
17
17
  const bundlePath = relative(outDir, mainBundle.path).replace(/\\/g, '/');
18
18
  const defaultMeta = config.meta || {};
19
19
 
20
- // ✅ Copy bertui-icons AND bertui-animate to dist/
21
20
  const bertuiPackages = await copyBertuiPackagesToProduction(root, outDir);
22
21
 
23
22
  logger.info(`📄 Generating HTML for ${routes.length} routes...`);
@@ -36,12 +35,12 @@ export async function generateProductionHTML(root, outDir, buildResult, routes,
36
35
  logger.success(`✅ HTML generation complete for ${routes.length} routes`);
37
36
  }
38
37
 
39
- // ✅ UPDATED: Copy ALL bertui-* packages to dist/
40
38
  async function copyBertuiPackagesToProduction(root, outDir) {
41
39
  const nodeModulesDir = join(root, 'node_modules');
42
40
  const packages = {
43
41
  bertuiIcons: false,
44
- bertuiAnimate: false
42
+ bertuiAnimate: false,
43
+ elysiaEden: false
45
44
  };
46
45
 
47
46
  if (!existsSync(nodeModulesDir)) {
@@ -63,14 +62,13 @@ async function copyBertuiPackagesToProduction(root, outDir) {
63
62
  }
64
63
  }
65
64
 
66
- // ✅ NEW: Copy ONLY bertui-animate CSS files (not the whole package)
65
+ // Copy bertui-animate CSS files
67
66
  const bertuiAnimateSource = join(nodeModulesDir, 'bertui-animate', 'dist');
68
67
  if (existsSync(bertuiAnimateSource)) {
69
68
  try {
70
69
  const bertuiAnimateDest = join(outDir, 'css');
71
70
  mkdirSync(bertuiAnimateDest, { recursive: true });
72
71
 
73
- // Copy minified CSS
74
72
  const minCSSPath = join(bertuiAnimateSource, 'bertui-animate.min.css');
75
73
  if (existsSync(minCSSPath)) {
76
74
  cpSync(minCSSPath, join(bertuiAnimateDest, 'bertui-animate.min.css'));
@@ -81,6 +79,20 @@ async function copyBertuiPackagesToProduction(root, outDir) {
81
79
  logger.error(`Failed to copy bertui-animate: ${error.message}`);
82
80
  }
83
81
  }
82
+
83
+ // Copy @elysiajs/eden
84
+ const elysiaEdenSource = join(nodeModulesDir, '@elysiajs', 'eden');
85
+ if (existsSync(elysiaEdenSource)) {
86
+ try {
87
+ const elysiaEdenDest = join(outDir, 'node_modules', '@elysiajs', 'eden');
88
+ mkdirSync(join(outDir, 'node_modules', '@elysiajs'), { recursive: true });
89
+ cpSync(elysiaEdenSource, elysiaEdenDest, { recursive: true });
90
+ logger.success('✅ Copied @elysiajs/eden to dist/node_modules/');
91
+ packages.elysiaEden = true;
92
+ } catch (error) {
93
+ logger.error(`Failed to copy @elysiajs/eden: ${error.message}`);
94
+ }
95
+ }
84
96
 
85
97
  return packages;
86
98
  }
@@ -261,7 +273,6 @@ async function extractStaticHTMLFromComponent(sourceCode, filePath) {
261
273
  }
262
274
  }
263
275
 
264
- // ✅ UPDATED: Add bertui-animate CSS to production HTML
265
276
  function generateHTML(meta, route, bundlePath, staticHTML = '', isServerIsland = false, bertuiPackages = {}) {
266
277
  const rootContent = staticHTML
267
278
  ? `<div id="root">${staticHTML}</div>`
@@ -271,15 +282,18 @@ function generateHTML(meta, route, bundlePath, staticHTML = '', isServerIsland =
271
282
  ? '<!-- 🏝️ Server Island: Static content rendered at build time -->'
272
283
  : '<!-- ⚡ Client-only: Content rendered by JavaScript -->';
273
284
 
274
- // ✅ Add bertui-icons to import map
275
285
  const bertuiIconsImport = bertuiPackages.bertuiIcons
276
286
  ? ',\n "bertui-icons": "/node_modules/bertui-icons/generated/index.js"'
277
287
  : '';
278
288
 
279
- // ✅ Add bertui-animate CSS link
280
289
  const bertuiAnimateCSS = bertuiPackages.bertuiAnimate
281
290
  ? ' <link rel="stylesheet" href="/css/bertui-animate.min.css">'
282
291
  : '';
292
+
293
+ // ✅ NEW: @elysiajs/eden local import map
294
+ const elysiaEdenImport = bertuiPackages.elysiaEden
295
+ ? ',\n "@elysiajs/eden": "/node_modules/@elysiajs/eden/dist/index.mjs"'
296
+ : '';
283
297
 
284
298
  return `<!DOCTYPE html>
285
299
  <html lang="${meta.lang || 'en'}">
@@ -306,8 +320,9 @@ ${bertuiAnimateCSS}
306
320
  "imports": {
307
321
  "react": "https://esm.sh/react@18.2.0",
308
322
  "react-dom": "https://esm.sh/react-dom@18.2.0",
323
+ "@bunnyx/api": "/bunnyx-api/api-client.js",
309
324
  "react-dom/client": "https://esm.sh/react-dom@18.2.0/client",
310
- "react/jsx-runtime": "https://esm.sh/react@18.2.0/jsx-runtime"${bertuiIconsImport}
325
+ "react/jsx-runtime": "https://esm.sh/react@18.2.0/jsx-runtime"${bertuiIconsImport}${elysiaEdenImport}
311
326
  }
312
327
  }
313
328
  </script>
package/src/build.js CHANGED
@@ -97,7 +97,8 @@ export async function buildProduction(options = {}) {
97
97
 
98
98
  if (existsSync(buildDir)) rmSync(buildDir, { recursive: true, force: true });
99
99
 
100
- await analyzeBuild(outDir, { outputFile: join(outDir, 'bundle-report.html') });
100
+ // Fire-and-forget don't let the report generator block process exit
101
+ analyzeBuild(outDir, { outputFile: join(outDir, 'bundle-report.html') }).catch(() => {});
101
102
 
102
103
  // ── Summary ──────────────────────────────────────────────────────────────
103
104
  logger.printSummary({
@@ -126,6 +127,7 @@ async function generateProductionImportMap(root, config) {
126
127
  'react-dom': 'https://esm.sh/react-dom@18.2.0',
127
128
  'react-dom/client': 'https://esm.sh/react-dom@18.2.0/client',
128
129
  'react/jsx-runtime': 'https://esm.sh/react@18.2.0/jsx-runtime',
130
+ '@bunnyx/api': '/bunnyx-api/api-client.js',
129
131
  };
130
132
 
131
133
  const nodeModulesDir = join(root, 'node_modules');
@@ -183,6 +185,13 @@ async function bundleJavaScript(buildEntry, routerPath, outDir, envVars, buildDi
183
185
  await Bun.write(join(outDir, 'import-map.json'), JSON.stringify({ imports: importMap }, null, 2));
184
186
  await copyNodeModulesToDist(root, outDir, importMap);
185
187
 
188
+ // Copy @bunnyx/api client to dist so the importmap entry resolves
189
+ const bunnyxSrc = join(root, 'bunnyx-api', 'api-client.js');
190
+ if (existsSync(bunnyxSrc)) {
191
+ mkdirSync(join(outDir, 'bunnyx-api'), { recursive: true });
192
+ await Bun.write(join(outDir, 'bunnyx-api', 'api-client.js'), Bun.file(bunnyxSrc));
193
+ }
194
+
186
195
  const result = await Bun.build({
187
196
  entrypoints,
188
197
  outdir: join(outDir, 'assets'),
@@ -201,7 +210,7 @@ async function bundleJavaScript(buildEntry, routerPath, outDir, envVars, buildDi
201
210
  chunk: 'js/chunks/[name]-[hash].js',
202
211
  asset: 'assets/[name]-[hash].[ext]',
203
212
  },
204
- external: ['react', 'react-dom', 'react-dom/client', 'react/jsx-runtime'],
213
+ external: ['react', 'react-dom', 'react-dom/client', 'react/jsx-runtime', '@bunnyx/api'],
205
214
  define: {
206
215
  'process.env.NODE_ENV': '"production"',
207
216
  ...Object.fromEntries(
@@ -228,9 +237,9 @@ async function bundleJavaScript(buildEntry, routerPath, outDir, envVars, buildDi
228
237
  export async function build(options = {}) {
229
238
  try {
230
239
  await buildProduction(options);
231
- process.exit(0);
232
240
  } catch (error) {
233
241
  console.error(error);
234
242
  process.exit(1);
235
243
  }
244
+ process.exit(0);
236
245
  }
@@ -302,6 +302,7 @@ async function compileDirectory(srcDir, outDir, root, envVars, aliasMap) {
302
302
 
303
303
  if (stat.isDirectory()) {
304
304
  if (file === 'templates') { logger.debug('⏭️ Skipping src/templates/'); continue; }
305
+ if (file === 'api') { logger.debug('⏭️ Skipping src/api/'); continue; }
305
306
  const subOutDir = join(outDir, file);
306
307
  mkdirSync(subOutDir, { recursive: true });
307
308
  const subStats = await compileDirectory(srcPath, subOutDir, root, envVars, aliasMap);
package/src/dev.js CHANGED
@@ -1,237 +1,68 @@
1
- // bertui/src/build.js
2
- import { join } from 'path';
3
- import { existsSync, mkdirSync, rmSync, readdirSync, statSync } from 'fs';
4
- import logger from './logger/logger.js';
5
- import { loadEnvVariables } from './utils/env.js';
6
- import { globalCache } from './utils/cache.js';
7
-
8
- import { compileForBuild } from './build/compiler/index.js';
9
- import { buildAllCSS } from './build/processors/css-builder.js';
10
- import { copyAllStaticAssets } from './build/processors/asset-processor.js';
11
- import { generateProductionHTML } from './build/generators/html-generator.js';
12
- import { generateSitemap } from './build/generators/sitemap-generator.js';
13
- import { generateRobots } from './build/generators/robots-generator.js';
1
+ // bertui/src/dev.js
2
+ import { compileProject } from './client/compiler.js';
3
+ import { startDevServer } from './server/dev-server.js';
4
+ import { MiddlewareManager } from './middleware/index.js';
14
5
  import { compileLayouts } from './layouts/index.js';
15
6
  import { compileLoadingComponents } from './loading/index.js';
16
7
  import { analyzeRoutes, logHydrationReport } from './hydration/index.js';
17
- import { analyzeBuild } from './analyzer/index.js';
18
- import { buildAliasMap } from './utils/importhow.js';
19
-
20
- const TOTAL_STEPS = 10;
21
-
22
- export async function buildProduction(options = {}) {
23
- const root = options.root || process.cwd();
24
- const buildDir = join(root, '.bertuibuild');
25
- const outDir = join(root, 'dist');
26
-
27
- process.env.NODE_ENV = 'production';
8
+ import logger from './logger/logger.js';
9
+ import { loadConfig } from './config/loadConfig.js';
28
10
 
29
- logger.printHeader('BUILD');
11
+ const TOTAL_STEPS = 6;
30
12
 
31
- if (existsSync(buildDir)) rmSync(buildDir, { recursive: true, force: true });
32
- if (existsSync(outDir)) rmSync(outDir, { recursive: true, force: true });
33
- mkdirSync(buildDir, { recursive: true });
34
- mkdirSync(outDir, { recursive: true });
13
+ export async function startDev(options = {}) {
14
+ const root = options.root || process.cwd();
15
+ const port = options.port || 3000;
35
16
 
36
- let totalKB = '0';
17
+ logger.printHeader('DEV');
37
18
 
38
19
  try {
39
- // ── Step 1: Env ──────────────────────────────────────────────────────────
40
- logger.step(1, TOTAL_STEPS, 'Loading env');
41
- const envVars = loadEnvVariables(root);
42
- const { loadConfig } = await import('./config/loadConfig.js');
43
- const config = await loadConfig(root);
44
- const importhow = config.importhow || {};
45
- logger.stepDone('Loading env', `${Object.keys(envVars).length} vars`);
46
-
47
- // ── Step 2: Compile ──────────────────────────────────────────────────────
48
- logger.step(2, TOTAL_STEPS, 'Compiling');
49
- const { routes, serverIslands, clientRoutes } = await compileForBuild(root, buildDir, envVars, config);
50
- logger.stepDone('Compiling', `${routes.length} routes · ${serverIslands.length} islands`);
51
-
52
- // ── Step 3: Layouts ──────────────────────────────────────────────────────
53
- logger.step(3, TOTAL_STEPS, 'Layouts');
54
- const layouts = await compileLayouts(root, buildDir);
55
- logger.stepDone('Layouts', `${Object.keys(layouts).length} found`);
56
-
57
- // ── Step 4: Loading states ───────────────────────────────────────────────
58
- logger.step(4, TOTAL_STEPS, 'Loading states');
59
- await compileLoadingComponents(root, buildDir);
20
+ const config = await loadConfig(root);
21
+
22
+ // ── Step 1: Compile ──────────────────────────────────────────────────────
23
+ logger.step(1, TOTAL_STEPS, 'Compiling');
24
+ const { routes, outDir } = await compileProject(root);
25
+ logger.stepDone('Compiling', `${routes.length} routes`);
26
+
27
+ // ── Step 2: Layouts ──────────────────────────────────────────────────────
28
+ logger.step(2, TOTAL_STEPS, 'Layouts');
29
+ const layouts = await compileLayouts(root, outDir);
30
+ const layoutCount = Object.keys(layouts).length;
31
+ logger.stepDone('Layouts', layoutCount > 0 ? `${layoutCount} found` : 'none');
32
+
33
+ // ── Step 3: Loading states ───────────────────────────────────────────────
34
+ logger.step(3, TOTAL_STEPS, 'Loading states');
35
+ const loadingComponents = await compileLoadingComponents(root, outDir);
60
36
  logger.stepDone('Loading states');
61
37
 
62
- // ── Step 5: Hydration analysis ───────────────────────────────────────────
63
- logger.step(5, TOTAL_STEPS, 'Hydration analysis');
64
- const analyzedRoutes = await analyzeRoutes(routes);
65
- logger.stepDone('Hydration analysis',
66
- `${analyzedRoutes.interactive.length} interactive · ${analyzedRoutes.static.length} static`);
67
-
68
- // ── Step 6: CSS ──────────────────────────────────────────────────────────
69
- logger.step(6, TOTAL_STEPS, 'Processing CSS');
70
- await buildAllCSS(root, outDir);
71
- logger.stepDone('Processing CSS');
72
-
73
- // ── Step 7: Static assets ────────────────────────────────────────────────
74
- logger.step(7, TOTAL_STEPS, 'Static assets');
75
- await copyAllStaticAssets(root, outDir);
76
- logger.stepDone('Static assets');
77
-
78
- // ── Step 8: Bundle JS ────────────────────────────────────────────────────
79
- logger.step(8, TOTAL_STEPS, 'Bundling JS');
80
- const buildEntry = join(buildDir, 'main.js');
81
- const routerPath = join(buildDir, 'router.js');
82
- if (!existsSync(buildEntry)) throw new Error('main.js not found in build dir');
83
- const result = await bundleJavaScript(buildEntry, routerPath, outDir, envVars, buildDir, analyzedRoutes, importhow, root, config);
84
- totalKB = (result.outputs.reduce((a, o) => a + (o.size || 0), 0) / 1024).toFixed(1);
85
- logger.stepDone('Bundling JS', `${totalKB} KB · tree-shaken`);
86
-
87
- // ── Step 9: HTML ─────────────────────────────────────────────────────────
88
- logger.step(9, TOTAL_STEPS, 'Generating HTML');
89
- await generateProductionHTML(root, outDir, result, routes, serverIslands, config);
90
- logger.stepDone('Generating HTML', `${routes.length} pages`);
91
-
92
- // ── Step 10: Sitemap + robots ────────────────────────────────────────────
93
- logger.step(10, TOTAL_STEPS, 'Sitemap & robots');
94
- await generateSitemap(routes, config, outDir);
95
- await generateRobots(config, outDir, routes);
96
- logger.stepDone('Sitemap & robots');
97
-
98
- if (existsSync(buildDir)) rmSync(buildDir, { recursive: true, force: true });
99
-
100
- // Fire-and-forget — don't let the report generator block process exit
101
- analyzeBuild(outDir, { outputFile: join(outDir, 'bundle-report.html') }).catch(() => {});
102
-
103
- // ── Summary ──────────────────────────────────────────────────────────────
104
- logger.printSummary({
105
- routes: routes.length,
106
- serverIslands: serverIslands.length,
107
- interactive: analyzedRoutes.interactive.length,
108
- staticRoutes: analyzedRoutes.static.length,
109
- jsSize: `${totalKB} KB`,
110
- outDir: 'dist/',
111
- });
112
-
113
- return { success: true };
114
-
115
- } catch (error) {
116
- logger.stepFail('Build', error.message);
117
- if (existsSync(buildDir)) rmSync(buildDir, { recursive: true, force: true });
118
- throw error;
119
- }
120
- }
121
-
122
- // ─────────────────────────────────────────────────────────────────────────────
123
-
124
- async function generateProductionImportMap(root, config) {
125
- const importMap = {
126
- 'react': 'https://esm.sh/react@18.2.0',
127
- 'react-dom': 'https://esm.sh/react-dom@18.2.0',
128
- 'react-dom/client': 'https://esm.sh/react-dom@18.2.0/client',
129
- 'react/jsx-runtime': 'https://esm.sh/react@18.2.0/jsx-runtime',
130
- };
131
-
132
- const nodeModulesDir = join(root, 'node_modules');
133
- if (!existsSync(nodeModulesDir)) return importMap;
134
-
135
- try {
136
- for (const pkg of readdirSync(nodeModulesDir)) {
137
- if (!pkg.startsWith('bertui-') || pkg.startsWith('.')) continue;
138
- const pkgDir = join(nodeModulesDir, pkg);
139
- const pkgJson = join(pkgDir, 'package.json');
140
- if (!existsSync(pkgJson)) continue;
141
- try {
142
- const p = JSON.parse(await Bun.file(pkgJson).text());
143
- for (const entry of [p.browser, p.module, p.main, 'dist/index.js', 'index.js'].filter(Boolean)) {
144
- if (existsSync(join(pkgDir, entry))) {
145
- importMap[pkg] = `/assets/node_modules/${pkg}/${entry}`;
146
- break;
147
- }
148
- }
149
- } catch { continue; }
150
- }
151
- } catch { /* ignore */ }
152
-
153
- return importMap;
154
- }
155
-
156
- async function copyNodeModulesToDist(root, outDir, importMap) {
157
- const dest = join(outDir, 'assets', 'node_modules');
158
- mkdirSync(dest, { recursive: true });
159
- const src = join(root, 'node_modules');
160
-
161
- for (const [, assetPath] of Object.entries(importMap)) {
162
- if (assetPath.startsWith('https://')) continue;
163
- const match = assetPath.match(/\/assets\/node_modules\/(.+)$/);
164
- if (!match) continue;
165
- const parts = match[1].split('/');
166
- const pkgName = parts[0];
167
- const subPath = parts.slice(1);
168
- const srcFile = join(src, pkgName, ...subPath);
169
- const destFile = join(dest, pkgName, ...subPath);
170
- mkdirSync(join(dest, pkgName, ...subPath.slice(0, -1)), { recursive: true });
171
- if (existsSync(srcFile)) await Bun.write(destFile, Bun.file(srcFile));
172
- }
173
- }
174
-
175
- async function bundleJavaScript(buildEntry, routerPath, outDir, envVars, buildDir, analyzedRoutes, importhow, root, config) {
176
- const originalCwd = process.cwd();
177
- process.chdir(buildDir);
178
-
179
- try {
180
- const entrypoints = [buildEntry];
181
- if (existsSync(routerPath)) entrypoints.push(routerPath);
182
-
183
- const importMap = await generateProductionImportMap(root, config);
184
- await Bun.write(join(outDir, 'import-map.json'), JSON.stringify({ imports: importMap }, null, 2));
185
- await copyNodeModulesToDist(root, outDir, importMap);
186
-
187
- const result = await Bun.build({
188
- entrypoints,
189
- outdir: join(outDir, 'assets'),
190
- target: 'browser',
191
- format: 'esm',
192
- minify: {
193
- whitespace: true,
194
- syntax: true,
195
- identifiers: true,
196
- },
197
- splitting: true,
198
- sourcemap: 'external',
199
- metafile: true,
200
- naming: {
201
- entry: 'js/[name]-[hash].js',
202
- chunk: 'js/chunks/[name]-[hash].js',
203
- asset: 'assets/[name]-[hash].[ext]',
204
- },
205
- external: ['react', 'react-dom', 'react-dom/client', 'react/jsx-runtime'],
206
- define: {
207
- 'process.env.NODE_ENV': '"production"',
208
- ...Object.fromEntries(
209
- Object.entries(envVars).map(([k, v]) => [`process.env.${k}`, JSON.stringify(v)])
210
- ),
211
- },
212
- });
213
-
214
- if (!result.success) {
215
- throw new Error(`Bundle failed\n${result.logs?.map(l => l.message).join('\n') || 'Unknown error'}`);
38
+ // ── Step 4: Hydration analysis ───────────────────────────────────────────
39
+ logger.step(4, TOTAL_STEPS, 'Hydration analysis');
40
+ if (routes && routes.length > 0) {
41
+ const analyzedRoutes = await analyzeRoutes(routes);
42
+ logger.stepDone('Hydration analysis',
43
+ `${analyzedRoutes.interactive.length} interactive · ${analyzedRoutes.static.length} static`);
44
+ } else {
45
+ logger.stepDone('Hydration analysis', 'no routes');
216
46
  }
217
47
 
218
- if (result.metafile) {
219
- await Bun.write(join(outDir, 'metafile.json'), JSON.stringify(result.metafile, null, 2));
220
- }
48
+ // ── Step 5: Middleware ───────────────────────────────────────────────────
49
+ logger.step(5, TOTAL_STEPS, 'Middleware');
50
+ const middlewareManager = new MiddlewareManager(root);
51
+ await middlewareManager.load();
52
+ middlewareManager.watch();
53
+ logger.stepDone('Middleware');
221
54
 
222
- return result;
55
+ // ── Step 6: Dev server ───────────────────────────────────────────────────
56
+ logger.step(6, TOTAL_STEPS, 'Starting server');
57
+ await startDevServer({ root, port, middleware: middlewareManager, layouts, loadingComponents });
58
+ logger.stepDone('Starting server', `http://localhost:${port}`);
223
59
 
224
- } finally {
225
- process.chdir(originalCwd);
226
- }
227
- }
60
+ // ── Ready ────────────────────────────────────────────────────────────────
61
+ process.stdout.write(`\n ${'\x1b[1m'}\x1b[32m▶ Ready on http://localhost:${port}\x1b[0m\n\n`);
228
62
 
229
- export async function build(options = {}) {
230
- try {
231
- await buildProduction(options);
232
63
  } catch (error) {
233
- console.error(error);
64
+ logger.stepFail('Dev server', error.message);
65
+ logger.error(error.stack || error.message);
234
66
  process.exit(1);
235
67
  }
236
- process.exit(0);
237
68
  }
@@ -77,8 +77,9 @@ export async function buildDevImportMap(root) {
77
77
  'react-dom': 'https://esm.sh/react-dom@18.2.0',
78
78
  'react-dom/client': 'https://esm.sh/react-dom@18.2.0/client',
79
79
  'react/jsx-runtime': 'https://esm.sh/react@18.2.0/jsx-runtime',
80
+ '@bunnyx/api': '/bunnyx-api/api-client.js',
81
+ '@elysiajs/eden': '/node_modules/@elysiajs/eden/dist/index.mjs',
80
82
  };
81
-
82
83
  const SKIP = new Set(['react', 'react-dom', '.bin', '.cache', '.package-lock.json', '.yarn']);
83
84
 
84
85
  if (existsSync(nodeModulesDir)) {
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bun
2
+ export {};
3
+ //# sourceMappingURL=bertui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bertui.d.ts","sourceRoot":"","sources":["../../bin/bertui.js"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=error-overlay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-overlay.d.ts","sourceRoot":"","sources":["../error-overlay.js"],"names":[],"mappings":""}