bertui 1.2.2 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/src/build.js +3 -2
  3. package/src/dev.js +49 -218
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bertui",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
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",
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({
@@ -228,9 +229,9 @@ async function bundleJavaScript(buildEntry, routerPath, outDir, envVars, buildDi
228
229
  export async function build(options = {}) {
229
230
  try {
230
231
  await buildProduction(options);
231
- process.exit(0);
232
232
  } catch (error) {
233
233
  console.error(error);
234
234
  process.exit(1);
235
235
  }
236
+ process.exit(0);
236
237
  }
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
  }