quickstart-react
Advanced tools
| --- | ||
| name: Bug report | ||
| about: Create a report to help us improve | ||
| title: '' | ||
| labels: '' | ||
| assignees: '' | ||
| --- | ||
| **Describe the bug** | ||
| A clear and concise description of what the bug is. | ||
| **To Reproduce** | ||
| Steps to reproduce the behavior: | ||
| **Expected behavior** | ||
| A clear and concise description of what you expected to happen. | ||
| **Screenshots** | ||
| If applicable, add screenshots to help explain your problem. | ||
| **Additional context** | ||
| Add any other context about the problem here. |
| --- | ||
| name: Feature request | ||
| about: Suggest an idea for this project | ||
| title: '' | ||
| labels: '' | ||
| assignees: '' | ||
| --- | ||
| **Is your feature request related to a problem? Please describe.** | ||
| A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] | ||
| **Describe the solution you'd like** | ||
| A clear and concise description of what you want to happen. | ||
| **Describe alternatives you've considered** | ||
| A clear and concise description of any alternative solutions or features you've considered. | ||
| **Additional context** | ||
| Add any other context or screenshots about the feature request here. |
| # Contributor Covenant Code of Conduct | ||
| ## Our Pledge | ||
| We as members, contributors, and leaders pledge to make participation in our | ||
| community a harassment-free experience for everyone, regardless of age, body | ||
| size, visible or invisible disability, ethnicity, sex characteristics, gender | ||
| identity and expression, level of experience, education, socio-economic status, | ||
| nationality, personal appearance, race, religion, or sexual identity | ||
| and orientation. | ||
| We pledge to act and interact in ways that contribute to an open, welcoming, | ||
| diverse, inclusive, and healthy community. | ||
| ## Our Standards | ||
| Examples of behavior that contributes to a positive environment for our | ||
| community include: | ||
| * Demonstrating empathy and kindness toward other people | ||
| * Being respectful of differing opinions, viewpoints, and experiences | ||
| * Giving and gracefully accepting constructive feedback | ||
| * Accepting responsibility and apologizing to those affected by our mistakes, | ||
| and learning from the experience | ||
| * Focusing on what is best not just for us as individuals, but for the | ||
| overall community | ||
| Examples of unacceptable behavior include: | ||
| * The use of sexualized language or imagery, and sexual attention or | ||
| advances of any kind | ||
| * Trolling, insulting or derogatory comments, and personal or political attacks | ||
| * Public or private harassment | ||
| * Publishing others' private information, such as a physical or email | ||
| address, without their explicit permission | ||
| * Other conduct which could reasonably be considered inappropriate in a | ||
| professional setting | ||
| ## Enforcement Responsibilities | ||
| Community leaders are responsible for clarifying and enforcing our standards of | ||
| acceptable behavior and will take appropriate and fair corrective action in | ||
| response to any behavior that they deem inappropriate, threatening, offensive, | ||
| or harmful. | ||
| Community leaders have the right and responsibility to remove, edit, or reject | ||
| comments, commits, code, wiki edits, issues, and other contributions that are | ||
| not aligned to this Code of Conduct, and will communicate reasons for moderation | ||
| decisions when appropriate. | ||
| ## Scope | ||
| This Code of Conduct applies within all community spaces, and also applies when | ||
| an individual is officially representing the community in public spaces. | ||
| Examples of representing our community include using an official e-mail address, | ||
| posting via an official social media account, or acting as an appointed | ||
| representative at an online or offline event. | ||
| ## Enforcement | ||
| Instances of abusive, harassing, or otherwise unacceptable behavior may be | ||
| reported to the community leaders responsible for enforcement at | ||
| By Raising Issue in Reporitory. | ||
| All complaints will be reviewed and investigated promptly and fairly. | ||
| All community leaders are obligated to respect the privacy and security of the | ||
| reporter of any incident. | ||
| ## Enforcement Guidelines | ||
| Community leaders will follow these Community Impact Guidelines in determining | ||
| the consequences for any action they deem in violation of this Code of Conduct: | ||
| ### 1. Correction | ||
| **Community Impact**: Use of inappropriate language or other behavior deemed | ||
| unprofessional or unwelcome in the community. | ||
| **Consequence**: A private, written warning from community leaders, providing | ||
| clarity around the nature of the violation and an explanation of why the | ||
| behavior was inappropriate. A public apology may be requested. | ||
| ### 2. Warning | ||
| **Community Impact**: A violation through a single incident or series | ||
| of actions. | ||
| **Consequence**: A warning with consequences for continued behavior. No | ||
| interaction with the people involved, including unsolicited interaction with | ||
| those enforcing the Code of Conduct, for a specified period of time. This | ||
| includes avoiding interactions in community spaces as well as external channels | ||
| like social media. Violating these terms may lead to a temporary or | ||
| permanent ban. | ||
| ### 3. Temporary Ban | ||
| **Community Impact**: A serious violation of community standards, including | ||
| sustained inappropriate behavior. | ||
| **Consequence**: A temporary ban from any sort of interaction or public | ||
| communication with the community for a specified period of time. No public or | ||
| private interaction with the people involved, including unsolicited interaction | ||
| with those enforcing the Code of Conduct, is allowed during this period. | ||
| Violating these terms may lead to a permanent ban. | ||
| ### 4. Permanent Ban | ||
| **Community Impact**: Demonstrating a pattern of violation of community | ||
| standards, including sustained inappropriate behavior, harassment of an | ||
| individual, or aggression toward or disparagement of classes of individuals. | ||
| **Consequence**: A permanent ban from any sort of public interaction within | ||
| the community. | ||
| ## Attribution | ||
| This Code of Conduct is adapted from the [Contributor Covenant][homepage], | ||
| version 2.0, available at | ||
| https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. | ||
| Community Impact Guidelines were inspired by [Mozilla's code of conduct | ||
| enforcement ladder](https://github.com/mozilla/diversity). | ||
| [homepage]: https://www.contributor-covenant.org | ||
| For answers to common questions about this code of conduct, see the FAQ at | ||
| https://www.contributor-covenant.org/faq. Translations are available at | ||
| https://www.contributor-covenant.org/translations. |
| # Contributing to QuickStart React | ||
| Thank you for your interest in contributing to **QuickStart React**! | ||
| Your contributions help make this project better for everyone. | ||
| ## How You Can Contribute | ||
| ### 🚀 Reporting Bugs | ||
| - Use the [Issues](https://github.com/harshgupta20/quickstart-react/issues) tab to report bugs. | ||
| - Provide clear steps to reproduce, expected behavior, and screenshots if applicable. | ||
| ### 🛠️ Submitting Enhancements or Features | ||
| - Open an issue first to discuss your idea. | ||
| - Please wait for approval before starting major work. | ||
| ### 🧪 Writing Tests | ||
| - If you’re fixing a bug or adding a feature, add relevant tests. | ||
| - Ensure all tests pass before opening a pull request. | ||
| ## Getting Started | ||
| ### 1. Fork the Repository | ||
| Click the “Fork” button at the top right of the [repo](https://github.com/harshgupta20/quickstart-react). | ||
| ### 2. Clone Your Fork | ||
| ```bash | ||
| git clone https://github.com/your-username/quickstart-react.git | ||
| cd quickstart-react | ||
| ``` | ||
| ### 3. Install Dependencies | ||
| ```bash | ||
| npm install | ||
| ``` | ||
| ### 4. Run the Project | ||
| ```bash | ||
| npm start | ||
| ``` | ||
| ## Code Guidelines | ||
| - Follow the existing code style. | ||
| - Use meaningful commit messages. | ||
| - Write clean, modular, and reusable code. | ||
| ## Submitting a Pull Request | ||
| 1. Create a new branch: | ||
| ```bash | ||
| git checkout -b feature/your-feature-name | ||
| ``` | ||
| 2. Commit your changes: | ||
| ```bash | ||
| git commit -m "Add feature: description" | ||
| ``` | ||
| 3. Push to your fork and open a PR: | ||
| ```bash | ||
| git push origin feature/your-feature-name | ||
| ``` | ||
| Then go to GitHub and open a pull request from your branch. | ||
| ## Code of Conduct | ||
| Please note we have a [Code of Conduct](https://github.com/harshgupta20/quickstart-react/blob/main/CODE_OF_CONDUCT.md). | ||
| By participating, you agree to abide by its terms. | ||
| ## Need Help? | ||
| If you have any questions, open an issue or reach out via discussions. | ||
| Happy coding! 💻✨ |
| import { run, writeFile, readFile, fileExists } from './utils.js'; | ||
| import path from 'path'; | ||
| export const setupTailwind = (projectPath) => { | ||
| run(`npm install tailwindcss @tailwindcss/vite`, projectPath); | ||
| const viteConfigPath = path.join(projectPath, "vite.config.js"); | ||
| let viteConfig = readFile(viteConfigPath); | ||
| viteConfig = `import tailwindcss from '@tailwindcss/vite'\n` + viteConfig; | ||
| viteConfig = viteConfig.replace(/plugins:\s*\[/, "plugins: [\n tailwindcss(),"); | ||
| writeFile(viteConfigPath, viteConfig); | ||
| writeFile(path.join(projectPath, "src", "index.css"), `@import "tailwindcss";\n`); | ||
| const mainFile = fileExists(path.join(projectPath, "src/main.jsx")) ? "src/main.jsx" : "src/main.tsx"; | ||
| const mainPath = path.join(projectPath, mainFile); | ||
| let mainContent = readFile(mainPath); | ||
| mainContent = mainContent.replace(/import\s+['"]\.\/index\.css['"];?/g, ""); | ||
| if (!mainContent.includes(`import './index.css'`)) { | ||
| mainContent = `import './index.css';\n` + mainContent; | ||
| } | ||
| writeFile(mainPath, mainContent); | ||
| }; | ||
| export const setupBootstrapCDN = (projectPath) => { | ||
| const indexHtmlPath = path.join(projectPath, "index.html"); | ||
| let indexHtml = readFile(indexHtmlPath); | ||
| indexHtml = indexHtml.replace( | ||
| /<head>/, | ||
| `<head>\n <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">\n <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>` | ||
| ); | ||
| writeFile(indexHtmlPath, indexHtml); | ||
| }; | ||
| export const setupReactBootstrap = (projectPath) => { | ||
| run(`npm install react-bootstrap bootstrap`, projectPath); | ||
| const mainFile = fileExists(path.join(projectPath, "src/main.jsx")) ? "src/main.jsx" : "src/main.tsx"; | ||
| const mainPath = path.join(projectPath, mainFile); | ||
| let mainContent = readFile(mainPath); | ||
| mainContent = mainContent | ||
| .replace(/import\s+['"]\.\/index\.css['"];?/g, "") | ||
| .replace(/import\s+['"]\.\/App\.css['"];?/g, ""); | ||
| mainContent = `import 'bootstrap/dist/css/bootstrap.min.css';\n` + mainContent; | ||
| writeFile(mainPath, mainContent); | ||
| }; | ||
| export const setupMUI = (projectPath) => { | ||
| run(`npm install @mui/material @emotion/react @emotion/styled`, projectPath); | ||
| const mainFile = fileExists(path.join(projectPath, "src/main.jsx")) ? "src/main.jsx" : "src/main.tsx"; | ||
| const mainPath = path.join(projectPath, mainFile); | ||
| let mainContent = readFile(mainPath); | ||
| mainContent = mainContent | ||
| .replace(/import\s+['"]\.\/index\.css['"];?/g, "") | ||
| .replace(/import\s+['"]\.\/App\.css['"];?/g, ""); | ||
| writeFile(mainPath, mainContent); | ||
| }; | ||
| export const setupCSSFramework = (cssFramework, projectPath) => { | ||
| const frameworkMap = { | ||
| "Tailwind": () => setupTailwind(projectPath), | ||
| "Bootstrap (CDN)": () => setupBootstrapCDN(projectPath), | ||
| "React Bootstrap": () => setupReactBootstrap(projectPath), | ||
| "MUI": () => setupMUI(projectPath) | ||
| }; | ||
| const setupFunction = frameworkMap[cssFramework]; | ||
| if (setupFunction) { | ||
| setupFunction(); | ||
| } | ||
| }; |
+155
| import { run, writeFile, readFile, createFolder } from './utils.js'; | ||
| import path from 'path'; | ||
| export const installPWADependencies = (projectPath) => { | ||
| run(`npm install vite-plugin-pwa workbox-window`, projectPath); | ||
| }; | ||
| export const setupPWAConfig = (projectPath, projectName) => { | ||
| const viteConfigPath = path.join(projectPath, "vite.config.js"); | ||
| let viteConfig = readFile(viteConfigPath); | ||
| if (!viteConfig.includes("vite-plugin-pwa")) { | ||
| viteConfig = `import { VitePWA } from 'vite-plugin-pwa'\n` + viteConfig; | ||
| } | ||
| const pwaConfig = `VitePWA({ | ||
| registerType: 'autoUpdate', | ||
| includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'masked-icon.svg'], | ||
| manifest: { | ||
| name: '${projectName}', | ||
| short_name: '${projectName}', | ||
| description: 'A Progressive Web App built with React and Vite', | ||
| theme_color: '#2563eb', | ||
| background_color: '#ffffff', | ||
| display: 'standalone', | ||
| scope: '/', | ||
| start_url: '/', | ||
| icons: [ | ||
| { | ||
| src: 'pwa-192x192.png', | ||
| sizes: '192x192', | ||
| type: 'image/png' | ||
| }, | ||
| { | ||
| src: 'pwa-512x512.png', | ||
| sizes: '512x512', | ||
| type: 'image/png' | ||
| } | ||
| ] | ||
| }, | ||
| workbox: { | ||
| globPatterns: ['**/*.{js,css,html,ico,png,svg}'], | ||
| runtimeCaching: [ | ||
| { | ||
| urlPattern: /^https:\\/\\/api\\./, | ||
| handler: 'NetworkFirst', | ||
| options: { | ||
| cacheName: 'api-cache', | ||
| expiration: { | ||
| maxEntries: 100, | ||
| maxAgeSeconds: 60 * 60 * 24 | ||
| } | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| })`; | ||
| viteConfig = viteConfig.replace(/plugins:\s*\[/, `plugins: [\n ${pwaConfig},`); | ||
| writeFile(viteConfigPath, viteConfig); | ||
| }; | ||
| export const createPWAAssets = (projectPath, projectName) => { | ||
| const publicPath = path.join(projectPath, "public"); | ||
| const createDummyIcon = (size) => { | ||
| return `<svg width="${size}" height="${size}" xmlns="http://www.w3.org/2000/svg"> | ||
| <rect width="100%" height="100%" fill="#2563eb"/> | ||
| <text x="50%" y="50%" text-anchor="middle" dy=".3em" fill="white" font-size="${Math.floor(size/6)}" font-family="Arial, sans-serif" font-weight="bold">${projectName.charAt(0).toUpperCase()}</text> | ||
| </svg>`; | ||
| }; | ||
| const icons = [ | ||
| { name: "pwa-192x192.svg", size: 192 }, | ||
| { name: "pwa-512x512.svg", size: 512 }, | ||
| { name: "apple-touch-icon.svg", size: 180 }, | ||
| { name: "favicon.svg", size: 32 } | ||
| ]; | ||
| icons.forEach(({ name, size }) => { | ||
| writeFile(path.join(publicPath, name), createDummyIcon(size)); | ||
| }); | ||
| console.log("📱 PWA placeholder assets created"); | ||
| }; | ||
| export const createPWAHook = (projectPath) => { | ||
| const hooksDir = path.join(projectPath, "src", "hooks"); | ||
| createFolder(hooksDir); | ||
| const hookContent = `import { useEffect, useState } from 'react'; | ||
| export const usePWA = () => { | ||
| const [deferredPrompt, setDeferredPrompt] = useState(null); | ||
| const [isInstallable, setIsInstallable] = useState(false); | ||
| useEffect(() => { | ||
| // Register service worker | ||
| if ('serviceWorker' in navigator) { | ||
| navigator.serviceWorker.register('/sw.js') | ||
| .then((registration) => { | ||
| console.log('✅ SW registered: ', registration); | ||
| }) | ||
| .catch((registrationError) => { | ||
| console.log('❌ SW registration failed: ', registrationError); | ||
| }); | ||
| } | ||
| // Listen for install prompt | ||
| const handleBeforeInstallPrompt = (e) => { | ||
| e.preventDefault(); | ||
| setDeferredPrompt(e); | ||
| setIsInstallable(true); | ||
| }; | ||
| window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt); | ||
| return () => { | ||
| window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt); | ||
| }; | ||
| }, []); | ||
| const installApp = async () => { | ||
| if (!deferredPrompt) return; | ||
| deferredPrompt.prompt(); | ||
| const { outcome } = await deferredPrompt.userChoice; | ||
| if (outcome === 'accepted') { | ||
| console.log('✅ User accepted the install prompt'); | ||
| } else { | ||
| console.log('❌ User dismissed the install prompt'); | ||
| } | ||
| setDeferredPrompt(null); | ||
| setIsInstallable(false); | ||
| }; | ||
| return { | ||
| isInstallable, | ||
| installApp, | ||
| isOnline: navigator.onLine | ||
| }; | ||
| };`; | ||
| writeFile(path.join(hooksDir, "usePWA.js"), hookContent); | ||
| }; | ||
| export const initializePWA = (projectPath, projectName) => { | ||
| installPWADependencies(projectPath); | ||
| setupPWAConfig(projectPath, projectName); | ||
| createPWAAssets(projectPath, projectName); | ||
| createPWAHook(projectPath); | ||
| console.log("✅ PWA configuration complete!"); | ||
| }; |
| import path from 'path'; | ||
| import { fileExists, readFile, writeFile } from './utils.js'; | ||
| // Content for the main.tsx/jsx for react router | ||
| const mainContentReactRouter = ( | ||
| cssImports | ||
| ) => `${cssImports}import React from 'react'; | ||
| import ReactDOM from 'react-dom/client'; | ||
| import { BrowserRouter, Routes, Route } from 'react-router'; | ||
| import App from './App'; | ||
| ReactDOM.createRoot(document.getElementById('root')).render( | ||
| <React.StrictMode> | ||
| <BrowserRouter> | ||
| <Routes> | ||
| <Route path="/" element={<App />} /> | ||
| </Routes> | ||
| </BrowserRouter> | ||
| </React.StrictMode> | ||
| );`; | ||
| // Content for the main.tsx/jsx for tanstack router | ||
| const mainContentTanstack = (cssImports, mainFile) => { | ||
| const typeDeclaration = | ||
| mainFile === "src/main.tsx" ? | ||
| `\n// Register the router instance for type safety | ||
| declare module '@tanstack/react-router' { | ||
| interface Register { | ||
| router: typeof router | ||
| } | ||
| }` : ""; | ||
| return `import React from 'react'; | ||
| import ReactDOM from 'react-dom/client'; | ||
| import { RouterProvider, createRouter } from '@tanstack/react-router'; | ||
| import { routeTree } from './routeTree.gen'; | ||
| ${cssImports} | ||
| // Create a new router instance | ||
| const router = createRouter({ routeTree }); | ||
| ${typeDeclaration} | ||
| ReactDOM.createRoot(document.getElementById('root')).render( | ||
| <React.StrictMode> | ||
| <RouterProvider router={router} /> | ||
| </React.StrictMode> | ||
| ); | ||
| ` | ||
| }; | ||
| /** | ||
| * * ----------------------------- Tanstack Setup Start ----------------------------- | ||
| */ | ||
| // Contents for the src/routes/__root.tsx/jsx | ||
| const ROOT = ` | ||
| import { createRootRoute, Link, Outlet } from '@tanstack/react-router' | ||
| import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' | ||
| export const Route = createRootRoute({ | ||
| component: () => ( | ||
| <> | ||
| <div style={{ padding: '0.5rem', display: 'flex', gap: '0.5rem' }}> | ||
| <Link to="/" style={{ fontWeight: 'bold' }}> | ||
| Home | ||
| </Link>{' '} | ||
| <Link to="/about" style={{ fontWeight: 'bold' }}> | ||
| About | ||
| </Link> | ||
| </div> | ||
| <hr /> | ||
| <Outlet /> | ||
| <TanStackRouterDevtools /> | ||
| </> | ||
| ), | ||
| }) | ||
| `; | ||
| // Contents for the src/routes/index.tsx/jsx | ||
| const INDEX = ` | ||
| import { createFileRoute } from '@tanstack/react-router' | ||
| import App from "../App" | ||
| export const Route = createFileRoute('/')({ | ||
| component: Index, | ||
| }) | ||
| function Index() { | ||
| return ( | ||
| <div className="p-2"> | ||
| <App /> | ||
| </div> | ||
| ) | ||
| } | ||
| `; | ||
| // Contents for the src/routes/about.tsx/jsx | ||
| const ABOUT = ` | ||
| import { createFileRoute } from '@tanstack/react-router' | ||
| export const Route = createFileRoute('/about')({ | ||
| component: About, | ||
| }) | ||
| function About() { | ||
| return <div className="p-2">Hello from About!</div> | ||
| } | ||
| `; | ||
| /** | ||
| * Updates the project's `vite.config.js` or `vite.config.ts` to enable the TanStack Router plugin | ||
| * if it is not already configured. | ||
| * | ||
| * - Adds the `tanstackRouter` import | ||
| * - Inserts the plugin as the first entry in the `plugins` array | ||
| * - Supports both JavaScript and TypeScript configurations | ||
| * | ||
| * @param {string} projectPath Absolute path to the project root | ||
| */ | ||
| const updateViteConfig = (projectPath) => { | ||
| // Check if vite.config.ts exists, otherwise use vite.config.js | ||
| const viteConfigTsPath = path.join(projectPath, "vite.config.ts"); | ||
| const viteConfigJsPath = path.join(projectPath, "vite.config.js"); | ||
| const viteConfigPath = fileExists(viteConfigTsPath) ? viteConfigTsPath : viteConfigJsPath; | ||
| let viteConfig = readFile(viteConfigPath); | ||
| // Add the import for tanstack router plugin | ||
| if (!viteConfig.includes('@tanstack/router-plugin/vite')) { | ||
| viteConfig = viteConfig.replace( | ||
| "import { defineConfig } from 'vite'", | ||
| "import { defineConfig } from 'vite'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'" | ||
| ); | ||
| } | ||
| // Add the plugin to the plugins array at index 0 | ||
| if (!viteConfig.includes('tanstackRouter(')) { | ||
| viteConfig = viteConfig.replace( | ||
| "plugins: [", | ||
| "plugins: [\n tanstackRouter({\n target: 'react',\n autoCodeSplitting: true,\n })," | ||
| ); | ||
| } | ||
| writeFile(viteConfigPath, viteConfig); | ||
| } | ||
| /** | ||
| * Sets up TanStack Router file-based routing in a project. | ||
| * | ||
| * Actions: | ||
| * - Creates `src/routes/__root.(tsx|jsx)`, `src/routes/index.(tsx|jsx)`, and `src/routes/about.(tsx|jsx)` | ||
| * - Writes the React entry file with TanStack Router bootstrapping | ||
| * - Updates `vite.config.js` to include the TanStack Router plugin | ||
| * | ||
| * @param {string} cssImports String of CSS import statements to prepend in the entry file | ||
| * @param {"src/main.jsx"|"src/main.tsx"} mainFile Relative path to the app entry file used to infer TypeScript vs JavaScript | ||
| * @param {string} mainPath Absolute path to the app entry file to write | ||
| * @param {string} projectPath Absolute path to the project root | ||
| * @returns {void} | ||
| */ | ||
| const setUpTanstackRouter = (mainFile, projectPath) => { | ||
| const isProjectTs = mainFile === "src/main.tsx"; // To check if the project uses typescript. | ||
| const routesPath = `${projectPath}/src/routes` | ||
| // 1 Create route files in src/routes | ||
| const ext = isProjectTs ? 'tsx' : 'jsx'; | ||
| [ | ||
| [`__root.${ext}`, ROOT], | ||
| [`index.${ext}`, INDEX], | ||
| [`about.${ext}`, ABOUT], | ||
| ].forEach(([filename, contents]) => { | ||
| writeFile(path.join(routesPath, filename), contents); | ||
| }); | ||
| } | ||
| /** | ||
| * * ----------------------------- Tanstack Setup End ------------------------------- | ||
| */ | ||
| /** | ||
| * Configures routing for the scaffolded project based on the selected framework. | ||
| * | ||
| * - When `routingFramework` is "React Router": writes a minimal `<BrowserRouter>` setup into the entry file | ||
| * - When `routingFramework` is "Tanstack Router": performs file-based routing setup and adjusts Vite config | ||
| * - Otherwise: writes a default, no-routing entry file | ||
| * | ||
| * @param {string} projectPath Absolute path to the project root | ||
| * @param {"React Router"|"Tanstack Router"|string} routingFramework Selected routing framework | ||
| * @param {"React Bootstrap"|"Tailwind"|"Bootstrap (CDN)"|"MUI"|string} cssFramework Selected CSS framework for initial imports | ||
| * @returns {void} | ||
| */ | ||
| export const setupRoutingFramework = (projectPath, routingFramework, cssFramework) => { | ||
| // Check if the project is created with typescript | ||
| const mainFile = fileExists(path.join(projectPath, "src/main.jsx")) | ||
| ? "src/main.jsx" | ||
| : "src/main.tsx"; | ||
| const mainPath = path.join(projectPath, mainFile); | ||
| let cssImports = ""; | ||
| const cssImportMap = { | ||
| "React Bootstrap": `import 'bootstrap/dist/css/bootstrap.min.css';\n`, | ||
| "Tailwind": `import './index.css';\n`, | ||
| "Bootstrap (CDN)": "", | ||
| "MUI": "", | ||
| }; | ||
| cssImports = cssImportMap[cssFramework] || ""; | ||
| let mainContents = "" | ||
| if(routingFramework === 'React Router') { | ||
| mainContents = mainContentReactRouter(cssImports) | ||
| } | ||
| else { | ||
| setUpTanstackRouter(mainFile, projectPath); | ||
| // 1. Update the main.tsx/jsx | ||
| mainContents = mainContentTanstack(cssImports, mainFile); | ||
| // 2. Update the vite.config. handles both js and ts configs | ||
| updateViteConfig(projectPath); | ||
| } | ||
| writeFile(mainPath, mainContents); | ||
| return; | ||
| }; |
+381
| import { writeFile, fileExists, createFolder } from './utils.js'; | ||
| import path from 'path'; | ||
| export const createAxiosSetup = (projectPath) => { | ||
| const utilsDir = path.join(projectPath, "src", "utils"); | ||
| createFolder(utilsDir); | ||
| const axiosContent = `import axios from "axios"; | ||
| export const api = axios.create({ | ||
| baseURL: import.meta.env.VITE_API_URL || "http://localhost:5000", | ||
| headers: { "Content-Type": "application/json" }, | ||
| timeout: 10000 | ||
| }); | ||
| // ✅ Request Interceptor | ||
| api.interceptors.request.use( | ||
| (config) => { | ||
| const token = localStorage.getItem("token"); | ||
| if (token) { | ||
| config.headers.Authorization = \`Bearer \${token}\`; | ||
| } | ||
| return config; | ||
| }, | ||
| (error) => { | ||
| return Promise.reject(error); | ||
| } | ||
| ); | ||
| // ✅ Response Interceptor | ||
| api.interceptors.response.use( | ||
| (response) => { | ||
| return response.data; | ||
| }, | ||
| (error) => { | ||
| if (error.response) { | ||
| console.error("API Error:", error.response.data?.message || error.message); | ||
| if (error.response.status === 401) { | ||
| window.location.href = "/login"; | ||
| } | ||
| } else if (error.request) { | ||
| console.error("No response received from server."); | ||
| } else { | ||
| console.error("Request setup error:", error.message); | ||
| } | ||
| return Promise.reject(error); | ||
| } | ||
| );`; | ||
| writeFile(path.join(utilsDir, "axiosInstance.js"), axiosContent); | ||
| }; | ||
| export const createAppComponent = (projectPath, projectName, isPWA) => { | ||
| const appFile = fileExists(path.join(projectPath, "src/App.jsx")) | ||
| ? path.join(projectPath, "src/App.jsx") | ||
| : path.join(projectPath, "src/App.tsx"); | ||
| const appContent = `${isPWA ? `import { usePWA } from './hooks/usePWA';\n\n` : ''}export default function App() { | ||
| ${isPWA ? `const { isInstallable, installApp, isOnline } = usePWA();\n\n ` : ''}return ( | ||
| <div | ||
| style={{ | ||
| display: "flex", | ||
| flexDirection: "column", | ||
| justifyContent: "center", | ||
| alignItems: "center", | ||
| height: "100vh", | ||
| fontFamily: "sans-serif", | ||
| background: "#f9fafb", | ||
| color: "#111", | ||
| textAlign: "center", | ||
| padding: "2rem", | ||
| }} | ||
| > | ||
| <h1 | ||
| style={{ | ||
| fontSize: "2.5rem", | ||
| marginBottom: "0.5rem", | ||
| fontWeight: 600, | ||
| }} | ||
| > | ||
| Welcome to{" "} | ||
| <span style={{ color: "#2563eb" }}>${projectName}</span> 🚀 | ||
| </h1> | ||
| <p style={{ fontSize: "1.1rem", color: "#555", marginBottom: "2rem" }}> | ||
| Your ${isPWA ? 'PWA is' : 'project is'} ready. Start building amazing things! | ||
| </p> | ||
| ${isPWA ? `<div style={{ display: "flex", flexDirection: "column", gap: "1rem", alignItems: "center" }}> | ||
| <div style={{ | ||
| padding: "0.5rem 1rem", | ||
| background: "#2563eb", | ||
| color: "white", | ||
| borderRadius: "0.5rem", | ||
| fontSize: "0.9rem" | ||
| }}> | ||
| 📱 PWA Enabled | ||
| </div> | ||
| <div style={{ | ||
| padding: "0.5rem 1rem", | ||
| background: isOnline ? "#10b981" : "#ef4444", | ||
| color: "white", | ||
| borderRadius: "0.5rem", | ||
| fontSize: "0.9rem" | ||
| }}> | ||
| {isOnline ? "🟢 Online" : "🔴 Offline"} | ||
| </div> | ||
| {isInstallable && ( | ||
| <button | ||
| onClick={installApp} | ||
| style={{ | ||
| padding: "0.75rem 1.5rem", | ||
| background: "#2563eb", | ||
| color: "white", | ||
| border: "none", | ||
| borderRadius: "0.5rem", | ||
| cursor: "pointer", | ||
| fontSize: "1rem", | ||
| fontWeight: "500" | ||
| }} | ||
| > | ||
| 📲 Install App | ||
| </button> | ||
| )} | ||
| </div>` : ''} | ||
| </div> | ||
| ); | ||
| }`; | ||
| writeFile(appFile, appContent); | ||
| }; | ||
| // This function is not being used any more. | ||
| // export const setupRouterMain = (projectPath, cssFramework) => { | ||
| // const mainFile = fileExists(path.join(projectPath, "src/main.jsx")) ? "src/main.jsx" : "src/main.tsx"; | ||
| // const mainPath = path.join(projectPath, mainFile); | ||
| // let cssImports = ""; | ||
| // const cssImportMap = { | ||
| // "React Bootstrap": `import 'bootstrap/dist/css/bootstrap.min.css';\n`, | ||
| // "Tailwind": `import './index.css';\n`, | ||
| // "Bootstrap (CDN)": "", | ||
| // "MUI": "" | ||
| // }; | ||
| // cssImports = cssImportMap[cssFramework] || ""; | ||
| // const routerSetup = `${cssImports}import React from 'react'; | ||
| // import ReactDOM from 'react-dom/client'; | ||
| // import { BrowserRouter, Routes, Route } from 'react-router-dom'; | ||
| // import App from './App'; | ||
| // ReactDOM.createRoot(document.getElementById('root')).render( | ||
| // <React.StrictMode> | ||
| // <BrowserRouter> | ||
| // <Routes> | ||
| // <Route path="/" element={<App />} /> | ||
| // </Routes> | ||
| // </BrowserRouter> | ||
| // </React.StrictMode> | ||
| // );`; | ||
| // writeFile(mainPath, routerSetup); | ||
| // }; | ||
| export const createPWAReadme = (projectPath, projectName, cssFramework, packages, isPWA) => { | ||
| const readmeContent = `# ${projectName} | ||
| A modern React application${isPWA ? ' with Progressive Web App (PWA) capabilities' : ''} built with Vite. | ||
| ## 🚀 Features | ||
| - ⚡ **Vite** - Fast build tool and development server | ||
| - ⚛️ **React 18** - Latest React with modern hooks | ||
| - 🎨 **${cssFramework}** - Styling framework | ||
| - 🛣️ **React Router** - Client-side routing | ||
| ${isPWA ? `- 📱 **PWA Ready** - Installable, offline-capable app | ||
| - 🔄 **Auto-updates** - Service worker with auto-update functionality | ||
| - 📊 **Caching Strategy** - Smart caching for better performance` : ''} | ||
| ${packages.length > 0 ? `- 📦 **Additional Packages**: ${packages.join(', ')}` : ''} | ||
| ## 📋 Prerequisites | ||
| - Node.js (v16 or higher) | ||
| - npm or yarn | ||
| ## 🛠️ Installation | ||
| 1. Navigate to the project directory: | ||
| \`\`\`bash | ||
| cd ${projectName} | ||
| \`\`\` | ||
| 2. Install dependencies: | ||
| \`\`\`bash | ||
| npm install | ||
| \`\`\` | ||
| ## 🏃♂️ Running the Application | ||
| ### Development Mode | ||
| \`\`\`bash | ||
| npm run dev | ||
| \`\`\` | ||
| The app will be available at \`http://localhost:5173\` | ||
| ### Production Build | ||
| \`\`\`bash | ||
| npm run build | ||
| \`\`\` | ||
| ### Preview Production Build | ||
| \`\`\`bash | ||
| npm run preview | ||
| \`\`\` | ||
| ${isPWA ? `## 📱 PWA Features | ||
| ### Installation | ||
| - **Desktop**: Look for the install icon in the address bar or use the "Install App" button | ||
| - **Mobile**: Use "Add to Home Screen" option in your browser menu | ||
| ### Offline Support | ||
| This app works offline thanks to service worker caching: | ||
| - Static assets are cached automatically | ||
| - API responses are cached with NetworkFirst strategy | ||
| - Fallback pages for offline scenarios | ||
| ### Testing PWA Features | ||
| 1. **Install Prompt Testing**: | ||
| \`\`\`bash | ||
| # Serve the built app locally | ||
| npm run build | ||
| npm run preview | ||
| \`\`\` | ||
| 2. **Service Worker Testing**: | ||
| - Open DevTools → Application → Service Workers | ||
| - Check if SW is registered and active | ||
| 3. **Offline Testing**: | ||
| - Build and serve the app | ||
| - Open DevTools → Network → check "Offline" | ||
| - Refresh the page - it should still work | ||
| ### PWA Asset Replacement | ||
| ⚠️ **Important**: Replace the placeholder SVG icons with proper PNG icons: | ||
| 1. Replace these files in \`public/\` folder: | ||
| - \`pwa-192x192.svg\` → \`pwa-192x192.png\` | ||
| - \`pwa-512x512.svg\` → \`pwa-512x512.png\` | ||
| - \`apple-touch-icon.svg\` → \`apple-touch-icon.png\` | ||
| - \`favicon.svg\` → \`favicon.ico\` | ||
| 2. Use tools like: | ||
| - [PWA Asset Generator](https://www.pwabuilder.com/) | ||
| - [Favicon Generator](https://www.favicon-generator.org/) | ||
| - [App Icon Generator](https://appicon.co/) | ||
| ### PWA Checklist | ||
| - ✅ Web App Manifest configured | ||
| - ✅ Service Worker registered | ||
| - ✅ HTTPS ready (required for PWA) | ||
| - ✅ Responsive design | ||
| - ⚠️ Replace placeholder icons with real ones | ||
| - ⚠️ Test on actual devices | ||
| - ⚠️ Test offline functionality | ||
| ` : ''}## 📁 Project Structure | ||
| \`\`\` | ||
| ${projectName}/ | ||
| ├── public/ | ||
| ${isPWA ? `│ ├── pwa-192x192.svg # Replace with PNG | ||
| │ ├── pwa-512x512.svg # Replace with PNG | ||
| │ └── apple-touch-icon.svg # Replace with PNG | ||
| ` : ''}├── src/ | ||
| │ ├── components/ # Reusable components | ||
| │ ├── pages/ # Page components | ||
| │ ├── hooks/ # Custom React hooks | ||
| ${isPWA ? `│ │ └── usePWA.js # PWA functionality hook | ||
| ` : ''}│ ├── store/ # State management | ||
| │ ├── utils/ # Utility functions | ||
| ${packages.includes('axios') ? `│ │ └── axiosInstance.js # Axios configuration | ||
| ` : ''}│ ├── assets/ # Static assets | ||
| │ ├── App.jsx # Main App component | ||
| │ └── main.jsx # Entry point | ||
| ├── vite.config.js # Vite configuration | ||
| └── package.json | ||
| \`\`\` | ||
| ## 🎨 Styling | ||
| This project uses **${cssFramework}** for styling: | ||
| ${cssFramework === 'Tailwind' ? `- Classes are available globally | ||
| - Configuration in \`vite.config.js\` | ||
| - Customize in \`src/index.css\`` : | ||
| cssFramework === 'Bootstrap (CDN)' ? `- Bootstrap 5.3.3 loaded via CDN | ||
| - All Bootstrap classes available globally | ||
| - No additional configuration needed` : | ||
| cssFramework === 'React Bootstrap' ? `- React Bootstrap components installed | ||
| - Import components: \`import { Button, Container } from 'react-bootstrap'\` | ||
| - Bootstrap CSS included automatically` : | ||
| cssFramework === 'MUI' ? `- Material-UI components installed | ||
| - Import components: \`import { Button, Container } from '@mui/material'\` | ||
| - Emotion for CSS-in-JS styling` : ''} | ||
| ${packages.includes('axios') ? `## 🌐 API Integration | ||
| Axios is pre-configured in \`src/utils/axiosInstance.js\`: | ||
| \`\`\`javascript | ||
| import { api } from './utils/axiosInstance'; | ||
| // GET request | ||
| const data = await api.get('/users'); | ||
| // POST request | ||
| const response = await api.post('/users', { name: 'John' }); | ||
| \`\`\` | ||
| ### Environment Variables | ||
| Create a \`.env\` file: | ||
| \`\`\` | ||
| VITE_API_URL=https://your-api-url.com | ||
| \`\`\` | ||
| ` : ''}## 🔧 Available Scripts | ||
| - \`npm run dev\` - Start development server | ||
| - \`npm run build\` - Build for production | ||
| - \`npm run preview\` - Preview production build | ||
| - \`npm run lint\` - Run ESLint (if configured) | ||
| ## 🚀 Deployment | ||
| ### Vercel | ||
| \`\`\`bash | ||
| npm install -g vercel | ||
| vercel --prod | ||
| \`\`\` | ||
| ### Netlify | ||
| \`\`\`bash | ||
| npm run build | ||
| # Upload dist/ folder to Netlify | ||
| \`\`\` | ||
| ${isPWA ? `### PWA Deployment Checklist | ||
| - ✅ Build with \`npm run build\` | ||
| - ✅ Serve over HTTPS | ||
| - ✅ Test service worker registration | ||
| - ✅ Verify manifest.json is accessible | ||
| - ✅ Test install prompt on mobile/desktop | ||
| - ✅ Replace placeholder icons with real ones | ||
| ` : ''}## 🎯 Next Steps | ||
| ${isPWA ? `1. **Replace PWA Icons**: Replace SVG placeholders with proper PNG icons | ||
| 2. **Test PWA Features**: Test installation and offline functionality | ||
| 3. **Customize Caching**: Modify caching strategy in vite.config.js | ||
| 4. **Add Components**: Start building your app components | ||
| 5. **Configure API**: Set up your API endpoints | ||
| 6. **Deploy**: Deploy to a PWA-compatible hosting service` : `1. **Add Components**: Start building your app components | ||
| 2. **Set up Routing**: Add more routes in main.jsx | ||
| 3. **Configure API**: Set up your API endpoints if using Axios | ||
| 4. **Add State Management**: Implement Redux/Zustand if needed | ||
| 5. **Deploy**: Deploy to your preferred hosting service`} | ||
| --- | ||
| Built using React + Vite${isPWA ? ' + PWA' : ''} | ||
| `; | ||
| writeFile(path.join(projectPath, "README.md"), readmeContent); | ||
| }; |
+34
| import { execSync } from "child_process"; | ||
| import fs from "fs"; | ||
| import path from "path"; | ||
| export const run = (cmd, cwd = process.cwd()) => { | ||
| console.log(`\n📦 Running: ${cmd}`); | ||
| execSync(cmd, { stdio: "inherit", cwd }); | ||
| }; | ||
| export const writeFile = (filePath, content) => { | ||
| const dir = path.dirname(filePath); | ||
| if (!fs.existsSync(dir)) { | ||
| fs.mkdirSync(dir, { recursive: true }); | ||
| } | ||
| fs.writeFileSync(filePath, content); | ||
| }; | ||
| export const readFile = (filePath) => { | ||
| return fs.readFileSync(filePath, "utf-8"); | ||
| }; | ||
| export const fileExists = (filePath) => { | ||
| return fs.existsSync(filePath); | ||
| }; | ||
| export const createFolder = (folderPath) => { | ||
| fs.mkdirSync(folderPath, { recursive: true }); | ||
| }; | ||
| export const deleteFile = (filePath) => { | ||
| if (fs.existsSync(filePath)) { | ||
| fs.unlinkSync(filePath); | ||
| } | ||
| }; |
+33
| # Security Policy | ||
| ## Reporting a Vulnerability | ||
| If you discover a security vulnerability in this project, please report it responsibly. | ||
| We appreciate your help in making this project more secure. | ||
| Please follow these steps: | ||
| 1. Email the maintainer at [hgupta42774@gmail.com](mailto:hgupta42774@gmail.com) with details. | ||
| 2. Do **not** open a public issue until the vulnerability is confirmed and fixed. | ||
| 3. Provide as much information as possible to help us understand and reproduce the issue. | ||
| We will acknowledge receipt of your report within 2 business days. | ||
| We aim to investigate and respond with an initial assessment within 7 business days. | ||
| ## Supported Versions | ||
| We currently support the latest version of the project. | ||
| Please ensure you're using the latest release before reporting a vulnerability. | ||
| | Version | Supported | | ||
| |---------|-----------| | ||
| | latest | ✅ | | ||
| Older versions are not actively maintained unless critical. | ||
| ## Disclosure Policy | ||
| Once a vulnerability is confirmed and a fix is available, | ||
| we will disclose it in a responsible and transparent manner. | ||
| We may credit the reporter if desired. | ||
| ## Contact | ||
| Email: [hgupta42774@gmail.com](mailto:hgupta42774@gmail.com) | ||
| GitHub: [@harshgupta20](https://github.com/harshgupta20) |
+83
-215
| #!/usr/bin/env node | ||
| import inquirer from "inquirer"; | ||
| import { execSync } from "child_process"; | ||
| import path from "path"; | ||
| import fs from "fs"; | ||
| import { run, createFolder, deleteFile } from './lib/utils.js'; | ||
| import { initializePWA } from './lib/pwa.js'; | ||
| import { setupCSSFramework } from './lib/css-frameworks.js'; | ||
| import { createAxiosSetup, createAppComponent, createPWAReadme } from './lib/templates.js'; | ||
| import { setupRoutingFramework } from "./lib/router-setup.js"; | ||
| const run = (cmd, cwd = process.cwd()) => { | ||
| console.log(`\n📦 Running: ${cmd}`); | ||
| execSync(cmd, { stdio: "inherit", cwd }); | ||
| }; | ||
| (async () => { | ||
| // 1. Ask project name | ||
| const { projectName } = await inquirer.prompt([ | ||
| { type: "input", name: "projectName", message: "Enter project name:" } | ||
| ]); | ||
| // 2. Ask for CSS framework | ||
| const { cssFramework } = await inquirer.prompt([ | ||
| // 1. Collect user inputs | ||
| const answers = await inquirer.prompt([ | ||
| { | ||
| type: "input", | ||
| name: "projectName", | ||
| message: "Enter project name:" | ||
| }, | ||
| { | ||
@@ -25,14 +22,17 @@ type: "list", | ||
| message: "Choose a CSS framework:", | ||
| choices: [ | ||
| "Tailwind", | ||
| "Bootstrap (CDN)", | ||
| "React Bootstrap", | ||
| "MUI" | ||
| ] | ||
| } | ||
| ]); | ||
| // 3. Ask optional packages | ||
| const { packages } = await inquirer.prompt([ | ||
| choices: ["Tailwind", "Bootstrap (CDN)", "React Bootstrap", "MUI"] | ||
| }, | ||
| { | ||
| type: "list", | ||
| name: "routingFramework", | ||
| message: "Choose a routing framework:", | ||
| choices: ["React Router", "Tanstack Router",] | ||
| }, | ||
| { | ||
| type: "confirm", | ||
| name: "isPWA", | ||
| message: "Do you want to make this a Progressive Web App (PWA)?", | ||
| default: false | ||
| }, | ||
| { | ||
| type: "checkbox", | ||
@@ -52,212 +52,80 @@ name: "packages", | ||
| // 4. Create Vite + React project | ||
| run(`npm create vite@latest ${projectName} -- --template react`); | ||
| const { projectName, cssFramework, routingFramework, isPWA, packages } = answers; | ||
| const projectPath = path.join(process.cwd(), projectName); | ||
| // 5. Install chosen CSS framework | ||
| if (cssFramework === "Tailwind") { | ||
| run(`npm install tailwindcss @tailwindcss/vite`, projectPath); | ||
| console.log(`\n🚀 Creating ${projectName}${isPWA ? ' with PWA capabilities' : ''}...`); | ||
| const viteConfigPath = path.join(projectPath, "vite.config.js"); | ||
| let viteConfig = fs.readFileSync(viteConfigPath, "utf-8"); | ||
| viteConfig = `import tailwindcss from '@tailwindcss/vite'\n` + viteConfig; | ||
| viteConfig = viteConfig.replace(/plugins:\s*\[/, "plugins: [\n tailwindcss(),"); | ||
| fs.writeFileSync(viteConfigPath, viteConfig); | ||
| // 2. Create Vite project | ||
| run(`npm create vite@latest ${projectName} -- --template react`); | ||
| fs.writeFileSync(path.join(projectPath, "src", "index.css"), `@import "tailwindcss";\n`); | ||
| // 3. Create all necessary folder structure first | ||
| const folders = ["components", "pages", "hooks", "store", "utils", "assets"]; | ||
| const mainFile = fs.existsSync(path.join(projectPath, "src/main.jsx")) | ||
| ? "src/main.jsx" | ||
| : "src/main.tsx"; | ||
| const mainPath = path.join(projectPath, mainFile); | ||
| let mainContent = fs.readFileSync(mainPath, "utf-8"); | ||
| mainContent = mainContent.replace(/import\s+['"]\.\/index\.css['"];?/g, ""); | ||
| if (!mainContent.includes(`import './index.css'`)) { | ||
| mainContent = `import './index.css';\n` + mainContent; | ||
| // Create the routes folder (for tanstack router) and the necessary packages for Router setup | ||
| const routingConfig = { | ||
| "Tanstack Router": { | ||
| folders: ["routes"], | ||
| packages: ["@tanstack/react-router", "@tanstack/react-router-devtools"], | ||
| devPackages: ["@tanstack/router-plugin"] | ||
| }, | ||
| "React Router": { | ||
| folders: [], | ||
| packages: ["react-router"], | ||
| devPackages: [] | ||
| } | ||
| fs.writeFileSync(mainPath, mainContent); | ||
| }; | ||
| const config = routingConfig[routingFramework] || { folders: [], packages: [], devPackages: [] }; | ||
| folders.push(...config.folders); | ||
| const routingPackages = config.packages; | ||
| folders.forEach((folder) => { | ||
| createFolder(path.join(projectPath, "src", folder)); | ||
| }); | ||
| } else if (cssFramework === "Bootstrap (CDN)") { | ||
| const indexHtmlPath = path.join(projectPath, "index.html"); | ||
| let indexHtml = fs.readFileSync(indexHtmlPath, "utf-8"); | ||
| indexHtml = indexHtml.replace( | ||
| /<head>/, | ||
| `<head>\n <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">\n <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>` | ||
| ); | ||
| fs.writeFileSync(indexHtmlPath, indexHtml); | ||
| } else if (cssFramework === "React Bootstrap") { | ||
| run(`npm install react-bootstrap bootstrap`, projectPath); | ||
| const mainFile = fs.existsSync(path.join(projectPath, "src/main.jsx")) | ||
| ? "src/main.jsx" | ||
| : "src/main.tsx"; | ||
| const mainPath = path.join(projectPath, mainFile); | ||
| let mainContent = fs.readFileSync(mainPath, "utf-8"); | ||
| mainContent = mainContent | ||
| .replace(/import\s+['"]\.\/index\.css['"];?/g, "") | ||
| .replace(/import\s+['"]\.\/App\.css['"];?/g, ""); | ||
| mainContent = `import 'bootstrap/dist/css/bootstrap.min.css';\n` + mainContent; | ||
| fs.writeFileSync(mainPath, mainContent); | ||
| } else if (cssFramework === "MUI") { | ||
| run(`npm install @mui/material @emotion/react @emotion/styled`, projectPath); | ||
| const mainFile = fs.existsSync(path.join(projectPath, "src/main.jsx")) | ||
| ? "src/main.jsx" | ||
| : "src/main.tsx"; | ||
| const mainPath = path.join(projectPath, mainFile); | ||
| let mainContent = fs.readFileSync(mainPath, "utf-8"); | ||
| mainContent = mainContent | ||
| .replace(/import\s+['"]\.\/index\.css['"];?/g, "") | ||
| .replace(/import\s+['"]\.\/App\.css['"];?/g, ""); | ||
| fs.writeFileSync(mainPath, mainContent); | ||
| } | ||
| // 6. Install default + optional packages | ||
| const defaultPackages = ["react-router-dom"]; | ||
| const allPackages = [...defaultPackages, ...packages]; | ||
| // 4. Install packages | ||
| const allPackages = [...routingPackages, ...packages]; | ||
| if (allPackages.length > 0) { | ||
| run(`npm install ${allPackages.join(" ")}`, projectPath); | ||
| } | ||
| // 7. Create folder structure | ||
| const folders = ["components", "pages", "hooks", "store", "utils", "assets"]; | ||
| folders.forEach((folder) => { | ||
| fs.mkdirSync(path.join(projectPath, "src", folder), { recursive: true }); | ||
| }); | ||
| // 8. Axios setup if chosen | ||
| if (packages.includes("axios")) { | ||
| const axiosContent = `import axios from "axios"; | ||
| export const api = axios.create({ | ||
| baseURL: import.meta.env.VITE_API_URL || "http://localhost:5000", | ||
| headers: { "Content-Type": "application/json" }, | ||
| timeout: 10000 | ||
| }); | ||
| // ✅ Request Interceptor | ||
| api.interceptors.request.use( | ||
| (config) => { | ||
| // Example: Add token if available | ||
| const token = localStorage.getItem("token"); | ||
| if (token) { | ||
| config.headers.Authorization = \`Bearer \${token}\`; | ||
| if (config.devPackages.length > 0) { | ||
| run(`npm i -D ${config.devPackages.join(" ")}`, projectPath); | ||
| } | ||
| return config; | ||
| }, | ||
| (error) => { | ||
| return Promise.reject(error); | ||
| } | ||
| ); | ||
| // ✅ Response Interceptor | ||
| api.interceptors.response.use( | ||
| (response) => { | ||
| return response.data; // Return only data for convenience | ||
| }, | ||
| (error) => { | ||
| if (error.response) { | ||
| console.error("API Error:", error.response.data?.message || error.message); | ||
| // Example: Handle unauthorized | ||
| if (error.response.status === 401) { | ||
| // Optionally redirect to login | ||
| window.location.href = "/login"; | ||
| } | ||
| } else if (error.request) { | ||
| console.error("No response received from server."); | ||
| } else { | ||
| console.error("Request setup error:", error.message); | ||
| } | ||
| return Promise.reject(error); | ||
| } | ||
| ); | ||
| `; | ||
| fs.writeFileSync(path.join(projectPath, "src", "utils", "axiosInstance.js"), axiosContent); | ||
| // 5. Setup PWA if selected (after folder structure is created) | ||
| if (isPWA) { | ||
| initializePWA(projectPath, projectName); | ||
| } | ||
| // 9. Clean up default CSS files (centralized) | ||
| const appCssPath = path.join(projectPath, "src", "App.css"); | ||
| if (fs.existsSync(appCssPath)) fs.unlinkSync(appCssPath); | ||
| // 6. Setup CSS framework | ||
| setupCSSFramework(cssFramework, projectPath); | ||
| const indexCssPath = path.join(projectPath, "src", "index.css"); | ||
| if (cssFramework !== "Tailwind" && fs.existsSync(indexCssPath)) { | ||
| fs.unlinkSync(indexCssPath); | ||
| // 7. Setup Axios if selected | ||
| if (packages.includes("axios")) { | ||
| createAxiosSetup(projectPath); | ||
| } | ||
| // 10. Replace App.jsx content | ||
| const appFile = fs.existsSync(path.join(projectPath, "src/App.jsx")) | ||
| ? path.join(projectPath, "src/App.jsx") | ||
| : path.join(projectPath, "src/App.tsx"); | ||
| let appContent = `export default function App() { | ||
| return ( | ||
| <div | ||
| style={{ | ||
| display: "flex", | ||
| flexDirection: "column", | ||
| justifyContent: "center", | ||
| alignItems: "center", | ||
| height: "100vh", | ||
| fontFamily: "sans-serif", | ||
| background: "#f9fafb", | ||
| color: "#111", | ||
| textAlign: "center", | ||
| }} | ||
| > | ||
| <h1 | ||
| style={{ | ||
| fontSize: "2.5rem", | ||
| marginBottom: "0.5rem", | ||
| fontWeight: 600, | ||
| }} | ||
| > | ||
| Welcome to{" "} | ||
| <span style={{ color: "#2563eb" }}>${projectName}</span> 🚀 | ||
| </h1> | ||
| <p style={{ fontSize: "1.1rem", color: "#555" }}> | ||
| Your project is ready. Start building amazing things! | ||
| </p> | ||
| </div> | ||
| ); | ||
| }`; | ||
| fs.writeFileSync(appFile, appContent); | ||
| // 11. Default Router setup in main.jsx | ||
| const mainFile = fs.existsSync(path.join(projectPath, "src/main.jsx")) | ||
| ? "src/main.jsx" | ||
| : "src/main.tsx"; | ||
| const mainPath = path.join(projectPath, mainFile); | ||
| let cssImports = ""; | ||
| if (cssFramework === "React Bootstrap") { | ||
| cssImports = `import 'bootstrap/dist/css/bootstrap.min.css';\n`; | ||
| } else if (cssFramework === "Tailwind") { | ||
| cssImports = `import './index.css';\n`; | ||
| } else if (cssFramework === "Bootstrap (CDN)") { | ||
| cssImports = ""; // CDN already added in index.html | ||
| } else if (cssFramework === "MUI") { | ||
| cssImports = ""; // no CSS import needed | ||
| // 8. Clean up default boilerplate files | ||
| deleteFile(path.join(projectPath, "src", "App.css")); | ||
| if (cssFramework !== "Tailwind") { | ||
| deleteFile(path.join(projectPath, "src", "index.css")); | ||
| } | ||
| const routerSetup = `${cssImports}import React from 'react'; | ||
| import ReactDOM from 'react-dom/client'; | ||
| import { BrowserRouter, Routes, Route } from 'react-router-dom'; | ||
| import App from './App'; | ||
| // 9. Generate clean templates | ||
| createAppComponent(projectPath, projectName, isPWA); | ||
| setupRoutingFramework(projectPath, routingFramework, cssFramework); | ||
| // 10. Create comprehensive README | ||
| createPWAReadme(projectPath, projectName, cssFramework, packages, isPWA); | ||
| ReactDOM.createRoot(document.getElementById('root')).render( | ||
| <React.StrictMode> | ||
| <BrowserRouter> | ||
| <Routes> | ||
| <Route path="/" element={<App />} /> | ||
| </Routes> | ||
| </BrowserRouter> | ||
| </React.StrictMode> | ||
| );`; | ||
| fs.writeFileSync(mainPath, routerSetup); | ||
| // 11. Success message | ||
| console.log("\n✅ Setup complete!"); | ||
| console.log(`\nNext steps:\n cd ${projectName}\n npm run dev`); | ||
| if (isPWA) { | ||
| console.log("📱 PWA features enabled - your app can be installed on mobile devices!"); | ||
| console.log("⚠️ Important: Replace placeholder SVG icons with proper PNG icons for production"); | ||
| } | ||
| console.log(`\nNext steps:\n cd ${projectName}\n npm install\n npm run dev`); | ||
| if (isPWA) { | ||
| console.log(`\n📱 To test PWA:\n npm run build\n npm run preview\n Open http://localhost:5173 and test install/offline features`); | ||
| } | ||
| })(); |
+2
-2
| { | ||
| "name": "quickstart-react", | ||
| "version": "1.1.2", | ||
| "version": "1.2.0", | ||
| "description": "A CLI tool to quickly scaffold a React + Vite project with optional CSS frameworks and useful packages, ready to use out of the box.", | ||
@@ -28,3 +28,3 @@ "main": "index.js", | ||
| ], | ||
| "author": "Your Name", | ||
| "author": "harshgupta20", | ||
| "license": "MIT", | ||
@@ -31,0 +31,0 @@ "repository": { |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
50704
193.68%16
166.67%844
265.37%9
200%2
100%