create-quapp 1.0.1
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 +20 -0
- package/index.js +232 -0
- package/package.json +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# create-quapp
|
|
2
|
+
|
|
3
|
+
> 🚀 A modern CLI to scaffold front-end projects lightning-fast with Vite – powered by QR code-based dev server access and flexible configuration.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## ✨ Features
|
|
8
|
+
|
|
9
|
+
- ⚡ **Quick Scaffolding**
|
|
10
|
+
- 🔍 **Interactive CLI** – choose your framework, template, and preferences.
|
|
11
|
+
- 📦 **Smart `package.json` tweaks**
|
|
12
|
+
- 🌐 **QR Code Dev Access** – scan and open the app on your mobile instantly.
|
|
13
|
+
- 📁 **Clean Projects** – no clutter, templates are cloned from GitHub.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 📦 Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm create quapp@latest
|
package/index.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import readline from 'readline';
|
|
8
|
+
import { createRequire } from 'module';
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
|
|
11
|
+
const degit = (await import('degit')).default;
|
|
12
|
+
const prompts = (await import('prompts')).default;
|
|
13
|
+
|
|
14
|
+
// Terminal Colors
|
|
15
|
+
const red = (text) => `\x1b[31m${text}\x1b[0m`;
|
|
16
|
+
const green = (text) => `\x1b[32m${text}\x1b[0m`;
|
|
17
|
+
const yellow = (text) => `\x1b[33m${text}\x1b[0m`;
|
|
18
|
+
const blue = (text) => `\x1b[34m${text}\x1b[0m`;
|
|
19
|
+
const boldBlue = (text) => `\x1b[1m\x1b[34m${text}\x1b[0m`;
|
|
20
|
+
|
|
21
|
+
const colorize = (text, fn) => (process.argv.includes('--no-color') ? text : fn(text));
|
|
22
|
+
|
|
23
|
+
// Ctrl+C handler
|
|
24
|
+
process.on('SIGINT', () => {
|
|
25
|
+
cleanupInput();
|
|
26
|
+
console.log(red('\n Setup canceled (Ctrl+C).\n'));
|
|
27
|
+
process.exit(0);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
function setupEscapeHandler() {
|
|
31
|
+
if (process.stdin.isTTY) {
|
|
32
|
+
process.stdin.setRawMode(true);
|
|
33
|
+
process.stdin.resume();
|
|
34
|
+
process.stdin.on('data', handleKeyPress);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function handleKeyPress(key) {
|
|
39
|
+
if (key.toString() === '\u001b') {
|
|
40
|
+
cleanupInput();
|
|
41
|
+
console.log(red('\n Setup canceled (Escape).\n'));
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function cleanupInput() {
|
|
47
|
+
if (process.stdin.isTTY && process.stdin.isRaw) {
|
|
48
|
+
process.stdin.setRawMode(false);
|
|
49
|
+
}
|
|
50
|
+
process.stdin.pause();
|
|
51
|
+
process.stdin.removeListener('data', handleKeyPress);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Core fs helper replacements for fs-extra
|
|
55
|
+
function removeDir(dirPath) {
|
|
56
|
+
if (fs.existsSync(dirPath)) {
|
|
57
|
+
fs.rmSync(dirPath, { recursive: true, force: true });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function copyRecursiveSync(src, dest) {
|
|
62
|
+
const stat = fs.statSync(src);
|
|
63
|
+
if (stat.isDirectory()) {
|
|
64
|
+
if (!fs.existsSync(dest)) fs.mkdirSync(dest);
|
|
65
|
+
for (const file of fs.readdirSync(src)) {
|
|
66
|
+
const srcPath = path.join(src, file);
|
|
67
|
+
const destPath = path.join(dest, file);
|
|
68
|
+
copyRecursiveSync(srcPath, destPath);
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
fs.copyFileSync(src, dest);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
76
|
+
const __dirname = path.dirname(__filename);
|
|
77
|
+
|
|
78
|
+
const args = process.argv.slice(2);
|
|
79
|
+
let providedName = args[0];
|
|
80
|
+
let templateFlagIndex = args.indexOf('--template');
|
|
81
|
+
let templateArg = templateFlagIndex !== -1 ? args[templateFlagIndex + 1] : null;
|
|
82
|
+
const force = args.includes('--force');
|
|
83
|
+
const noColor = args.includes('--no-color');
|
|
84
|
+
const autoGit = args.includes('--git');
|
|
85
|
+
const autoInstall = args.includes('--install');
|
|
86
|
+
|
|
87
|
+
const askProjectName = async () => {
|
|
88
|
+
if (providedName) {
|
|
89
|
+
return providedName;
|
|
90
|
+
}
|
|
91
|
+
console.log('\n');
|
|
92
|
+
console.log(boldBlue(' Welcome to Quapp Setup!'));
|
|
93
|
+
console.log('\n');
|
|
94
|
+
const response = await prompts({
|
|
95
|
+
type: 'text',
|
|
96
|
+
name: 'projectName',
|
|
97
|
+
message: colorize(' Enter Project Name:', yellow),
|
|
98
|
+
validate: (name) => (name.trim() === '' ? 'Project name is required' : true),
|
|
99
|
+
});
|
|
100
|
+
if (!response.projectName) {
|
|
101
|
+
console.log(red(' Project name is required. Exiting...'));
|
|
102
|
+
cleanupInput();
|
|
103
|
+
process.exit(0);
|
|
104
|
+
}
|
|
105
|
+
return response.projectName.trim();
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
setupEscapeHandler();
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const projectName = await askProjectName();
|
|
112
|
+
|
|
113
|
+
const allTemplates = {
|
|
114
|
+
react: ['react', 'react-ts', 'react+swc', 'react-ts+swc'],
|
|
115
|
+
vue: ['vue', 'vue-ts'],
|
|
116
|
+
vanilla: ['vanilla-js', 'vanilla-ts'],
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
console.log('\n');
|
|
120
|
+
if (!templateArg || !Object.values(allTemplates).flat().includes(templateArg)) {
|
|
121
|
+
const frameworkChoice = await prompts({
|
|
122
|
+
type: 'select',
|
|
123
|
+
name: 'framework',
|
|
124
|
+
message: 'Choose a framework:',
|
|
125
|
+
choices: [
|
|
126
|
+
{ title: 'React', value: 'react' },
|
|
127
|
+
{ title: 'Vue', value: 'vue' },
|
|
128
|
+
{ title: 'Vanilla', value: 'vanilla' },
|
|
129
|
+
],
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (!frameworkChoice.framework) {
|
|
133
|
+
console.log(red('\n Setup canceled.\n'));
|
|
134
|
+
process.exit(0);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
console.log('\n');
|
|
138
|
+
|
|
139
|
+
const response = await prompts({
|
|
140
|
+
type: 'select',
|
|
141
|
+
name: 'template',
|
|
142
|
+
message: `Choose a ${frameworkChoice.framework} template:`,
|
|
143
|
+
choices: allTemplates[frameworkChoice.framework].map((item) => ({
|
|
144
|
+
title: item,
|
|
145
|
+
value: item,
|
|
146
|
+
})),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
if (!response.template) {
|
|
150
|
+
console.log(red('\n Setup canceled.\n'));
|
|
151
|
+
process.exit(0);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
templateArg = response.template;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
console.log('\n' + colorize(' Creating a new Quapp project...\n', green));
|
|
158
|
+
|
|
159
|
+
const projectDir = path.join(process.cwd(), projectName);
|
|
160
|
+
const templateRepo = `Quapp-Store/Quapp/packages/templates/${templateArg}`;
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const emitter = degit(templateRepo, { cache: false, force });
|
|
164
|
+
await emitter.clone(projectDir);
|
|
165
|
+
} catch (err) {
|
|
166
|
+
console.error(red(' Error creating project:'), err.message);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
171
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
172
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
173
|
+
packageJson.name = projectName;
|
|
174
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
175
|
+
} else {
|
|
176
|
+
console.error(red(' package.json not found!'));
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let gitInit = autoGit;
|
|
181
|
+
if (!autoGit) {
|
|
182
|
+
const gitResponse = await prompts({
|
|
183
|
+
type: 'confirm',
|
|
184
|
+
name: 'gitInit',
|
|
185
|
+
message: 'Do you want to initialize a Git repository?',
|
|
186
|
+
});
|
|
187
|
+
gitInit = gitResponse.gitInit;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (gitInit) {
|
|
191
|
+
try {
|
|
192
|
+
execSync('git init', { cwd: projectDir });
|
|
193
|
+
console.log(blue(' Initialized empty Git repository.'));
|
|
194
|
+
} catch (err) {
|
|
195
|
+
console.log(yellow(' Git not found. Skipping Git init.'));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
let doInstall = autoInstall;
|
|
200
|
+
if (!autoInstall) {
|
|
201
|
+
const installResponse = await prompts({
|
|
202
|
+
type: 'confirm',
|
|
203
|
+
name: 'install',
|
|
204
|
+
message: 'Do you want to install dependencies?',
|
|
205
|
+
});
|
|
206
|
+
doInstall = installResponse.install;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (doInstall) {
|
|
210
|
+
console.log(blue(' Installing dependencies...\n'));
|
|
211
|
+
try {
|
|
212
|
+
execSync('npm install', { cwd: projectDir, stdio: 'inherit' });
|
|
213
|
+
execSync('npm install qrcode-terminal', { cwd: projectDir, stdio: 'ignore' });
|
|
214
|
+
} catch (err) {
|
|
215
|
+
console.log(yellow(' NPM not found or installation failed. Please install dependencies manually.'));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
console.log('\n');
|
|
220
|
+
console.log(boldBlue(` cd ${projectName}`));
|
|
221
|
+
if (!doInstall) {
|
|
222
|
+
console.log(boldBlue(' npm install'));
|
|
223
|
+
}
|
|
224
|
+
console.log(boldBlue(' npm quapp serve or quapp serve\n'));
|
|
225
|
+
|
|
226
|
+
cleanupInput();
|
|
227
|
+
setTimeout(() => process.exit(0), 100);
|
|
228
|
+
} catch (err) {
|
|
229
|
+
cleanupInput();
|
|
230
|
+
console.error(red(' Unexpected error:'), err);
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-quapp",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Quapp Development tool of the future",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-quapp": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"author": "Quapp",
|
|
14
|
+
"license": "ISC",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"degit": "^2.8.4",
|
|
17
|
+
"prompts": "^2.4.2"
|
|
18
|
+
}
|
|
19
|
+
}
|