From 965ec77416769ae0a7d6ea617cfd84551d39d976 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 16 Sep 2025 16:28:29 +0530 Subject: [PATCH 1/2] =?UTF-8?q?Port:=20Firebase=20update=20=E2=80=94=20app?= =?UTF-8?q?lied=20on=20top=20of=20upstream=20master?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.js | 27 ++++++++-- lib/firebase.js | 133 +++++++++++++++++++++++++++++++++++++++++++++++ lib/templates.js | 40 +++++++++++++- 3 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 lib/firebase.js diff --git a/index.js b/index.js index 55be215..146dc53 100644 --- a/index.js +++ b/index.js @@ -8,6 +8,7 @@ import { setupCSSFramework } from './lib/css-frameworks.js'; import { createAxiosSetup, createAppComponent, createPWAReadme } from './lib/templates.js'; import { setupRoutingFramework } from "./lib/router-setup.js"; import { initializeGit } from "./lib/setup-git.js"; +import { setupFirebase } from "./lib/firebase.js"; const getExtraPackages = async (input) => { if (!input) return []; //if no input, return empty array @@ -44,7 +45,7 @@ const GITHUB_REPO_URL = "https://github.com/harshgupta20/quickstart-react/issues }) const isPWA = await confirm({ message: "Do you want to make this a Progressive Web App (PWA)?", default: false }); - const packages = await checkbox({ + let packages = await checkbox({ message: "Select optional packages:", choices: [ { name: "Axios", value: "axios" }, @@ -52,10 +53,24 @@ const GITHUB_REPO_URL = "https://github.com/harshgupta20/quickstart-react/issues { name: "React Hook Form", value: "react-hook-form" }, { name: "Yup", value: "yup" }, { name: "Formik", value: "formik" }, - { name: "Moment.js", value: "moment" } + { name: "Moment.js", value: "moment" }, + { name: "Firebase (Firestore utils + env)", value: "firebase" } ] }); + // Fallback: some terminals or clients may not toggle checkboxes reliably. + // If Firebase wasn't picked above, ask a simple confirm so the user can't miss it. + if (!packages.includes('firebase')) { + try { + const firebaseConfirm = await confirm({ message: 'Would you like to add Firebase (Firestore utils + env)?', default: false }); + if (firebaseConfirm) { + packages = [...packages, 'firebase']; + } + } catch (e) { + // ignore confirm errors and continue + } + } + const extraPackages = await selectPro({ message: 'Search extra packages to add', multiple: true, @@ -116,7 +131,8 @@ const GITHUB_REPO_URL = "https://github.com/harshgupta20/quickstart-react/issues }); // 4. Install packages - const allPackages = [...routingPackages, ...packages, ...selectedExtraPackages]; + const firebasePkg = packages.includes("firebase") ? ["firebase"] : []; + const allPackages = [...routingPackages, ...packages.filter(p => p !== 'firebase'), ...selectedExtraPackages, ...firebasePkg]; if (allPackages.length > 0) { run(`npm install ${allPackages.join(" ")}`, projectPath); if (config.devPackages.length > 0) { @@ -137,6 +153,11 @@ const GITHUB_REPO_URL = "https://github.com/harshgupta20/quickstart-react/issues createAxiosSetup(projectPath, isTS); } + // 7b. Setup Firebase if selected + if (packages.includes("firebase")) { + setupFirebase(projectPath, isTS); + } + // 8. Clean up default boilerplate files deleteFile(path.join(projectPath, "src", "App.css")); if (cssFramework !== "Tailwind") { diff --git a/lib/firebase.js b/lib/firebase.js new file mode 100644 index 0000000..6e0f5d7 --- /dev/null +++ b/lib/firebase.js @@ -0,0 +1,133 @@ +import path from 'path'; +import { writeFile, readFile, fileExists, createFolder } from './utils.js'; + +const FIREBASE_ENV_KEYS = [ + 'VITE_FIREBASE_API_KEY', + 'VITE_FIREBASE_AUTH_DOMAIN', + 'VITE_FIREBASE_PROJECT_ID', + 'VITE_FIREBASE_STORAGE_BUCKET', + 'VITE_FIREBASE_MESSAGING_SENDER_ID', + 'VITE_FIREBASE_APP_ID', + 'VITE_FIREBASE_MEASUREMENT_ID', +]; + +const envBlock = () => + FIREBASE_ENV_KEYS.map((k) => `${k}=`).join('\n') + '\n'; + +const ensureEnvFileHasFirebase = (filePath) => { + if (!fileExists(filePath)) { + writeFile(filePath, envBlock()); + return; + } + const existing = readFile(filePath); + const missing = FIREBASE_ENV_KEYS.filter((k) => !new RegExp(`^${k}=`, 'm').test(existing)); + if (missing.length === 0) return; + const appended = existing.endsWith('\n') ? existing : existing + '\n'; + writeFile(filePath, appended + missing.map((k) => `${k}=`).join('\n') + '\n'); +}; + +const createFirebaseUtil = (projectPath, isTS) => { + const utilsDir = path.join(projectPath, 'src', 'utils'); + createFolder(utilsDir); + + const tsContent = `import { initializeApp, getApps, getApp, FirebaseApp } from 'firebase/app'; +import { getFirestore, Firestore, collection, doc, addDoc, getDoc, getDocs, updateDoc, deleteDoc, setDoc, DocumentData } from 'firebase/firestore'; + +const firebaseConfig = { + apiKey: import.meta.env.VITE_FIREBASE_API_KEY as string, + authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN as string, + projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID as string, + storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET as string, + messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID as string, + appId: import.meta.env.VITE_FIREBASE_APP_ID as string, + measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID as string, +}; + +export const app: FirebaseApp = getApps().length ? getApp() : initializeApp(firebaseConfig); +export const db: Firestore = getFirestore(app); + +export async function createDoc(path: string, data: T, id?: string): Promise { + if (id) { + await setDoc(doc(db, path, id), data); + return id; + } + const ref = await addDoc(collection(db, path), data); + return ref.id; +} + +export async function readDocById(path: string, id: string): Promise { + const snapshot = await getDoc(doc(db, path, id)); + return snapshot.exists() ? (snapshot.data() as T) : null; +} + +export async function readCollection(path: string): Promise> { + const snap = await getDocs(collection(db, path)); + return snap.docs.map((d) => ({ id: d.id, data: d.data() as T })); +} + +export async function updateDocById(path: string, id: string, data: Partial): Promise { + await updateDoc(doc(db, path, id), data as unknown as DocumentData); +} + +export async function deleteDocById(path: string, id: string): Promise { + await deleteDoc(doc(db, path, id)); +} +`; + + const jsContent = `import { initializeApp, getApps, getApp } from 'firebase/app'; +import { getFirestore, collection, doc, addDoc, getDoc, getDocs, updateDoc, deleteDoc, setDoc } from 'firebase/firestore'; + +const firebaseConfig = { + apiKey: import.meta.env.VITE_FIREBASE_API_KEY, + authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, + projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, + storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET, + messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID, + appId: import.meta.env.VITE_FIREBASE_APP_ID, + measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID, +}; + +export const app = getApps().length ? getApp() : initializeApp(firebaseConfig); +export const db = getFirestore(app); + +export async function createDoc(path, data, id) { + if (id) { + await setDoc(doc(db, path, id), data); + return id; + } + const ref = await addDoc(collection(db, path), data); + return ref.id; +} + +export async function readDocById(path, id) { + const snapshot = await getDoc(doc(db, path, id)); + return snapshot.exists() ? snapshot.data() : null; +} + +export async function readCollection(path) { + const snap = await getDocs(collection(db, path)); + return snap.docs.map((d) => ({ id: d.id, data: d.data() })); +} + +export async function updateDocById(path, id, data) { + await updateDoc(doc(db, path, id), data); +} + +export async function deleteDocById(path, id) { + await deleteDoc(doc(db, path, id)); +} +`; + + writeFile(path.join(utilsDir, `firebase.${isTS ? 'ts' : 'js'}`), isTS ? tsContent : jsContent); +}; + +export const setupFirebase = (projectPath, isTS) => { + // 1. Create utils with CRUD helpers + createFirebaseUtil(projectPath, isTS); + + // 2. Ensure env files contain firebase variables, values left blank + ensureEnvFileHasFirebase(path.join(projectPath, '.env')); + ensureEnvFileHasFirebase(path.join(projectPath, '.env.example')); + + console.log('✅ Firebase initialized: utils and env variables added'); +}; diff --git a/lib/templates.js b/lib/templates.js index 1748378..214a66d 100644 --- a/lib/templates.js +++ b/lib/templates.js @@ -375,6 +375,44 @@ ${isPWA ? `1. **Replace PWA Icons**: Replace SVG placeholders with proper PNG ic Built using React + Vite${isPWA ? ' + PWA' : ''} `; + const firebaseSection = packages.includes('firebase') ? ` +## 🔥 Firebase - writeFile(path.join(projectPath, "README.md"), readmeContent); +Firebase SDK and Firestore helpers are available in \`src/utils/firebase.${isTS ? 'ts' : 'js'}\`. + +### Env Variables +Create a \`.env\` (or use the generated \`.env.example\`) and fill these keys: +\`\`\` +VITE_FIREBASE_API_KEY= +VITE_FIREBASE_AUTH_DOMAIN= +VITE_FIREBASE_PROJECT_ID= +VITE_FIREBASE_STORAGE_BUCKET= +VITE_FIREBASE_MESSAGING_SENDER_ID= +VITE_FIREBASE_APP_ID= +VITE_FIREBASE_MEASUREMENT_ID= +\`\`\` + +### Usage +\`\`\`${isTS ? 'ts' : 'js'} +import { db, createDoc, readDocById, readCollection, updateDocById, deleteDocById } from './utils/firebase'; + +// Create with auto-id +const id = await createDoc('users', { name: 'Ada', age: 30 }); + +// Read single doc +const user = await readDocById('users', id); + +// Read collection +const users = await readCollection('users'); + +// Update +await updateDocById('users', id, { age: 31 }); + +// Delete +await deleteDocById('users', id); +\`\`\` +` : ''; + + const finalReadmeContent = readmeContent + firebaseSection; + writeFile(path.join(projectPath, "README.md"), finalReadmeContent); }; From 99620b031e623fd5e4abeec59b779264066f366a Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 17 Sep 2025 10:13:52 +0530 Subject: [PATCH 2/2] chore: add Firebase confirm and docs update; ignore nested yuv repo --- .gitignore | 3 ++- index.js | 27 ++++++++++----------------- readme.md | 7 ++++++- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 215205f..edd8ae4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules package-lock.json -.vscode \ No newline at end of file +.vscode +yuv/ \ No newline at end of file diff --git a/index.js b/index.js index 146dc53..cc077e5 100644 --- a/index.js +++ b/index.js @@ -45,6 +45,9 @@ const GITHUB_REPO_URL = "https://github.com/harshgupta20/quickstart-react/issues }) const isPWA = await confirm({ message: "Do you want to make this a Progressive Web App (PWA)?", default: false }); + // Dedicated Firebase confirmation + const includeFirebase = await confirm({ message: "Do you want to setup Firebase in this project?", default: false }); + let packages = await checkbox({ message: "Select optional packages:", choices: [ @@ -54,22 +57,12 @@ const GITHUB_REPO_URL = "https://github.com/harshgupta20/quickstart-react/issues { name: "Yup", value: "yup" }, { name: "Formik", value: "formik" }, { name: "Moment.js", value: "moment" }, - { name: "Firebase (Firestore utils + env)", value: "firebase" } + // Firebase now handled via dedicated confirmation above ] }); - // Fallback: some terminals or clients may not toggle checkboxes reliably. - // If Firebase wasn't picked above, ask a simple confirm so the user can't miss it. - if (!packages.includes('firebase')) { - try { - const firebaseConfirm = await confirm({ message: 'Would you like to add Firebase (Firestore utils + env)?', default: false }); - if (firebaseConfirm) { - packages = [...packages, 'firebase']; - } - } catch (e) { - // ignore confirm errors and continue - } - } + // Merge firebase selection into a derived list for docs/readme purposes + const packagesWithFirebase = includeFirebase ? [...packages, 'firebase'] : [...packages]; const extraPackages = await selectPro({ message: 'Search extra packages to add', @@ -131,8 +124,8 @@ const GITHUB_REPO_URL = "https://github.com/harshgupta20/quickstart-react/issues }); // 4. Install packages - const firebasePkg = packages.includes("firebase") ? ["firebase"] : []; - const allPackages = [...routingPackages, ...packages.filter(p => p !== 'firebase'), ...selectedExtraPackages, ...firebasePkg]; + const firebasePkg = includeFirebase ? ["firebase"] : []; + const allPackages = Array.from(new Set([...routingPackages, ...packages, ...selectedExtraPackages, ...firebasePkg])); if (allPackages.length > 0) { run(`npm install ${allPackages.join(" ")}`, projectPath); if (config.devPackages.length > 0) { @@ -154,7 +147,7 @@ const GITHUB_REPO_URL = "https://github.com/harshgupta20/quickstart-react/issues } // 7b. Setup Firebase if selected - if (packages.includes("firebase")) { + if (includeFirebase) { setupFirebase(projectPath, isTS); } @@ -169,7 +162,7 @@ const GITHUB_REPO_URL = "https://github.com/harshgupta20/quickstart-react/issues setupRoutingFramework(projectPath, routingFramework, cssFramework, isTS); // 10. Create comprehensive README - createPWAReadme(projectPath, projectName, cssFramework, packages, isPWA, isTS); + createPWAReadme(projectPath, projectName, cssFramework, packagesWithFirebase, isPWA, isTS); // 11. Initialize Git repository initializeGit(projectPath); diff --git a/readme.md b/readme.md index 4fb9f77..f41ce24 100644 --- a/readme.md +++ b/readme.md @@ -21,7 +21,10 @@ npx quickstart-react When you run `npx quickstart-react`, you will be prompted to: 1. **Enter Project Name** — e.g., `my-app` 2. **Choose CSS Framework** — Tailwind, Bootstrap, or MUI -3. **Select Optional Packages** — choose from a list of commonly used React libraries +3. **Choose Routing** — React Router or TanStack Router +4. **Enable PWA** — optional +5. **Setup Firebase** — simple Yes/No confirmation +6. **Select Other Optional Packages** — Axios, React Icons, React Hook Form, Yup, Formik, Moment.js, etc. Example run: ```bash @@ -32,6 +35,7 @@ npx quickstart-react ``` ? Enter project name: my-portfolio ? Choose a CSS framework: Tailwind +? Setup Firebase in this project? Yes ? Select optional packages: Axios, React Icons ``` @@ -87,6 +91,7 @@ You can add these during setup: - **Yup** — schema validation - **Formik** — form management - **Moment.js** — date/time utilities + - **Firebase** — added via a dedicated confirmation; installs the `firebase` package and generates Firestore utils (`src/utils/firebase.[ts|js]`) plus `.env` placeholders ## 🚀 Quick Start ```bash