@nishant0121/set-it-up 0.0.2 β 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -37
- package/package.json +1 -1
- package/src/engines/react.js +210 -0
- package/src/templates/react/components.js +47 -0
- package/src/templates/react/main.js +105 -0
- package/src/templates/react/pages.js +44 -0
- package/src/utils/checkEnv.js +6 -1
- package/src/wizard.js +44 -3
- package/test_inquirer.js +0 -16
package/README.md
CHANGED
|
@@ -1,66 +1,75 @@
|
|
|
1
|
-
# set-it-up
|
|
1
|
+
# set-it-up
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
set-it-up is a high-performance CLI tool designed to bootstrap and forge new projects with pre-configured templates, prerequisite checking, and interactive setup.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
7
|
+
- Interactive Wizard: Professional terminal interface to guide you through project configuration.
|
|
8
|
+
- Prerequisite Checks: Automatically verifies necessary tools (Node.js, Git, etc.) are installed before starting.
|
|
9
|
+
- React Support (Vite):
|
|
10
|
+
- Fast project scaffolding using Vite.
|
|
11
|
+
- Automated Tailwind CSS v4 setup.
|
|
12
|
+
- Standardized project structure (src/pages, src/components).
|
|
13
|
+
- Integrated Layout and Navbar with React Router DOM support.
|
|
14
|
+
- Optional Shadcn UI initialization and component addition.
|
|
15
|
+
- Global AppContext setup for state management.
|
|
16
|
+
- Automatic TypeScript to JavaScript conversion for Shadcn UI if JS is selected.
|
|
17
|
+
- React Native Support:
|
|
18
|
+
- Rapid initialization with @react-native-community/cli.
|
|
19
|
+
- Full support for both TypeScript and JavaScript.
|
|
20
|
+
- Automated React Navigation integration with boilerplate code.
|
|
21
|
+
- Smart Defaults: Supports npm, yarn, and pnpm based on user preference.
|
|
14
22
|
|
|
15
|
-
##
|
|
23
|
+
## Installation
|
|
16
24
|
|
|
17
|
-
To use
|
|
25
|
+
To use set-it-up globally on your system:
|
|
18
26
|
|
|
19
27
|
```bash
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
npm install -g @nishant0121/set-it-up
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Using npx:
|
|
22
32
|
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
```bash
|
|
34
|
+
npx @nishant0121/set-it-up
|
|
25
35
|
```
|
|
26
36
|
|
|
27
|
-
##
|
|
37
|
+
## Usage
|
|
28
38
|
|
|
29
|
-
|
|
39
|
+
Run the setup command in your terminal:
|
|
30
40
|
|
|
31
41
|
```bash
|
|
32
42
|
setup
|
|
33
43
|
```
|
|
34
44
|
|
|
35
|
-
Follow the
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
45
|
+
Follow the prompts to:
|
|
46
|
+
|
|
47
|
+
1. Select your project type (React or React Native).
|
|
48
|
+
2. Provide a project name.
|
|
49
|
+
3. Choose your preferred package manager.
|
|
50
|
+
4. Select language (TypeScript or JavaScript).
|
|
51
|
+
5. Configure additional features (Shadcn UI, Router, Context, Navigation).
|
|
40
52
|
|
|
41
53
|
### Example Workflow
|
|
42
54
|
|
|
43
55
|
```text
|
|
44
|
-
|
|
56
|
+
SET-IT-UP
|
|
45
57
|
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
Hi there! Let's configure your new project.
|
|
59
|
+
|
|
60
|
+
? What do you want to build today? React
|
|
61
|
+
? Enter your project name: my-web-app
|
|
48
62
|
? Select your preferred package manager: npm
|
|
49
63
|
? Which language do you want to use? TypeScript
|
|
50
|
-
? Would you like to
|
|
64
|
+
? Would you like to setup Shadcn UI? Yes
|
|
65
|
+
? Would you like to add React Router DOM? Yes
|
|
66
|
+
? Would you like to setup a global AppContext? Yes
|
|
51
67
|
```
|
|
52
68
|
|
|
53
|
-
##
|
|
54
|
-
|
|
55
|
-
- [x] React Native (JS/TS + Navigation)
|
|
56
|
-
- [ ] Next.js Support
|
|
57
|
-
- [ ] Custom GitHub Templates
|
|
58
|
-
- [ ] Backend Templates (Express, NestJS)
|
|
59
|
-
|
|
60
|
-
## π€ Contributing
|
|
69
|
+
## Contributing
|
|
61
70
|
|
|
62
|
-
Contributions are welcome
|
|
71
|
+
Contributions are welcome. Please open an issue or submit a pull request for improvements.
|
|
63
72
|
|
|
64
|
-
##
|
|
73
|
+
## License
|
|
65
74
|
|
|
66
75
|
ISC
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nishant0121/set-it-up",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "A high-performance CLI tool to bootstrap and forge new projects with pre-configured templates, prerequisite checking, and interactive setup.",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "Nishant Patil",
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
// Import templates
|
|
8
|
+
import { getNavbar, getLayout } from '../templates/react/components.js';
|
|
9
|
+
import { getHome, getAbout } from '../templates/react/pages.js';
|
|
10
|
+
import { getApp, getAppContext, getViteConfig } from '../templates/react/main.js';
|
|
11
|
+
|
|
12
|
+
export async function setupReact(answers, reactAnswers) {
|
|
13
|
+
const spinner = ora('Initializing React project with Vite...').start();
|
|
14
|
+
try {
|
|
15
|
+
const { projectName, packageManager } = answers;
|
|
16
|
+
const isTypescript = reactAnswers.language === 'TypeScript';
|
|
17
|
+
// Force TypeScript template if Shadcn is selected, otherwise use user choice
|
|
18
|
+
const template = (isTypescript || reactAnswers.addShadcn) ? 'react-ts' : 'react';
|
|
19
|
+
|
|
20
|
+
// 1. Scaffold with Vite
|
|
21
|
+
await execa('npm', ['create', 'vite@latest', projectName, '--', '--template', template]);
|
|
22
|
+
|
|
23
|
+
spinner.text = 'Installing dependencies...';
|
|
24
|
+
// 2. Install Dependencies
|
|
25
|
+
await execa(packageManager, ['install'], { cwd: projectName });
|
|
26
|
+
|
|
27
|
+
// Install Node types for path resolution if TypeScript OR if we forced TS for Shadcn
|
|
28
|
+
if (isTypescript || reactAnswers.addShadcn) {
|
|
29
|
+
await execa(packageManager, [packageManager === 'npm' ? 'install' : 'add', '-D', '@types/node'], { cwd: projectName });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
spinner.text = 'Setting up Tailwind CSS v4...';
|
|
33
|
+
// 3. Install Tailwind CSS & Vite Plugin
|
|
34
|
+
const installCmd = packageManager === 'npm' ? 'install' : 'add';
|
|
35
|
+
await execa(packageManager, [installCmd, 'tailwindcss', '@tailwindcss/vite'], { cwd: projectName });
|
|
36
|
+
|
|
37
|
+
// 4. Configure TSConfig for aliases (if TypeScript OR Shadcn forced TS)
|
|
38
|
+
if (isTypescript || reactAnswers.addShadcn) {
|
|
39
|
+
const tsConfigPath = path.join(projectName, 'tsconfig.json');
|
|
40
|
+
const tsConfigAppPath = path.join(projectName, 'tsconfig.app.json');
|
|
41
|
+
|
|
42
|
+
const readJsonSafe = async (filePath) => {
|
|
43
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
44
|
+
// Simple regex to strip comments (// and /* */)
|
|
45
|
+
const jsonContent = content.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
46
|
+
return JSON.parse(jsonContent);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Update tsconfig.json
|
|
50
|
+
if (await fs.pathExists(tsConfigPath)) {
|
|
51
|
+
try {
|
|
52
|
+
const tsConfig = await readJsonSafe(tsConfigPath);
|
|
53
|
+
tsConfig.compilerOptions = tsConfig.compilerOptions || {};
|
|
54
|
+
tsConfig.compilerOptions.baseUrl = ".";
|
|
55
|
+
tsConfig.compilerOptions.paths = tsConfig.compilerOptions.paths || {};
|
|
56
|
+
tsConfig.compilerOptions.paths["@/*"] = ["./src/*"];
|
|
57
|
+
|
|
58
|
+
await fs.writeJson(tsConfigPath, tsConfig, { spaces: 2 });
|
|
59
|
+
} catch (e) {
|
|
60
|
+
console.warn('Could not update tsconfig.json automatically. Please add baseUrl and paths manually.');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Update tsconfig.app.json
|
|
65
|
+
if (await fs.pathExists(tsConfigAppPath)) {
|
|
66
|
+
try {
|
|
67
|
+
const tsConfigApp = await readJsonSafe(tsConfigAppPath);
|
|
68
|
+
tsConfigApp.compilerOptions = tsConfigApp.compilerOptions || {};
|
|
69
|
+
tsConfigApp.compilerOptions.baseUrl = ".";
|
|
70
|
+
tsConfigApp.compilerOptions.paths = tsConfigApp.compilerOptions.paths || {};
|
|
71
|
+
tsConfigApp.compilerOptions.paths["@/*"] = ["./src/*"];
|
|
72
|
+
|
|
73
|
+
await fs.writeJson(tsConfigAppPath, tsConfigApp, { spaces: 2 });
|
|
74
|
+
} catch (e) {
|
|
75
|
+
console.warn('Could not update tsconfig.app.json automatically.');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 5. Configure Vite (vite.config.js/ts)
|
|
81
|
+
const viteConfigContent = getViteConfig();
|
|
82
|
+
const viteConfigPath = path.join(projectName, (isTypescript || reactAnswers.addShadcn) ? 'vite.config.ts' : 'vite.config.js');
|
|
83
|
+
await fs.writeFile(viteConfigPath, viteConfigContent);
|
|
84
|
+
|
|
85
|
+
// 6. Configure CSS (src/index.css)
|
|
86
|
+
const indexCssPath = path.join(projectName, 'src', 'index.css');
|
|
87
|
+
await fs.writeFile(indexCssPath, '@import "tailwindcss";');
|
|
88
|
+
|
|
89
|
+
// Handle Optional Features
|
|
90
|
+
if (reactAnswers.addShadcn) {
|
|
91
|
+
spinner.stop();
|
|
92
|
+
console.log(chalk.blue('\nπ¨ Initializing Shadcn UI...'));
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
await execa('npx', ['shadcn@latest', 'init', '-d'], {
|
|
96
|
+
cwd: projectName,
|
|
97
|
+
stdio: 'inherit'
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
console.log(chalk.green('β Shadcn UI initialized.'));
|
|
101
|
+
|
|
102
|
+
// Add a button component as requested
|
|
103
|
+
console.log(chalk.blue('Adding Button component...'));
|
|
104
|
+
await execa('npx', ['shadcn@latest', 'add', 'button', '-y'], {
|
|
105
|
+
cwd: projectName,
|
|
106
|
+
stdio: 'inherit'
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
} catch (e) {
|
|
110
|
+
console.warn(chalk.yellow('β Shadcn UI setup encountered an issue. You might need to run "npx shadcn@latest init" manually.'));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
spinner.start();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (reactAnswers.addRouter) {
|
|
117
|
+
spinner.text = 'Installing React Router DOM...';
|
|
118
|
+
await execa(packageManager, [installCmd, 'react-router-dom'], { cwd: projectName });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (reactAnswers.addContext) {
|
|
122
|
+
spinner.text = 'Creating AppContext...';
|
|
123
|
+
const contextDir = path.join(projectName, 'src', 'context');
|
|
124
|
+
await fs.ensureDir(contextDir);
|
|
125
|
+
|
|
126
|
+
const contextFile = (isTypescript || reactAnswers.addShadcn) ? 'AppContext.tsx' : 'AppContext.jsx';
|
|
127
|
+
const contextContent = getAppContext({ isTypescript, addShadcn: reactAnswers.addShadcn });
|
|
128
|
+
await fs.writeFile(path.join(contextDir, contextFile), contextContent);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Rewrite App.jsx/tsx and create structure
|
|
132
|
+
spinner.text = 'Generating Project Structure...';
|
|
133
|
+
await generateProjectStructure(projectName, (isTypescript || reactAnswers.addShadcn), reactAnswers);
|
|
134
|
+
|
|
135
|
+
// FINAL STEP: Downgrade to JS if requested but forced to TS
|
|
136
|
+
if (!isTypescript && reactAnswers.addShadcn) {
|
|
137
|
+
spinner.text = 'Converting to JavaScript...';
|
|
138
|
+
await convertToJs(projectName);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
spinner.succeed(chalk.green(`Project ${projectName} created successfully! π`));
|
|
142
|
+
console.log(chalk.cyan(`
|
|
143
|
+
To get started:
|
|
144
|
+
cd ${projectName}
|
|
145
|
+
${packageManager} run dev`));
|
|
146
|
+
|
|
147
|
+
} catch (error) {
|
|
148
|
+
spinner.fail('Setup failed.');
|
|
149
|
+
console.error(error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function generateProjectStructure(projectName, isTypescript, answers) {
|
|
154
|
+
const ext = isTypescript ? 'tsx' : 'jsx';
|
|
155
|
+
const componentsDir = path.join(projectName, 'src', 'components');
|
|
156
|
+
const pagesDir = path.join(projectName, 'src', 'pages');
|
|
157
|
+
|
|
158
|
+
await fs.ensureDir(componentsDir);
|
|
159
|
+
await fs.ensureDir(pagesDir);
|
|
160
|
+
|
|
161
|
+
// 1. Create Navbar
|
|
162
|
+
const navbarContent = getNavbar(answers);
|
|
163
|
+
await fs.writeFile(path.join(componentsDir, `Navbar.${ext}`), navbarContent);
|
|
164
|
+
|
|
165
|
+
// 2. Create Layout (with Outlet)
|
|
166
|
+
const layoutContent = getLayout(answers);
|
|
167
|
+
await fs.writeFile(path.join(componentsDir, `Layout.${ext}`), layoutContent);
|
|
168
|
+
|
|
169
|
+
// 3. Create Pages (Home & About)
|
|
170
|
+
const homeContent = getHome(answers);
|
|
171
|
+
await fs.writeFile(path.join(pagesDir, `Home.${ext}`), homeContent);
|
|
172
|
+
|
|
173
|
+
const aboutContent = getAbout();
|
|
174
|
+
await fs.writeFile(path.join(pagesDir, `About.${ext}`), aboutContent);
|
|
175
|
+
|
|
176
|
+
// 4. Update App.jsx/tsx
|
|
177
|
+
const finalAppContent = getApp(answers);
|
|
178
|
+
await fs.writeFile(path.join(projectName, 'src', `App.${ext}`), finalAppContent);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function convertToJs(projectName) {
|
|
182
|
+
const srcDir = path.join(projectName, 'src');
|
|
183
|
+
|
|
184
|
+
// Recursive function to rename .tsx -> .jsx and .ts -> .js
|
|
185
|
+
async function renameFiles(dir) {
|
|
186
|
+
const items = await fs.readdir(dir);
|
|
187
|
+
for (const item of items) {
|
|
188
|
+
const fullPath = path.join(dir, item);
|
|
189
|
+
const stat = await fs.stat(fullPath);
|
|
190
|
+
|
|
191
|
+
if (stat.isDirectory()) {
|
|
192
|
+
await renameFiles(fullPath);
|
|
193
|
+
} else {
|
|
194
|
+
if (item.endsWith('.tsx')) {
|
|
195
|
+
await fs.rename(fullPath, fullPath.replace('.tsx', '.jsx'));
|
|
196
|
+
} else if (item.endsWith('.ts') && !item.endsWith('.d.ts') && item !== 'vite.config.ts') {
|
|
197
|
+
// Careful not to rename config files if they are in root (this is in src)
|
|
198
|
+
await fs.rename(fullPath, fullPath.replace('.ts', '.js'));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
await renameFiles(srcDir);
|
|
205
|
+
|
|
206
|
+
// Also rename root files if necessary
|
|
207
|
+
const mainTsx = path.join(projectName, 'src', 'main.tsx'); // Standard Vite entry
|
|
208
|
+
const mainJsx = path.join(projectName, 'src', 'main.jsx');
|
|
209
|
+
if (await fs.pathExists(mainTsx)) await fs.rename(mainTsx, mainJsx);
|
|
210
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const getNavbar = (options) => {
|
|
2
|
+
const { addRouter, addContext } = options;
|
|
3
|
+
return `import React from 'react';
|
|
4
|
+
${addRouter ? `import { Link } from 'react-router-dom';` : ''}
|
|
5
|
+
${addContext ? `import { useAppContext } from '../context/AppContext';` : ''}
|
|
6
|
+
|
|
7
|
+
export function Navbar() {
|
|
8
|
+
${addContext ? `const { theme, toggleTheme } = useAppContext();` : ''}
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<nav className="p-4 bg-white dark:bg-gray-800 shadow-md flex justify-between items-center">
|
|
12
|
+
<h1 className="text-xl font-bold text-blue-600">My App</h1>
|
|
13
|
+
<div className="flex gap-4">
|
|
14
|
+
${addRouter ? `
|
|
15
|
+
<Link to="/" className="text-gray-700 dark:text-gray-200 hover:text-blue-500">Home</Link>
|
|
16
|
+
<Link to="/about" className="text-gray-700 dark:text-gray-200 hover:text-blue-500">About</Link>
|
|
17
|
+
` : ''}
|
|
18
|
+
${addContext ? `
|
|
19
|
+
<button onClick={toggleTheme} className="px-3 py-1 bg-gray-200 rounded text-sm">
|
|
20
|
+
{theme === 'light' ? 'π' : 'βοΈ'}
|
|
21
|
+
</button>
|
|
22
|
+
` : ''}
|
|
23
|
+
</div>
|
|
24
|
+
</nav>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const getLayout = (options) => {
|
|
31
|
+
const { addRouter } = options;
|
|
32
|
+
return `import React from 'react';
|
|
33
|
+
import { Navbar } from './Navbar';
|
|
34
|
+
${addRouter ? `import { Outlet } from 'react-router-dom';` : ''}
|
|
35
|
+
|
|
36
|
+
export function Layout() {
|
|
37
|
+
return (
|
|
38
|
+
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 transition-colors font-sans">
|
|
39
|
+
<Navbar />
|
|
40
|
+
<main className="container mx-auto p-4">
|
|
41
|
+
${addRouter ? `<Outlet />` : `<div className="text-center p-10">Outlet requires Router</div>`}
|
|
42
|
+
</main>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export const getApp = (options) => {
|
|
2
|
+
const { addRouter, addContext } = options;
|
|
3
|
+
let appImports = `import { Layout } from './components/Layout';
|
|
4
|
+
import Home from './pages/Home';
|
|
5
|
+
import About from './pages/About';
|
|
6
|
+
`;
|
|
7
|
+
|
|
8
|
+
if (addRouter) {
|
|
9
|
+
appImports += `import { BrowserRouter, Routes, Route } from 'react-router-dom';\n`;
|
|
10
|
+
}
|
|
11
|
+
if (addContext) {
|
|
12
|
+
appImports += `import { AppProvider } from './context/AppContext';\n`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let appContent = `
|
|
16
|
+
${addRouter ? `
|
|
17
|
+
<BrowserRouter>
|
|
18
|
+
<Routes>
|
|
19
|
+
<Route path="/" element={<Layout />}>
|
|
20
|
+
<Route index element={<Home />} />
|
|
21
|
+
<Route path="about" element={<About />} />
|
|
22
|
+
</Route>
|
|
23
|
+
</Routes>
|
|
24
|
+
</BrowserRouter>
|
|
25
|
+
` : `
|
|
26
|
+
<Layout>
|
|
27
|
+
<Home />
|
|
28
|
+
</Layout>
|
|
29
|
+
`}
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
if (addContext) {
|
|
33
|
+
appContent = `
|
|
34
|
+
<AppProvider>
|
|
35
|
+
${appContent}
|
|
36
|
+
</AppProvider>
|
|
37
|
+
`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return `
|
|
41
|
+
${appImports}
|
|
42
|
+
|
|
43
|
+
function App() {
|
|
44
|
+
return (
|
|
45
|
+
${appContent}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default App;
|
|
50
|
+
`;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const getAppContext = (options) => {
|
|
54
|
+
const { isTypescript, addShadcn } = options;
|
|
55
|
+
const useTs = isTypescript || addShadcn;
|
|
56
|
+
|
|
57
|
+
return `import React, { createContext, useContext, useState${useTs ? ', ReactNode' : ''} } from 'react';
|
|
58
|
+
|
|
59
|
+
${useTs ? 'interface AppContextType {\n theme: string;\n toggleTheme: () => void;\n}\n' : ''}
|
|
60
|
+
const AppContext = createContext${useTs ? '<AppContextType | undefined>(undefined)' : '()'};
|
|
61
|
+
|
|
62
|
+
export const AppProvider = ({ children }${useTs ? ': { children: ReactNode }' : ''}) => {
|
|
63
|
+
const [theme, setTheme] = useState('light');
|
|
64
|
+
|
|
65
|
+
const toggleTheme = () => {
|
|
66
|
+
setTheme(prev => prev === 'light' ? 'dark' : 'light');
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<AppContext.Provider value={{ theme, toggleTheme }}>
|
|
71
|
+
{children}
|
|
72
|
+
</AppContext.Provider>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const useAppContext = () => {
|
|
77
|
+
const context = useContext(AppContext);
|
|
78
|
+
if (!context) {
|
|
79
|
+
throw new Error('useAppContext must be used within an AppProvider');
|
|
80
|
+
}
|
|
81
|
+
return context;
|
|
82
|
+
};
|
|
83
|
+
`;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const getViteConfig = () => {
|
|
87
|
+
return `import path from "path"
|
|
88
|
+
import react from '@vitejs/plugin-react'
|
|
89
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
90
|
+
import { defineConfig } from 'vite'
|
|
91
|
+
|
|
92
|
+
// https://vitejs.dev/config/
|
|
93
|
+
export default defineConfig({
|
|
94
|
+
plugins: [
|
|
95
|
+
react(),
|
|
96
|
+
tailwindcss(),
|
|
97
|
+
],
|
|
98
|
+
resolve: {
|
|
99
|
+
alias: {
|
|
100
|
+
"@": path.resolve(__dirname, "./src"),
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
`;
|
|
105
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const getHome = (options) => {
|
|
2
|
+
const { addShadcn } = options;
|
|
3
|
+
return `import React from 'react';
|
|
4
|
+
${addShadcn ? `import { Button } from "@/components/ui/button"` : ''}
|
|
5
|
+
|
|
6
|
+
export default function Home() {
|
|
7
|
+
return (
|
|
8
|
+
<div className="flex flex-col items-center justify-center py-10">
|
|
9
|
+
<h1 className="text-4xl font-bold text-gray-800 dark:text-white mb-6">Welcome Home π </h1>
|
|
10
|
+
<p className="text-gray-600 dark:text-gray-300 mb-8 max-w-md text-center">
|
|
11
|
+
This is the starting point of your amazing application.
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<div className="flex gap-4">
|
|
15
|
+
<div className="px-4 py-2 bg-white rounded shadow text-green-600 font-bold">β React</div>
|
|
16
|
+
<div className="px-4 py-2 bg-white rounded shadow text-blue-600 font-bold">β Tailwind</div>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
${addShadcn ? `
|
|
20
|
+
<div className="mt-8">
|
|
21
|
+
<Button onClick={() => alert('Shadcn Button Works!')}>Shadcn Button</Button>
|
|
22
|
+
</div>
|
|
23
|
+
` : ''}
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const getAbout = () => {
|
|
31
|
+
return `import React from 'react';
|
|
32
|
+
|
|
33
|
+
export default function About() {
|
|
34
|
+
return (
|
|
35
|
+
<div className="text-center py-10">
|
|
36
|
+
<h1 className="text-3xl font-bold text-gray-800 dark:text-white">About Us</h1>
|
|
37
|
+
<p className="mt-4 text-gray-600 dark:text-gray-300">
|
|
38
|
+
We are building the future with modern web technologies.
|
|
39
|
+
</p>
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
};
|
package/src/utils/checkEnv.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import commandExists from 'command-exists';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
|
|
4
|
-
export async function checkPrerequisites(type) {
|
|
4
|
+
export async function checkPrerequisites(type, options = {}) {
|
|
5
5
|
const requirements = {
|
|
6
6
|
'React Native': ['node', 'git', 'npm', 'java'],
|
|
7
|
+
'React': ['node', 'git', 'npm'],
|
|
7
8
|
'Next.js': ['node', 'git']
|
|
8
9
|
};
|
|
9
10
|
|
|
11
|
+
if (type === 'React Native' && options.targetPlatform !== 'Android') {
|
|
12
|
+
requirements['React Native'].push('pod');
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
console.log(chalk.blue('\nπ Checking prerequisites...'));
|
|
11
16
|
|
|
12
17
|
for (const cmd of requirements[type]) {
|
package/src/wizard.js
CHANGED
|
@@ -4,6 +4,7 @@ import gradient from 'gradient-string';
|
|
|
4
4
|
import boxen from 'boxen';
|
|
5
5
|
import { checkPrerequisites } from './utils/checkEnv.js';
|
|
6
6
|
import { setupReactNative } from './engines/reactNative.js';
|
|
7
|
+
import { setupReact } from './engines/react.js';
|
|
7
8
|
|
|
8
9
|
export async function mainWizard() {
|
|
9
10
|
// Clear the console for a fresh start
|
|
@@ -38,7 +39,7 @@ export async function mainWizard() {
|
|
|
38
39
|
type: 'rawlist',
|
|
39
40
|
name: 'projectType',
|
|
40
41
|
message: 'What do you want to build today?',
|
|
41
|
-
choices: ['React Native', 'Next.js (Coming Soon)', 'Custom GitHub Template'],
|
|
42
|
+
choices: ['React', 'React Native', 'Next.js (Coming Soon)', 'Custom GitHub Template'],
|
|
42
43
|
},
|
|
43
44
|
{
|
|
44
45
|
type: 'input',
|
|
@@ -59,7 +60,16 @@ export async function mainWizard() {
|
|
|
59
60
|
]);
|
|
60
61
|
|
|
61
62
|
if (answers.projectType === 'React Native') {
|
|
62
|
-
await
|
|
63
|
+
const platformAnswer = await inquirer.prompt([
|
|
64
|
+
{
|
|
65
|
+
type: 'rawlist',
|
|
66
|
+
name: 'targetPlatform',
|
|
67
|
+
message: 'Which platform do you want to target?',
|
|
68
|
+
choices: ['Android', 'iOS', 'Both'],
|
|
69
|
+
}
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
await checkPrerequisites('React Native', { targetPlatform: platformAnswer.targetPlatform });
|
|
63
73
|
|
|
64
74
|
// Additional RN specific questions
|
|
65
75
|
const rnAnswers = await inquirer.prompt([
|
|
@@ -76,8 +86,39 @@ export async function mainWizard() {
|
|
|
76
86
|
}
|
|
77
87
|
]);
|
|
78
88
|
|
|
89
|
+
Object.assign(rnAnswers, platformAnswer);
|
|
90
|
+
|
|
79
91
|
await setupReactNative(answers, rnAnswers);
|
|
92
|
+
} else if (answers.projectType === 'React') {
|
|
93
|
+
await checkPrerequisites('React');
|
|
94
|
+
|
|
95
|
+
const reactAnswers = await inquirer.prompt([
|
|
96
|
+
{
|
|
97
|
+
type: 'rawlist',
|
|
98
|
+
name: 'language',
|
|
99
|
+
message: 'Which language do you want to use?',
|
|
100
|
+
choices: ['TypeScript', 'JavaScript'],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
type: 'confirm',
|
|
104
|
+
name: 'addShadcn',
|
|
105
|
+
message: 'Would you like to setup Shadcn UI (install utils & dependencies)?',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
type: 'confirm',
|
|
109
|
+
name: 'addRouter',
|
|
110
|
+
message: 'Would you like to add React Router DOM?',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
type: 'confirm',
|
|
114
|
+
name: 'addContext',
|
|
115
|
+
message: 'Would you like to setup a global AppContext?',
|
|
116
|
+
}
|
|
117
|
+
]);
|
|
118
|
+
|
|
119
|
+
await setupReact(answers, reactAnswers);
|
|
120
|
+
|
|
80
121
|
} else {
|
|
81
122
|
console.log(chalk.yellow('\nπ§ This feature is coming soon! Stay tuned.\n'));
|
|
82
123
|
}
|
|
83
|
-
}
|
|
124
|
+
}
|
package/test_inquirer.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import inquirer from 'inquirer';
|
|
2
|
-
|
|
3
|
-
console.log('Testing inquirer...');
|
|
4
|
-
try {
|
|
5
|
-
const answers = await inquirer.prompt([
|
|
6
|
-
{
|
|
7
|
-
type: 'list',
|
|
8
|
-
name: 'test',
|
|
9
|
-
message: 'Does this work?',
|
|
10
|
-
choices: ['Yes', 'No'],
|
|
11
|
-
}
|
|
12
|
-
]);
|
|
13
|
-
console.log('Answers:', answers);
|
|
14
|
-
} catch (error) {
|
|
15
|
-
console.error('Error:', error);
|
|
16
|
-
}
|