@seip/blue-bird 0.2.0 → 0.2.2

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/core/cli/react.js CHANGED
@@ -1,394 +1,409 @@
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
- pkg.devDependencies["vite"] = "^5.0.0";
82
- pkg.devDependencies["@vitejs/plugin-react"] = "^4.2.0";
83
-
84
- pkg.dependencies = pkg.dependencies || {};
85
- pkg.dependencies["react"] = "^18.2.0";
86
- pkg.dependencies["react-dom"] = "^18.2.0";
87
- pkg.dependencies["react-router-dom"] = "^6.21.0";
88
-
89
- fs.writeFileSync(packagePath, JSON.stringify(pkg, null, 2));
90
- console.log(chalk.gray("Updated package.json dependencies and scripts."));
91
- }
92
-
93
- /**
94
- * Creates the vite.config.js file with appropriate root and outDir settings.
95
- */
96
- createViteConfig() {
97
- const file = path.join(this.appDir, 'vite.config.js');
98
- if (fs.existsSync(file)) {
99
- console.warn(chalk.yellow("vite.config.js already exists. Skipping."));
100
- return;
101
- }
102
-
103
- // We use props.static.path to determine where the build goes
104
- const outDir = path.join(props.static.path, 'build');
105
-
106
- const content = `import { defineConfig } from 'vite';
107
- import react from '@vitejs/plugin-react';
108
- import path from 'path';
109
-
110
- export default defineConfig({
111
- plugins: [react()],
112
- root: path.resolve(__dirname, 'frontend/resources/js'),
113
- base: '/build/',
114
- build: {
115
- outDir: path.resolve(__dirname, '${outDir.replace(/\\/g, '/')}'),
116
- emptyOutDir: true,
117
- manifest: true,
118
- rollupOptions: {
119
- input: path.resolve(__dirname, 'frontend/resources/js/main.jsx'),
120
- },
121
- },
122
- server: {
123
- origin: 'http://localhost:5173',
124
- strictPort: true,
125
- cors: true,
126
- },
127
- });`;
128
- fs.writeFileSync(file, content);
129
- console.log(chalk.gray("Created vite.config.js"));
130
- }
131
-
132
- createPagesJs() {
133
- const file = path.join(this.appDir, 'frontend/resources/js/pages/Home.jsx');
134
- if (fs.existsSync(file)) {
135
- console.warn(chalk.yellow("Home.jsx already exists. Skipping."));
136
- return;
137
- }
138
-
139
- const content = `import React, { useEffect } from 'react';
140
-
141
- export default function Home() {
142
- useEffect(() => {
143
- // Example API call to the backend
144
- fetch("http://localhost:3000/login", {
145
- method: "POST",
146
- headers: {
147
- "Content-Type": "application/json",
148
- },
149
- body: JSON.stringify({}),
150
- })
151
- .then((response) => response.json())
152
- .then((data) => console.log('Backend response:', data))
153
- .catch((error) => console.error('Error fetching from backend:', error));
154
- }, []);
155
-
156
- return (
157
- <div style={{ textAlign: 'center', padding: '4rem 2rem' }}>
158
- <header style={{ marginBottom: '3rem' }}>
159
- <h1 style={{
160
- fontSize: '3.5rem',
161
- fontWeight: '800',
162
- background: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)',
163
- WebkitBackgroundClip: 'text',
164
- WebkitTextFillColor: 'transparent',
165
- marginBottom: '1rem'
166
- }}>
167
- Welcome to Blue Bird
168
- </h1>
169
- <p style={{ fontSize: '1.25rem', color: '#6b7280', maxWidth: '600px', margin: '0 auto' }}>
170
- The elegant, fast, and weightless framework for modern web development.
171
- </p>
172
- </header>
173
-
174
- <div style={{ display: 'flex', gap: '1rem', justifyContent: 'center', marginBottom: '4rem' }}>
175
- <a
176
- href="https://seip25.github.io/Blue-bird/"
177
- target="_blank"
178
- rel="noopener noreferrer"
179
- style={{
180
- backgroundColor: '#2563eb',
181
- color: 'white',
182
- padding: '0.75rem 1.5rem',
183
- borderRadius: '0.5rem',
184
- textDecoration: 'none',
185
- fontWeight: '600',
186
- transition: 'background-color 0.2s'
187
- }}
188
- onMouseOver={(e) => e.target.style.backgroundColor = '#1d4ed8'}
189
- onMouseOut={(e) => e.target.style.backgroundColor = '#2563eb'}
190
- >
191
- Documentation
192
- </a>
193
- <a
194
- href="https://seip25.github.io/Blue-bird/en.html"
195
- target="_blank"
196
- rel="noopener noreferrer"
197
- style={{
198
- backgroundColor: 'white',
199
- color: '#374151',
200
- padding: '0.75rem 1.5rem',
201
- borderRadius: '0.5rem',
202
- textDecoration: 'none',
203
- fontWeight: '600',
204
- border: '1px solid #d1d5db',
205
- transition: 'background-color 0.2s'
206
- }}
207
- onMouseOver={(e) => e.target.style.backgroundColor = '#f9fafb'}
208
- onMouseOut={(e) => e.target.style.backgroundColor = 'white'}
209
- >
210
- English Docs
211
- </a>
212
- </div>
213
-
214
- <div style={{
215
- display: 'grid',
216
- gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))',
217
- gap: '2rem',
218
- maxWidth: '1000px',
219
- margin: '0 auto'
220
- }}>
221
- <div style={cardStyle}>
222
- <h3>Lightweight</h3>
223
- <p>Built with performance and simplicity in mind.</p>
224
- </div>
225
- <div style={cardStyle}>
226
- <h3>React Powered</h3>
227
- <p>Full React + Vite integration with island hydration.</p>
228
- </div>
229
- <div style={cardStyle}>
230
- <h3>Express Backend</h3>
231
- <p>Robust and scalable backend architecture.</p>
232
- </div>
233
- </div>
234
- </div>
235
- );
236
- }
237
-
238
- const cardStyle = {
239
- padding: '1.5rem',
240
- borderRadius: '0.75rem',
241
- border: '1px solid #e5e7eb',
242
- textAlign: 'left',
243
- backgroundColor: 'white',
244
- boxShadow: '0 1px 2px 0 rgba(0, 0, 0, 0.05)'
245
- };`;
246
- fs.writeFileSync(file, content);
247
- console.log(chalk.gray("Created frontend/resources/js/pages/Home.jsx"));
248
-
249
- const file2 = path.join(this.appDir, 'frontend/resources/js/pages/About.jsx');
250
- if (fs.existsSync(file2)) {
251
- console.warn(chalk.yellow("About.jsx already exists. Skipping."));
252
- return;
253
- }
254
-
255
- const content2 = `import React from 'react';
256
-
257
- export default function About() {
258
- return (
259
- <div style={{ padding: '2rem' }}>
260
- <h1 style={{ color: '#111827', marginBottom: '1rem' }}>About Blue Bird</h1>
261
- <p style={{ color: '#4b5563', lineHeight: '1.6' }}>
262
- Blue Bird is a modern framework designed to bridge the gap between backend routing and frontend interactivity.
263
- It provides a seamless developer experience for building fast, reactive web applications.
264
- </p>
265
- </div>
266
- );
267
- }`;
268
- fs.writeFileSync(file2, content2);
269
- console.log(chalk.gray("Created frontend/resources/js/pages/About.jsx"));
270
- }
271
-
272
- /**
273
- * Creates the main entry point for React (main.jsx) which handles island hydration.
274
- */
275
- createAppjs() {
276
- const file = path.join(this.appDir, 'frontend/resources/js/App.jsx');
277
- if (fs.existsSync(file)) {
278
- console.warn(chalk.yellow("App.jsx already exists. Skipping."));
279
- return;
280
- }
281
-
282
- const content = `import React from 'react';
283
- import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
284
- import Home from './pages/Home';
285
- import About from './pages/About';
286
-
287
- export default function App(props) {
288
- return (
289
- <Router>
290
- <div style={{
291
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
292
- minHeight: '100vh',
293
- backgroundColor: '#f9fafb',
294
- color: '#111827'
295
- }}>
296
- <nav style={{
297
- background: 'white',
298
- padding: '1rem 2rem',
299
- display: 'flex',
300
- justifyContent: 'space-between',
301
- alignItems: 'center',
302
- boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1)',
303
- position: 'sticky',
304
- top: 0,
305
- zIndex: 10
306
- }}>
307
- <div style={{ fontWeight: 'bold', fontSize: '1.25rem', color: '#2563eb' }}>
308
- Blue Bird
309
- </div>
310
- <div style={{ display: 'flex', gap: '2rem' }}>
311
- <Link to="/" style={navLinkStyle}>Home</Link>
312
- <Link to="/about" style={navLinkStyle}>About</Link>
313
- </div>
314
- </nav>
315
-
316
- <main style={{ maxWidth: '1200px', margin: '0 auto' }}>
317
- {/* Uncomment to debug props if needed */}
318
- {/* <div style={{ padding: '0.5rem', background: '#ececec', fontSize: '0.75rem' }}>Props: {JSON.stringify(props)}</div> */}
319
-
320
- <Routes>
321
- <Route path="/" element={<Home />} />
322
- <Route path="/about" element={<About />} />
323
- </Routes>
324
- </main>
325
- </div>
326
- </Router>
327
- );
328
- }
329
-
330
- const navLinkStyle = {
331
- color: '#4b5563',
332
- textDecoration: 'none',
333
- fontWeight: '500',
334
- fontSize: '0.95rem',
335
- transition: 'color 0.2s'
336
- };`;
337
- fs.writeFileSync(file, content);
338
- console.log(chalk.gray("Created frontend/resources/js/App.jsx"));
339
- }
340
- createMainJs() {
341
- const file = path.join(this.appDir, 'frontend/resources/js/Main.jsx');
342
- if (fs.existsSync(file)) {
343
- console.warn(chalk.yellow("Main.jsx already exists. Skipping."));
344
- return;
345
- }
346
-
347
- const content = `import React from 'react';
348
- import { createRoot } from 'react-dom/client';
349
- import App from './App';
350
-
351
- document.addEventListener('DOMContentLoaded', () => {
352
- document.querySelectorAll('[data-react-component]').forEach(el => {
353
- const name = el.dataset.reactComponent;
354
- const props = JSON.parse(el.dataset.props || '{}');
355
- createRoot(el).render(<App {...props} />);
356
- });
357
- });`;
358
- fs.writeFileSync(file, content);
359
- console.log(chalk.gray("Created frontend/resources/js/Main.jsx"));
360
- }
361
-
362
-
363
-
364
- /**
365
- * Ensures node_modules and other build artifacts are ignored by Git.
366
- */
367
- updateGitIgnore() {
368
- const file = path.join(this.appDir, '.gitignore');
369
- const entry = "\nnode_modules\ndist\nfrontend/public/build\n";
370
-
371
- if (fs.existsSync(file)) {
372
- const content = fs.readFileSync(file, 'utf8');
373
- if (!content.includes('node_modules')) {
374
- fs.appendFileSync(file, entry);
375
- console.log(chalk.gray("Updated .gitignore"));
376
- }
377
- } else {
378
- fs.writeFileSync(file, entry);
379
- console.log(chalk.gray("Created .gitignore"));
380
- }
381
- }
382
- npmInstall() {
383
- try {
384
- execSync('npm install', { cwd: this.appDir });
385
- console.log(chalk.gray("Installed dependencies"));
386
- } catch (error) {
387
- console.error(chalk.red("Error installing dependencies:"), error.message);
388
- }
389
- }
390
- }
391
-
392
-
393
- const scaffold = new ReactScaffold();
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
+ pkg.devDependencies["vite"] = "^5.0.0";
82
+ pkg.devDependencies["@vitejs/plugin-react"] = "^4.2.0";
83
+
84
+ pkg.dependencies = pkg.dependencies || {};
85
+ pkg.dependencies["react"] = "^18.2.0";
86
+ pkg.dependencies["react-dom"] = "^18.2.0";
87
+ pkg.dependencies["react-router-dom"] = "^6.21.0";
88
+
89
+ fs.writeFileSync(packagePath, JSON.stringify(pkg, null, 2));
90
+ console.log(chalk.gray("Updated package.json dependencies and scripts."));
91
+ }
92
+
93
+ /**
94
+ * Creates the vite.config.js file with appropriate root and outDir settings.
95
+ */
96
+ createViteConfig() {
97
+ const file = path.join(this.appDir, 'vite.config.js');
98
+ if (fs.existsSync(file)) {
99
+ console.warn(chalk.yellow("vite.config.js already exists. Skipping."));
100
+ return;
101
+ }
102
+
103
+ // We use props.static.path to determine where the build goes
104
+ const outDir = path.join(props.static.path, 'build');
105
+
106
+ const content = `import { defineConfig } from 'vite';
107
+ import react from '@vitejs/plugin-react';
108
+ import path from 'path';
109
+
110
+ export default defineConfig({
111
+ plugins: [react()],
112
+ root: path.resolve(__dirname, 'frontend/resources/js'),
113
+ base: '/build/',
114
+ build: {
115
+ outDir: path.resolve(__dirname, '${outDir.replace(/\\/g, '/')}'),
116
+ emptyOutDir: true,
117
+ manifest: true,
118
+ rollupOptions: {
119
+ input: path.resolve(__dirname, 'frontend/resources/js/Main.jsx'),
120
+ },
121
+ },
122
+ server: {
123
+ origin: 'http://localhost:5173',
124
+ strictPort: true,
125
+ cors: true,
126
+ },
127
+ });`;
128
+ fs.writeFileSync(file, content);
129
+ console.log(chalk.gray("Created vite.config.js"));
130
+ }
131
+
132
+ createPagesJs() {
133
+ const file = path.join(this.appDir, 'frontend/resources/js/pages/Home.jsx');
134
+ if (fs.existsSync(file)) {
135
+ console.warn(chalk.yellow("Home.jsx already exists. Skipping."));
136
+ return;
137
+ }
138
+
139
+ const content = `import React, { useEffect } from 'react';
140
+
141
+ export default function Home() {
142
+ useEffect(() => {
143
+ // Example API call to the backend
144
+ fetch("http://localhost:3000/login", {
145
+ method: "POST",
146
+ headers: {
147
+ "Content-Type": "application/json",
148
+ },
149
+ body: JSON.stringify({
150
+ email:"example@example.com",
151
+ password: "myPassword123"
152
+ }),
153
+ })
154
+ .then((response) => response.json())
155
+ .then((data) => console.log('Backend response:', data))
156
+ .catch((error) => console.error('Error fetching from backend:', error));
157
+ }, []);
158
+
159
+ return (
160
+ <div style={{ textAlign: 'center', padding: '4rem 2rem' }}>
161
+ <header style={{ marginBottom: '3rem' }}>
162
+ <h1 style={{
163
+ fontSize: '3.5rem',
164
+ fontWeight: '800',
165
+ background: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)',
166
+ WebkitBackgroundClip: 'text',
167
+ WebkitTextFillColor: 'transparent',
168
+ marginBottom: '1rem'
169
+ }}>
170
+ Welcome to Blue Bird
171
+ </h1>
172
+ <p style={{ fontSize: '1.25rem', color: '#6b7280', maxWidth: '600px', margin: '0 auto' }}>
173
+ The elegant, fast, and weightless framework for modern web development.
174
+ </p>
175
+ </header>
176
+
177
+ <div style={{ display: 'flex', gap: '1rem', justifyContent: 'center', marginBottom: '4rem' }}>
178
+ <a
179
+ href="https://seip25.github.io/Blue-bird/"
180
+ target="_blank"
181
+ rel="noopener noreferrer"
182
+ style={{
183
+ backgroundColor: '#2563eb',
184
+ color: 'white',
185
+ padding: '0.75rem 1.5rem',
186
+ borderRadius: '0.5rem',
187
+ textDecoration: 'none',
188
+ fontWeight: '600',
189
+ transition: 'background-color 0.2s'
190
+ }}
191
+ onMouseOver={(e) => e.target.style.backgroundColor = '#1d4ed8'}
192
+ onMouseOut={(e) => e.target.style.backgroundColor = '#2563eb'}
193
+ >
194
+ Documentation
195
+ </a>
196
+ <a
197
+ href="https://seip25.github.io/Blue-bird/en.html"
198
+ target="_blank"
199
+ rel="noopener noreferrer"
200
+ style={{
201
+ backgroundColor: 'white',
202
+ color: '#374151',
203
+ padding: '0.75rem 1.5rem',
204
+ borderRadius: '0.5rem',
205
+ textDecoration: 'none',
206
+ fontWeight: '600',
207
+ border: '1px solid #d1d5db',
208
+ transition: 'background-color 0.2s'
209
+ }}
210
+ onMouseOver={(e) => e.target.style.backgroundColor = '#f9fafb'}
211
+ onMouseOut={(e) => e.target.style.backgroundColor = 'white'}
212
+ >
213
+ English Docs
214
+ </a>
215
+ </div>
216
+
217
+ <div style={{
218
+ display: 'grid',
219
+ gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))',
220
+ gap: '2rem',
221
+ maxWidth: '1000px',
222
+ margin: '0 auto'
223
+ }}>
224
+ <div style={cardStyle}>
225
+ <h3>Lightweight</h3>
226
+ <p>Built with performance and simplicity in mind.</p>
227
+ </div>
228
+ <div style={cardStyle}>
229
+ <h3>React Powered</h3>
230
+ <p>Full React + Vite integration with island hydration.</p>
231
+ </div>
232
+ <div style={cardStyle}>
233
+ <h3>Express Backend</h3>
234
+ <p>Robust and scalable backend architecture.</p>
235
+ </div>
236
+ </div>
237
+ </div>
238
+ );
239
+ }
240
+
241
+ const cardStyle = {
242
+ padding: '1.5rem',
243
+ borderRadius: '0.75rem',
244
+ border: '1px solid #e5e7eb',
245
+ textAlign: 'left',
246
+ backgroundColor: 'white',
247
+ boxShadow: '0 1px 2px 0 rgba(0, 0, 0, 0.05)'
248
+ };`;
249
+ fs.writeFileSync(file, content);
250
+ console.log(chalk.gray("Created frontend/resources/js/pages/Home.jsx"));
251
+
252
+ const file2 = path.join(this.appDir, 'frontend/resources/js/pages/About.jsx');
253
+ if (fs.existsSync(file2)) {
254
+ console.warn(chalk.yellow("About.jsx already exists. Skipping."));
255
+ return;
256
+ }
257
+
258
+ const content2 = `import React from 'react';
259
+
260
+ export default function About() {
261
+ return (
262
+ <div style={{ padding: '2rem' }}>
263
+ <h1 style={{ color: '#111827', marginBottom: '1rem' }}>About Blue Bird</h1>
264
+ <p style={{ color: '#4b5563', lineHeight: '1.6' }}>
265
+ Blue Bird is a modern framework designed to bridge the gap between backend routing and frontend interactivity.
266
+ It provides a seamless developer experience for building fast, reactive web applications.
267
+ </p>
268
+ </div>
269
+ );
270
+ }`;
271
+ fs.writeFileSync(file2, content2);
272
+ console.log(chalk.gray("Created frontend/resources/js/pages/About.jsx"));
273
+ }
274
+
275
+ /**
276
+ * Creates the main entry point for React (main.jsx) which handles island hydration.
277
+ */
278
+ createAppjs() {
279
+ const file = path.join(this.appDir, 'frontend/resources/js/App.jsx');
280
+ if (fs.existsSync(file)) {
281
+ console.warn(chalk.yellow("App.jsx already exists. Skipping."));
282
+ return;
283
+ }
284
+
285
+ const content = `import React from 'react';
286
+ import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
287
+ import Home from './pages/Home';
288
+ import About from './pages/About';
289
+
290
+ export default function App(_props) {
291
+ const {
292
+ component,
293
+ props
294
+ } = _props;
295
+
296
+ return (
297
+ <Router>
298
+ <div style={{
299
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
300
+ minHeight: '100vh',
301
+ backgroundColor: '#f9fafb',
302
+ color: '#111827'
303
+ }}>
304
+ <nav style={{
305
+ background: 'white',
306
+ padding: '1rem 2rem',
307
+ display: 'flex',
308
+ justifyContent: 'space-between',
309
+ alignItems: 'center',
310
+ boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1)',
311
+ position: 'sticky',
312
+ top: 0,
313
+ zIndex: 10
314
+ }}>
315
+ <div style={{ fontWeight: 'bold', fontSize: '1.25rem', color: '#2563eb' }}>
316
+ Blue Bird
317
+ </div>
318
+ <div style={{ display: 'flex', gap: '2rem' }}>
319
+ <Link to="/" style={navLinkStyle}>Home</Link>
320
+ <Link to="/about" style={navLinkStyle}>About</Link>
321
+ </div>
322
+ </nav>
323
+
324
+ <main style={{ maxWidth: '1200px', margin: '0 auto' }}>
325
+ {/* Uncomment to debug props if needed */}
326
+ {/* <div style={{ padding: '0.5rem', background: '#ececec', fontSize: '0.75rem' }}>Props: {JSON.stringify(props)}</div> */}
327
+
328
+ <Routes>
329
+ <Route path="/" element={<Home />} />
330
+ <Route path="/about" element={<About />} />
331
+ </Routes>
332
+ </main>
333
+ </div>
334
+ </Router>
335
+ );
336
+ }
337
+
338
+ const navLinkStyle = {
339
+ color: '#4b5563',
340
+ textDecoration: 'none',
341
+ fontWeight: '500',
342
+ fontSize: '0.95rem',
343
+ transition: 'color 0.2s'
344
+ };`;
345
+ fs.writeFileSync(file, content);
346
+ console.log(chalk.gray("Created frontend/resources/js/App.jsx"));
347
+ }
348
+ createMainJs() {
349
+ const file = path.join(this.appDir, 'frontend/resources/js/Main.jsx');
350
+ if (fs.existsSync(file)) {
351
+ console.warn(chalk.yellow("Main.jsx already exists. Skipping."));
352
+ return;
353
+ }
354
+
355
+ const content = `import React from 'react';
356
+ import { createRoot } from 'react-dom/client';
357
+ import App from './App';
358
+
359
+
360
+ document.addEventListener('DOMContentLoaded', () => {
361
+ document.querySelectorAll('[data-react-component]').forEach(el => {
362
+ const component = {
363
+ component:el.dataset.reactComponent
364
+ };
365
+ const props = JSON.parse(el.dataset.props || '{}');
366
+ const allProps={
367
+ ...props,
368
+ ...component
369
+ }
370
+ createRoot(el).render(<App {...allProps} />);
371
+ });
372
+ });`;
373
+ fs.writeFileSync(file, content);
374
+ console.log(chalk.gray("Created frontend/resources/js/Main.jsx"));
375
+ }
376
+
377
+
378
+
379
+ /**
380
+ * Ensures node_modules and other build artifacts are ignored by Git.
381
+ */
382
+ updateGitIgnore() {
383
+ const file = path.join(this.appDir, '.gitignore');
384
+ const entry = "\nnode_modules\ndist\nfrontend/public/build\n";
385
+
386
+ if (fs.existsSync(file)) {
387
+ const content = fs.readFileSync(file, 'utf8');
388
+ if (!content.includes('node_modules')) {
389
+ fs.appendFileSync(file, entry);
390
+ console.log(chalk.gray("Updated .gitignore"));
391
+ }
392
+ } else {
393
+ fs.writeFileSync(file, entry);
394
+ console.log(chalk.gray("Created .gitignore"));
395
+ }
396
+ }
397
+ npmInstall() {
398
+ try {
399
+ execSync('npm install', { cwd: this.appDir });
400
+ console.log(chalk.gray("Installed dependencies"));
401
+ } catch (error) {
402
+ console.error(chalk.red("Error installing dependencies:"), error.message);
403
+ }
404
+ }
405
+ }
406
+
407
+
408
+ const scaffold = new ReactScaffold();
394
409
  scaffold.install();