@nishant0121/set-it-up 0.0.7 → 0.0.9
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/DESCRIPTION.md +64 -0
- package/README.md +75 -75
- package/bin/index.js +22 -22
- package/package.json +2 -1
- package/src/engines/react.js +279 -210
- package/src/engines/reactNative.js +77 -77
- package/src/templates/react/main.js +2 -2
- package/src/wizard.js +123 -123
package/DESCRIPTION.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @nishant0121/set-it-up: The Ultimate Project Scaffolding CLI
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
**Set It Up** is a powerful, developer-centric CLI tool engineered to eliminate the repetitive boilerplate work associated with starting new React and React Native projects. It bridges the gap between "Hello World" and a production-ready architecture.
|
|
6
|
+
|
|
7
|
+
Unlike standard create scripts that give you a bare-bones entry point, `set-it-up` (via its command `forge`) constructs a robust, scalable foundation tailored to your preferences (TypeScript/JavaScript, Styling, Routing, etc.).
|
|
8
|
+
|
|
9
|
+
## Why Use This?
|
|
10
|
+
|
|
11
|
+
Starting a modern frontend project often involves 30-60 minutes of configuration:
|
|
12
|
+
- Setting up tailwind.
|
|
13
|
+
- configuring the router.
|
|
14
|
+
- creating folder structures (`pages`, `components`, `context`).
|
|
15
|
+
- installing navigation libraries for mobile.
|
|
16
|
+
- handling safe area contexts.
|
|
17
|
+
|
|
18
|
+
**Set It Up** does this in seconds.
|
|
19
|
+
|
|
20
|
+
## Key Capabilities
|
|
21
|
+
|
|
22
|
+
### 1. Interactive "Forge" Wizard
|
|
23
|
+
The `forge` command launches an intuitive,inquirer-based terminal wizard that asks you exactly what you need. No complex flags to remember—just answer the questions, and the CLI handles the rest.
|
|
24
|
+
|
|
25
|
+
### 2. React (Web) Excellence
|
|
26
|
+
- **Vite-Powered:** Uses the fastest build tool in the ecosystem.
|
|
27
|
+
- **Tailwind CSS v4:** Automatically configured with the latest version using the Vite plugin.
|
|
28
|
+
- **Shadcn UI Integration:** The only CLI that can set up Shadcn UI and automatically transpile its TypeScript components to JavaScript if you prefer a JS-only project.
|
|
29
|
+
- **Auto-configured Path Aliases:** Pre-sets `@/` aliases in Vite and TS/JS configs for cleaner imports.
|
|
30
|
+
- **Structure:** Automatically creates `src/pages`, `src/components`, and `src/layouts`.
|
|
31
|
+
- **Router Ready:** Installs `react-router-dom` and sets up a default routing configuration with a Layout and Navbar.
|
|
32
|
+
- **State & Theme:** Optional `AppContext` boilerplating for global state and dark-mode ready theme management.
|
|
33
|
+
|
|
34
|
+
### 3. React Native (Mobile) Mastery
|
|
35
|
+
- **Modular Architecture:** Moves away from the monolithic `App.tsx` pattern. It generates a clean separation of concerns:
|
|
36
|
+
- `src/pages/` (Screens)
|
|
37
|
+
- `src/components/` (Reusable UI)
|
|
38
|
+
- **Navigation Pre-baked:** Installs and configures React Navigation (Native Stack).
|
|
39
|
+
- **Auto-configured Path Aliases:** Ready-to-use path resolution for cleaner codebase management.
|
|
40
|
+
- **Safe Area Handling:** Implements `react-native-safe-area-context` with `SafeAreaProvider` wrapping the app and `SafeAreaView` in screens.
|
|
41
|
+
- **Polished Templates:** The generated starter screens (`HomeScreen`, `DetailsScreen`) are not blank white pages. They feature modern design, cards, shadows, and proper typography, giving you a beautiful starting point.
|
|
42
|
+
|
|
43
|
+
### 4. Smart & Safe
|
|
44
|
+
- **Prerequisite Checking:** Scans your environment for Node.js, Git, and other tools before starting to prevent mid-install failures.
|
|
45
|
+
- **Package Manager Agnostic:** Works seamlessly with `npm`, `yarn`, and `pnpm`.
|
|
46
|
+
- **Lightning Fast Conversion:** Uses `esbuild` for instant TypeScript to JavaScript transpilation when bridging Shadcn UI into JS projects.
|
|
47
|
+
|
|
48
|
+
## Technology Stack
|
|
49
|
+
|
|
50
|
+
- **CLI Engine:** Node.js, Commander, Inquirer, Ora (spinners), Chalk (styling), Boxen, Gradient-String.
|
|
51
|
+
- **Automation:** Execa (process execution), FS-Extra (file system), Esbuild (fast transpilation).
|
|
52
|
+
- **Templates:** React (Vite), React Native (CLI), Tailwind CSS v4, React Navigation, React Router DOM.
|
|
53
|
+
|
|
54
|
+
## Roadmap (Coming Soon)
|
|
55
|
+
- **Next.js Support:** Full App Router scaffolding with advanced features.
|
|
56
|
+
- **Custom GitHub Templates:** Forge projects from your own private or public repositories.
|
|
57
|
+
- **Backend Forging:** Express.js and FastAPI starter templates.
|
|
58
|
+
|
|
59
|
+
## Get Started
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm install -g @nishant0121/set-it-up
|
|
63
|
+
forge
|
|
64
|
+
```
|
package/README.md
CHANGED
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
# set-it-up
|
|
2
|
-
|
|
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
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
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.
|
|
22
|
-
|
|
23
|
-
## Installation
|
|
24
|
-
|
|
25
|
-
To use set-it-up globally on your system:
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
npm install -g @nishant0121/set-it-up
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Using npx:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
npx @nishant0121/set-it-up
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Usage
|
|
38
|
-
|
|
39
|
-
Run the tool in your terminal:
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
forge
|
|
43
|
-
```
|
|
44
|
-
|
|
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).
|
|
52
|
-
|
|
53
|
-
### Example Workflow
|
|
54
|
-
|
|
55
|
-
```text
|
|
56
|
-
SET-IT-UP
|
|
57
|
-
|
|
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
|
|
62
|
-
? Select your preferred package manager: npm
|
|
63
|
-
? Which language do you want to use? TypeScript
|
|
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
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Contributing
|
|
70
|
-
|
|
71
|
-
Contributions are welcome. Please open an issue or submit a pull request for improvements.
|
|
72
|
-
|
|
73
|
-
## License
|
|
74
|
-
|
|
75
|
-
ISC
|
|
1
|
+
# set-it-up
|
|
2
|
+
|
|
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
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
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.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
To use set-it-up globally on your system:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install -g @nishant0121/set-it-up
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Using npx:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx @nishant0121/set-it-up
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
Run the tool in your terminal:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
forge
|
|
43
|
+
```
|
|
44
|
+
|
|
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).
|
|
52
|
+
|
|
53
|
+
### Example Workflow
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
SET-IT-UP
|
|
57
|
+
|
|
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
|
|
62
|
+
? Select your preferred package manager: npm
|
|
63
|
+
? Which language do you want to use? TypeScript
|
|
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
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Contributing
|
|
70
|
+
|
|
71
|
+
Contributions are welcome. Please open an issue or submit a pull request for improvements.
|
|
72
|
+
|
|
73
|
+
## License
|
|
74
|
+
|
|
75
|
+
ISC
|
package/bin/index.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { program } from 'commander';
|
|
4
|
-
import { mainWizard } from '../src/wizard.js';
|
|
5
|
-
|
|
6
|
-
program
|
|
7
|
-
.version('1.0.0')
|
|
8
|
-
.description('A powerful CLI to forge new projects')
|
|
9
|
-
.action(async () => {
|
|
10
|
-
try {
|
|
11
|
-
await mainWizard();
|
|
12
|
-
} catch (error) {
|
|
13
|
-
if (error.name === 'ExitPromptError' || error.message.includes('User force closed the prompt')) {
|
|
14
|
-
console.log('\n👋 Goodbye!');
|
|
15
|
-
process.exit(0);
|
|
16
|
-
} else {
|
|
17
|
-
console.error(error);
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
program.parse(process.argv);
|
|
2
|
+
|
|
3
|
+
import { program } from 'commander';
|
|
4
|
+
import { mainWizard } from '../src/wizard.js';
|
|
5
|
+
|
|
6
|
+
program
|
|
7
|
+
.version('1.0.0')
|
|
8
|
+
.description('A powerful CLI to forge new projects')
|
|
9
|
+
.action(async () => {
|
|
10
|
+
try {
|
|
11
|
+
await mainWizard();
|
|
12
|
+
} catch (error) {
|
|
13
|
+
if (error.name === 'ExitPromptError' || error.message.includes('User force closed the prompt')) {
|
|
14
|
+
console.log('\n👋 Goodbye!');
|
|
15
|
+
process.exit(0);
|
|
16
|
+
} else {
|
|
17
|
+
console.error(error);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
program.parse(process.argv);
|
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.9",
|
|
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",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"chalk": "^5.6.2",
|
|
18
18
|
"command-exists": "^1.2.9",
|
|
19
19
|
"commander": "^14.0.2",
|
|
20
|
+
"esbuild": "^0.27.2",
|
|
20
21
|
"execa": "^9.6.1",
|
|
21
22
|
"fs-extra": "^11.3.3",
|
|
22
23
|
"gradient-string": "^3.0.0",
|
package/src/engines/react.js
CHANGED
|
@@ -1,210 +1,279 @@
|
|
|
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,
|
|
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
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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);
|
|
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
|
+
// 1. Handle main.tsx -> main.jsx
|
|
185
|
+
const mainTsx = path.join(srcDir, 'main.tsx');
|
|
186
|
+
const mainJsx = path.join(srcDir, 'main.jsx');
|
|
187
|
+
|
|
188
|
+
if (await fs.pathExists(mainTsx)) {
|
|
189
|
+
try {
|
|
190
|
+
await execa('npx', [
|
|
191
|
+
'esbuild',
|
|
192
|
+
mainTsx,
|
|
193
|
+
'--jsx=preserve',
|
|
194
|
+
`--outfile=${mainJsx}`,
|
|
195
|
+
'--format=esm'
|
|
196
|
+
], { cwd: process.cwd() });
|
|
197
|
+
await fs.remove(mainTsx);
|
|
198
|
+
|
|
199
|
+
// Fix imports in main.jsx (esbuild preserves local imports)
|
|
200
|
+
let mainContent = await fs.readFile(mainJsx, 'utf8');
|
|
201
|
+
mainContent = mainContent.replace(/from\s+['"]\.\/App\.tsx['"]/g, 'from "./App"');
|
|
202
|
+
await fs.writeFile(mainJsx, mainContent);
|
|
203
|
+
|
|
204
|
+
} catch (e) {
|
|
205
|
+
console.warn(`Failed to convert main.tsx: ${e.message}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 2. Handle App.tsx (Delete if App.jsx exists, otherwise convert)
|
|
210
|
+
const appTsx = path.join(srcDir, 'App.tsx');
|
|
211
|
+
const appJsx = path.join(srcDir, 'App.jsx');
|
|
212
|
+
|
|
213
|
+
if (await fs.pathExists(appTsx)) {
|
|
214
|
+
if (await fs.pathExists(appJsx)) {
|
|
215
|
+
// We generated a custom App.jsx, so the default App.tsx is garbage.
|
|
216
|
+
await fs.remove(appTsx);
|
|
217
|
+
} else {
|
|
218
|
+
// Fallback: convert App.tsx if we didn't generate App.jsx
|
|
219
|
+
try {
|
|
220
|
+
await execa('npx', [
|
|
221
|
+
'esbuild',
|
|
222
|
+
appTsx,
|
|
223
|
+
'--jsx=preserve',
|
|
224
|
+
`--outfile=${appJsx}`,
|
|
225
|
+
'--format=esm'
|
|
226
|
+
], { cwd: process.cwd() });
|
|
227
|
+
await fs.remove(appTsx);
|
|
228
|
+
} catch (e) {
|
|
229
|
+
console.warn(`Failed to convert App.tsx: ${e.message}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// 3. Remove vite-env.d.ts
|
|
235
|
+
const viteEnv = path.join(srcDir, 'vite-env.d.ts');
|
|
236
|
+
if (await fs.pathExists(viteEnv)) {
|
|
237
|
+
await fs.remove(viteEnv);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// 4. Handle vite.config.ts -> vite.config.js
|
|
241
|
+
const viteConfigTs = path.join(projectName, 'vite.config.ts');
|
|
242
|
+
const viteConfigJs = path.join(projectName, 'vite.config.js');
|
|
243
|
+
|
|
244
|
+
if (await fs.pathExists(viteConfigTs)) {
|
|
245
|
+
try {
|
|
246
|
+
await execa('npx', [
|
|
247
|
+
'esbuild',
|
|
248
|
+
viteConfigTs,
|
|
249
|
+
`--outfile=${viteConfigJs}`,
|
|
250
|
+
'--format=esm'
|
|
251
|
+
], { cwd: process.cwd() });
|
|
252
|
+
await fs.remove(viteConfigTs);
|
|
253
|
+
} catch (e) {
|
|
254
|
+
console.warn(`Failed to convert vite.config.ts: ${e.message}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Update index.html to reference main.jsx
|
|
259
|
+
const indexHtmlPath = path.join(projectName, 'index.html');
|
|
260
|
+
if (await fs.pathExists(indexHtmlPath)) {
|
|
261
|
+
let indexHtml = await fs.readFile(indexHtmlPath, 'utf8');
|
|
262
|
+
indexHtml = indexHtml.replace('/src/main.tsx', '/src/main.jsx');
|
|
263
|
+
await fs.writeFile(indexHtmlPath, indexHtml);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Clean up TS configs and create jsconfig.json
|
|
267
|
+
await fs.remove(path.join(projectName, 'tsconfig.json'));
|
|
268
|
+
await fs.remove(path.join(projectName, 'tsconfig.app.json'));
|
|
269
|
+
await fs.remove(path.join(projectName, 'tsconfig.node.json'));
|
|
270
|
+
|
|
271
|
+
const jsConfig = {
|
|
272
|
+
"compilerOptions": {
|
|
273
|
+
"paths": {
|
|
274
|
+
"@/*": ["./src/*"]
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
await fs.writeJson(path.join(projectName, 'jsconfig.json'), jsConfig, { spaces: 2 });
|
|
279
|
+
}
|
|
@@ -1,78 +1,78 @@
|
|
|
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
|
-
import { fileURLToPath } from 'url';
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
-
|
|
11
|
-
export async function setupReactNative(answers, rnAnswers) {
|
|
12
|
-
const spinner = ora('Initializing React Native project...').start();
|
|
13
|
-
try {
|
|
14
|
-
const { projectName, packageManager } = answers;
|
|
15
|
-
const isTypescript = rnAnswers.language === 'TypeScript';
|
|
16
|
-
|
|
17
|
-
// Basic init command using the community CLI for latest version
|
|
18
|
-
const args = ['--yes', '@react-native-community/cli@latest', 'init', projectName, '--skip-install'];
|
|
19
|
-
|
|
20
|
-
await execa('npx', args);
|
|
21
|
-
|
|
22
|
-
// Handle language selection (rename App.tsx to App.jsx if JS is selected)
|
|
23
|
-
const appTsxPath = path.join(projectName, 'App.tsx');
|
|
24
|
-
const appJsxPath = path.join(projectName, 'App.jsx');
|
|
25
|
-
|
|
26
|
-
if (!isTypescript && await fs.pathExists(appTsxPath)) {
|
|
27
|
-
await fs.rename(appTsxPath, appJsxPath);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
spinner.text = 'Installing dependencies...';
|
|
31
|
-
|
|
32
|
-
// Install dependencies using the selected package manager
|
|
33
|
-
await execa(packageManager, ['install'], { cwd: projectName });
|
|
34
|
-
|
|
35
|
-
if (rnAnswers.addNavigation) {
|
|
36
|
-
spinner.text = 'Installing React Navigation...';
|
|
37
|
-
const installCmd = packageManager === 'npm' ? 'install' : 'add';
|
|
38
|
-
const packages = [
|
|
39
|
-
'@react-navigation/native',
|
|
40
|
-
'react-native-screens',
|
|
41
|
-
'react-native-safe-area-context',
|
|
42
|
-
'@react-navigation/native-stack'
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
await execa(packageManager, [installCmd, ...packages], { cwd: projectName });
|
|
46
|
-
|
|
47
|
-
spinner.text = 'Configuring Navigation...';
|
|
48
|
-
|
|
49
|
-
// Create modular folder structure
|
|
50
|
-
const srcDir = path.join(projectName, 'src');
|
|
51
|
-
const pagesDir = path.join(srcDir, 'pages');
|
|
52
|
-
const componentsDir = path.join(srcDir, 'components');
|
|
53
|
-
|
|
54
|
-
await fs.ensureDir(pagesDir);
|
|
55
|
-
await fs.ensureDir(componentsDir);
|
|
56
|
-
|
|
57
|
-
const ext = isTypescript ? 'tsx' : 'jsx';
|
|
58
|
-
const templateBase = path.join(__dirname, '..', 'templates', 'reactNative', 'modular');
|
|
59
|
-
|
|
60
|
-
// Copy App file
|
|
61
|
-
const appContent = await fs.readFile(path.join(templateBase, `App.${ext}`), 'utf8');
|
|
62
|
-
const targetAppPath = isTypescript ? appTsxPath : appJsxPath;
|
|
63
|
-
await fs.writeFile(targetAppPath, appContent);
|
|
64
|
-
|
|
65
|
-
// Copy Screens
|
|
66
|
-
const homeContent = await fs.readFile(path.join(templateBase, 'pages', `HomeScreen.${ext}`), 'utf8');
|
|
67
|
-
await fs.writeFile(path.join(pagesDir, `HomeScreen.${ext}`), homeContent);
|
|
68
|
-
|
|
69
|
-
const detailsContent = await fs.readFile(path.join(templateBase, 'pages', `DetailsScreen.${ext}`), 'utf8');
|
|
70
|
-
await fs.writeFile(path.join(pagesDir, `DetailsScreen.${ext}`), detailsContent);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
spinner.succeed(chalk.green(`Project ${projectName} created successfully! 🚀`));
|
|
74
|
-
} catch (error) {
|
|
75
|
-
spinner.fail('Setup failed.');
|
|
76
|
-
console.error(error);
|
|
77
|
-
}
|
|
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
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
|
|
11
|
+
export async function setupReactNative(answers, rnAnswers) {
|
|
12
|
+
const spinner = ora('Initializing React Native project...').start();
|
|
13
|
+
try {
|
|
14
|
+
const { projectName, packageManager } = answers;
|
|
15
|
+
const isTypescript = rnAnswers.language === 'TypeScript';
|
|
16
|
+
|
|
17
|
+
// Basic init command using the community CLI for latest version
|
|
18
|
+
const args = ['--yes', '@react-native-community/cli@latest', 'init', projectName, '--skip-install'];
|
|
19
|
+
|
|
20
|
+
await execa('npx', args);
|
|
21
|
+
|
|
22
|
+
// Handle language selection (rename App.tsx to App.jsx if JS is selected)
|
|
23
|
+
const appTsxPath = path.join(projectName, 'App.tsx');
|
|
24
|
+
const appJsxPath = path.join(projectName, 'App.jsx');
|
|
25
|
+
|
|
26
|
+
if (!isTypescript && await fs.pathExists(appTsxPath)) {
|
|
27
|
+
await fs.rename(appTsxPath, appJsxPath);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
spinner.text = 'Installing dependencies...';
|
|
31
|
+
|
|
32
|
+
// Install dependencies using the selected package manager
|
|
33
|
+
await execa(packageManager, ['install'], { cwd: projectName });
|
|
34
|
+
|
|
35
|
+
if (rnAnswers.addNavigation) {
|
|
36
|
+
spinner.text = 'Installing React Navigation...';
|
|
37
|
+
const installCmd = packageManager === 'npm' ? 'install' : 'add';
|
|
38
|
+
const packages = [
|
|
39
|
+
'@react-navigation/native',
|
|
40
|
+
'react-native-screens',
|
|
41
|
+
'react-native-safe-area-context',
|
|
42
|
+
'@react-navigation/native-stack'
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
await execa(packageManager, [installCmd, ...packages], { cwd: projectName });
|
|
46
|
+
|
|
47
|
+
spinner.text = 'Configuring Navigation...';
|
|
48
|
+
|
|
49
|
+
// Create modular folder structure
|
|
50
|
+
const srcDir = path.join(projectName, 'src');
|
|
51
|
+
const pagesDir = path.join(srcDir, 'pages');
|
|
52
|
+
const componentsDir = path.join(srcDir, 'components');
|
|
53
|
+
|
|
54
|
+
await fs.ensureDir(pagesDir);
|
|
55
|
+
await fs.ensureDir(componentsDir);
|
|
56
|
+
|
|
57
|
+
const ext = isTypescript ? 'tsx' : 'jsx';
|
|
58
|
+
const templateBase = path.join(__dirname, '..', 'templates', 'reactNative', 'modular');
|
|
59
|
+
|
|
60
|
+
// Copy App file
|
|
61
|
+
const appContent = await fs.readFile(path.join(templateBase, `App.${ext}`), 'utf8');
|
|
62
|
+
const targetAppPath = isTypescript ? appTsxPath : appJsxPath;
|
|
63
|
+
await fs.writeFile(targetAppPath, appContent);
|
|
64
|
+
|
|
65
|
+
// Copy Screens
|
|
66
|
+
const homeContent = await fs.readFile(path.join(templateBase, 'pages', `HomeScreen.${ext}`), 'utf8');
|
|
67
|
+
await fs.writeFile(path.join(pagesDir, `HomeScreen.${ext}`), homeContent);
|
|
68
|
+
|
|
69
|
+
const detailsContent = await fs.readFile(path.join(templateBase, 'pages', `DetailsScreen.${ext}`), 'utf8');
|
|
70
|
+
await fs.writeFile(path.join(pagesDir, `DetailsScreen.${ext}`), detailsContent);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
spinner.succeed(chalk.green(`Project ${projectName} created successfully! 🚀`));
|
|
74
|
+
} catch (error) {
|
|
75
|
+
spinner.fail('Setup failed.');
|
|
76
|
+
console.error(error);
|
|
77
|
+
}
|
|
78
78
|
}
|
|
@@ -51,8 +51,8 @@ export default App;
|
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
export const getAppContext = (options) => {
|
|
54
|
-
const { isTypescript
|
|
55
|
-
const useTs = isTypescript
|
|
54
|
+
const { isTypescript } = options;
|
|
55
|
+
const useTs = isTypescript;
|
|
56
56
|
|
|
57
57
|
return `import React, { createContext, useContext, useState${useTs ? ', ReactNode' : ''} } from 'react';
|
|
58
58
|
|
package/src/wizard.js
CHANGED
|
@@ -1,124 +1,124 @@
|
|
|
1
|
-
import inquirer from 'inquirer';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import gradient from 'gradient-string';
|
|
4
|
-
import boxen from 'boxen';
|
|
5
|
-
import { checkPrerequisites } from './utils/checkEnv.js';
|
|
6
|
-
import { setupReactNative } from './engines/reactNative.js';
|
|
7
|
-
import { setupReact } from './engines/react.js';
|
|
8
|
-
|
|
9
|
-
export async function mainWizard() {
|
|
10
|
-
// Clear the console for a fresh start
|
|
11
|
-
console.clear();
|
|
12
|
-
|
|
13
|
-
// Create a beautiful header
|
|
14
|
-
const title = `
|
|
15
|
-
SET - IT - UP
|
|
16
|
-
`;
|
|
17
|
-
|
|
18
|
-
const boxenOptions = {
|
|
19
|
-
padding: 1,
|
|
20
|
-
margin: 1,
|
|
21
|
-
borderStyle: 'round',
|
|
22
|
-
borderColor: 'cyan',
|
|
23
|
-
backgroundColor: '#1b1b1b'
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const welcomeMessage = boxen(
|
|
27
|
-
gradient.pastel.multiline(title) +
|
|
28
|
-
'\n' +
|
|
29
|
-
chalk.white('🚀 Ready to launch your next idea?'),
|
|
30
|
-
boxenOptions
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
console.log(welcomeMessage);
|
|
34
|
-
|
|
35
|
-
console.log(chalk.hex('#4285F4')(' Hi there! Let\'s configure your new project.\n'));
|
|
36
|
-
|
|
37
|
-
const answers = await inquirer.prompt([
|
|
38
|
-
{
|
|
39
|
-
type: 'rawlist',
|
|
40
|
-
name: 'projectType',
|
|
41
|
-
message: 'What do you want to build today?',
|
|
42
|
-
choices: ['React', 'React Native', 'Next.js (Coming Soon)', 'Custom GitHub Template'],
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
type: 'input',
|
|
46
|
-
name: 'projectName',
|
|
47
|
-
message: 'Enter your project name:',
|
|
48
|
-
default: 'my-awesome-app',
|
|
49
|
-
validate: (input) => {
|
|
50
|
-
if (/^([a-z0-9\-\_])+$/.test(input)) return true;
|
|
51
|
-
return 'Project name may only include lowercase letters, numbers, underscores and hashes.';
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
type: 'rawlist',
|
|
56
|
-
name: 'packageManager',
|
|
57
|
-
message: 'Select your preferred package manager:',
|
|
58
|
-
choices: ['npm', 'yarn', 'pnpm'],
|
|
59
|
-
}
|
|
60
|
-
]);
|
|
61
|
-
|
|
62
|
-
if (answers.projectType === 'React Native') {
|
|
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 });
|
|
73
|
-
|
|
74
|
-
// Additional RN specific questions
|
|
75
|
-
const rnAnswers = await inquirer.prompt([
|
|
76
|
-
{
|
|
77
|
-
type: 'rawlist',
|
|
78
|
-
name: 'language',
|
|
79
|
-
message: 'Which language do you want to use?',
|
|
80
|
-
choices: ['TypeScript', 'JavaScript'],
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
type: 'confirm',
|
|
84
|
-
name: 'addNavigation',
|
|
85
|
-
message: 'Would you like to add React Navigation setup?',
|
|
86
|
-
}
|
|
87
|
-
]);
|
|
88
|
-
|
|
89
|
-
Object.assign(rnAnswers, platformAnswer);
|
|
90
|
-
|
|
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
|
-
|
|
121
|
-
} else {
|
|
122
|
-
console.log(chalk.yellow('\n🚧 This feature is coming soon! Stay tuned.\n'));
|
|
123
|
-
}
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import gradient from 'gradient-string';
|
|
4
|
+
import boxen from 'boxen';
|
|
5
|
+
import { checkPrerequisites } from './utils/checkEnv.js';
|
|
6
|
+
import { setupReactNative } from './engines/reactNative.js';
|
|
7
|
+
import { setupReact } from './engines/react.js';
|
|
8
|
+
|
|
9
|
+
export async function mainWizard() {
|
|
10
|
+
// Clear the console for a fresh start
|
|
11
|
+
console.clear();
|
|
12
|
+
|
|
13
|
+
// Create a beautiful header
|
|
14
|
+
const title = `
|
|
15
|
+
SET - IT - UP
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const boxenOptions = {
|
|
19
|
+
padding: 1,
|
|
20
|
+
margin: 1,
|
|
21
|
+
borderStyle: 'round',
|
|
22
|
+
borderColor: 'cyan',
|
|
23
|
+
backgroundColor: '#1b1b1b'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const welcomeMessage = boxen(
|
|
27
|
+
gradient.pastel.multiline(title) +
|
|
28
|
+
'\n' +
|
|
29
|
+
chalk.white('🚀 Ready to launch your next idea?'),
|
|
30
|
+
boxenOptions
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
console.log(welcomeMessage);
|
|
34
|
+
|
|
35
|
+
console.log(chalk.hex('#4285F4')(' Hi there! Let\'s configure your new project.\n'));
|
|
36
|
+
|
|
37
|
+
const answers = await inquirer.prompt([
|
|
38
|
+
{
|
|
39
|
+
type: 'rawlist',
|
|
40
|
+
name: 'projectType',
|
|
41
|
+
message: 'What do you want to build today?',
|
|
42
|
+
choices: ['React', 'React Native', 'Next.js (Coming Soon)', 'Custom GitHub Template'],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: 'input',
|
|
46
|
+
name: 'projectName',
|
|
47
|
+
message: 'Enter your project name:',
|
|
48
|
+
default: 'my-awesome-app',
|
|
49
|
+
validate: (input) => {
|
|
50
|
+
if (/^([a-z0-9\-\_])+$/.test(input)) return true;
|
|
51
|
+
return 'Project name may only include lowercase letters, numbers, underscores and hashes.';
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: 'rawlist',
|
|
56
|
+
name: 'packageManager',
|
|
57
|
+
message: 'Select your preferred package manager:',
|
|
58
|
+
choices: ['npm', 'yarn', 'pnpm'],
|
|
59
|
+
}
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
if (answers.projectType === 'React Native') {
|
|
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 });
|
|
73
|
+
|
|
74
|
+
// Additional RN specific questions
|
|
75
|
+
const rnAnswers = await inquirer.prompt([
|
|
76
|
+
{
|
|
77
|
+
type: 'rawlist',
|
|
78
|
+
name: 'language',
|
|
79
|
+
message: 'Which language do you want to use?',
|
|
80
|
+
choices: ['TypeScript', 'JavaScript'],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: 'confirm',
|
|
84
|
+
name: 'addNavigation',
|
|
85
|
+
message: 'Would you like to add React Navigation setup?',
|
|
86
|
+
}
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
Object.assign(rnAnswers, platformAnswer);
|
|
90
|
+
|
|
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
|
+
|
|
121
|
+
} else {
|
|
122
|
+
console.log(chalk.yellow('\n🚧 This feature is coming soon! Stay tuned.\n'));
|
|
123
|
+
}
|
|
124
124
|
}
|