create-firem 1.0.1

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 (33) hide show
  1. package/.github/workflows/auto-update.yml +42 -0
  2. package/.github/workflows/npm-publish.yml +42 -0
  3. package/LICENSE +21 -0
  4. package/agents.md +32 -0
  5. package/bin/index.js +405 -0
  6. package/package.json +32 -0
  7. package/readme.md +90 -0
  8. package/scripts/auto-update-packages.js +110 -0
  9. package/specs/cli.md +83 -0
  10. package/specs/fireabse.md +66 -0
  11. package/specs/render.md +140 -0
  12. package/specs/template.md +170 -0
  13. package/templates/basic/package.json +32 -0
  14. package/templates/basic/src/App.jsx +28 -0
  15. package/templates/basic/src/main.jsx +15 -0
  16. package/templates/common/gitignore +27 -0
  17. package/templates/common/index.html +15 -0
  18. package/templates/common/src/components/AppRouter.jsx +23 -0
  19. package/templates/common/src/config/firebase.ts +30 -0
  20. package/templates/common/src/config/routerConfig.js +6 -0
  21. package/templates/common/src/theme/theme.js +14 -0
  22. package/templates/common/vite.config.js +28 -0
  23. package/templates/dashboard/package.json +32 -0
  24. package/templates/dashboard/src/App.jsx +50 -0
  25. package/templates/dashboard/src/components/Login.jsx +67 -0
  26. package/templates/dashboard/src/context/AuthContext.jsx +43 -0
  27. package/templates/dashboard/src/main.jsx +15 -0
  28. package/templates/dashboard/src/pages/DashboardOverview.jsx +21 -0
  29. package/templates/dashboard/src/pages/Profile.jsx +23 -0
  30. package/templates/landing/package.json +32 -0
  31. package/templates/landing/src/App.jsx +131 -0
  32. package/templates/landing/src/context/AuthContext.jsx +43 -0
  33. package/templates/landing/src/main.jsx +15 -0
