@seip/blue-bird 0.3.3 → 0.3.4

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 (43) hide show
  1. package/.env_example +23 -13
  2. package/LICENSE +21 -21
  3. package/README.md +79 -79
  4. package/backend/index.js +12 -12
  5. package/backend/routes/api.js +34 -34
  6. package/backend/routes/frontend.js +1 -8
  7. package/core/app.js +359 -359
  8. package/core/auth.js +69 -69
  9. package/core/cache.js +35 -35
  10. package/core/cli/component.js +42 -42
  11. package/core/cli/init.js +120 -118
  12. package/core/cli/react.js +383 -411
  13. package/core/cli/route.js +42 -42
  14. package/core/cli/scaffolding-auth.js +967 -0
  15. package/core/config.js +41 -41
  16. package/core/debug.js +248 -248
  17. package/core/logger.js +80 -80
  18. package/core/middleware.js +27 -27
  19. package/core/router.js +134 -134
  20. package/core/swagger.js +24 -24
  21. package/core/template.js +288 -288
  22. package/core/upload.js +76 -76
  23. package/core/validate.js +291 -290
  24. package/frontend/index.html +28 -22
  25. package/frontend/resources/js/App.jsx +28 -42
  26. package/frontend/resources/js/Main.jsx +17 -17
  27. package/frontend/resources/js/blue-bird/components/Button.jsx +67 -0
  28. package/frontend/resources/js/blue-bird/components/Card.jsx +17 -0
  29. package/frontend/resources/js/blue-bird/components/DataTable.jsx +126 -0
  30. package/frontend/resources/js/blue-bird/components/Input.jsx +21 -0
  31. package/frontend/resources/js/blue-bird/components/Label.jsx +12 -0
  32. package/frontend/resources/js/blue-bird/components/Modal.jsx +27 -0
  33. package/frontend/resources/js/blue-bird/components/Translate.jsx +12 -0
  34. package/frontend/resources/js/blue-bird/components/Typography.jsx +25 -0
  35. package/frontend/resources/js/blue-bird/contexts/LanguageContext.jsx +29 -0
  36. package/frontend/resources/js/blue-bird/contexts/SnackbarContext.jsx +38 -0
  37. package/frontend/resources/js/blue-bird/contexts/ThemeContext.jsx +49 -0
  38. package/frontend/resources/js/blue-bird/locales/en.json +30 -0
  39. package/frontend/resources/js/blue-bird/locales/es.json +30 -0
  40. package/frontend/resources/js/pages/About.jsx +33 -15
  41. package/frontend/resources/js/pages/Home.jsx +93 -68
  42. package/package.json +56 -55
  43. package/vite.config.js +21 -21
