bertui 1.2.8 → 2.0.0
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/README.md +45 -196
- package/TYPES_PATCH.md +17 -0
- package/bin/bertui.js +2 -7
- package/package.json +32 -98
- package/src/config.ts +4 -0
- package/src/index.ts +32 -0
- package/src/optional.ts +49 -0
- package/src/router.ts +3 -0
- package/tsconfig.json +29 -0
- package/LICENSE +0 -21
- package/index.js +0 -103
- package/src/analyzer/index.js +0 -370
- package/src/build/compiler/file-transpiler.js +0 -216
- package/src/build/compiler/index.js +0 -31
- package/src/build/compiler/route-discoverer.js +0 -49
- package/src/build/compiler/router-generator.js +0 -105
- package/src/build/css-builder.js +0 -81
- package/src/build/generators/html-generator.js +0 -151
- package/src/build/generators/robots-generator.js +0 -58
- package/src/build/generators/sitemap-generator.js +0 -63
- package/src/build/image-optimizer.js +0 -137
- package/src/build/processors/asset-processor.js +0 -19
- package/src/build/processors/css-builder.js +0 -142
- package/src/build/server-island-validator.js +0 -12
- package/src/build.js +0 -266
- package/src/cli.js +0 -131
- package/src/client/compiler.js +0 -522
- package/src/client/fast-refresh.js +0 -72
- package/src/client/hmr-runtime.js +0 -59
- package/src/compiler/index.js +0 -25
- package/src/compiler/router-generator-pure.js +0 -104
- package/src/compiler/transform.js +0 -149
- package/src/config/defaultConfig.js +0 -37
- package/src/config/index.js +0 -2
- package/src/config/loadConfig.js +0 -64
- package/src/config/og-image.png +0 -0
- package/src/css/index.js +0 -46
- package/src/css/processor.js +0 -172
- package/src/dev.js +0 -68
- package/src/hydration/index.js +0 -151
- package/src/image-optimizer/index.js +0 -103
- package/src/images/index.js +0 -102
- package/src/images/processor.js +0 -169
- package/src/layouts/index.js +0 -165
- package/src/loading/index.js +0 -210
- package/src/logger/logger.js +0 -320
- package/src/logger/notes.md +0 -20
- package/src/middleware/index.js +0 -182
- package/src/router/Router.js +0 -150
- package/src/router/SSRRouter.js +0 -156
- package/src/router/index.js +0 -3
- package/src/scaffolder/index.js +0 -310
- package/src/serve.js +0 -193
- package/src/server/dev-handler.js +0 -195
- package/src/server/dev-server-utils.js +0 -406
- package/src/server/dev-server.js +0 -15
- package/src/server/hmr-handler.js +0 -148
- package/src/server/index.js +0 -3
- package/src/server/notes.md +0 -1
- package/src/server/request-handler.js +0 -36
- package/src/server-islands/extractor.js +0 -198
- package/src/server-islands/index.js +0 -59
- package/src/styles/bertui.css +0 -210
- package/src/utils/cache.js +0 -297
- package/src/utils/env.js +0 -87
- package/src/utils/importhow.js +0 -52
- package/src/utils/index.js +0 -11
- package/src/utils/meta-extractor.js +0 -127
- package/types/bin/bertui.d.ts +0 -3
- package/types/bin/bertui.d.ts.map +0 -1
- package/types/error-overlay.d.ts +0 -2
- package/types/error-overlay.d.ts.map +0 -1
- package/types/index.d.ts +0 -26
- package/types/index.d.ts.map +0 -1
- package/types/scripts/fix-wasm-exports.d.ts +0 -2
- package/types/scripts/fix-wasm-exports.d.ts.map +0 -1
- package/types/src/analyzer/index.d.ts +0 -8
- package/types/src/analyzer/index.d.ts.map +0 -1
- package/types/src/build/compiler/file-transpiler.d.ts +0 -5
- package/types/src/build/compiler/file-transpiler.d.ts.map +0 -1
- package/types/src/build/compiler/index.d.ts +0 -12
- package/types/src/build/compiler/index.d.ts.map +0 -1
- package/types/src/build/compiler/route-discoverer.d.ts +0 -2
- package/types/src/build/compiler/route-discoverer.d.ts.map +0 -1
- package/types/src/build/compiler/router-generator.d.ts +0 -2
- package/types/src/build/compiler/router-generator.d.ts.map +0 -1
- package/types/src/build/css-builder.d.ts +0 -18
- package/types/src/build/css-builder.d.ts.map +0 -1
- package/types/src/build/generators/html-generator.d.ts +0 -2
- package/types/src/build/generators/html-generator.d.ts.map +0 -1
- package/types/src/build/generators/robots-generator.d.ts +0 -11
- package/types/src/build/generators/robots-generator.d.ts.map +0 -1
- package/types/src/build/generators/sitemap-generator.d.ts +0 -5
- package/types/src/build/generators/sitemap-generator.d.ts.map +0 -1
- package/types/src/build/image-optimizer.d.ts +0 -11
- package/types/src/build/image-optimizer.d.ts.map +0 -1
- package/types/src/build/processors/asset-processor.d.ts +0 -2
- package/types/src/build/processors/asset-processor.d.ts.map +0 -1
- package/types/src/build/processors/css-builder.d.ts +0 -2
- package/types/src/build/processors/css-builder.d.ts.map +0 -1
- package/types/src/build/server-island-validator.d.ts +0 -27
- package/types/src/build/server-island-validator.d.ts.map +0 -1
- package/types/src/build.d.ts +0 -5
- package/types/src/build.d.ts.map +0 -1
- package/types/src/cli.d.ts +0 -2
- package/types/src/cli.d.ts.map +0 -1
- package/types/src/client/compiler.d.ts +0 -16
- package/types/src/client/compiler.d.ts.map +0 -1
- package/types/src/client/fast-refresh.d.ts +0 -3
- package/types/src/client/fast-refresh.d.ts.map +0 -1
- package/types/src/client/hmr-runtime.d.ts +0 -4
- package/types/src/client/hmr-runtime.d.ts.map +0 -1
- package/types/src/compiler/index.d.ts +0 -8
- package/types/src/compiler/index.d.ts.map +0 -1
- package/types/src/compiler/router-generator-pure.d.ts +0 -2
- package/types/src/compiler/router-generator-pure.d.ts.map +0 -1
- package/types/src/compiler/transform.d.ts +0 -36
- package/types/src/compiler/transform.d.ts.map +0 -1
- package/types/src/config/defaultConfig.d.ts +0 -26
- package/types/src/config/defaultConfig.d.ts.map +0 -1
- package/types/src/config/index.d.ts +0 -3
- package/types/src/config/index.d.ts.map +0 -1
- package/types/src/config/loadConfig.d.ts +0 -2
- package/types/src/config/loadConfig.d.ts.map +0 -1
- package/types/src/css/index.d.ts +0 -6
- package/types/src/css/index.d.ts.map +0 -1
- package/types/src/css/processor.d.ts +0 -23
- package/types/src/css/processor.d.ts.map +0 -1
- package/types/src/dev.d.ts +0 -2
- package/types/src/dev.d.ts.map +0 -1
- package/types/src/hydration/index.d.ts +0 -33
- package/types/src/hydration/index.d.ts.map +0 -1
- package/types/src/image-optimizer/index.d.ts +0 -24
- package/types/src/image-optimizer/index.d.ts.map +0 -1
- package/types/src/images/index.d.ts +0 -12
- package/types/src/images/index.d.ts.map +0 -1
- package/types/src/images/processor.d.ts +0 -30
- package/types/src/images/processor.d.ts.map +0 -1
- package/types/src/layouts/index.d.ts +0 -28
- package/types/src/layouts/index.d.ts.map +0 -1
- package/types/src/loading/index.d.ts +0 -28
- package/types/src/loading/index.d.ts.map +0 -1
- package/types/src/logger/logger.d.ts +0 -30
- package/types/src/logger/logger.d.ts.map +0 -1
- package/types/src/middleware/index.d.ts +0 -61
- package/types/src/middleware/index.d.ts.map +0 -1
- package/types/src/router/Router.d.ts +0 -16
- package/types/src/router/Router.d.ts.map +0 -1
- package/types/src/router/SSRRouter.d.ts +0 -20
- package/types/src/router/SSRRouter.d.ts.map +0 -1
- package/types/src/router/index.d.ts +0 -3
- package/types/src/router/index.d.ts.map +0 -1
- package/types/src/scaffolder/index.d.ts +0 -14
- package/types/src/scaffolder/index.d.ts.map +0 -1
- package/types/src/serve.d.ts +0 -3
- package/types/src/serve.d.ts.map +0 -1
- package/types/src/server/dev-handler.d.ts +0 -13
- package/types/src/server/dev-handler.d.ts.map +0 -1
- package/types/src/server/dev-server-utils.d.ts +0 -6
- package/types/src/server/dev-server-utils.d.ts.map +0 -1
- package/types/src/server/dev-server.d.ts +0 -18
- package/types/src/server/dev-server.d.ts.map +0 -1
- package/types/src/server/hmr-handler.d.ts +0 -19
- package/types/src/server/hmr-handler.d.ts.map +0 -1
- package/types/src/server/index.d.ts +0 -4
- package/types/src/server/index.d.ts.map +0 -1
- package/types/src/server/request-handler.d.ts +0 -19
- package/types/src/server/request-handler.d.ts.map +0 -1
- package/types/src/server-islands/extractor.d.ts +0 -16
- package/types/src/server-islands/extractor.d.ts.map +0 -1
- package/types/src/server-islands/index.d.ts +0 -3
- package/types/src/server-islands/index.d.ts.map +0 -1
- package/types/src/utils/cache.d.ts +0 -52
- package/types/src/utils/cache.d.ts.map +0 -1
- package/types/src/utils/env.d.ts +0 -20
- package/types/src/utils/env.d.ts.map +0 -1
- package/types/src/utils/importhow.d.ts +0 -15
- package/types/src/utils/importhow.d.ts.map +0 -1
- package/types/src/utils/index.d.ts +0 -3
- package/types/src/utils/index.d.ts.map +0 -1
- package/types/src/utils/meta-extractor.d.ts +0 -13
- package/types/src/utils/meta-extractor.d.ts.map +0 -1
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// bertui/src/build/processors/asset-processor.js
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import { existsSync, mkdirSync } from 'fs'; // ✅ ADD THIS IMPORT
|
|
4
|
-
import { copyImages } from '../image-optimizer.js';
|
|
5
|
-
|
|
6
|
-
export async function copyAllStaticAssets(root, outDir) {
|
|
7
|
-
const publicDir = join(root, 'public');
|
|
8
|
-
const srcImagesDir = join(root, 'src', 'images');
|
|
9
|
-
|
|
10
|
-
if (existsSync(publicDir)) {
|
|
11
|
-
copyImages(publicDir, outDir);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (existsSync(srcImagesDir)) {
|
|
15
|
-
const distImagesDir = join(outDir, 'images');
|
|
16
|
-
mkdirSync(distImagesDir, { recursive: true });
|
|
17
|
-
copyImages(srcImagesDir, distImagesDir);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
// bertui/src/build/processors/css-builder.js - WITH SCSS + CACHING + CSS MODULES
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import { existsSync, readdirSync, mkdirSync } from 'fs';
|
|
4
|
-
import logger from '../../logger/logger.js';
|
|
5
|
-
import { globalCache } from '../../utils/cache.js';
|
|
6
|
-
import { minifyCSS, processSCSS } from '../../css/processor.js';
|
|
7
|
-
|
|
8
|
-
export async function buildAllCSS(root, outDir) {
|
|
9
|
-
const startTime = process.hrtime.bigint();
|
|
10
|
-
|
|
11
|
-
const srcStylesDir = join(root, 'src', 'styles');
|
|
12
|
-
// CSS modules scoped CSS is staged here by file-transpiler.js
|
|
13
|
-
const modulesStagingDir = join(root, '.bertuibuild', 'styles-staged');
|
|
14
|
-
const stylesOutDir = join(outDir, 'styles');
|
|
15
|
-
|
|
16
|
-
mkdirSync(stylesOutDir, { recursive: true });
|
|
17
|
-
|
|
18
|
-
const cacheKey = `css-build:${root}:${Date.now()}`;
|
|
19
|
-
const cached = globalCache.get(cacheKey, { ttl: 1000 });
|
|
20
|
-
|
|
21
|
-
if (cached) {
|
|
22
|
-
logger.info(`⚡ Using cached CSS (${cached.files} files)`);
|
|
23
|
-
await Bun.write(join(stylesOutDir, 'bertui.min.css'), cached.content);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let combinedCSS = '';
|
|
28
|
-
let fileCount = 0;
|
|
29
|
-
|
|
30
|
-
// 1. Process src/styles/ (plain CSS + SCSS)
|
|
31
|
-
if (existsSync(srcStylesDir)) {
|
|
32
|
-
await processSCSSDirectory(srcStylesDir, root);
|
|
33
|
-
const cssFiles = readdirSync(srcStylesDir).filter(f => f.endsWith('.css') && !f.endsWith('.module.css'));
|
|
34
|
-
|
|
35
|
-
logger.info(`Processing ${cssFiles.length} CSS file(s)...`);
|
|
36
|
-
|
|
37
|
-
for (const cssFile of cssFiles) {
|
|
38
|
-
const srcPath = join(srcStylesDir, cssFile);
|
|
39
|
-
const fileBuffer = await globalCache.getFile(srcPath, { logSpeed: true });
|
|
40
|
-
if (fileBuffer) {
|
|
41
|
-
const content = fileBuffer.toString('utf-8');
|
|
42
|
-
combinedCSS += `/* ${cssFile} */\n${content}\n\n`;
|
|
43
|
-
fileCount++;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
} else {
|
|
47
|
-
logger.info('No styles directory found');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// 2. Include scoped CSS from CSS modules (staged by file-transpiler.js)
|
|
51
|
-
if (existsSync(modulesStagingDir)) {
|
|
52
|
-
const moduleFiles = readdirSync(modulesStagingDir).filter(f => f.endsWith('.css'));
|
|
53
|
-
if (moduleFiles.length > 0) {
|
|
54
|
-
logger.info(`Including ${moduleFiles.length} CSS module(s)...`);
|
|
55
|
-
for (const cssFile of moduleFiles) {
|
|
56
|
-
const srcPath = join(modulesStagingDir, cssFile);
|
|
57
|
-
const content = await Bun.file(srcPath).text();
|
|
58
|
-
combinedCSS += `/* module: ${cssFile} */\n${content}\n\n`;
|
|
59
|
-
fileCount++;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (!combinedCSS.trim()) {
|
|
65
|
-
await Bun.write(join(stylesOutDir, 'bertui.min.css'), '/* No CSS */');
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const combinedPath = join(stylesOutDir, 'bertui.min.css');
|
|
70
|
-
|
|
71
|
-
const minifyCacheKey = `minify:${Buffer.from(combinedCSS).length}:${combinedCSS.substring(0, 100)}`;
|
|
72
|
-
let minified = globalCache.get(minifyCacheKey);
|
|
73
|
-
|
|
74
|
-
if (!minified) {
|
|
75
|
-
minified = await minifyCSS(combinedCSS, {
|
|
76
|
-
filename: 'bertui.min.css',
|
|
77
|
-
sourceMap: false
|
|
78
|
-
});
|
|
79
|
-
globalCache.set(minifyCacheKey, minified, { ttl: 60000 });
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
await Bun.write(combinedPath, minified);
|
|
83
|
-
|
|
84
|
-
const originalSize = Buffer.byteLength(combinedCSS);
|
|
85
|
-
const minifiedSize = Buffer.byteLength(minified);
|
|
86
|
-
const reduction = ((1 - minifiedSize / originalSize) * 100).toFixed(1);
|
|
87
|
-
|
|
88
|
-
const endTime = process.hrtime.bigint();
|
|
89
|
-
const duration = Number(endTime - startTime) / 1000;
|
|
90
|
-
|
|
91
|
-
logger.success(`CSS optimized: ${(originalSize/1024).toFixed(2)}KB → ${(minifiedSize/1024).toFixed(2)}KB (-${reduction}%)`);
|
|
92
|
-
logger.info(`⚡ Processing time: ${duration.toFixed(3)}µs`);
|
|
93
|
-
|
|
94
|
-
globalCache.set(cacheKey, {
|
|
95
|
-
files: fileCount,
|
|
96
|
-
content: minified,
|
|
97
|
-
size: minifiedSize
|
|
98
|
-
}, { ttl: 5000 });
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
async function processSCSSDirectory(stylesDir, root) {
|
|
102
|
-
try {
|
|
103
|
-
const sass = await import('sass').catch(() => null);
|
|
104
|
-
if (!sass) return;
|
|
105
|
-
|
|
106
|
-
const files = readdirSync(stylesDir);
|
|
107
|
-
const scssFiles = files.filter(f => f.endsWith('.scss') || f.endsWith('.sass'));
|
|
108
|
-
|
|
109
|
-
if (scssFiles.length === 0) return;
|
|
110
|
-
|
|
111
|
-
logger.info(`📝 Compiling ${scssFiles.length} SCSS files...`);
|
|
112
|
-
|
|
113
|
-
for (const file of scssFiles) {
|
|
114
|
-
const srcPath = join(stylesDir, file);
|
|
115
|
-
const cssPath = join(stylesDir, file.replace(/\.(scss|sass)$/, '.css'));
|
|
116
|
-
|
|
117
|
-
const fileBuffer = await globalCache.getFile(srcPath);
|
|
118
|
-
const cacheKey = `scss:${file}:${Buffer.from(fileBuffer).length}`;
|
|
119
|
-
const cached = globalCache.get(cacheKey);
|
|
120
|
-
|
|
121
|
-
if (cached && existsSync(cssPath)) {
|
|
122
|
-
logger.debug(`⚡ Cached SCSS: ${file} → ${file.replace(/\.(scss|sass)$/, '.css')}`);
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const result = sass.compile(srcPath, {
|
|
127
|
-
style: 'expanded',
|
|
128
|
-
sourceMap: false,
|
|
129
|
-
loadPaths: [stylesDir, join(root, 'node_modules')]
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
await Bun.write(cssPath, result.css);
|
|
133
|
-
globalCache.set(cacheKey, true, { ttl: 30000 });
|
|
134
|
-
|
|
135
|
-
logger.debug(` ${file} → ${file.replace(/\.(scss|sass)$/, '.css')}`);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
logger.success(`✅ SCSS compilation complete`);
|
|
139
|
-
} catch (error) {
|
|
140
|
-
logger.warn(`SCSS processing skipped: ${error.message}`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// bertui/src/build/server-island-validator.js
|
|
2
|
-
// Server Islands removed — all pages are client-rendered.
|
|
3
|
-
|
|
4
|
-
export function validateServerIsland(sourceCode, filePath) {
|
|
5
|
-
return { valid: true, errors: [] };
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function displayValidationErrors(filePath, errors) {}
|
|
9
|
-
|
|
10
|
-
export async function validateAllServerIslands(routes) {
|
|
11
|
-
return { serverIslands: [], validationResults: [] };
|
|
12
|
-
}
|
package/src/build.js
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
// bertui/src/build.js
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import { existsSync, mkdirSync, rmSync, readdirSync } 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';
|
|
14
|
-
import { compileLayouts } from './layouts/index.js';
|
|
15
|
-
import { compileLoadingComponents } from './loading/index.js';
|
|
16
|
-
import { analyzeRoutes } from './hydration/index.js';
|
|
17
|
-
import { analyzeBuild } from './analyzer/index.js';
|
|
18
|
-
|
|
19
|
-
const TOTAL_STEPS = 10;
|
|
20
|
-
|
|
21
|
-
export async function buildProduction(options = {}) {
|
|
22
|
-
const root = options.root || process.cwd();
|
|
23
|
-
const buildDir = join(root, '.bertuibuild');
|
|
24
|
-
const outDir = join(root, 'dist');
|
|
25
|
-
|
|
26
|
-
process.env.NODE_ENV = 'production';
|
|
27
|
-
|
|
28
|
-
logger.printHeader('BUILD');
|
|
29
|
-
|
|
30
|
-
if (existsSync(buildDir)) rmSync(buildDir, { recursive: true, force: true });
|
|
31
|
-
if (existsSync(outDir)) rmSync(outDir, { recursive: true, force: true });
|
|
32
|
-
mkdirSync(buildDir, { recursive: true });
|
|
33
|
-
mkdirSync(outDir, { recursive: true });
|
|
34
|
-
|
|
35
|
-
let totalKB = '0';
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
// ── Step 1: Env ──────────────────────────────────────────────────────────
|
|
39
|
-
logger.step(1, TOTAL_STEPS, 'Loading env');
|
|
40
|
-
const envVars = loadEnvVariables(root);
|
|
41
|
-
const { loadConfig } = await import('./config/loadConfig.js');
|
|
42
|
-
const config = await loadConfig(root);
|
|
43
|
-
const importhow = config.importhow || {};
|
|
44
|
-
logger.stepDone('Loading env', `${Object.keys(envVars).length} vars`);
|
|
45
|
-
|
|
46
|
-
// ── Step 2: Compile ──────────────────────────────────────────────────────
|
|
47
|
-
logger.step(2, TOTAL_STEPS, 'Compiling');
|
|
48
|
-
const { routes } = await compileForBuild(root, buildDir, envVars, config);
|
|
49
|
-
logger.stepDone('Compiling', `${routes.length} routes`);
|
|
50
|
-
|
|
51
|
-
// ── Step 3: Layouts ──────────────────────────────────────────────────────
|
|
52
|
-
logger.step(3, TOTAL_STEPS, 'Layouts');
|
|
53
|
-
const layouts = await compileLayouts(root, buildDir);
|
|
54
|
-
logger.stepDone('Layouts', `${Object.keys(layouts).length} found`);
|
|
55
|
-
|
|
56
|
-
// ── Step 4: Loading states ───────────────────────────────────────────────
|
|
57
|
-
logger.step(4, TOTAL_STEPS, 'Loading states');
|
|
58
|
-
await compileLoadingComponents(root, buildDir);
|
|
59
|
-
logger.stepDone('Loading states');
|
|
60
|
-
|
|
61
|
-
// ── Step 5: Hydration analysis ───────────────────────────────────────────
|
|
62
|
-
logger.step(5, TOTAL_STEPS, 'Hydration analysis');
|
|
63
|
-
const analyzedRoutes = await analyzeRoutes(routes);
|
|
64
|
-
logger.stepDone('Hydration analysis',
|
|
65
|
-
`${analyzedRoutes.interactive.length} interactive · ${analyzedRoutes.static.length} static`);
|
|
66
|
-
|
|
67
|
-
// ── Step 6: CSS ──────────────────────────────────────────────────────────
|
|
68
|
-
logger.step(6, TOTAL_STEPS, 'Processing CSS');
|
|
69
|
-
await buildAllCSS(root, outDir);
|
|
70
|
-
logger.stepDone('Processing CSS');
|
|
71
|
-
|
|
72
|
-
// ── Step 7: Static assets ────────────────────────────────────────────────
|
|
73
|
-
logger.step(7, TOTAL_STEPS, 'Static assets');
|
|
74
|
-
await copyAllStaticAssets(root, outDir);
|
|
75
|
-
logger.stepDone('Static assets');
|
|
76
|
-
|
|
77
|
-
// ── Step 8: Bundle JS ────────────────────────────────────────────────────
|
|
78
|
-
logger.step(8, TOTAL_STEPS, 'Bundling JS');
|
|
79
|
-
const buildEntry = join(buildDir, 'main.js');
|
|
80
|
-
if (!existsSync(buildEntry)) {
|
|
81
|
-
throw new Error('main.js not found in build dir — make sure src/main.jsx exists');
|
|
82
|
-
}
|
|
83
|
-
const result = await bundleJavaScript(buildEntry, outDir, envVars, buildDir, 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, 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
|
-
try {
|
|
101
|
-
await analyzeBuild(outDir, { outputFile: join(outDir, 'bundle-report.html') });
|
|
102
|
-
} catch (reportErr) {
|
|
103
|
-
logger.debug(`Bundle report generation skipped: ${reportErr.message}`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
logger.printSummary({
|
|
107
|
-
routes: routes.length,
|
|
108
|
-
interactive: analyzedRoutes.interactive.length,
|
|
109
|
-
staticRoutes: analyzedRoutes.static.length,
|
|
110
|
-
jsSize: `${totalKB} KB`,
|
|
111
|
-
outDir: 'dist/',
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
logger.cleanup();
|
|
115
|
-
return { success: true };
|
|
116
|
-
|
|
117
|
-
} catch (error) {
|
|
118
|
-
logger.stepFail('Build', error?.message || String(error));
|
|
119
|
-
if (existsSync(buildDir)) rmSync(buildDir, { recursive: true, force: true });
|
|
120
|
-
throw error;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
125
|
-
|
|
126
|
-
async function generateProductionImportMap(root, config) {
|
|
127
|
-
const importMap = {
|
|
128
|
-
'react': 'https://esm.sh/react@18.2.0',
|
|
129
|
-
'react-dom': 'https://esm.sh/react-dom@18.2.0',
|
|
130
|
-
'react-dom/client': 'https://esm.sh/react-dom@18.2.0/client',
|
|
131
|
-
'react/jsx-runtime': 'https://esm.sh/react@18.2.0/jsx-runtime',
|
|
132
|
-
'@bunnyx/api': '/bunnyx-api/api-client.js',
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
const nodeModulesDir = join(root, 'node_modules');
|
|
136
|
-
if (!existsSync(nodeModulesDir)) return importMap;
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
for (const pkg of readdirSync(nodeModulesDir)) {
|
|
140
|
-
if (!pkg.startsWith('bertui-') || pkg.startsWith('.')) continue;
|
|
141
|
-
const pkgDir = join(nodeModulesDir, pkg);
|
|
142
|
-
const pkgJson = join(pkgDir, 'package.json');
|
|
143
|
-
if (!existsSync(pkgJson)) continue;
|
|
144
|
-
try {
|
|
145
|
-
const p = JSON.parse(await Bun.file(pkgJson).text());
|
|
146
|
-
for (const entry of [p.browser, p.module, p.main, 'dist/index.js', 'index.js'].filter(Boolean)) {
|
|
147
|
-
if (existsSync(join(pkgDir, entry))) {
|
|
148
|
-
importMap[pkg] = `/assets/node_modules/${pkg}/${entry}`;
|
|
149
|
-
break;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
} catch { continue; }
|
|
153
|
-
}
|
|
154
|
-
} catch { /* ignore */ }
|
|
155
|
-
|
|
156
|
-
return importMap;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async function copyNodeModulesToDist(root, outDir, importMap) {
|
|
160
|
-
const { mkdirSync } = await import('fs');
|
|
161
|
-
const dest = join(outDir, 'assets', 'node_modules');
|
|
162
|
-
mkdirSync(dest, { recursive: true });
|
|
163
|
-
const src = join(root, 'node_modules');
|
|
164
|
-
|
|
165
|
-
for (const [, assetPath] of Object.entries(importMap)) {
|
|
166
|
-
if (assetPath.startsWith('https://')) continue;
|
|
167
|
-
const match = assetPath.match(/\/assets\/node_modules\/(.+)$/);
|
|
168
|
-
if (!match) continue;
|
|
169
|
-
const parts = match[1].split('/');
|
|
170
|
-
const pkgName = parts[0];
|
|
171
|
-
const subPath = parts.slice(1);
|
|
172
|
-
const srcFile = join(src, pkgName, ...subPath);
|
|
173
|
-
const destFile = join(dest, pkgName, ...subPath);
|
|
174
|
-
mkdirSync(join(dest, pkgName, ...subPath.slice(0, -1)), { recursive: true });
|
|
175
|
-
if (existsSync(srcFile)) await Bun.write(destFile, Bun.file(srcFile));
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async function bundleJavaScript(buildEntry, outDir, envVars, buildDir, root, config) {
|
|
180
|
-
const originalCwd = process.cwd();
|
|
181
|
-
process.chdir(buildDir);
|
|
182
|
-
|
|
183
|
-
try {
|
|
184
|
-
const importMap = await generateProductionImportMap(root, config);
|
|
185
|
-
await Bun.write(join(outDir, 'import-map.json'), JSON.stringify({ imports: importMap }, null, 2));
|
|
186
|
-
await copyNodeModulesToDist(root, outDir, importMap);
|
|
187
|
-
|
|
188
|
-
const bunnyxSrc = join(root, 'bunnyx-api', 'api-client.js');
|
|
189
|
-
if (existsSync(bunnyxSrc)) {
|
|
190
|
-
const { mkdirSync } = await import('fs');
|
|
191
|
-
mkdirSync(join(outDir, 'bunnyx-api'), { recursive: true });
|
|
192
|
-
await Bun.write(join(outDir, 'bunnyx-api', 'api-client.js'), Bun.file(bunnyxSrc));
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const cssModulePlugin = {
|
|
196
|
-
name: 'css-modules',
|
|
197
|
-
setup(build) {
|
|
198
|
-
build.onLoad({ filter: /\.module\.css$/ }, () => ({
|
|
199
|
-
contents: 'export default new Proxy({}, { get: (_, k) => k });',
|
|
200
|
-
loader: 'js',
|
|
201
|
-
}));
|
|
202
|
-
build.onLoad({ filter: /\.css$/ }, () => ({
|
|
203
|
-
contents: '',
|
|
204
|
-
loader: 'js',
|
|
205
|
-
}));
|
|
206
|
-
},
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
let result;
|
|
210
|
-
try {
|
|
211
|
-
result = await Bun.build({
|
|
212
|
-
entrypoints: [buildEntry],
|
|
213
|
-
outdir: join(outDir, 'assets'),
|
|
214
|
-
target: 'browser',
|
|
215
|
-
format: 'esm',
|
|
216
|
-
plugins: [cssModulePlugin],
|
|
217
|
-
minify: {
|
|
218
|
-
whitespace: true,
|
|
219
|
-
syntax: true,
|
|
220
|
-
identifiers: true,
|
|
221
|
-
},
|
|
222
|
-
splitting: true,
|
|
223
|
-
sourcemap: 'external',
|
|
224
|
-
metafile: true,
|
|
225
|
-
naming: {
|
|
226
|
-
entry: 'js/[name]-[hash].js',
|
|
227
|
-
chunk: 'js/chunks/[name]-[hash].js',
|
|
228
|
-
asset: 'assets/[name]-[hash].[ext]',
|
|
229
|
-
},
|
|
230
|
-
external: ['react', 'react-dom', 'react-dom/client', 'react/jsx-runtime', '@bunnyx/api'],
|
|
231
|
-
define: {
|
|
232
|
-
'process.env.NODE_ENV': '"production"',
|
|
233
|
-
...Object.fromEntries(
|
|
234
|
-
Object.entries(envVars).map(([k, v]) => [`process.env.${k}`, JSON.stringify(v)])
|
|
235
|
-
),
|
|
236
|
-
},
|
|
237
|
-
});
|
|
238
|
-
} catch (err) {
|
|
239
|
-
throw new Error(`Bun.build failed: ${err?.message || String(err)}`);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (!result.success) {
|
|
243
|
-
const msgs = (result.logs || []).map(l => l?.message || l?.text || JSON.stringify(l)).join('\n');
|
|
244
|
-
throw new Error(`Bundle failed\n${msgs || 'Check your imports for .jsx extensions or unresolvable paths'}`);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (result.metafile) {
|
|
248
|
-
await Bun.write(join(outDir, 'metafile.json'), JSON.stringify(result.metafile, null, 2));
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return result;
|
|
252
|
-
|
|
253
|
-
} finally {
|
|
254
|
-
process.chdir(originalCwd);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
export async function build(options = {}) {
|
|
259
|
-
try {
|
|
260
|
-
await buildProduction(options);
|
|
261
|
-
process.exit(0);
|
|
262
|
-
} catch (error) {
|
|
263
|
-
console.error('Build error:', error?.message || String(error));
|
|
264
|
-
process.exit(1);
|
|
265
|
-
}
|
|
266
|
-
}
|
package/src/cli.js
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
// bertui/src/cli.js - WITH ALL COMMANDS
|
|
2
|
-
import { startDev } from './dev.js';
|
|
3
|
-
import { buildProduction } from './build.js';
|
|
4
|
-
import { startPreviewServer } from './serve.js';
|
|
5
|
-
import { scaffold, parseCreateArgs } from './scaffolder/index.js';
|
|
6
|
-
import { analyzeBuild } from './analyzer/index.js';
|
|
7
|
-
import logger from './logger/logger.js';
|
|
8
|
-
import { join } from 'path';
|
|
9
|
-
|
|
10
|
-
export function program() {
|
|
11
|
-
const args = process.argv.slice(2);
|
|
12
|
-
const command = args[0] || 'dev';
|
|
13
|
-
|
|
14
|
-
switch (command) {
|
|
15
|
-
case 'dev': {
|
|
16
|
-
const devPort = getArg('--port', '-p') || 3000;
|
|
17
|
-
startDev({
|
|
18
|
-
port: parseInt(devPort),
|
|
19
|
-
root: process.cwd(),
|
|
20
|
-
});
|
|
21
|
-
break;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
case 'build': {
|
|
25
|
-
buildProduction({ root: process.cwd() });
|
|
26
|
-
break;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
case 'serve':
|
|
30
|
-
case 'preview': {
|
|
31
|
-
const previewPort = getArg('--port', '-p') || 5000;
|
|
32
|
-
startPreviewServer({
|
|
33
|
-
port: parseInt(previewPort),
|
|
34
|
-
root: process.cwd(),
|
|
35
|
-
dir: 'dist',
|
|
36
|
-
});
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// ✅ NEW: Component/page/layout scaffolder
|
|
41
|
-
case 'create': {
|
|
42
|
-
const createArgs = args.slice(1);
|
|
43
|
-
const parsed = parseCreateArgs(createArgs);
|
|
44
|
-
if (parsed) {
|
|
45
|
-
scaffold(parsed.type, parsed.name, { root: process.cwd() })
|
|
46
|
-
.then(result => {
|
|
47
|
-
if (!result) process.exit(1);
|
|
48
|
-
})
|
|
49
|
-
.catch(err => {
|
|
50
|
-
logger.error(`Create failed: ${err.message}`);
|
|
51
|
-
process.exit(1);
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// ✅ NEW: Bundle analyzer
|
|
58
|
-
case 'analyze': {
|
|
59
|
-
const outDir = join(process.cwd(), 'dist');
|
|
60
|
-
const open = args.includes('--open');
|
|
61
|
-
analyzeBuild(outDir, { open })
|
|
62
|
-
.then(result => {
|
|
63
|
-
if (!result) {
|
|
64
|
-
logger.error('No build found. Run: bertui build');
|
|
65
|
-
process.exit(1);
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
.catch(err => {
|
|
69
|
-
logger.error(`Analyze failed: ${err.message}`);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
});
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
case '--version':
|
|
76
|
-
case '-v':
|
|
77
|
-
console.log('bertui v1.2.0');
|
|
78
|
-
break;
|
|
79
|
-
|
|
80
|
-
case '--help':
|
|
81
|
-
case '-h':
|
|
82
|
-
showHelp();
|
|
83
|
-
break;
|
|
84
|
-
|
|
85
|
-
default:
|
|
86
|
-
logger.error(`Unknown command: ${command}`);
|
|
87
|
-
showHelp();
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function getArg(longForm, shortForm) {
|
|
92
|
-
const args = process.argv.slice(2);
|
|
93
|
-
const longIndex = args.indexOf(longForm);
|
|
94
|
-
const shortIndex = args.indexOf(shortForm);
|
|
95
|
-
const index = longIndex !== -1 ? longIndex : shortIndex;
|
|
96
|
-
return index !== -1 && args[index + 1] ? args[index + 1] : null;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function showHelp() {
|
|
100
|
-
logger.bigLog('BERTUI CLI', { color: 'blue' });
|
|
101
|
-
console.log(`
|
|
102
|
-
Commands:
|
|
103
|
-
bertui dev [--port] Start development server (default: 3000)
|
|
104
|
-
bertui build Build for production
|
|
105
|
-
bertui serve [--port] Preview production build (default: 5000)
|
|
106
|
-
bertui analyze [--open] Analyze bundle size (opens report in browser)
|
|
107
|
-
|
|
108
|
-
bertui create component <Name> Scaffold a React component
|
|
109
|
-
bertui create page <name> Scaffold a page (adds to file-based routing)
|
|
110
|
-
bertui create layout <name> Scaffold a layout (default wraps all pages)
|
|
111
|
-
bertui create loading <route> Scaffold a per-route loading state
|
|
112
|
-
bertui create middleware Scaffold src/middleware.ts
|
|
113
|
-
|
|
114
|
-
Options:
|
|
115
|
-
--port, -p <number> Port for server
|
|
116
|
-
--open Open browser after command
|
|
117
|
-
|
|
118
|
-
Examples:
|
|
119
|
-
bertui dev
|
|
120
|
-
bertui dev --port 8080
|
|
121
|
-
bertui build
|
|
122
|
-
bertui analyze --open
|
|
123
|
-
bertui create component Button
|
|
124
|
-
bertui create page About
|
|
125
|
-
bertui create page blog/[slug]
|
|
126
|
-
bertui create layout default
|
|
127
|
-
bertui create layout blog
|
|
128
|
-
bertui create loading blog
|
|
129
|
-
bertui create middleware
|
|
130
|
-
`);
|
|
131
|
-
}
|