bertui 0.1.6 → 0.1.8

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/src/cli.js CHANGED
@@ -1,66 +1,66 @@
1
- // src/cli.js
2
- import { startDev } from './dev.js';
3
- import { buildProduction } from './build.js';
4
- import logger from './logger/logger.js';
5
-
6
- export function program() {
7
- const args = process.argv.slice(2);
8
- const command = args[0] || 'dev';
9
-
10
- switch (command) {
11
- case 'dev':
12
- const devPort = getArg('--port', '-p') || 3000;
13
- startDev({
14
- port: parseInt(devPort),
15
- root: process.cwd()
16
- });
17
- break;
18
-
19
- case 'build':
20
- buildProduction({
21
- root: process.cwd()
22
- });
23
- break;
24
-
25
- case '--version':
26
- case '-v':
27
- console.log('bertui v0.1.0');
28
- break;
29
-
30
- case '--help':
31
- case '-h':
32
- showHelp();
33
- break;
34
-
35
- default:
36
- logger.error(`Unknown command: ${command}`);
37
- showHelp();
38
- }
39
- }
40
-
41
- function getArg(longForm, shortForm) {
42
- const args = process.argv.slice(2);
43
- const longIndex = args.indexOf(longForm);
44
- const shortIndex = args.indexOf(shortForm);
45
- const index = longIndex !== -1 ? longIndex : shortIndex;
46
- return index !== -1 && args[index + 1] ? args[index + 1] : null;
47
- }
48
-
49
- function showHelp() {
50
- logger.bigLog('BERTUI CLI', { color: 'blue' });
51
- console.log(`
52
- Commands:
53
- bertui dev Start development server
54
- bertui build Build for production
55
- bertui --version Show version
56
- bertui --help Show help
57
-
58
- Options:
59
- --port, -p <number> Port for dev server (default: 3000)
60
-
61
- Examples:
62
- bertui dev
63
- bertui dev --port 8080
64
- bertui build
65
- `);
1
+ // src/cli.js
2
+ import { startDev } from './dev.js';
3
+ import { buildProduction } from './build.js';
4
+ import logger from './logger/logger.js';
5
+
6
+ export function program() {
7
+ const args = process.argv.slice(2);
8
+ const command = args[0] || 'dev';
9
+
10
+ switch (command) {
11
+ case 'dev':
12
+ const devPort = getArg('--port', '-p') || 3000;
13
+ startDev({
14
+ port: parseInt(devPort),
15
+ root: process.cwd()
16
+ });
17
+ break;
18
+
19
+ case 'build':
20
+ buildProduction({
21
+ root: process.cwd()
22
+ });
23
+ break;
24
+
25
+ case '--version':
26
+ case '-v':
27
+ console.log('bertui v0.1.0');
28
+ break;
29
+
30
+ case '--help':
31
+ case '-h':
32
+ showHelp();
33
+ break;
34
+
35
+ default:
36
+ logger.error(`Unknown command: ${command}`);
37
+ showHelp();
38
+ }
39
+ }
40
+
41
+ function getArg(longForm, shortForm) {
42
+ const args = process.argv.slice(2);
43
+ const longIndex = args.indexOf(longForm);
44
+ const shortIndex = args.indexOf(shortForm);
45
+ const index = longIndex !== -1 ? longIndex : shortIndex;
46
+ return index !== -1 && args[index + 1] ? args[index + 1] : null;
47
+ }
48
+
49
+ function showHelp() {
50
+ logger.bigLog('BERTUI CLI', { color: 'blue' });
51
+ console.log(`
52
+ Commands:
53
+ bertui dev Start development server
54
+ bertui build Build for production
55
+ bertui --version Show version
56
+ bertui --help Show help
57
+
58
+ Options:
59
+ --port, -p <number> Port for dev server (default: 3000)
60
+
61
+ Examples:
62
+ bertui dev
63
+ bertui dev --port 8080
64
+ bertui build
65
+ `);
66
66
  }
@@ -1,226 +1,219 @@
1
- // src/client/compiler.js
2
- import { existsSync, mkdirSync, readdirSync, statSync } from 'fs';
3
- import { join, extname, relative, sep } from 'path';
4
- import logger from '../logger/logger.js';
5
-
6
- export async function compileProject(root) {
7
- logger.bigLog('COMPILING PROJECT', { color: 'blue' });
8
-
9
- const srcDir = join(root, 'src');
10
- const pagesDir = join(srcDir, 'pages');
11
- const outDir = join(root, '.bertui', 'compiled');
12
-
13
- // Check if src exists
14
- if (!existsSync(srcDir)) {
15
- logger.error('src/ directory not found!');
16
- process.exit(1);
17
- }
18
-
19
- // Create output directory
20
- if (!existsSync(outDir)) {
21
- mkdirSync(outDir, { recursive: true });
22
- logger.info('Created .bertui/compiled/');
23
- }
24
-
25
- // Discover routes if pages directory exists
26
- let routes = [];
27
- if (existsSync(pagesDir)) {
28
- routes = await discoverRoutes(pagesDir);
29
- logger.info(`Discovered ${routes.length} routes`);
30
-
31
- // Display routes table
32
- if (routes.length > 0) {
33
- logger.bigLog('ROUTES DISCOVERED', { color: 'blue' });
34
- logger.table(routes.map((r, i) => ({
35
- '': i,
36
- route: r.route,
37
- file: r.file,
38
- type: r.type
39
- })));
40
-
41
- // Generate router file
42
- await generateRouter(routes, outDir);
43
- logger.info('Generated router.js');
44
- }
45
- }
46
-
47
- // Compile all files
48
- const startTime = Date.now();
49
- const stats = await compileDirectory(srcDir, outDir, root);
50
- const duration = Date.now() - startTime;
51
-
52
- logger.success(`Compiled ${stats.files} files in ${duration}ms`);
53
- logger.info(`Output: ${outDir}`);
54
-
55
- return { outDir, stats, routes };
56
- }
57
-
58
- async function discoverRoutes(pagesDir) {
59
- const routes = [];
60
-
61
- async function scanDirectory(dir, basePath = '') {
62
- const entries = readdirSync(dir, { withFileTypes: true });
63
-
64
- for (const entry of entries) {
65
- const fullPath = join(dir, entry.name);
66
- const relativePath = join(basePath, entry.name);
67
-
68
- if (entry.isDirectory()) {
69
- // Recursively scan subdirectories
70
- await scanDirectory(fullPath, relativePath);
71
- } else if (entry.isFile()) {
72
- const ext = extname(entry.name);
73
- if (['.jsx', '.tsx', '.js', '.ts'].includes(ext)) {
74
- const fileName = entry.name.replace(ext, '');
75
-
76
- // Generate route path
77
- let route = '/' + relativePath.replace(/\\/g, '/').replace(ext, '');
78
-
79
- // Handle index files
80
- if (fileName === 'index') {
81
- route = route.replace('/index', '') || '/';
82
- }
83
-
84
- // Determine route type
85
- const isDynamic = fileName.includes('[') && fileName.includes(']');
86
- const type = isDynamic ? 'dynamic' : 'static';
87
-
88
- routes.push({
89
- route: route === '' ? '/' : route,
90
- file: relativePath,
91
- path: fullPath,
92
- type
93
- });
94
- }
95
- }
96
- }
97
- }
98
-
99
- await scanDirectory(pagesDir);
100
-
101
- // Sort routes: static routes first, then dynamic
102
- routes.sort((a, b) => {
103
- if (a.type === b.type) {
104
- return a.route.localeCompare(b.route);
105
- }
106
- return a.type === 'static' ? -1 : 1;
107
- });
108
-
109
- return routes;
110
- }
111
-
112
- async function generateRouter(routes, outDir) {
113
- const imports = routes.map((route, i) => {
114
- const componentName = `Page${i}`;
115
- const importPath = `./pages/${route.file.replace(/\\/g, '/')}`;
116
- return `import ${componentName} from '${importPath}';`;
117
- }).join('\n');
118
-
119
- const routeConfigs = routes.map((route, i) => {
120
- const componentName = `Page${i}`;
121
- return ` { path: '${route.route}', component: ${componentName}, type: '${route.type}' }`;
122
- }).join(',\n');
123
-
124
- const routerCode = `// Auto-generated router - DO NOT EDIT
125
- ${imports}
126
-
127
- export const routes = [
128
- ${routeConfigs}
129
- ];
130
-
131
- export function matchRoute(pathname) {
132
- // Try exact match first
133
- for (const route of routes) {
134
- if (route.type === 'static' && route.path === pathname) {
135
- return route;
136
- }
137
- }
138
-
139
- // Try dynamic routes
140
- for (const route of routes) {
141
- if (route.type === 'dynamic') {
142
- const pattern = route.path.replace(/\\[([^\\]]+)\\]/g, '([^/]+)');
143
- const regex = new RegExp('^' + pattern + '$');
144
- const match = pathname.match(regex);
145
-
146
- if (match) {
147
- // Extract params
148
- const paramNames = [...route.path.matchAll(/\\[([^\\]]+)\\]/g)].map(m => m[1]);
149
- const params = {};
150
- paramNames.forEach((name, i) => {
151
- params[name] = match[i + 1];
152
- });
153
-
154
- return { ...route, params };
155
- }
156
- }
157
- }
158
-
159
- return null;
160
- }
161
- `;
162
-
163
- const routerPath = join(outDir, 'router.js');
164
- await Bun.write(routerPath, routerCode);
165
- }
166
-
167
- async function compileDirectory(srcDir, outDir, root) {
168
- const stats = { files: 0, skipped: 0 };
169
-
170
- const files = readdirSync(srcDir);
171
-
172
- for (const file of files) {
173
- const srcPath = join(srcDir, file);
174
- const stat = statSync(srcPath);
175
-
176
- if (stat.isDirectory()) {
177
- // Recursively compile subdirectories
178
- const subOutDir = join(outDir, file);
179
- mkdirSync(subOutDir, { recursive: true });
180
- const subStats = await compileDirectory(srcPath, subOutDir, root);
181
- stats.files += subStats.files;
182
- stats.skipped += subStats.skipped;
183
- } else {
184
- // Compile file
185
- const ext = extname(file);
186
- const relativePath = relative(join(root, 'src'), srcPath);
187
-
188
- if (['.jsx', '.tsx', '.ts'].includes(ext)) {
189
- await compileFile(srcPath, outDir, file, relativePath);
190
- stats.files++;
191
- } else if (ext === '.js' || ext === '.css') {
192
- // Copy as-is
193
- const outPath = join(outDir, file);
194
- await Bun.write(outPath, Bun.file(srcPath));
195
- logger.debug(`Copied: ${relativePath}`);
196
- stats.files++;
197
- } else {
198
- logger.debug(`Skipped: ${relativePath}`);
199
- stats.skipped++;
200
- }
201
- }
202
- }
203
-
204
- return stats;
205
- }
206
-
207
- async function compileFile(srcPath, outDir, filename, relativePath) {
208
- const ext = extname(filename);
209
- const loader = ext === '.tsx' ? 'tsx' : ext === '.ts' ? 'ts' : 'jsx';
210
-
211
- try {
212
- const transpiler = new Bun.Transpiler({ loader });
213
- const code = await Bun.file(srcPath).text();
214
- const compiled = await transpiler.transform(code);
215
-
216
- // Change extension to .js
217
- const outFilename = filename.replace(/\.(jsx|tsx|ts)$/, '.js');
218
- const outPath = join(outDir, outFilename);
219
-
220
- await Bun.write(outPath, compiled);
221
- logger.debug(`Compiled: ${relativePath} → ${outFilename}`);
222
- } catch (error) {
223
- logger.error(`Failed to compile ${relativePath}: ${error.message}`);
224
- throw error;
225
- }
1
+ // src/client/compiler.js
2
+ import { existsSync, mkdirSync, readdirSync, statSync } from 'fs';
3
+ import { join, extname, relative } from 'path';
4
+ import logger from '../logger/logger.js';
5
+
6
+ export async function compileProject(root) {
7
+ logger.bigLog('COMPILING PROJECT', { color: 'blue' });
8
+
9
+ const srcDir = join(root, 'src');
10
+ const pagesDir = join(srcDir, 'pages');
11
+ const outDir = join(root, '.bertui', 'compiled');
12
+
13
+ if (!existsSync(srcDir)) {
14
+ logger.error('src/ directory not found!');
15
+ process.exit(1);
16
+ }
17
+
18
+ if (!existsSync(outDir)) {
19
+ mkdirSync(outDir, { recursive: true });
20
+ logger.info('Created .bertui/compiled/');
21
+ }
22
+
23
+ let routes = [];
24
+ if (existsSync(pagesDir)) {
25
+ routes = await discoverRoutes(pagesDir);
26
+ logger.info(`Discovered ${routes.length} routes`);
27
+
28
+ if (routes.length > 0) {
29
+ logger.bigLog('ROUTES DISCOVERED', { color: 'blue' });
30
+ logger.table(routes.map((r, i) => ({
31
+ '': i,
32
+ route: r.route,
33
+ file: r.file,
34
+ type: r.type
35
+ })));
36
+ }
37
+ }
38
+
39
+ const startTime = Date.now();
40
+ const stats = await compileDirectory(srcDir, outDir, root);
41
+ const duration = Date.now() - startTime;
42
+
43
+ // Generate router AFTER compilation so we reference .js files
44
+ if (routes.length > 0) {
45
+ await generateRouter(routes, outDir, root);
46
+ logger.info('Generated router.js');
47
+ }
48
+
49
+ logger.success(`Compiled ${stats.files} files in ${duration}ms`);
50
+ logger.info(`Output: ${outDir}`);
51
+
52
+ return { outDir, stats, routes };
53
+ }
54
+
55
+ async function discoverRoutes(pagesDir) {
56
+ const routes = [];
57
+
58
+ async function scanDirectory(dir, basePath = '') {
59
+ const entries = readdirSync(dir, { withFileTypes: true });
60
+
61
+ for (const entry of entries) {
62
+ const fullPath = join(dir, entry.name);
63
+ const relativePath = join(basePath, entry.name);
64
+
65
+ if (entry.isDirectory()) {
66
+ await scanDirectory(fullPath, relativePath);
67
+ } else if (entry.isFile()) {
68
+ const ext = extname(entry.name);
69
+ if (['.jsx', '.tsx', '.js', '.ts'].includes(ext)) {
70
+ const fileName = entry.name.replace(ext, '');
71
+
72
+ let route = '/' + relativePath.replace(/\\/g, '/').replace(ext, '');
73
+
74
+ if (fileName === 'index') {
75
+ route = route.replace('/index', '') || '/';
76
+ }
77
+
78
+ const isDynamic = fileName.includes('[') && fileName.includes(']');
79
+ const type = isDynamic ? 'dynamic' : 'static';
80
+
81
+ routes.push({
82
+ route: route === '' ? '/' : route,
83
+ file: relativePath.replace(/\\/g, '/'),
84
+ path: fullPath,
85
+ type
86
+ });
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ await scanDirectory(pagesDir);
93
+
94
+ routes.sort((a, b) => {
95
+ if (a.type === b.type) {
96
+ return a.route.localeCompare(b.route);
97
+ }
98
+ return a.type === 'static' ? -1 : 1;
99
+ });
100
+
101
+ return routes;
102
+ }
103
+
104
+ async function generateRouter(routes, outDir, root) {
105
+ const imports = routes.map((route, i) => {
106
+ const componentName = `Page${i}`;
107
+ // CRITICAL FIX: Import .js files (compiled), not .jsx
108
+ const importPath = `./pages/${route.file.replace(/\.(jsx|tsx|ts)$/, '.js')}`;
109
+ return `import ${componentName} from '${importPath}';`;
110
+ }).join('\n');
111
+
112
+ const routeConfigs = routes.map((route, i) => {
113
+ const componentName = `Page${i}`;
114
+ return ` { path: '${route.route}', component: ${componentName}, type: '${route.type}' }`;
115
+ }).join(',\n');
116
+
117
+ const routerCode = `// Auto-generated router - DO NOT EDIT
118
+ ${imports}
119
+
120
+ export const routes = [
121
+ ${routeConfigs}
122
+ ];
123
+
124
+ export function matchRoute(pathname) {
125
+ for (const route of routes) {
126
+ if (route.type === 'static' && route.path === pathname) {
127
+ return route;
128
+ }
129
+ }
130
+
131
+ for (const route of routes) {
132
+ if (route.type === 'dynamic') {
133
+ const pattern = route.path.replace(/\\[([^\\]]+)\\]/g, '([^/]+)');
134
+ const regex = new RegExp('^' + pattern + '$');
135
+ const match = pathname.match(regex);
136
+
137
+ if (match) {
138
+ const paramNames = [...route.path.matchAll(/\\[([^\\]]+)\\]/g)].map(m => m[1]);
139
+ const params = {};
140
+ paramNames.forEach((name, i) => {
141
+ params[name] = match[i + 1];
142
+ });
143
+
144
+ return { ...route, params };
145
+ }
146
+ }
147
+ }
148
+
149
+ return null;
150
+ }
151
+ `;
152
+
153
+ const routerPath = join(outDir, 'router.js');
154
+ await Bun.write(routerPath, routerCode);
155
+ }
156
+
157
+ async function compileDirectory(srcDir, outDir, root) {
158
+ const stats = { files: 0, skipped: 0 };
159
+
160
+ const files = readdirSync(srcDir);
161
+
162
+ for (const file of files) {
163
+ const srcPath = join(srcDir, file);
164
+ const stat = statSync(srcPath);
165
+
166
+ if (stat.isDirectory()) {
167
+ const subOutDir = join(outDir, file);
168
+ mkdirSync(subOutDir, { recursive: true });
169
+ const subStats = await compileDirectory(srcPath, subOutDir, root);
170
+ stats.files += subStats.files;
171
+ stats.skipped += subStats.skipped;
172
+ } else {
173
+ const ext = extname(file);
174
+ const relativePath = relative(join(root, 'src'), srcPath);
175
+
176
+ if (['.jsx', '.tsx', '.ts'].includes(ext)) {
177
+ await compileFile(srcPath, outDir, file, relativePath);
178
+ stats.files++;
179
+ } else if (ext === '.js' || ext === '.css') {
180
+ const outPath = join(outDir, file);
181
+ await Bun.write(outPath, Bun.file(srcPath));
182
+ logger.debug(`Copied: ${relativePath}`);
183
+ stats.files++;
184
+ } else {
185
+ logger.debug(`Skipped: ${relativePath}`);
186
+ stats.skipped++;
187
+ }
188
+ }
189
+ }
190
+
191
+ return stats;
192
+ }
193
+
194
+ async function compileFile(srcPath, outDir, filename, relativePath) {
195
+ const ext = extname(filename);
196
+ const loader = ext === '.tsx' ? 'tsx' : ext === '.ts' ? 'ts' : 'jsx';
197
+
198
+ try {
199
+ let code = await Bun.file(srcPath).text();
200
+
201
+ // Remove bertui/styles imports completely
202
+ code = code.replace(/import\s+['"]bertui\/styles['"]\s*;?\s*/g, '');
203
+
204
+ // Replace bertui/router imports with CDN React Router (we'll use a simpler approach)
205
+ // For now, keep the import as-is and handle it differently
206
+
207
+ const transpiler = new Bun.Transpiler({ loader });
208
+ const compiled = await transpiler.transform(code);
209
+
210
+ const outFilename = filename.replace(/\.(jsx|tsx|ts)$/, '.js');
211
+ const outPath = join(outDir, outFilename);
212
+
213
+ await Bun.write(outPath, compiled);
214
+ logger.debug(`Compiled: ${relativePath} ${outFilename}`);
215
+ } catch (error) {
216
+ logger.error(`Failed to compile ${relativePath}: ${error.message}`);
217
+ throw error;
218
+ }
226
219
  }
@@ -1,16 +1,16 @@
1
- export const defaultConfig = {
2
- meta: {
3
- title: "BertUI App",
4
- description: "Built with BertUI - Lightning fast React development",
5
- keywords: "react, bun, bertui",
6
- author: "Pease Ernest",
7
- ogImage: "/og-image.png",
8
- themeColor: "#f30606ff",
9
- lang: "en"
10
- },
11
- appShell: {
12
- loading: true,
13
- loadingText: "Loading...",
14
- backgroundColor: "#ffffff"
15
- }
1
+ export const defaultConfig = {
2
+ meta: {
3
+ title: "BertUI App",
4
+ description: "Built with BertUI - Lightning fast React development",
5
+ keywords: "react, bun, bertui",
6
+ author: "Pease Ernest",
7
+ ogImage: "/og-image.png",
8
+ themeColor: "#f30606ff",
9
+ lang: "en"
10
+ },
11
+ appShell: {
12
+ loading: true,
13
+ loadingText: "Loading...",
14
+ backgroundColor: "#ffffff"
15
+ }
16
16
  };