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.
- package/package.json +1 -1
- package/src/build.js +3 -2
- package/src/dev.js +49 -218
package/package.json
CHANGED
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
|
-
|
|
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/
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
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
|
|
18
|
-
import {
|
|
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
|
-
|
|
11
|
+
const TOTAL_STEPS = 6;
|
|
30
12
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
17
|
+
logger.printHeader('DEV');
|
|
37
18
|
|
|
38
19
|
try {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
logger.stepDone('
|
|
51
|
-
|
|
52
|
-
// ── Step 3:
|
|
53
|
-
logger.step(3, TOTAL_STEPS, '
|
|
54
|
-
const
|
|
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
|
|
63
|
-
logger.step(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
219
|
-
|
|
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
|
-
|
|
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
|
-
|
|
225
|
-
process.
|
|
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
|
-
|
|
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
|
}
|