bertui 1.2.9 → 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 +44 -242
- 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 -263
- 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 -67
- package/src/build/ssr-renderer.js +0 -64
- package/src/build.js +0 -273
- 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,137 +0,0 @@
|
|
|
1
|
-
// bertui/src/build/image-optimizer.js - UPDATED WITH WASM
|
|
2
|
-
import { join, extname } from 'path';
|
|
3
|
-
import { existsSync, mkdirSync, readdirSync } from 'fs';
|
|
4
|
-
import logger from '../logger/logger.js';
|
|
5
|
-
import { optimizeImage, hasWasm } from '../image-optimizer/index.js';
|
|
6
|
-
import { copyImagesSync } from '../images/index.js';
|
|
7
|
-
|
|
8
|
-
export async function optimizeImages(srcDir, outDir, options = {}) {
|
|
9
|
-
const {
|
|
10
|
-
quality = 80,
|
|
11
|
-
webpQuality = 75,
|
|
12
|
-
verbose = false
|
|
13
|
-
} = options;
|
|
14
|
-
|
|
15
|
-
// Check if WASM is available
|
|
16
|
-
const wasmAvailable = await hasWasm();
|
|
17
|
-
|
|
18
|
-
if (wasmAvailable) {
|
|
19
|
-
logger.info(`🦀 Optimizing images with Rust WASM (quality: ${quality})...`);
|
|
20
|
-
} else {
|
|
21
|
-
logger.info('📋 Copying images (WASM optimizer not available)...');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const imageExtensions = [
|
|
25
|
-
'.png', '.jpg', '.jpeg', '.webp', '.gif', '.svg',
|
|
26
|
-
'.avif', '.ico', '.bmp', '.tiff', '.tif'
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
let optimized = 0;
|
|
30
|
-
let copied = 0;
|
|
31
|
-
let skipped = 0;
|
|
32
|
-
let totalSaved = 0;
|
|
33
|
-
const results = [];
|
|
34
|
-
|
|
35
|
-
if (!existsSync(srcDir)) {
|
|
36
|
-
logger.warn(`⚠️ Source not found: ${srcDir}`);
|
|
37
|
-
return { optimized: 0, copied: 0, saved: 0, results: [] };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
mkdirSync(outDir, { recursive: true });
|
|
41
|
-
|
|
42
|
-
async function processDirectory(dir, targetDir) {
|
|
43
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
44
|
-
|
|
45
|
-
for (const entry of entries) {
|
|
46
|
-
const srcPath = join(dir, entry.name);
|
|
47
|
-
const destPath = join(targetDir, entry.name);
|
|
48
|
-
|
|
49
|
-
if (entry.isDirectory()) {
|
|
50
|
-
const subDestPath = join(targetDir, entry.name);
|
|
51
|
-
mkdirSync(subDestPath, { recursive: true });
|
|
52
|
-
await processDirectory(srcPath, subDestPath);
|
|
53
|
-
} else if (entry.isFile()) {
|
|
54
|
-
const ext = extname(entry.name).toLowerCase();
|
|
55
|
-
|
|
56
|
-
if (imageExtensions.includes(ext)) {
|
|
57
|
-
try {
|
|
58
|
-
const file = Bun.file(srcPath);
|
|
59
|
-
const buffer = await file.arrayBuffer();
|
|
60
|
-
const originalSize = buffer.byteLength;
|
|
61
|
-
|
|
62
|
-
// Try WASM optimization first, fallback to copy
|
|
63
|
-
if (wasmAvailable && ['.png', '.jpg', '.jpeg', '.webp'].includes(ext)) {
|
|
64
|
-
const format = ext.slice(1);
|
|
65
|
-
const result = await optimizeImage(buffer, {
|
|
66
|
-
format,
|
|
67
|
-
quality,
|
|
68
|
-
webpQuality
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
await Bun.write(destPath, new Uint8Array(result.data));
|
|
72
|
-
|
|
73
|
-
const saved = originalSize - result.optimized_size;
|
|
74
|
-
totalSaved += saved;
|
|
75
|
-
optimized++;
|
|
76
|
-
|
|
77
|
-
results.push({
|
|
78
|
-
file: entry.name,
|
|
79
|
-
original: formatBytes(originalSize),
|
|
80
|
-
optimized: formatBytes(result.optimized_size),
|
|
81
|
-
saved: formatBytes(saved),
|
|
82
|
-
percent: result.savings_percent.toFixed(1) + '%'
|
|
83
|
-
});
|
|
84
|
-
} else {
|
|
85
|
-
// Just copy unsupported formats
|
|
86
|
-
await Bun.write(destPath, file);
|
|
87
|
-
copied++;
|
|
88
|
-
results.push({
|
|
89
|
-
file: entry.name,
|
|
90
|
-
status: 'copied'
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
} catch (error) {
|
|
94
|
-
logger.warn(` Failed to process ${entry.name}: ${error.message}`);
|
|
95
|
-
// Fallback: copy original
|
|
96
|
-
await Bun.write(destPath, Bun.file(srcPath));
|
|
97
|
-
copied++;
|
|
98
|
-
}
|
|
99
|
-
} else {
|
|
100
|
-
// Copy non-image files
|
|
101
|
-
await Bun.write(destPath, Bun.file(srcPath));
|
|
102
|
-
skipped++;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
await processDirectory(srcDir, outDir);
|
|
109
|
-
|
|
110
|
-
// Show summary
|
|
111
|
-
if (optimized > 0) {
|
|
112
|
-
logger.success(`✅ Optimized ${optimized} images with Rust WASM`);
|
|
113
|
-
logger.table(results.slice(0, 10));
|
|
114
|
-
if (results.length > 10) {
|
|
115
|
-
logger.info(` ... and ${results.length - 10} more images`);
|
|
116
|
-
}
|
|
117
|
-
logger.info(`📊 Total saved: ${formatBytes(totalSaved)}`);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (copied > 0) {
|
|
121
|
-
logger.info(`📋 Copied ${copied} images (fallback)`);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return { optimized, copied, saved: totalSaved, results };
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function copyImages(srcDir, outDir) {
|
|
128
|
-
return copyImagesSync(srcDir, outDir);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function formatBytes(bytes) {
|
|
132
|
-
if (bytes === 0) return '0 B';
|
|
133
|
-
const k = 1024;
|
|
134
|
-
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
135
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
136
|
-
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
137
|
-
}
|
|
@@ -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,67 +0,0 @@
|
|
|
1
|
-
// bertui/src/build/server-island-validator.js
|
|
2
|
-
import logger from '../logger/logger.js'
|
|
3
|
-
|
|
4
|
-
const BANNED_HOOKS = [
|
|
5
|
-
'useState', 'useEffect', 'useReducer', 'useCallback', 'useMemo',
|
|
6
|
-
'useRef', 'useContext', 'useLayoutEffect', 'useId',
|
|
7
|
-
'useImperativeHandle', 'useDebugValue', 'useDeferredValue',
|
|
8
|
-
'useTransition', 'useSyncExternalStore',
|
|
9
|
-
]
|
|
10
|
-
|
|
11
|
-
const BANNED_EVENTS = [
|
|
12
|
-
'onClick', 'onChange', 'onSubmit', 'onInput', 'onFocus',
|
|
13
|
-
'onBlur', 'onMouseEnter', 'onMouseLeave', 'onKeyDown', 'onKeyUp',
|
|
14
|
-
]
|
|
15
|
-
|
|
16
|
-
export function validateServerIsland(sourceCode, filePath) {
|
|
17
|
-
const errors = []
|
|
18
|
-
|
|
19
|
-
for (const hook of BANNED_HOOKS) {
|
|
20
|
-
if (new RegExp(`\\b${hook}\\s*\\(`).test(sourceCode)) {
|
|
21
|
-
errors.push(`Cannot use React hook "${hook}" in a static/server page — hooks need a browser runtime`)
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
for (const event of BANNED_EVENTS) {
|
|
26
|
-
if (sourceCode.includes(`${event}=`)) {
|
|
27
|
-
errors.push(`Cannot use event handler "${event}" in a static/server page — events need a browser runtime`)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (/window\.|document\.|localStorage\.|sessionStorage\./.test(sourceCode)) {
|
|
32
|
-
errors.push('Cannot access browser APIs (window/document/localStorage) in a static/server page')
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return { valid: errors.length === 0, errors }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function displayValidationErrors(filePath, errors) {
|
|
39
|
-
logger.error(`\n❌ Static/Server page validation failed: ${filePath}`)
|
|
40
|
-
for (const err of errors) {
|
|
41
|
-
logger.error(` · ${err}`)
|
|
42
|
-
}
|
|
43
|
-
logger.error(` → Remove the above or switch to default render mode\n`)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export async function validateAllServerIslands(routes) {
|
|
47
|
-
const serverIslands = []
|
|
48
|
-
const validationResults = []
|
|
49
|
-
|
|
50
|
-
for (const route of routes) {
|
|
51
|
-
try {
|
|
52
|
-
const src = await Bun.file(route.path).text()
|
|
53
|
-
const isStatic = /export\s+const\s+render\s*=\s*["'](server|static)["']/.test(src)
|
|
54
|
-
if (!isStatic) continue
|
|
55
|
-
|
|
56
|
-
const result = validateServerIsland(src, route.path)
|
|
57
|
-
serverIslands.push(route)
|
|
58
|
-
validationResults.push({ ...result, route: route.route, path: route.path })
|
|
59
|
-
|
|
60
|
-
if (!result.valid) {
|
|
61
|
-
displayValidationErrors(route.path, result.errors)
|
|
62
|
-
}
|
|
63
|
-
} catch {}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return { serverIslands, validationResults }
|
|
67
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// bertui/src/build/ssr-renderer.js
|
|
2
|
-
import { join } from 'path'
|
|
3
|
-
import { existsSync, mkdirSync, rmSync } from 'fs'
|
|
4
|
-
import logger from '../logger/logger.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Render a static page to HTML string using renderToString.
|
|
8
|
-
* Forces both the page and renderToString to use the same React
|
|
9
|
-
* instance from the project's own node_modules.
|
|
10
|
-
*/
|
|
11
|
-
export async function renderPageToHTML(compiledPagePath, buildDir) {
|
|
12
|
-
try {
|
|
13
|
-
// Extract project root from compiled path
|
|
14
|
-
// e.g. /project/.bertuibuild/pages/about.js → /project/
|
|
15
|
-
const projectRoot = compiledPagePath.split('.bertuibuild')[0]
|
|
16
|
-
|
|
17
|
-
// Explicitly resolve React from the PROJECT's node_modules
|
|
18
|
-
// This prevents the two-React-instances problem when bertui is bun linked
|
|
19
|
-
const reactPath = join(projectRoot, 'node_modules', 'react', 'index.js')
|
|
20
|
-
const reactDomServerPath = join(projectRoot, 'node_modules', 'react-dom', 'server.js')
|
|
21
|
-
|
|
22
|
-
if (!existsSync(reactPath)) {
|
|
23
|
-
logger.warn(`React not found in project node_modules: ${reactPath}`)
|
|
24
|
-
return null
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (!existsSync(reactDomServerPath)) {
|
|
28
|
-
logger.warn(`react-dom/server not found in project node_modules: ${reactDomServerPath}`)
|
|
29
|
-
return null
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const React = await import(reactPath)
|
|
33
|
-
const { renderToString } = await import(reactDomServerPath)
|
|
34
|
-
|
|
35
|
-
// Import the compiled page
|
|
36
|
-
const mod = await import(`${compiledPagePath}?t=${Date.now()}`)
|
|
37
|
-
const Component = mod.default
|
|
38
|
-
|
|
39
|
-
if (typeof Component !== 'function') {
|
|
40
|
-
logger.warn(`No default export found in ${compiledPagePath}`)
|
|
41
|
-
return null
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return renderToString(React.createElement(Component))
|
|
45
|
-
|
|
46
|
-
} catch (err) {
|
|
47
|
-
logger.warn(`renderToString failed for ${compiledPagePath}: ${err.message}`)
|
|
48
|
-
return null
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Check if a route's source file has render = "server" or render = "static"
|
|
54
|
-
*/
|
|
55
|
-
export async function getPageRenderMode(sourcePath) {
|
|
56
|
-
try {
|
|
57
|
-
const src = await Bun.file(sourcePath).text()
|
|
58
|
-
if (/export\s+const\s+render\s*=\s*["']server["']/.test(src)) return 'server'
|
|
59
|
-
if (/export\s+const\s+render\s*=\s*["']static["']/.test(src)) return 'static'
|
|
60
|
-
return 'default'
|
|
61
|
-
} catch {
|
|
62
|
-
return 'default'
|
|
63
|
-
}
|
|
64
|
-
}
|