package/core/cli/react.js CHANGED
@@ -1,411 +1,383 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { execSync } from "node:child_process";
4
- import chalk from "chalk";
5
- import Config from "../config.js";
6
-
7
- const __dirname = Config.dirname();
8
- const props = Config.props();
9
-
10
- /**
11
- * Scaffolding class for setting up React and Vite in the project.
12
- */
13
- class ReactScaffold {
14
- /**
15
- * Initializes the Scaffolder with the base application directory.
16
- */
17
- constructor() {
18
- this.appDir = __dirname;
19
- }
20
-
21
- /**
22
- * Executes the installation process.
23
- */
24
- async install() {
25
- console.log(chalk.cyan("Initializing React + Vite setup..."));
26
-
27
- try {
28
- this.createStructure();
29
- this.updatePackageJson();
30
- this.createViteConfig();
31
- this.createAppjs();
32
- this.createMainJs();
33
- this.createPagesJs();
34
- this.updateGitIgnore();
35
- this.npmInstall();
36
-
37
- console.log(chalk.green("React scaffolding created successfully!"));
38
- console.log(chalk.cyan("\nNext steps:"));
39
- console.log(chalk.white(" 1. Run (Blue Bird Server): ") + chalk.bold("npm run dev"));
40
- console.log(chalk.white(" 2. Run (React Vite Dev): ") + chalk.bold("npm run vite:dev"));
41
- console.log(chalk.blue("\nBlue Bird React setup completed!"));
42
- } catch (error) {
43
- console.error(chalk.red("Fatal error during scaffolding:"), error.message);
44
- }
45
- }
46
-
47
- /**
48
- * Creates the necessary directory structure for React resources.
49
- */
50
- createStructure() {
51
- const dirs = [
52
- 'frontend/resources/js/pages'
53
- ];
54
-
55
- dirs.forEach(dir => {
56
- const fullPath = path.join(this.appDir, dir);
57
- if (!fs.existsSync(fullPath)) {
58
- fs.mkdirSync(fullPath, { recursive: true });
59
- console.log(chalk.gray(`Created directory: ${dir}`));
60
- }
61
- });
62
- }
63
-
64
- /**
65
- * Updates the project's package.json with React and Vite dependencies and scripts.
66
- */
67
- updatePackageJson() {
68
- const packagePath = path.join(this.appDir, 'package.json');
69
- if (!fs.existsSync(packagePath)) {
70
- console.warn(chalk.yellow("package.json not found. Initializing with npm init..."));
71
- execSync('npm init -y', { cwd: this.appDir });
72
- }
73
-
74
- const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
75
-
76
- pkg.scripts = pkg.scripts || {};
77
- pkg.scripts["vite:dev"] = "vite";
78
- pkg.scripts["vite:build"] = "vite build";
79
-
80
- pkg.devDependencies = pkg.devDependencies || {};
81
-
82
- pkg.devDependencies["vite"] = "^7.3.1";
83
- pkg.devDependencies["@vitejs/plugin-react"] = "^4.3.4";
84
-
85
- pkg.dependencies = pkg.dependencies || {};
86
- pkg.dependencies["react"] = "^19.2.4";
87
- pkg.dependencies["react-dom"] = "^19.2.4";
88
- pkg.dependencies["react-router-dom"] = "^7.2.0";
89
-
90
-
91
- fs.writeFileSync(packagePath, JSON.stringify(pkg, null, 2));
92
- console.log(chalk.gray("Updated package.json dependencies and scripts."));
93
- }
94
-
95
- /**
96
- * Creates the vite.config.js file with appropriate root and outDir settings.
97
- */
98
- createViteConfig() {
99
- const file = path.join(this.appDir, 'vite.config.js');
100
- if (fs.existsSync(file)) {
101
- console.warn(chalk.yellow("vite.config.js already exists. Skipping."));
102
- return;
103
- }
104
-
105
- // We use props.static.path to determine where the build goes
106
- const outDir = path.join(props.static.path, 'build');
107
-
108
- const content = `import { defineConfig } from 'vite';
109
- import react from '@vitejs/plugin-react';
110
- import path from 'path';
111
-
112
- export default defineConfig({
113
- plugins: [react()],
114
- root: path.resolve(__dirname, 'frontend/resources/js'),
115
- base: '/build/',
116
- build: {
117
- outDir: path.resolve(__dirname, '${outDir.replace(/\\/g, '/')}'),
118
- emptyOutDir: true,
119
- manifest: true,
120
- rollupOptions: {
121
- input: path.resolve(__dirname, 'frontend/resources/js/Main.jsx'),
122
- },
123
- },
124
- server: {
125
- origin: 'http://localhost:5173',
126
- strictPort: true,
127
- cors: true,
128
- },
129
- });`;
130
- fs.writeFileSync(file, content);
131
- console.log(chalk.gray("Created vite.config.js"));
132
- }
133
-
134
- createPagesJs() {
135
- const file = path.join(this.appDir, 'frontend/resources/js/pages/Home.jsx');
136
- if (fs.existsSync(file)) {
137
- console.warn(chalk.yellow("Home.jsx already exists. Skipping."));
138
- return;
139
- }
140
-
141
- const content = `import React, { useEffect } from 'react';
142
-
143
- export default function Home() {
144
- useEffect(() => {
145
- // Example API call to the backend
146
- fetch("http://localhost:3000/login", {
147
- method: "POST",
148
- headers: {
149
- "Content-Type": "application/json",
150
- },
151
- body: JSON.stringify({
152
- email:"example@example.com",
153
- password: "myPassword123"
154
- }),
155
- })
156
- .then((response) => response.json())
157
- .then((data) => console.log('Backend response:', data))
158
- .catch((error) => console.error('Error fetching from backend:', error));
159
- }, []);
160
-
161
- return (
162
- <div style={{ textAlign: 'center', padding: '4rem 2rem' }}>
163
- <header style={{ marginBottom: '3rem' }}>
164
- <h1 style={{
165
- fontSize: '3.5rem',
166
- fontWeight: '800',
167
- background: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)',
168
- WebkitBackgroundClip: 'text',
169
- WebkitTextFillColor: 'transparent',
170
- marginBottom: '1rem'
171
- }}>
172
- Welcome to Blue Bird
173
- </h1>
174
- <p style={{ fontSize: '1.25rem', color: '#6b7280', maxWidth: '600px', margin: '0 auto' }}>
175
- The elegant, fast, and weightless framework for modern web development.
176
- </p>
177
- </header>
178
-
179
- <div style={{ display: 'flex', gap: '1rem', justifyContent: 'center', marginBottom: '4rem' }}>
180
- <a
181
- href="https://seip25.github.io/Blue-bird/"
182
- target="_blank"
183
- rel="noopener noreferrer"
184
- style={{
185
- backgroundColor: '#2563eb',
186
- color: 'white',
187
- padding: '0.75rem 1.5rem',
188
- borderRadius: '0.5rem',
189
- textDecoration: 'none',
190
- fontWeight: '600',
191
- transition: 'background-color 0.2s'
192
- }}
193
- onMouseOver={(e) => e.target.style.backgroundColor = '#1d4ed8'}
194
- onMouseOut={(e) => e.target.style.backgroundColor = '#2563eb'}
195
- >
196
- Documentation
197
- </a>
198
- <a
199
- href="https://seip25.github.io/Blue-bird/en.html"
200
- target="_blank"
201
- rel="noopener noreferrer"
202
- style={{
203
- backgroundColor: 'white',
204
- color: '#374151',
205
- padding: '0.75rem 1.5rem',
206
- borderRadius: '0.5rem',
207
- textDecoration: 'none',
208
- fontWeight: '600',
209
- border: '1px solid #d1d5db',
210
- transition: 'background-color 0.2s'
211
- }}
212
- onMouseOver={(e) => e.target.style.backgroundColor = '#f9fafb'}
213
- onMouseOut={(e) => e.target.style.backgroundColor = 'white'}
214
- >
215
- English Docs
216
- </a>
217
- </div>
218
-
219
- <div style={{
220
- display: 'grid',
221
- gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))',
222
- gap: '2rem',
223
- maxWidth: '1000px',
224
- margin: '0 auto'
225
- }}>
226
- <div style={cardStyle}>
227
- <h3>Lightweight</h3>
228
- <p>Built with performance and simplicity in mind.</p>
229
- </div>
230
- <div style={cardStyle}>
231
- <h3>React Powered</h3>
232
- <p>Full React + Vite integration with island hydration.</p>
233
- </div>
234
- <div style={cardStyle}>
235
- <h3>Express Backend</h3>
236
- <p>Robust and scalable backend architecture.</p>
237
- </div>
238
- </div>
239
- </div>
240
- );
241
- }
242
-
243
- const cardStyle = {
244
- padding: '1.5rem',
245
- borderRadius: '0.75rem',
246
- border: '1px solid #e5e7eb',
247
- textAlign: 'left',
248
- backgroundColor: 'white',
249
- boxShadow: '0 1px 2px 0 rgba(0, 0, 0, 0.05)'
250
- };`;
251
- fs.writeFileSync(file, content);
252
- console.log(chalk.gray("Created frontend/resources/js/pages/Home.jsx"));
253
-
254
- const file2 = path.join(this.appDir, 'frontend/resources/js/pages/About.jsx');
255
- if (fs.existsSync(file2)) {
256
- console.warn(chalk.yellow("About.jsx already exists. Skipping."));
257
- return;
258
- }
259
-
260
- const content2 = `import React from 'react';
261
-
262
- export default function About() {
263
- return (
264
- <div style={{ padding: '2rem' }}>
265
- <h1 style={{ color: '#111827', marginBottom: '1rem' }}>About Blue Bird</h1>
266
- <p style={{ color: '#4b5563', lineHeight: '1.6' }}>
267
- Blue Bird is a modern framework designed to bridge the gap between backend routing and frontend interactivity.
268
- It provides a seamless developer experience for building fast, reactive web applications.
269
- </p>
270
- </div>
271
- );
272
- }`;
273
- fs.writeFileSync(file2, content2);
274
- console.log(chalk.gray("Created frontend/resources/js/pages/About.jsx"));
275
- }
276
-
277
- /**
278
- * Creates the main entry point for React (main.jsx) which handles island hydration.
279
- */
280
- createAppjs() {
281
- const file = path.join(this.appDir, 'frontend/resources/js/App.jsx');
282
- if (fs.existsSync(file)) {
283
- console.warn(chalk.yellow("App.jsx already exists. Skipping."));
284
- return;
285
- }
286
-
287
- const content = `import React from 'react';
288
- import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
289
- import Home from './pages/Home';
290
- import About from './pages/About';
291
-
292
- export default function App(_props) {
293
- const {
294
- component,
295
- props
296
- } = _props;
297
-
298
- return (
299
- <Router>
300
- <div style={{
301
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
302
- minHeight: '100vh',
303
- backgroundColor: '#f9fafb',
304
- color: '#111827'
305
- }}>
306
- <nav style={{
307
- background: 'white',
308
- padding: '1rem 2rem',
309
- display: 'flex',
310
- justifyContent: 'space-between',
311
- alignItems: 'center',
312
- boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1)',
313
- position: 'sticky',
314
- top: 0,
315
- zIndex: 10
316
- }}>
317
- <div style={{ fontWeight: 'bold', fontSize: '1.25rem', color: '#2563eb' }}>
318
- Blue Bird
319
- </div>
320
- <div style={{ display: 'flex', gap: '2rem' }}>
321
- <Link to="/" style={navLinkStyle}>Home</Link>
322
- <Link to="/about" style={navLinkStyle}>About</Link>
323
- </div>
324
- </nav>
325
-
326
- <main style={{ maxWidth: '1200px', margin: '0 auto' }}>
327
- {/* Uncomment to debug props if needed */}
328
- {/* <div style={{ padding: '0.5rem', background: '#ececec', fontSize: '0.75rem' }}>Props: {JSON.stringify(props)}</div> */}
329
-
330
- <Routes>
331
- <Route path="/" element={<Home />} />
332
- <Route path="/about" element={<About />} />
333
- </Routes>
334
- </main>
335
- </div>
336
- </Router>
337
- );
338
- }
339
-
340
- const navLinkStyle = {
341
- color: '#4b5563',
342
- textDecoration: 'none',
343
- fontWeight: '500',
344
- fontSize: '0.95rem',
345
- transition: 'color 0.2s'
346
- };`;
347
- fs.writeFileSync(file, content);
348
- console.log(chalk.gray("Created frontend/resources/js/App.jsx"));
349
- }
350
- createMainJs() {
351
- const file = path.join(this.appDir, 'frontend/resources/js/Main.jsx');
352
- if (fs.existsSync(file)) {
353
- console.warn(chalk.yellow("Main.jsx already exists. Skipping."));
354
- return;
355
- }
356
-
357
- const content = `import React from 'react';
358
- import { createRoot } from 'react-dom/client';
359
- import App from './App';
360
-
361
-
362
- document.addEventListener('DOMContentLoaded', () => {
363
- document.querySelectorAll('[data-react-component]').forEach(el => {
364
- const component = {
365
- component:el.dataset.reactComponent
366
- };
367
- const props = JSON.parse(el.dataset.props || '{}');
368
- const allProps={
369
- ...props,
370
- ...component
371
- }
372
- createRoot(el).render(<App {...allProps} />);
373
- });
374
- });`;
375
- fs.writeFileSync(file, content);
376
- console.log(chalk.gray("Created frontend/resources/js/Main.jsx"));
377
- }
378
-
379
-
380
-
381
- /**
382
- * Ensures node_modules and other build artifacts are ignored by Git.
383
- */
384
- updateGitIgnore() {
385
- const file = path.join(this.appDir, '.gitignore');
386
- const entry = "\nnode_modules\ndist\nfrontend/public/build\n";
387
-
388
- if (fs.existsSync(file)) {
389
- const content = fs.readFileSync(file, 'utf8');
390
- if (!content.includes('node_modules')) {
391
- fs.appendFileSync(file, entry);
392
- console.log(chalk.gray("Updated .gitignore"));
393
- }
394
- } else {
395
- fs.writeFileSync(file, entry);
396
- console.log(chalk.gray("Created .gitignore"));
397
- }
398
- }
399
- npmInstall() {
400
- try {
401
- execSync('npm install', { cwd: this.appDir });
402
- console.log(chalk.gray("Installed dependencies"));
403
- } catch (error) {
404
- console.error(chalk.red("Error installing dependencies:"), error.message);
405
- }
406
- }
407
- }
408
-
409
-
410
- const scaffold = new ReactScaffold();
411
- scaffold.install();
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { execSync } from "node:child_process";
4
+ import chalk from "chalk";
5
+ import Config from "../config.js";
6
+
7
+ const __dirname = Config.dirname();
8
+ const props = Config.props();
9
+
10
+ /**
11
+ * Scaffolding class for setting up React and Vite in the project.
12
+ */
13
+ class ReactScaffold {
14
+ /**
15
+ * Initializes the Scaffolder with the base application directory.
16
+ */
17
+ constructor() {
18
+ this.appDir = __dirname;
19
+ }
20
+
21
+ /**
22
+ * Executes the installation process.
23
+ */
24
+ async install() {
25
+ console.log(chalk.cyan("Initializing React + Vite setup..."));
26
+
27
+ try {
28
+ this.createStructure();
29
+ this.updatePackageJson();
30
+ this.createViteConfig();
31
+ this.createAppjs();
32
+ this.createMainJs();
33
+ this.createPagesJs();
34
+ this.updateGitIgnore();
35
+ this.npmInstall();
36
+
37
+ console.log(chalk.green("React scaffolding created successfully!"));
38
+ console.log(chalk.cyan("\nNext steps:"));
39
+ console.log(chalk.white(" 1. Run (Blue Bird Server): ") + chalk.bold("npm run dev"));
40
+ console.log(chalk.white(" 2. Run (React Vite Dev): ") + chalk.bold("npm run vite:dev"));
41
+ console.log(chalk.blue("\nBlue Bird React setup completed!"));
42
+ } catch (error) {
43
+ console.error(chalk.red("Fatal error during scaffolding:"), error.message);
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Creates the necessary directory structure for React resources.
49
+ */
50
+ createStructure() {
51
+ const dirs = [
52
+ 'frontend/resources/js/pages'
53
+ ];
54
+
55
+ dirs.forEach(dir => {
56
+ const fullPath = path.join(this.appDir, dir);
57
+ if (!fs.existsSync(fullPath)) {
58
+ fs.mkdirSync(fullPath, { recursive: true });
59
+ console.log(chalk.gray(`Created directory: ${dir}`));
60
+ }
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Updates the project's package.json with React and Vite dependencies and scripts.
66
+ */
67
+ updatePackageJson() {
68
+ const packagePath = path.join(this.appDir, 'package.json');
69
+ if (!fs.existsSync(packagePath)) {
70
+ console.warn(chalk.yellow("package.json not found. Initializing with npm init..."));
71
+ execSync('npm init -y', { cwd: this.appDir });
72
+ }
73
+
74
+ const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
75
+
76
+ pkg.scripts = pkg.scripts || {};
77
+ pkg.scripts["vite:dev"] = "vite";
78
+ pkg.scripts["vite:build"] = "vite build";
79
+
80
+ pkg.devDependencies = pkg.devDependencies || {};
81
+
82
+ pkg.devDependencies["vite"] = "^7.3.1";
83
+ pkg.devDependencies["@vitejs/plugin-react"] = "^4.3.4";
84
+
85
+ pkg.dependencies = pkg.dependencies || {};
86
+ pkg.dependencies["react"] = "^19.2.4";
87
+ pkg.dependencies["react-dom"] = "^19.2.4";
88
+ pkg.dependencies["react-router-dom"] = "^7.2.0";
89
+
90
+
91
+ fs.writeFileSync(packagePath, JSON.stringify(pkg, null, 2));
92
+ console.log(chalk.gray("Updated package.json dependencies and scripts."));
93
+ }
94
+
95
+ /**
96
+ * Creates the vite.config.js file with appropriate root and outDir settings.
97
+ */
98
+ createViteConfig() {
99
+ const file = path.join(this.appDir, 'vite.config.js');
100
+ if (fs.existsSync(file)) {
101
+ console.warn(chalk.yellow("vite.config.js already exists. Skipping."));
102
+ return;
103
+ }
104
+
105
+ // We use props.static.path to determine where the build goes
106
+ const outDir = path.join(props.static.path, 'build');
107
+
108
+ const content = `import { defineConfig } from 'vite';
109
+ import react from '@vitejs/plugin-react';
110
+ import path from 'path';
111
+
112
+ export default defineConfig({
113
+ plugins: [react()],
114
+ root: path.resolve(__dirname, 'frontend/resources/js'),
115
+ base: '/build/',
116
+ build: {
117
+ outDir: path.resolve(__dirname, '${outDir.replace(/\\/g, '/')}'),
118
+ emptyOutDir: true,
119
+ manifest: true,
120
+ rollupOptions: {
121
+ input: path.resolve(__dirname, 'frontend/resources/js/Main.jsx'),
122
+ },
123
+ },
124
+ server: {
125
+ origin: 'http://localhost:5173',
126
+ strictPort: true,
127
+ cors: true,
128
+ },
129
+ });`;
130
+ fs.writeFileSync(file, content);
131
+ console.log(chalk.gray("Created vite.config.js"));
132
+ }
133
+
134
+ createPagesJs() {
135
+ const file = path.join(this.appDir, 'frontend/resources/js/pages/Home.jsx');
136
+ if (fs.existsSync(file)) {
137
+ console.warn(chalk.yellow("Home.jsx already exists. Skipping."));
138
+ return;
139
+ }
140
+
141
+ const content = `import React, { useEffect } from 'react';
142
+ import { Link } from 'react-router-dom';
143
+ import Card from '../blue-bird/components/Card';
144
+
145
+ export default function Home() {
146
+ useEffect(() => {
147
+ // Example API call to the backend
148
+ fetch("http://localhost:3000/login", {
149
+ method: "POST",
150
+ headers: {
151
+ "Content-Type": "application/json",
152
+ },
153
+ body: JSON.stringify({
154
+ email: "example@example.com",
155
+ password: "myPassword123"
156
+ }),
157
+ })
158
+ .then((response) => response.json())
159
+ .then((data) => console.log('Backend response:', data))
160
+ .catch((error) => console.error('Error fetching from backend:', error));
161
+ }, []);
162
+
163
+ return (
164
+ <div
165
+ className="bg-white text-gray-900"
166
+ >
167
+ <nav
168
+ className='bg-white text-gray-900 border border-gray-200 px-4 py-4 flex justify-between items-center gap-4 sticky top-0 z-10'
169
+ >
170
+ <div className='font-bold text-xl text-blue-600'>
171
+ Blue Bird
172
+ </div>
173
+ <div className='flex justify-between items-center gap-4'>
174
+ <Link to="/" className='text-gray-500 hover:text-gray-900'>Home</Link>
175
+ <Link to="/about" className='text-gray-500 hover:text-gray-900'>About</Link>
176
+ </div>
177
+ </nav>
178
+ <main className='max-w-7xl mx-auto'>
179
+ <div className='text-center p-4'>
180
+ <header className='mb-4'>
181
+ <h1 className='text-3xl font-bold bg-gradient-to-r from-blue-500 to-blue-600 bg-clip-text text-transparent mb-4'>
182
+ Welcome to Blue Bird
183
+ </h1>
184
+ <p className='text-gray-500 max-w-600px mx-auto'>
185
+ The elegant, fast, and weightless framework for modern web development.
186
+ </p>
187
+ </header>
188
+
189
+ <Card title={" Documentation (Eng)"} className='mt-8 border-none shadow-none'>
190
+ <div className='flex gap-4 justify-center mb-8'>
191
+ <a
192
+ href="https://seip25.github.io/Blue-bird/en.html"
193
+ target="_blank"
194
+ rel="noopener noreferrer"
195
+ className='bg-blue-600 text-white px-4 py-2 rounded-lg font-semibold transition-colors hover:bg-blue-400'
196
+ >
197
+ Documentation(Eng)
198
+ </a>
199
+ <a
200
+ href="https://seip25.github.io/Blue-bird/"
201
+ target="_blank"
202
+ rel="noopener noreferrer"
203
+ className='bg-blue-50 text-blue-500 px-4 py-2 rounded-lg font-semibold transition-colors hover:bg-blue-100 '
204
+ >
205
+ Documentación (Esp)
206
+
207
+ </a>
208
+ </div>
209
+ </Card>
210
+
211
+ <Card className='mt-8 border-none shadow-none'>
212
+ <div className='mt-8 grid grid-cols-1 md:grid-cols-3 gap-4 max-w-1000px mx-auto'>
213
+ <div className='p-4 rounded-lg bg-gray-50 shadow-sm'>
214
+ <h3 className='text-blue-500 font-semibold text-xl mb-4'>Lightweight</h3>
215
+ <p>Built with performance and simplicity in mind.</p>
216
+ </div>
217
+ <div className='p-4 rounded-lg bg-gray-50 shadow-sm'>
218
+ <h3 className='text-blue-500 font-semibold text-xl mb-4'>React Powered</h3>
219
+ <p>Full React + Vite integration .</p>
220
+ </div>
221
+ <div className='p-4 rounded-lg bg-gray-50 shadow-sm'>
222
+ <h3 className='text-blue-500 font-semibold text-xl mb-4'>Express Backend</h3>
223
+ <p>Robust and scalable backend architecture.</p>
224
+ </div>
225
+ </div>
226
+ </Card>
227
+
228
+ </div>
229
+ </main>
230
+ </div>
231
+ );
232
+ }
233
+
234
+
235
+ `;
236
+ fs.writeFileSync(file, content);
237
+ console.log(chalk.gray("Created frontend/resources/js/pages/Home.jsx"));
238
+
239
+ const file2 = path.join(this.appDir, 'frontend/resources/js/pages/About.jsx');
240
+ if (fs.existsSync(file2)) {
241
+ console.warn(chalk.yellow("About.jsx already exists. Skipping."));
242
+ return;
243
+ }
244
+
245
+ const content2 = `import React from 'react';
246
+ import { Link } from 'react-router-dom';
247
+
248
+ export default function About() {
249
+ return (
250
+ <div
251
+ className="bg-white text-gray-900"
252
+ >
253
+ <nav
254
+ className='bg-white text-gray-900 border border-gray-200 px-4 py-4 flex justify-between items-center gap-4 sticky top-0 z-10'
255
+ >
256
+ <div className='font-bold text-xl text-blue-600'>
257
+ Blue Bird
258
+ </div>
259
+ <div className='flex justify-between items-center gap-4'>
260
+ <Link to="/" className='text-gray-500 hover:text-gray-900'>Home</Link>
261
+ <Link to="/about" className='text-gray-500 hover:text-gray-900'>About</Link>
262
+ </div>
263
+ </nav>
264
+ <main className='max-w-7xl mx-auto'>
265
+ <div className='p-4'>
266
+ <h1 className='text-xl font-bold text-gray-900 mb-4'>About Blue Bird</h1>
267
+ <p className='text-gray-500 leading-1.6'>
268
+ Blue Bird is a modern framework designed to bridge the gap between backend routing and frontend interactivity.
269
+ It provides a seamless developer experience for building fast, reactive web applications.
270
+ </p>
271
+ <p className='text-red-500 text-xl mt-8 '>
272
+ Check your console JS
273
+ </p>
274
+ </div>
275
+ </main>
276
+ </div>
277
+ );
278
+ }`;
279
+ fs.writeFileSync(file2, content2);
280
+ console.log(chalk.gray("Created frontend/resources/js/pages/About.jsx"));
281
+ }
282
+
283
+ /**
284
+ * Creates the main entry point for React (main.jsx) which handles island hydration.
285
+ */
286
+ createAppjs() {
287
+ const file = path.join(this.appDir, 'frontend/resources/js/App.jsx');
288
+ if (fs.existsSync(file)) {
289
+ console.warn(chalk.yellow("App.jsx already exists. Skipping."));
290
+ return;
291
+ }
292
+
293
+ const content = `import React from 'react';
294
+ import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
295
+ import Home from './pages/Home';
296
+ import About from './pages/About';
297
+
298
+ export default function App(_props) {
299
+ const {
300
+ component,
301
+ props
302
+ } = _props;
303
+
304
+ console.log('Check props and component ')
305
+ console.log('Component:'+component)
306
+ console.log(props)
307
+
308
+ return (
309
+ <Router>
310
+ <Routes>
311
+ <Route path="/" element={<Home />} />
312
+ <Route path="/about" element={<About />} />
313
+ </Routes>
314
+ </Router>
315
+ );
316
+ }
317
+
318
+ `;
319
+ fs.writeFileSync(file, content);
320
+ console.log(chalk.gray("Created frontend/resources/js/App.jsx"));
321
+ }
322
+ createMainJs() {
323
+ const file = path.join(this.appDir, 'frontend/resources/js/Main.jsx');
324
+ if (fs.existsSync(file)) {
325
+ console.warn(chalk.yellow("Main.jsx already exists. Skipping."));
326
+ return;
327
+ }
328
+
329
+ const content = `import React from 'react';
330
+ import { createRoot } from 'react-dom/client';
331
+ import App from './App';
332
+
333
+
334
+ document.addEventListener('DOMContentLoaded', () => {
335
+ document.querySelectorAll('[data-react-component]').forEach(el => {
336
+ const component = {
337
+ component:el.dataset.reactComponent
338
+ };
339
+ const props = JSON.parse(el.dataset.props || '{}');
340
+ const allProps={
341
+ ...props,
342
+ ...component
343
+ }
344
+ createRoot(el).render(<App {...allProps} />);
345
+ });
346
+ });`;
347
+ fs.writeFileSync(file, content);
348
+ console.log(chalk.gray("Created frontend/resources/js/Main.jsx"));
349
+ }
350
+
351
+
352
+
353
+ /**
354
+ * Ensures node_modules and other build artifacts are ignored by Git.
355
+ */
356
+ updateGitIgnore() {
357
+ const file = path.join(this.appDir, '.gitignore');
358
+ const entry = "\nnode_modules\ndist\nfrontend/public/build\n";
359
+
360
+ if (fs.existsSync(file)) {
361
+ const content = fs.readFileSync(file, 'utf8');
362
+ if (!content.includes('node_modules')) {
363
+ fs.appendFileSync(file, entry);
364
+ console.log(chalk.gray("Updated .gitignore"));
365
+ }
366
+ } else {
367
+ fs.writeFileSync(file, entry);
368
+ console.log(chalk.gray("Created .gitignore"));
369
+ }
370
+ }
371
+ npmInstall() {
372
+ try {
373
+ execSync('npm install', { cwd: this.appDir });
374
+ console.log(chalk.gray("Installed dependencies"));
375
+ } catch (error) {
376
+ console.error(chalk.red("Error installing dependencies:"), error.message);
377
+ }
378
+ }
379
+ }
380
+
381
+
382
+ const scaffold = new ReactScaffold();
383
+ scaffold.install();