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.
- package/.github/workflows/auto-update.yml +42 -0
- package/.github/workflows/npm-publish.yml +42 -0
- package/LICENSE +21 -0
- package/agents.md +32 -0
- package/bin/index.js +405 -0
- package/package.json +32 -0
- package/readme.md +90 -0
- package/scripts/auto-update-packages.js +110 -0
- package/specs/cli.md +83 -0
- package/specs/fireabse.md +66 -0
- package/specs/render.md +140 -0
- package/specs/template.md +170 -0
- package/templates/basic/package.json +32 -0
- package/templates/basic/src/App.jsx +28 -0
- package/templates/basic/src/main.jsx +15 -0
- package/templates/common/gitignore +27 -0
- package/templates/common/index.html +15 -0
- package/templates/common/src/components/AppRouter.jsx +23 -0
- package/templates/common/src/config/firebase.ts +30 -0
- package/templates/common/src/config/routerConfig.js +6 -0
- package/templates/common/src/theme/theme.js +14 -0
- package/templates/common/vite.config.js +28 -0
- package/templates/dashboard/package.json +32 -0
- package/templates/dashboard/src/App.jsx +50 -0
- package/templates/dashboard/src/components/Login.jsx +67 -0
- package/templates/dashboard/src/context/AuthContext.jsx +43 -0
- package/templates/dashboard/src/main.jsx +15 -0
- package/templates/dashboard/src/pages/DashboardOverview.jsx +21 -0
- package/templates/dashboard/src/pages/Profile.jsx +23 -0
- package/templates/landing/package.json +32 -0
- package/templates/landing/src/App.jsx +131 -0
- package/templates/landing/src/context/AuthContext.jsx +43 -0
- package/templates/landing/src/main.jsx +15 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
|
|
5
|
+
const packagesToUpdate = [
|
|
6
|
+
'firebase',
|
|
7
|
+
'react',
|
|
8
|
+
'react-dom',
|
|
9
|
+
'react-router-dom',
|
|
10
|
+
'@mui/material',
|
|
11
|
+
'@mui/icons-material',
|
|
12
|
+
'@emotion/react',
|
|
13
|
+
'@emotion/styled',
|
|
14
|
+
'vite',
|
|
15
|
+
'@vitejs/plugin-react',
|
|
16
|
+
'@types/react',
|
|
17
|
+
'@types/react-dom'
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
function getLatestVersion(pkg) {
|
|
21
|
+
try {
|
|
22
|
+
return execSync(`npm view ${pkg} version`).toString().trim();
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error(`Failed to get version for ${pkg}:`, error.message);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const rootDir = process.cwd();
|
|
30
|
+
const templatePaths = [
|
|
31
|
+
path.join(rootDir, 'templates/basic/package.json'),
|
|
32
|
+
path.join(rootDir, 'templates/landing/package.json'),
|
|
33
|
+
path.join(rootDir, 'templates/dashboard/package.json')
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const latestVersions = {};
|
|
37
|
+
for (const pkg of packagesToUpdate) {
|
|
38
|
+
const version = getLatestVersion(pkg);
|
|
39
|
+
if (version) {
|
|
40
|
+
latestVersions[pkg] = `^${version}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log('Latest resolved versions:', latestVersions);
|
|
45
|
+
|
|
46
|
+
let hasChanges = false;
|
|
47
|
+
|
|
48
|
+
// Update templates
|
|
49
|
+
for (const pjsonPath of templatePaths) {
|
|
50
|
+
if (fs.existsSync(pjsonPath)) {
|
|
51
|
+
const data = JSON.parse(fs.readFileSync(pjsonPath, 'utf8'));
|
|
52
|
+
let fileChanged = false;
|
|
53
|
+
|
|
54
|
+
if (data.dependencies) {
|
|
55
|
+
for (const [dep, ver] of Object.entries(data.dependencies)) {
|
|
56
|
+
if (latestVersions[dep] && latestVersions[dep] !== ver) {
|
|
57
|
+
data.dependencies[dep] = latestVersions[dep];
|
|
58
|
+
fileChanged = true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (data.devDependencies) {
|
|
64
|
+
for (const [dep, ver] of Object.entries(data.devDependencies)) {
|
|
65
|
+
if (latestVersions[dep] && latestVersions[dep] !== ver) {
|
|
66
|
+
data.devDependencies[dep] = latestVersions[dep];
|
|
67
|
+
fileChanged = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (fileChanged) {
|
|
73
|
+
fs.writeFileSync(pjsonPath, JSON.stringify(data, null, 2) + '\n');
|
|
74
|
+
console.log(`Updated ${path.relative(rootDir, pjsonPath)}`);
|
|
75
|
+
hasChanges = true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Update root package.json devDependencies (firebase)
|
|
81
|
+
const rootPjsonPath = path.join(rootDir, 'package.json');
|
|
82
|
+
if (fs.existsSync(rootPjsonPath)) {
|
|
83
|
+
const rootData = JSON.parse(fs.readFileSync(rootPjsonPath, 'utf8'));
|
|
84
|
+
let rootChanged = false;
|
|
85
|
+
|
|
86
|
+
if (rootData.devDependencies && rootData.devDependencies.firebase && latestVersions['firebase']) {
|
|
87
|
+
if (rootData.devDependencies.firebase !== latestVersions['firebase']) {
|
|
88
|
+
rootData.devDependencies.firebase = latestVersions['firebase'];
|
|
89
|
+
rootChanged = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (rootChanged) {
|
|
94
|
+
fs.writeFileSync(rootPjsonPath, JSON.stringify(rootData, null, 2) + '\n');
|
|
95
|
+
console.log('Updated root package.json devDependencies');
|
|
96
|
+
hasChanges = true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (hasChanges) {
|
|
101
|
+
// Bump create-firem patch version to ensure npm publish works
|
|
102
|
+
try {
|
|
103
|
+
execSync('npm version patch --no-git-tag-version');
|
|
104
|
+
console.log('Successfully bumped create-firem version');
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error('Failed to bump version:', error.message);
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
console.log('All packages are already up-to-date!');
|
|
110
|
+
}
|
package/specs/cli.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Specification: Command-Line Interface (`cli.md`)
|
|
2
|
+
|
|
3
|
+
This document specifies the behavior, interactive prompt flow, and command-line options for the `create-firem` CLI generator.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. CLI Commands & Arguments
|
|
8
|
+
Users can run the generator with options directly from the CLI or follow the interactive prompts.
|
|
9
|
+
|
|
10
|
+
### Command Syntax
|
|
11
|
+
```bash
|
|
12
|
+
npx create-firem [project-name] [options]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Options (CLI Flags)
|
|
16
|
+
- `--template <type>`: Skip prompt and select template: `dashboard`, `landing`, or `basic`.
|
|
17
|
+
- `--layout <type>`: Skip prompt and select navigation layout: `top`, `bottom`, `sidebar`, or `none`.
|
|
18
|
+
- `--auth <boolean>`: Skip prompt and enable/disable Firebase authentication (relevant for `landing`).
|
|
19
|
+
- `--install`: Automatically install dependencies after scaffolding.
|
|
20
|
+
- `--help` / `-h`: Display help documentation.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 2. Interactive Prompt Flow
|
|
25
|
+
|
|
26
|
+
If the CLI is run without arguments, it will guide the user through the following questions using `prompts` or `inquirer`.
|
|
27
|
+
|
|
28
|
+
### Step 1: Project Name
|
|
29
|
+
- **Prompt**: `What is the name of your project?`
|
|
30
|
+
- **Type**: Input text
|
|
31
|
+
- **Default**: `my-firem-app`
|
|
32
|
+
- **Validation**: Must be a valid npm package name (lowercase, no spaces, URL-safe characters).
|
|
33
|
+
|
|
34
|
+
### Step 2: Template Type
|
|
35
|
+
- **Prompt**: `Select a template type:`
|
|
36
|
+
- **Type**: Select list
|
|
37
|
+
- **Options**:
|
|
38
|
+
- 📊 **Dashboard**: Includes Firebase Auth with persistent session login, dashboard panels, and protected routes.
|
|
39
|
+
- 🚀 **Landing Page**: Public frontend landing page, loads directly, with an optional configuration to add authentication.
|
|
40
|
+
- 📦 **Basic**: A minimal skeleton project with React, Firebase SDK configured, and MUI installed.
|
|
41
|
+
|
|
42
|
+
### Step 3: Firebase Authentication (Conditional)
|
|
43
|
+
- **Condition**: Only prompt if **Landing Page** was selected in Step 2.
|
|
44
|
+
- **Prompt**: `Do you want to include Firebase Authentication in your Landing Page?`
|
|
45
|
+
- **Type**: Confirm (Yes/No)
|
|
46
|
+
- **Default**: `No`
|
|
47
|
+
|
|
48
|
+
### Step 4: Layout Navigation Menu Position
|
|
49
|
+
- **Prompt**: `Where should the main navigation menu be positioned?`
|
|
50
|
+
- **Type**: Select list
|
|
51
|
+
- **Options**:
|
|
52
|
+
- ⬆️ **Top (Superior)**: A classic horizontal navbar at the header.
|
|
53
|
+
- ⬇️ **Bottom (Inferior)**: Ideal for mobile-first layouts, showing a bottom navigation bar.
|
|
54
|
+
- ⬅️ **Sidebar (Lateral)**: A modern collapsible sidebar navigation drawer on the left side.
|
|
55
|
+
- ❌ **None (Sin menú)**: No navigation menu will be rendered (useful for clean/minimal setups).
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 3. Scaffolding & Customization Logic
|
|
60
|
+
|
|
61
|
+
Based on the selections made during the prompts, the CLI performs the following changes:
|
|
62
|
+
|
|
63
|
+
### A. Template Choice Implementation
|
|
64
|
+
- **Dashboard**: Copies dashboard-specific components (e.g. `Login.jsx`, `DashboardLayout.jsx`, persistent auth context in `AuthContext.jsx`).
|
|
65
|
+
- **Landing**: Copies public-facing homepage component (`LandingPage.jsx`). If auth was selected, includes a modal/page for login and hooks to Firebase Auth.
|
|
66
|
+
- **Basic**: Copies a minimal main page and sets up Vite + MUI + Firebase config without views.
|
|
67
|
+
|
|
68
|
+
### B. Layout Menu Customization
|
|
69
|
+
The CLI will copy or modify the navigation layout component (`src/components/Navigation.jsx`) according to the user's choice:
|
|
70
|
+
- **`top`**: Generates a component using MUI's `AppBar` and `Toolbar`.
|
|
71
|
+
- **`bottom`**: Generates a component using MUI's `BottomNavigation` and `BottomNavigationAction`.
|
|
72
|
+
- **`sidebar`**: Generates a component using MUI's `Drawer` and `List` with `ListItem`.
|
|
73
|
+
- **`none`**: Generates a stub component that returns `null` (rendering no navigation menu).
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 4. Dependencies of the CLI Tool
|
|
78
|
+
|
|
79
|
+
To ensure a lightweight and beautiful CLI execution, the CLI itself will use:
|
|
80
|
+
- **`prompts`**: For fast, user-friendly interactive prompts.
|
|
81
|
+
- **`kolorist`**: For terminal text coloring.
|
|
82
|
+
- **`ora`**: For loading spinners during dependency installation.
|
|
83
|
+
- **`commander`**: For option/flag parsing.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Specification: Firebase Configuration (`firebase.ts`)
|
|
2
|
+
|
|
3
|
+
This specification defines how Firebase must be integrated and initialized within the generated templates. To ensure simplicity and eliminate configuration layers, all templates must utilize a unified `firebase.ts` file with direct (hardcoded) configurations.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. File Location
|
|
8
|
+
The Firebase configuration file must always be placed at:
|
|
9
|
+
```text
|
|
10
|
+
src/config/firebase.ts
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Dependencies
|
|
16
|
+
The generated projects must have the `firebase` package installed:
|
|
17
|
+
- `firebase`: `^10.0.0` or higher.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 3. Configuration & Exports
|
|
22
|
+
The `firebase.ts` file initializes the Firebase application using a direct configuration constant and exports the required services (Authentication and Firestore database). No environment variables (`.env`) are used.
|
|
23
|
+
|
|
24
|
+
### File Template (`src/config/firebase.ts`)
|
|
25
|
+
```typescript
|
|
26
|
+
import { initializeApp, FirebaseApp } from 'firebase/app';
|
|
27
|
+
import { getAuth, Auth } from 'firebase/auth';
|
|
28
|
+
import { getFirestore, Firestore } from 'firebase/firestore';
|
|
29
|
+
|
|
30
|
+
// Define the structure of the configuration object
|
|
31
|
+
interface FirebaseConfig {
|
|
32
|
+
apiKey: string;
|
|
33
|
+
authDomain: string;
|
|
34
|
+
projectId: string;
|
|
35
|
+
storageBucket: string;
|
|
36
|
+
messagingSenderId: string;
|
|
37
|
+
appId: string;
|
|
38
|
+
measurementId?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Hardcoded Firebase configuration object
|
|
42
|
+
const firebaseConfig: FirebaseConfig = {
|
|
43
|
+
apiKey: "YOUR_API_KEY",
|
|
44
|
+
authDomain: "YOUR_AUTH_DOMAIN",
|
|
45
|
+
projectId: "YOUR_PROJECT_ID",
|
|
46
|
+
storageBucket: "YOUR_STORAGE_BUCKET",
|
|
47
|
+
messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
|
|
48
|
+
appId: "YOUR_APP_ID",
|
|
49
|
+
measurementId: "YOUR_MEASUREMENT_ID"
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Initialize Firebase
|
|
53
|
+
const app: FirebaseApp = initializeApp(firebaseConfig);
|
|
54
|
+
|
|
55
|
+
// Initialize and export services
|
|
56
|
+
export const auth: Auth = getAuth(app);
|
|
57
|
+
export const db: Firestore = getFirestore(app);
|
|
58
|
+
|
|
59
|
+
export default app;
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 4. Security & Verification
|
|
65
|
+
- **Configuration Values**: The initial scaffold contains placeholder strings (e.g., `"YOUR_API_KEY"`). The developer must replace these placeholders directly in `firebase.ts` with their project's credentials.
|
|
66
|
+
- **Strict Typing**: All services (`auth`, `db`) and configuration values must be explicitly or implicitly typed using Firebase's TypeScript definitions.
|
package/specs/render.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Specification: Rendering & Frontend Routing Architecture (`render.md`)
|
|
2
|
+
|
|
3
|
+
This document defines the rendering model, bundle optimization configurations, and routing patterns for projects generated by `create-firem`. The architecture is designed to remain highly performant, modular, and lightweight as applications (such as administrative dashboards) grow in size and complexity.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Single Page Application (SPA) Model
|
|
8
|
+
|
|
9
|
+
All templates scaffolded by `create-firem` must strictly adhere to a **Single Page Application (SPA)** model based on **Client-Side Rendering (CSR)**.
|
|
10
|
+
|
|
11
|
+
- **No Server-Side Rendering (SSR)**: The build pipeline must not generate server-side hydration scripts (e.g., Next.js, Remix style).
|
|
12
|
+
- **Static Assets Delivery**: The entire application compiles into static HTML, JavaScript, CSS, and asset files.
|
|
13
|
+
- **Zero-Config Hosting Compatibility**: Being a pure SPA ensures the output directory (`dist`) can be hosted directly on any static web server, CDN, or static hosting provider (e.g., Firebase Hosting, GitHub Pages, Netlify, Vercel, AWS S3, or standard Nginx).
|
|
14
|
+
- **Client-Side Routing**: The browser is responsible for managing the routing lifecycle and rendering components dynamically without full-page reloads.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 2. Vite Modular Loading & Optimization
|
|
19
|
+
|
|
20
|
+
To prevent initial load time degradation as dashboards and views scale, Vite must be configured to split the build bundle into optimized modular chunks. This prevents the browser from downloading a single, bloated JavaScript bundle.
|
|
21
|
+
|
|
22
|
+
### A. Dynamic Imports & Code Splitting (React.lazy)
|
|
23
|
+
Instead of importing all pages statically at startup, pages and sub-modules must be loaded dynamically using React’s native `React.lazy` and wrapped in a `<Suspense>` boundary.
|
|
24
|
+
|
|
25
|
+
- **Static Import (Avoid for Pages)**:
|
|
26
|
+
```javascript
|
|
27
|
+
import DashboardOverview from './pages/DashboardOverview'; // Loaded on initial start
|
|
28
|
+
```
|
|
29
|
+
- **Dynamic Import (Mandatory for Routes)**:
|
|
30
|
+
```javascript
|
|
31
|
+
const DashboardOverview = React.lazy(() => import('./pages/DashboardOverview'));
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### B. Vite Manual Chunking Configuration (`vite.config.js`)
|
|
35
|
+
To maintain optimal caching and keep chunks lightweight, Vite's build settings must split heavy dependencies (such as Material UI and Firebase SDKs) into dedicated chunks rather than packing them together.
|
|
36
|
+
|
|
37
|
+
The scaffolded `vite.config.js` must implement the following `rollupOptions` configuration:
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
import { defineConfig } from 'vite';
|
|
41
|
+
import react from '@vitejs/plugin-react';
|
|
42
|
+
|
|
43
|
+
export default defineConfig({
|
|
44
|
+
plugins: [react()],
|
|
45
|
+
build: {
|
|
46
|
+
// Split chunks aggressively for better browser caching and minimal initial payload
|
|
47
|
+
rollupOptions: {
|
|
48
|
+
output: {
|
|
49
|
+
manualChunks(id) {
|
|
50
|
+
// Check if dependency is within node_modules
|
|
51
|
+
if (id.includes('node_modules')) {
|
|
52
|
+
// Firebase SDK - separate chunk
|
|
53
|
+
if (id.includes('firebase')) {
|
|
54
|
+
return 'vendor-firebase';
|
|
55
|
+
}
|
|
56
|
+
// Material UI - separate chunk as it is very heavy
|
|
57
|
+
if (id.includes('@mui') || id.includes('@emotion')) {
|
|
58
|
+
return 'vendor-mui';
|
|
59
|
+
}
|
|
60
|
+
// Core React runtime
|
|
61
|
+
if (id.includes('react') || id.includes('react-dom') || id.includes('react-router')) {
|
|
62
|
+
return 'vendor-core';
|
|
63
|
+
}
|
|
64
|
+
// General fallback chunk for other dependencies
|
|
65
|
+
return 'vendor-helpers';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
// Prevent console spam warnings for larger individual files if they exceed default limit slightly
|
|
71
|
+
chunkSizeWarningLimit: 800,
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 3. Flexible React Router Configuration (Rewrite vs. Hash Route)
|
|
79
|
+
|
|
80
|
+
Standard SPAs require the host server to redirect all traffic back to `/index.html` (Rewrites) for paths to resolve correctly on direct loads or page refreshes. When server rewrites are not possible (e.g., hosting from subdirectory paths without rewrite control, local filesystem viewing, or simple static file hosting), **Hash Routing** is required.
|
|
81
|
+
|
|
82
|
+
To solve this, the template generator must provide a standardized structure that allows developers to toggle between history-based routing (`BrowserRouter`) and hash-based routing (`HashRouter`) by changing a single configuration value.
|
|
83
|
+
|
|
84
|
+
### A. Router Mode Settings File (`src/config/routerConfig.ts`)
|
|
85
|
+
A dedicated router configuration file determines the routing behavior:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// src/config/routerConfig.ts
|
|
89
|
+
|
|
90
|
+
export type RouterMode = 'browser' | 'hash';
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Configure the rendering route structure for the application.
|
|
94
|
+
* - 'browser': Standard HTML5 history routing. Requires hosting server redirects (rewrite to index.html).
|
|
95
|
+
* - 'hash': Hash-based routing (#/path). Works on any hosting setup out-of-the-box (zero-config).
|
|
96
|
+
*/
|
|
97
|
+
export const ROUTER_MODE: RouterMode = 'browser';
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### B. Standardized Router Implementation (`src/components/AppRouter.tsx`)
|
|
101
|
+
A wrapper router component selects the appropriate React Router class depending on the `ROUTER_MODE` setting:
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
// src/components/AppRouter.tsx
|
|
105
|
+
import React, { Suspense } from 'react';
|
|
106
|
+
import { BrowserRouter, HashRouter, Routes, Route, Navigate } from 'react-router-dom';
|
|
107
|
+
import { ROUTER_MODE } from '../config/routerConfig';
|
|
108
|
+
|
|
109
|
+
// Common fallback loader for Suspense during lazy chunk retrieval
|
|
110
|
+
const PageLoader = () => (
|
|
111
|
+
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
|
|
112
|
+
Loading component...
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
interface AppRouterProps {
|
|
117
|
+
children: React.ReactNode;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const AppRouter: React.FC<AppRouterProps> = ({ children }) => {
|
|
121
|
+
// Select router type dynamically
|
|
122
|
+
const Router = ROUTER_MODE === 'hash' ? HashRouter : BrowserRouter;
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<Router>
|
|
126
|
+
<Suspense fallback={<PageLoader />}>
|
|
127
|
+
{children}
|
|
128
|
+
</Suspense>
|
|
129
|
+
</Router>
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 4. Scaffold Templates Implementation Rules
|
|
137
|
+
|
|
138
|
+
1. **Routing Setup**: Generated templates must define routes clearly using path mappings inside `App.jsx` or a dedicated routes definition component, wrapped inside the dynamic `AppRouter` configuration.
|
|
139
|
+
2. **Lazy Loading Practices**: Any page/view template directory must split major logical views (e.g., `Dashboard`, `Profile`, `Settings`, `Login`) into separate files that are imported lazily.
|
|
140
|
+
3. **No Legacy Imports**: Avoid placing massive component sub-sections directly in the main layout file. Standardize on building components as separate modules to let Rollup optimize page chunks individually.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Specification: Template Configuration (`template.md`)
|
|
2
|
+
|
|
3
|
+
This document defines the structure, core libraries, and configuration rules for the base project template distributed by `create-firem`. The generated template is pre-configured to build web applications using React, Firebase, and Material UI (MUI).
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Core Stack
|
|
8
|
+
The template must include and pre-configure the following technologies:
|
|
9
|
+
- **Frontend Framework**: ReactJS (bootstrapped with [Vite](https://vite.dev/) for optimal performance).
|
|
10
|
+
- **Styling & UI Components**: Material UI (MUI) v5+ (`@mui/material`, `@mui/icons-material`, `@emotion/react`, `@emotion/styled`).
|
|
11
|
+
- **Backend & Services**: Firebase SDK v10+ (Authentication, Firestore, Storage, and Analytics).
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Directory Structure of the Template
|
|
16
|
+
The template is housed inside the `template/` directory of the generator. When copied to the target user directory, it should look like this:
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
template/
|
|
20
|
+
├── src/
|
|
21
|
+
│ ├── assets/ # Images, SVG icons, and static assets
|
|
22
|
+
│ ├── components/ # Common reusable MUI components
|
|
23
|
+
│ ├── config/
|
|
24
|
+
│ │ └── firebase.ts # Firebase initialization & exports (auth, db, etc.)
|
|
25
|
+
│ ├── theme/
|
|
26
|
+
│ │ └── theme.js # Custom MUI theme (colors, typography)
|
|
27
|
+
│ ├── App.jsx # Main layout containing theme provider and routing
|
|
28
|
+
│ └── main.jsx # App entry point (mounting React to the DOM)
|
|
29
|
+
├── gitignore # Placeholder (renamed to .gitignore by CLI)
|
|
30
|
+
├── index.html # Main HTML document template
|
|
31
|
+
├── package.json # Package definition with placeholders
|
|
32
|
+
└── vite.config.js # Vite configuration with React support
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 3. Libraries and Dependencies (`package.json`)
|
|
38
|
+
|
|
39
|
+
The template's `package.json` should contain at least the following dependencies:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"name": "{{PROJECT_NAME}}",
|
|
44
|
+
"private": true,
|
|
45
|
+
"version": "0.1.0",
|
|
46
|
+
"type": "module",
|
|
47
|
+
"scripts": {
|
|
48
|
+
"dev": "vite",
|
|
49
|
+
"build": "vite build",
|
|
50
|
+
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
|
51
|
+
"preview": "vite preview"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@emotion/react": "^11.11.0",
|
|
55
|
+
"@emotion/styled": "^11.11.0",
|
|
56
|
+
"@mui/icons-material": "^5.11.0",
|
|
57
|
+
"@mui/material": "^5.11.0",
|
|
58
|
+
"firebase": "^10.0.0",
|
|
59
|
+
"react": "^18.2.0",
|
|
60
|
+
"react-dom": "^18.2.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/react": "^18.2.0",
|
|
64
|
+
"@types/react-dom": "^18.2.0",
|
|
65
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
66
|
+
"eslint": "^8.45.0",
|
|
67
|
+
"eslint-plugin-react": "^7.32.0",
|
|
68
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
69
|
+
"eslint-plugin-react-refresh": "^0.4.0",
|
|
70
|
+
"vite": "^5.0.0"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 4. Integration Details
|
|
78
|
+
|
|
79
|
+
### A. Firebase Setup (`src/config/firebase.ts`)
|
|
80
|
+
Initialize Firebase using a direct configuration constant, with TypeScript typings:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { initializeApp, FirebaseApp } from 'firebase/app';
|
|
84
|
+
import { getAuth, Auth } from 'firebase/auth';
|
|
85
|
+
import { getFirestore, Firestore } from 'firebase/firestore';
|
|
86
|
+
|
|
87
|
+
interface FirebaseConfig {
|
|
88
|
+
apiKey: string;
|
|
89
|
+
authDomain: string;
|
|
90
|
+
projectId: string;
|
|
91
|
+
storageBucket: string;
|
|
92
|
+
messagingSenderId: string;
|
|
93
|
+
appId: string;
|
|
94
|
+
measurementId?: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const firebaseConfig: FirebaseConfig = {
|
|
98
|
+
apiKey: "YOUR_API_KEY",
|
|
99
|
+
authDomain: "YOUR_AUTH_DOMAIN",
|
|
100
|
+
projectId: "YOUR_PROJECT_ID",
|
|
101
|
+
storageBucket: "YOUR_STORAGE_BUCKET",
|
|
102
|
+
messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
|
|
103
|
+
appId: "YOUR_APP_ID",
|
|
104
|
+
measurementId: "YOUR_MEASUREMENT_ID"
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const app: FirebaseApp = initializeApp(firebaseConfig);
|
|
108
|
+
|
|
109
|
+
export const auth: Auth = getAuth(app);
|
|
110
|
+
export const db: Firestore = getFirestore(app);
|
|
111
|
+
export default app;
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### B. Material UI Theme & Provider (`src/theme/theme.js` & `src/App.jsx`)
|
|
115
|
+
Configure a basic customizable MUI theme:
|
|
116
|
+
|
|
117
|
+
**`src/theme/theme.js`**:
|
|
118
|
+
```javascript
|
|
119
|
+
import { createTheme } from '@mui/material/styles';
|
|
120
|
+
|
|
121
|
+
const theme = createTheme({
|
|
122
|
+
palette: {
|
|
123
|
+
primary: {
|
|
124
|
+
main: '#1976d2', // Customize primary color
|
|
125
|
+
},
|
|
126
|
+
secondary: {
|
|
127
|
+
main: '#9c27b0', // Customize secondary color
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
typography: {
|
|
131
|
+
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
export default theme;
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**`src/App.jsx`**:
|
|
139
|
+
```jsx
|
|
140
|
+
import React from 'react';
|
|
141
|
+
import { ThemeProvider, CssBaseline, Container, Typography, Box } from '@mui/material';
|
|
142
|
+
import theme from './theme/theme';
|
|
143
|
+
|
|
144
|
+
function App() {
|
|
145
|
+
return (
|
|
146
|
+
<ThemeProvider theme={theme}>
|
|
147
|
+
<CssBaseline />
|
|
148
|
+
<Container maxWidth="sm">
|
|
149
|
+
<Box sx={{ my: 4, textAlign: 'center' }}>
|
|
150
|
+
<Typography variant="h4" component="h1" gutterBottom>
|
|
151
|
+
Welcome to Firem Template!
|
|
152
|
+
</Typography>
|
|
153
|
+
<Typography variant="body1">
|
|
154
|
+
ReactJS + Firebase + Material UI are ready to go.
|
|
155
|
+
</Typography>
|
|
156
|
+
</Box>
|
|
157
|
+
</Container>
|
|
158
|
+
</ThemeProvider>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export default App;
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 5. Scaffold Replacement Rules
|
|
168
|
+
During the creation process, the CLI generator must apply these transformations:
|
|
169
|
+
1. **Placeholder Replacement**: Replace all occurrences of `{{PROJECT_NAME}}` in `package.json` and `index.html` with the name chosen by the user.
|
|
170
|
+
2. **Rename Dotfiles**: Rename `gitignore` in the template folder to `.gitignore` when writing to the target project directory.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PROJECT_NAME}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
|
10
|
+
"preview": "vite preview"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@emotion/react": "^11.14.0",
|
|
14
|
+
"@emotion/styled": "^11.14.1",
|
|
15
|
+
"@mui/icons-material": "^9.0.1",
|
|
16
|
+
"@mui/material": "^9.0.1",
|
|
17
|
+
"firebase": "^12.13.0",
|
|
18
|
+
"react": "^19.2.6",
|
|
19
|
+
"react-dom": "^19.2.6",
|
|
20
|
+
"react-router-dom": "^7.15.1"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/react": "^19.2.15",
|
|
24
|
+
"@types/react-dom": "^19.2.3",
|
|
25
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
26
|
+
"eslint": "^8.57.0",
|
|
27
|
+
"eslint-plugin-react": "^7.34.2",
|
|
28
|
+
"eslint-plugin-react-hooks": "^4.6.2",
|
|
29
|
+
"eslint-plugin-react-refresh": "^0.4.7",
|
|
30
|
+
"vite": "^8.0.13"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Container, Typography, Box } from '@mui/material';
|
|
3
|
+
import Navigation from './components/Navigation';
|
|
4
|
+
import { AppRouter } from './components/AppRouter';
|
|
5
|
+
|
|
6
|
+
function App() {
|
|
7
|
+
return (
|
|
8
|
+
<AppRouter>
|
|
9
|
+
<Box sx={{ display: 'flex', minHeight: '100vh', flexDirection: 'column' }}>
|
|
10
|
+
<Navigation isAuthenticated={false} />
|
|
11
|
+
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
|
|
12
|
+
<Container maxWidth="sm">
|
|
13
|
+
<Box sx={{ my: 4, textAlign: 'center' }}>
|
|
14
|
+
<Typography variant="h4" component="h1" gutterBottom>
|
|
15
|
+
Welcome to Firem (Basic Template)
|
|
16
|
+
</Typography>
|
|
17
|
+
<Typography variant="body1">
|
|
18
|
+
ReactJS + Firebase + Material UI are configured and ready.
|
|
19
|
+
</Typography>
|
|
20
|
+
</Box>
|
|
21
|
+
</Container>
|
|
22
|
+
</Box>
|
|
23
|
+
</Box>
|
|
24
|
+
</AppRouter>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default App;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import ReactDOM from 'react-dom/client'
|
|
3
|
+
import App from './App.jsx'
|
|
4
|
+
import { ThemeProvider } from '@mui/material/styles'
|
|
5
|
+
import CssBaseline from '@mui/material/CssBaseline'
|
|
6
|
+
import theme from './theme/theme'
|
|
7
|
+
|
|
8
|
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
9
|
+
<React.StrictMode>
|
|
10
|
+
<ThemeProvider theme={theme}>
|
|
11
|
+
<CssBaseline />
|
|
12
|
+
<App />
|
|
13
|
+
</ThemeProvider>
|
|
14
|
+
</React.StrictMode>,
|
|
15
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Logs
|
|
2
|
+
logs
|
|
3
|
+
*.log
|
|
4
|
+
npm-debug.log*
|
|
5
|
+
yarn-debug.log*
|
|
6
|
+
yarn-error.log*
|
|
7
|
+
pnpm-debug.log*
|
|
8
|
+
lerna-debug.log*
|
|
9
|
+
|
|
10
|
+
node_modules
|
|
11
|
+
dist
|
|
12
|
+
dist-ssr
|
|
13
|
+
*.local
|
|
14
|
+
|
|
15
|
+
# Editor directories and files
|
|
16
|
+
.vscode/*
|
|
17
|
+
!.vscode/extensions.json
|
|
18
|
+
.idea
|
|
19
|
+
.DS_Store
|
|
20
|
+
*.suo
|
|
21
|
+
*.ntvs*
|
|
22
|
+
*.njsproj
|
|
23
|
+
*.sln
|
|
24
|
+
*.sw?
|
|
25
|
+
|
|
26
|
+
# Firebase
|
|
27
|
+
.firebase
|