@@ -0,0 +1,42 @@
1
+ name: Auto Update Template Packages
2
+
3
+ on:
4
+ schedule:
5
+ # Run every Monday at 15:00 UTC (9:00 AM UTC-6)
6
+ - cron: '0 15 * * 1'
7
+ workflow_dispatch: # Allow manual trigger
8
+
9
+ permissions:
10
+ contents: write
11
+
12
+ jobs:
13
+ update-packages:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - name: Checkout Code
17
+ uses: actions/checkout@v6
18
+
19
+ - name: Setup Node.js
20
+ uses: actions/setup-node@v6
21
+ with:
22
+ node-version: 24
23
+ cache: 'npm'
24
+
25
+ - name: Install CLI Dependencies
26
+ run: npm ci || npm install
27
+
28
+ - name: Run Update Script
29
+ run: node scripts/auto-update-packages.js
30
+
31
+ - name: Commit and Push Changes
32
+ run: |
33
+ git config --global user.name "github-actions[bot]"
34
+ git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
35
+ git add .
36
+ # Check if there are staged changes to commit
37
+ if ! git diff-index --quiet HEAD; then
38
+ git commit -m "chore: auto-update template dependencies and bump version"
39
+ git push origin HEAD
40
+ else
41
+ echo "No updates found. Repository is already up-to-date."
42
+ fi
@@ -0,0 +1,42 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - master
8
+ workflow_dispatch: # Allow manual trigger
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ publish:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - name: Checkout Code
18
+ uses: actions/checkout@v6
19
+
20
+ - name: Setup Node.js
21
+ uses: actions/setup-node@v6
22
+ with:
23
+ node-version: 24
24
+ registry-url: 'https://registry.npmjs.org'
25
+
26
+ - name: Install CLI Dependencies
27
+ run: npm ci || npm install
28
+
29
+ - name: Check and Publish to npm
30
+ run: |
31
+ CURRENT_VERSION=$(node -p "require('./package.json').version")
32
+ PACKAGE_NAME=$(node -p "require('./package.json').name")
33
+
34
+ # Check if current version already exists on npm
35
+ if npm view "$PACKAGE_NAME@$CURRENT_VERSION" version >/dev/null 2>&1; then
36
+ echo "Version $CURRENT_VERSION of $PACKAGE_NAME is already published on npm. Skipping publication."
37
+ else
38
+ echo "Publishing version $CURRENT_VERSION of $PACKAGE_NAME to npm..."
39
+ npm publish
40
+ fi
41
+ env:
42
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Wayproyect
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/agents.md ADDED
@@ -0,0 +1,32 @@
1
+ # Technical Document for Agents (agents.md)
2
+
3
+ ## Repository Purpose
4
+ The primary objective of this repository is to build and maintain the `create-firem` project generator. This tool is designed to be executed via standard Node package managers to quickly scaffold a new project template for users.
5
+
6
+ ## Invocation Commands
7
+ The finished CLI tool will be invoked by end-users using either of the following commands:
8
+ - `npx create-firem`
9
+ - `npm init firem`
10
+
11
+ ## Architecture & Structure
12
+ As an agent working on this repository, please adhere to the following architectural guidelines for building a modern CLI generator:
13
+
14
+ 1. **CLI Entry Point**: The project must define a main executable script. This is typically configured in the `bin` field of the `package.json` file.
15
+ 2. **Template Directory**: A dedicated directory (e.g., `template/` or `templates/`) should exist containing the base boilerplate code that will be copied to the target user directory.
16
+ 3. **Interactivity**: The CLI should optionally prompt the user for relevant project details (e.g., project name, features to include, language preference) using tools like `inquirer` or `prompts`.
17
+ 4. **Scaffolding Logic**: The tool should handle copying files, correctly renaming dotfiles (such as renaming a placeholder like `gitignore` to `.gitignore`, as npm strips `.gitignore` during publish), and modifying `package.json` placeholders based on user input.
18
+ 5. **Dependencies**: Upon copying the template, the CLI should optionally install the required dependencies automatically or prompt the user if they'd like to install them.
19
+
20
+ ## AI Agent Instructions
21
+ - **Code Style**: Maintain clean, modular JavaScript/TypeScript code.
22
+ - **Dependencies**: Keep dependencies to a minimum to ensure fast and reliable execution. Prefer built-in Node.js modules like `fs`, `path`, and `child_process` when possible.
23
+ - **Testing**: Ensure any scaffolding logic is thoroughly tested to prevent broken templates for the end-user.
24
+ - **Documentation**: Update `README.md` with clear instructions on how users can leverage the generated project and how contributors can maintain and update the template generator.
25
+
26
+ ## Current State
27
+ The project generator CLI and base templates are fully functional:
28
+ - **CLI Scaffold Logic**: Completed (`bin/index.js`), with full support for interactive scaffolding and command flags (`--template`, `--layout`, `--auth`, `--install`). Includes layout flex direction adjustment for sidebar menus.
29
+ - **Templates**: Completed `basic`, `landing`, and `dashboard` templates under `templates/` with `react-router-dom` pre-configured.
30
+ - **SPA & Modular Rendering**: Pure Single Page Application (SPA) architecture implemented. Vite config includes advanced code splitting via Rollup manual chunks (`vendor-mui`, `vendor-firebase`, `vendor-core`, etc.) to keep initial bundle sizes lightweight.
31
+ - **Flexible Routing**: Pre-configured `AppRouter.jsx` wrapper reading from `routerConfig.js` to dynamically toggle between `'browser'` (BrowserRouter) and `'hash'` (HashRouter) routing modes.
32
+ - **Development Setup**: Root dependencies updated to include development packages (like `firebase`) to prevent editor resolution errors.
package/bin/index.js ADDED
@@ -0,0 +1,405 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import prompts from 'prompts';
5
+ import { blue, green, red, yellow, bold, cyan } from 'kolorist';
6
+ import ora from 'ora';
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+ import { execSync } from 'child_process';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+
15
+ const program = new Command();
16
+
17
+ program
18
+ .name('create-firem')
19
+ .description('Scaffold a new React + Firebase + Material-UI project')
20
+ .argument('[project-name]', 'Name of the project')
21
+ .option('-t, --template <type>', 'Template type: dashboard, landing, basic')
22
+ .option('-l, --layout <position>', 'Navigation layout: top, bottom, sidebar, none')
23
+ .option('-a, --auth <boolean>', 'Include authentication (for landing template)')
24
+ .option('--install', 'Automatically install dependencies')
25
+ .parse(process.argv);
26
+
27
+ async function main() {
28
+ console.log(cyan(bold('\n🔥 Welcome to create-firem! Scaffold React + Firebase + MUI in seconds.\n')));
29
+
30
+ const args = program.args;
31
+ const options = program.opts();
32
+
33
+ let targetDir = args[0];
34
+ let template = options.template;
35
+ let layout = options.layout;
36
+ let includeAuth = options.auth !== undefined ? options.auth === 'true' : null;
37
+
38
+ // Prompt for project name if not provided
39
+ if (!targetDir) {
40
+ const response = await prompts({
41
+ type: 'text',
42
+ name: 'projectName',
43
+ message: 'What is the name of your project?',
44
+ initial: 'my-firem-app',
45
+ validate: (value) => {
46
+ const pattern = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
47
+ return pattern.test(value) ? true : 'Invalid package.json name';
48
+ }
49
+ });
50
+
51
+ if (!response.projectName) {
52
+ console.log(yellow('\nOperation cancelled.'));
53
+ process.exit(0);
54
+ }
55
+ targetDir = response.projectName;
56
+ }
57
+
58
+ // Prompt for template type if not provided
59
+ if (!template) {
60
+ const response = await prompts({
61
+ type: 'select',
62
+ name: 'template',
63
+ message: 'Select a template type:',
64
+ choices: [
65
+ { title: '📊 Dashboard (Firebase Auth, Persistent Login, Admin View)', value: 'dashboard' },
66
+ { title: '🚀 Landing Page (Direct load, Optional Auth)', value: 'landing' },
67
+ { title: '📦 Basic (Minimal skeleton)', value: 'basic' }
68
+ ]
69
+ });
70
+
71
+ if (!response.template) {
72
+ console.log(yellow('\nOperation cancelled.'));
73
+ process.exit(0);
74
+ }
75
+ template = response.template;
76
+ }
77
+
78
+ // Prompt for auth conditionally (Landing template only)
79
+ if (template === 'landing' && includeAuth === null) {
80
+ const response = await prompts({
81
+ type: 'confirm',
82
+ name: 'includeAuth',
83
+ message: 'Do you want to include Firebase Authentication in your Landing Page?',
84
+ initial: false
85
+ });
86
+
87
+ includeAuth = response.includeAuth;
88
+ } else if (template === 'dashboard') {
89
+ includeAuth = true; // Dashboard always includes auth
90
+ } else if (template === 'basic') {
91
+ includeAuth = false; // Basic is minimal
92
+ }
93
+
94
+ // Prompt for navigation layout position if not provided
95
+ if (!layout) {
96
+ const response = await prompts({
97
+ type: 'select',
98
+ name: 'layout',
99
+ message: 'Where should the main navigation menu be positioned?',
100
+ choices: [
101
+ { title: '⬆️ Top (Superior)', value: 'top' },
102
+ { title: '⬇️ Bottom (Inferior - Mobile first)', value: 'bottom' },
103
+ { title: '⬅️ Sidebar (Lateral)', value: 'sidebar' },
104
+ { title: '❌ None (Sin menú)', value: 'none' }
105
+ ]
106
+ });
107
+
108
+ if (!response.layout) {
109
+ console.log(yellow('\nOperation cancelled.'));
110
+ process.exit(0);
111
+ }
112
+ layout = response.layout;
113
+ }
114
+
115
+ const rootPath = path.resolve(targetDir);
116
+
117
+ if (fs.existsSync(rootPath)) {
118
+ const overwriteResponse = await prompts({
119
+ type: 'confirm',
120
+ name: 'overwrite',
121
+ message: `Directory '${targetDir}' already exists. Overwrite?`,
122
+ initial: false
123
+ });
124
+
125
+ if (!overwriteResponse.overwrite) {
126
+ console.log(red('\nAborted.'));
127
+ process.exit(1);
128
+ }
129
+ fs.rmSync(rootPath, { recursive: true, force: true });
130
+ }
131
+
132
+ fs.mkdirSync(rootPath, { recursive: true });
133
+
134
+ const spinner = ora('Scaffolding project...').start();
135
+
136
+ try {
137
+ const templatesDir = path.join(__dirname, '../templates');
138
+
139
+ // Copy common templates
140
+ copyDir(path.join(templatesDir, 'common'), rootPath);
141
+
142
+ // Copy template-specific files
143
+ copyDir(path.join(templatesDir, template), rootPath);
144
+
145
+ // Dynamic Navigation template setup based on layout selection
146
+ setupNavigation(rootPath, layout);
147
+
148
+ // Dynamic Auth setup
149
+ setupAuth(rootPath, includeAuth, template);
150
+
151
+ // Configure layout flex direction in App.jsx if layout is sidebar
152
+ const appPath = path.join(rootPath, 'src/App.jsx');
153
+ if (fs.existsSync(appPath) && layout === 'sidebar') {
154
+ let content = fs.readFileSync(appPath, 'utf8');
155
+ content = content.replace(/flexDirection:\s*'column'/g, "flexDirection: 'row'");
156
+ fs.writeFileSync(appPath, content);
157
+ }
158
+
159
+ // Setup package.json name and other details
160
+ const pkgPath = path.join(rootPath, 'package.json');
161
+ if (fs.existsSync(pkgPath)) {
162
+ let pkgContent = fs.readFileSync(pkgPath, 'utf8');
163
+ pkgContent = pkgContent.replace(/{{PROJECT_NAME}}/g, path.basename(rootPath));
164
+ fs.writeFileSync(pkgPath, pkgContent);
165
+ }
166
+
167
+ // Setup index.html title and details
168
+ const indexPath = path.join(rootPath, 'index.html');
169
+ if (fs.existsSync(indexPath)) {
170
+ let indexContent = fs.readFileSync(indexPath, 'utf8');
171
+ indexContent = indexContent.replace(/{{PROJECT_NAME}}/g, path.basename(rootPath));
172
+ fs.writeFileSync(indexPath, indexContent);
173
+ }
174
+
175
+ // Rename gitignore to .gitignore
176
+ const gitignorePath = path.join(rootPath, 'gitignore');
177
+ if (fs.existsSync(gitignorePath)) {
178
+ fs.renameSync(gitignorePath, path.join(rootPath, '.gitignore'));
179
+ }
180
+
181
+
182
+ spinner.succeed(green('Project scaffolded successfully!'));
183
+
184
+ let autoInstall = options.install;
185
+ if (!autoInstall) {
186
+ const installResponse = await prompts({
187
+ type: 'confirm',
188
+ name: 'install',
189
+ message: 'Do you want to run "npm install" now?',
190
+ initial: true
191
+ });
192
+ autoInstall = installResponse.install;
193
+ }
194
+
195
+ if (autoInstall) {
196
+ const installSpinner = ora('Installing dependencies (this might take a minute)...').start();
197
+ try {
198
+ execSync('npm install', { cwd: rootPath, stdio: 'ignore' });
199
+ installSpinner.succeed(green('Dependencies installed successfully!'));
200
+ } catch (err) {
201
+ installSpinner.fail(red('Failed to install dependencies. You can run "npm install" manually.'));
202
+ }
203
+ }
204
+
205
+ console.log(cyan('\nDone! To get started:'));
206
+ console.log(cyan(` cd ${targetDir}`));
207
+ if (!autoInstall) {
208
+ console.log(cyan(' npm install'));
209
+ }
210
+ console.log(cyan(' npm run dev\n'));
211
+
212
+ } catch (error) {
213
+ spinner.fail(red('Error scaffolding project.'));
214
+ console.error(error);
215
+ }
216
+ }
217
+
218
+ function copyDir(src, dest) {
219
+ if (!fs.existsSync(src)) return;
220
+ fs.mkdirSync(dest, { recursive: true });
221
+ const entries = fs.readdirSync(src, { withFileTypes: true });
222
+
223
+ for (const entry of entries) {
224
+ const srcPath = path.join(src, entry.name);
225
+ const destPath = path.join(dest, entry.name);
226
+
227
+ if (entry.isDirectory()) {
228
+ copyDir(srcPath, destPath);
229
+ } else {
230
+ fs.copyFileSync(srcPath, destPath);
231
+ }
232
+ }
233
+ }
234
+
235
+ function setupNavigation(rootPath, layout) {
236
+ const navComponentPath = path.join(rootPath, 'src/components/Navigation.jsx');
237
+
238
+ // Custom navigation content based on selection
239
+ let navContent = '';
240
+
241
+ if (layout === 'top') {
242
+ navContent = `import React from 'react';
243
+ import { AppBar, Toolbar, Typography, Button, Box } from '@mui/material';
244
+ import { useNavigate } from 'react-router-dom';
245
+
246
+ export default function Navigation({ onLogout, isAuthenticated }) {
247
+ const navigate = useNavigate();
248
+
249
+ return (
250
+ <AppBar position="static">
251
+ <Toolbar>
252
+ <Typography
253
+ variant="h6"
254
+ component="div"
255
+ sx={{ flexGrow: 1, cursor: 'pointer' }}
256
+ onClick={() => navigate('/')}
257
+ >
258
+ Firem App
259
+ </Typography>
260
+ {isAuthenticated && (
261
+ <Box sx={{ display: 'flex', gap: 1 }}>
262
+ <Button color="inherit" onClick={() => navigate('/')}>Home</Button>
263
+ <Button color="inherit" onClick={() => navigate('/profile')}>Profile</Button>
264
+ <Button color="inherit" onClick={onLogout}>Logout</Button>
265
+ </Box>
266
+ )}
267
+ </Toolbar>
268
+ </AppBar>
269
+ );
270
+ }`;
271
+ } else if (layout === 'bottom') {
272
+ navContent = `import React from 'react';
273
+ import { Paper, BottomNavigation, BottomNavigationAction } from '@mui/material';
274
+ import HomeIcon from '@mui/icons-material/Home';
275
+ import PersonIcon from '@mui/icons-material/Person';
276
+ import ExitToAppIcon from '@mui/icons-material/ExitToApp';
277
+ import { useNavigate, useLocation } from 'react-router-dom';
278
+
279
+ export default function Navigation({ onLogout, isAuthenticated }) {
280
+ const navigate = useNavigate();
281
+ const location = useLocation();
282
+
283
+ const getIndexFromPath = (path) => {
284
+ if (path === '/profile') return 1;
285
+ return 0;
286
+ };
287
+
288
+ const value = getIndexFromPath(location.pathname);
289
+
290
+ return (
291
+ <Paper sx={{ position: 'fixed', bottom: 0, left: 0, right: 0 }} elevation={3}>
292
+ <BottomNavigation
293
+ showLabels
294
+ value={value}
295
+ onChange={(event, newValue) => {
296
+ if (newValue === 0) {
297
+ navigate('/');
298
+ } else if (newValue === 1) {
299
+ navigate('/profile');
300
+ } else if (newValue === 2 && isAuthenticated) {
301
+ onLogout();
302
+ }
303
+ }}
304
+ >
305
+ <BottomNavigationAction label="Home" icon={<HomeIcon />} />
306
+ <BottomNavigationAction label="Profile" icon={<PersonIcon />} />
307
+ {isAuthenticated && <BottomNavigationAction label="Logout" icon={<ExitToAppIcon />} />}
308
+ </BottomNavigation>
309
+ </Paper>
310
+ );
311
+ }`;
312
+ } else if (layout === 'sidebar') {
313
+ navContent = `import React from 'react';
314
+ import { Drawer, List, ListItem, ListItemIcon, ListItemText, Toolbar, Divider, Box } from '@mui/material';
315
+ import HomeIcon from '@mui/icons-material/Home';
316
+ import PersonIcon from '@mui/icons-material/Person';
317
+ import ExitToAppIcon from '@mui/icons-material/ExitToApp';
318
+ import { useNavigate } from 'react-router-dom';
319
+
320
+ const drawerWidth = 240;
321
+
322
+ export default function Navigation({ onLogout, isAuthenticated }) {
323
+ const navigate = useNavigate();
324
+
325
+ return (
326
+ <Drawer
327
+ variant="permanent"
328
+ sx={{
329
+ width: drawerWidth,
330
+ flexShrink: 0,
331
+ \`& .MuiDrawer-paper\`: { width: drawerWidth, boxSizing: 'border-box' },
332
+ }}
333
+ >
334
+ <Toolbar>
335
+ <Box fontWeight="bold" sx={{ cursor: 'pointer' }} onClick={() => navigate('/')}>
336
+ Firem App
337
+ </Box>
338
+ </Toolbar>
339
+ <Divider />
340
+ <List>
341
+ <ListItem button onClick={() => navigate('/')}>
342
+ <ListItemIcon><HomeIcon /></ListItemIcon>
343
+ <ListItemText primary="Home" />
344
+ </ListItem>
345
+ <ListItem button onClick={() => navigate('/profile')}>
346
+ <ListItemIcon><PersonIcon /></ListItemIcon>
347
+ <ListItemText primary="Profile" />
348
+ </ListItem>
349
+ {isAuthenticated && (
350
+ <ListItem button onClick={onLogout}>
351
+ <ListItemIcon><ExitToAppIcon /></ListItemIcon>
352
+ <ListItemText primary="Logout" />
353
+ </ListItem>
354
+ )}
355
+ </List>
356
+ </Drawer>
357
+ );
358
+ }`;
359
+ } else if (layout === 'none') {
360
+ navContent = `import React from 'react';
361
+
362
+ export default function Navigation() {
363
+ return null;
364
+ }`;
365
+ }
366
+
367
+ // Create src/components if not exists
368
+ fs.mkdirSync(path.dirname(navComponentPath), { recursive: true });
369
+ fs.writeFileSync(navComponentPath, navContent);
370
+ }
371
+
372
+ function setupAuth(rootPath, includeAuth, template) {
373
+ if (template === 'landing') {
374
+ const appPath = path.join(rootPath, 'src/App.jsx');
375
+ if (fs.existsSync(appPath)) {
376
+ let content = fs.readFileSync(appPath, 'utf8');
377
+ if (!includeAuth) {
378
+ // Remove auth features from app
379
+ content = content.replace(/\/\* AUTH_START \*\/([\s\S]*?)\/\* AUTH_END \*\//g, '');
380
+ // Enable AUTH_NO block
381
+ content = content.replace(/\/\* AUTH_NO_START \*\/([\s\S]*?)\/\* AUTH_NO_END \*\//g, (match, p1) => {
382
+ return p1.replace(/\/\/\s?/g, '');
383
+ });
384
+
385
+ // Remove context directory since auth isn't needed
386
+ const authCtxPath = path.join(rootPath, 'src/context');
387
+ if (fs.existsSync(authCtxPath)) {
388
+ fs.rmSync(authCtxPath, { recursive: true, force: true });
389
+ }
390
+ } else {
391
+ // Keep them, clean up tags
392
+ content = content.replace(/\/\* AUTH_START \*\//g, '');
393
+ content = content.replace(/\/\* AUTH_END \*\//g, '');
394
+ // Remove AUTH_NO block
395
+ content = content.replace(/\/\* AUTH_NO_START \*\/([\s\S]*?)\/\* AUTH_NO_END \*\//g, '');
396
+ }
397
+ fs.writeFileSync(appPath, content);
398
+ }
399
+ }
400
+ }
401
+
402
+ main().catch((err) => {
403
+ console.error(err);
404
+ process.exit(1);
405
+ });
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "create-firem",
3
+ "version": "1.0.1",
4
+ "description": "Scaffold a new React + Firebase + Material-UI project",
5
+ "main": "bin/index.js",
6
+ "bin": {
7
+ "create-firem": "./bin/index.js"
8
+ },
9
+ "type": "module",
10
+ "scripts": {
11
+ "start": "node bin/index.js"
12
+ },
13
+ "keywords": [
14
+ "cli",
15
+ "generator",
16
+ "scaffold",
17
+ "react",
18
+ "firebase",
19
+ "mui"
20
+ ],
21
+ "author": "",
22
+ "license": "MIT",
23
+ "dependencies": {
24
+ "commander": "^12.0.0",
25
+ "kolorist": "^1.8.0",
26
+ "ora": "^8.0.1",
27
+ "prompts": "^2.4.2"
28
+ },
29
+ "devDependencies": {
30
+ "firebase": "^12.13.0"
31
+ }
32
+ }
package/readme.md ADDED
@@ -0,0 +1,90 @@
1
+ # create-firem
2
+
3
+ A modern CLI tool for quickly scaffolding new Firem project templates.
4
+
5
+ ## Usage
6
+
7
+ You can generate a new project interactively by running one of the following commands in your terminal:
8
+
9
+ ```bash
10
+ npx create-firem
11
+ ```
12
+
13
+ Or, using npm init:
14
+
15
+ ```bash
16
+ npm init firem
17
+ ```
18
+
19
+ *(The CLI will prompt you for project details, features to include, and automatically set up the boilerplate code.)*
20
+
21
+ ## Development & Testing
22
+
23
+ If you want to contribute to this generator or test it locally, follow these steps:
24
+
25
+ 1. Clone this repository.
26
+ 2. Install the dependencies of the CLI itself:
27
+ ```bash
28
+ npm install
29
+ ```
30
+
31
+ ### Testing the Scaffolding & Templates Locally
32
+
33
+ You can test the scaffolding process using two methods:
34
+
35
+ #### Method A: Direct Execution (Recommended for quick iteration)
36
+ Run the generator script directly from the root of the repository:
37
+ ```bash
38
+ node bin/index.js <test-project-name> [options]
39
+ ```
40
+
41
+ Example commands to test different configurations:
42
+ - **Dashboard with Sidebar Layout**:
43
+ ```bash
44
+ node bin/index.js test-dashboard --template dashboard --layout sidebar
45
+ ```
46
+ - **Landing Page with Top Nav & Auth**:
47
+ ```bash
48
+ node bin/index.js test-landing --template landing --layout top --auth true
49
+ ```
50
+
51
+ #### Method B: Global Symlink
52
+ 1. Link the package globally:
53
+ ```bash
54
+ npm link
55
+ ```
56
+ 2. Run `create-firem` anywhere on your machine to test the scaffolding:
57
+ ```bash
58
+ create-firem my-scaffolded-app
59
+ ```
60
+ 3. To clean up and unlink when done:
61
+ ```bash
62
+ npm unlink -g create-firem
63
+ ```
64
+
65
+ ### Verifying the Generated Template
66
+
67
+ Once a template is generated, enter its directory and run:
68
+ ```bash
69
+ cd <generated-project-name>
70
+ npm install
71
+ npm run dev
72
+ ```
73
+
74
+ Key aspects to verify:
75
+ 1. **Routing Modes**: Open `src/config/routerConfig.js` and test toggling `ROUTER_MODE` between `'browser'` (BrowserRouter) and `'hash'` (HashRouter).
76
+ 2. **Vite Chunk Splitting**: Run `npm run build` to verify that assets are split modularly into `vendor-mui`, `vendor-firebase`, `vendor-core`, etc., keeping the initial bundle sizes lightweight.
77
+ 3. **Navigation Layouts**: Change navigation layout options (`top`, `bottom`, `sidebar`) during scaffolding to ensure they render and respond properly.
78
+
79
+ ## Architecture
80
+
81
+ - **`bin/index.js`**: Contains the executable CLI entry point and scaffolding code (copying files, prompting users, rewriting package placeholders, and adjusting layouts).
82
+ - **`templates/`**: Contains the boilerplate files for the generated projects:
83
+ - `common/`: Core configuration files (such as `firebase.ts`, MUI theme, and `AppRouter.jsx`) shared by all templates.
84
+ - `basic/`: Minimal skeleton template.
85
+ - `landing/`: Public-facing landing page layout.
86
+ - `dashboard/`: Full persistent login administration panel.
87
+
88
+ ## License
89
+
90
+ MIT