New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

quickstart-react

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

quickstart-react - npm Package Compare versions

Comparing version
1.1.2
to
1.2.0
+23
.github/ISSUE_TEMPLATE/bug_report.md
---
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();
}
};
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;
};
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);
};
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);
}
};
# 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`);
}
})();
{
"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": {