create-dev-to 1.1.0 → 1.3.0
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/dist/index.js +586 -554
- package/package.json +3 -3
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/installLogger.d.ts +0 -29
- package/dist/installLogger.d.ts.map +0 -1
- package/dist/installLogger.js +0 -252
- package/dist/installLogger.js.map +0 -1
- package/dist/outputParsers.d.ts +0 -29
- package/dist/outputParsers.d.ts.map +0 -1
- package/dist/outputParsers.js +0 -259
- package/dist/outputParsers.js.map +0 -1
- package/dist/visualComponents.d.ts +0 -35
- package/dist/visualComponents.d.ts.map +0 -1
- package/dist/visualComponents.js +0 -247
- package/dist/visualComponents.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,616 +1,648 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import fs from
|
|
3
|
-
import path from
|
|
4
|
-
import { spawn, execSync } from
|
|
5
|
-
import process from
|
|
6
|
-
import { randomUUID } from
|
|
7
|
-
import readline from
|
|
8
|
-
import * as clack from
|
|
9
|
-
import { red, cyan, yellow, green, dim } from
|
|
10
|
-
import { InstallLogger } from
|
|
11
|
-
import { displayInstallSummary } from
|
|
12
|
-
const PACKAGE_MANAGERS = [
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { spawn, execSync } from "node:child_process";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
7
|
+
import readline from "node:readline";
|
|
8
|
+
import * as clack from "@clack/prompts";
|
|
9
|
+
import { red, cyan, yellow, green, dim } from "kolorist";
|
|
10
|
+
import { InstallLogger } from "./installLogger.js";
|
|
11
|
+
import { displayInstallSummary } from "./visualComponents.js";
|
|
12
|
+
const PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"];
|
|
13
|
+
const __BUILD_INFO__ = {
|
|
14
|
+
commit: "519bc16",
|
|
15
|
+
branch: "main",
|
|
16
|
+
buildTime: "2026-01-10 14:05",
|
|
17
|
+
version: "1.3.0"
|
|
18
|
+
};
|
|
13
19
|
const FRAMEWORKS = [
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
20
|
+
{
|
|
21
|
+
name: "react",
|
|
22
|
+
display: "React",
|
|
23
|
+
color: cyan,
|
|
24
|
+
supported: true
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "vue",
|
|
28
|
+
display: "Vue",
|
|
29
|
+
color: green,
|
|
30
|
+
supported: false
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "svelte",
|
|
34
|
+
display: "Svelte",
|
|
35
|
+
color: red,
|
|
36
|
+
supported: false
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "solid",
|
|
40
|
+
display: "Solid",
|
|
41
|
+
color: cyan,
|
|
42
|
+
supported: false
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "preact",
|
|
46
|
+
display: "Preact",
|
|
47
|
+
color: cyan,
|
|
48
|
+
supported: false
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "lit",
|
|
52
|
+
display: "Lit",
|
|
53
|
+
color: yellow,
|
|
54
|
+
supported: false
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "qwik",
|
|
58
|
+
display: "Qwik",
|
|
59
|
+
color: cyan,
|
|
60
|
+
supported: false
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "vanilla",
|
|
64
|
+
display: "Vanilla",
|
|
65
|
+
color: yellow,
|
|
66
|
+
supported: false
|
|
67
|
+
}
|
|
62
68
|
];
|
|
63
69
|
const REACT_TEMPLATES = [
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
{
|
|
75
|
-
name: 'react',
|
|
76
|
-
display: 'JavaScript',
|
|
77
|
-
color: yellow,
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
name: 'react-swc',
|
|
81
|
-
display: 'JavaScript + SWC',
|
|
82
|
-
color: yellow,
|
|
83
|
-
},
|
|
70
|
+
{
|
|
71
|
+
name: "react-ts",
|
|
72
|
+
display: "TypeScript",
|
|
73
|
+
color: cyan
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "react",
|
|
77
|
+
display: "JavaScript",
|
|
78
|
+
color: yellow
|
|
79
|
+
}
|
|
84
80
|
];
|
|
85
81
|
const TEMPLATE_SOURCES = [
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
82
|
+
{
|
|
83
|
+
name: "GitHub",
|
|
84
|
+
getCloneCommand: (template, targetDir, packageManager) => {
|
|
85
|
+
const pmCommand = getDegitCommandForPM(packageManager);
|
|
86
|
+
return {
|
|
87
|
+
command: pmCommand.command,
|
|
88
|
+
args: pmCommand.args("degit", [`vitejs/vite/packages/create-vite/template-${template}`, targetDir, "--force"])
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "Gitee Mirror (\u56FD\u5185\u955C\u50CF)",
|
|
94
|
+
isGitBased: true,
|
|
95
|
+
getCloneCommand: (_template, targetDir) => ({
|
|
96
|
+
command: "git",
|
|
97
|
+
args: [
|
|
98
|
+
"clone",
|
|
99
|
+
"--depth",
|
|
100
|
+
"1",
|
|
101
|
+
"https://gitee.com/mirrors/ViteJS.git",
|
|
102
|
+
targetDir
|
|
103
|
+
]
|
|
104
|
+
})
|
|
105
|
+
}
|
|
110
106
|
];
|
|
111
107
|
const PM_CONFIGS = {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
108
|
+
pnpm: {
|
|
109
|
+
install: "pnpm install",
|
|
110
|
+
dev: "pnpm dev"
|
|
111
|
+
},
|
|
112
|
+
npm: {
|
|
113
|
+
install: "npm install",
|
|
114
|
+
dev: "npm run dev"
|
|
115
|
+
},
|
|
116
|
+
yarn: {
|
|
117
|
+
install: "yarn",
|
|
118
|
+
dev: "yarn dev"
|
|
119
|
+
},
|
|
120
|
+
bun: {
|
|
121
|
+
install: "bun install",
|
|
122
|
+
dev: "bun dev"
|
|
123
|
+
}
|
|
128
124
|
};
|
|
129
125
|
function checkCommandExists(command) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
126
|
+
try {
|
|
127
|
+
execSync(`${command} --version`, { stdio: "ignore" });
|
|
128
|
+
return true;
|
|
129
|
+
} catch {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
137
132
|
}
|
|
138
133
|
function detectPackageManager(userAgent) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
return null;
|
|
134
|
+
for (const pm of PACKAGE_MANAGERS) {
|
|
135
|
+
if (userAgent.includes(pm)) return pm;
|
|
136
|
+
}
|
|
137
|
+
for (const pm of PACKAGE_MANAGERS) {
|
|
138
|
+
if (checkCommandExists(pm)) return pm;
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
148
141
|
}
|
|
149
142
|
function formatTargetDir(targetDir) {
|
|
150
|
-
|
|
143
|
+
return targetDir?.trim().replace(/\/+$/g, "");
|
|
151
144
|
}
|
|
152
|
-
function isEmpty(
|
|
153
|
-
|
|
154
|
-
|
|
145
|
+
function isEmpty(path2) {
|
|
146
|
+
const files = fs.readdirSync(path2);
|
|
147
|
+
return files.length === 0 || files.length === 1 && files[0] === ".git";
|
|
155
148
|
}
|
|
156
149
|
function toValidPackageName(projectName) {
|
|
157
|
-
|
|
158
|
-
.trim()
|
|
159
|
-
.toLowerCase()
|
|
160
|
-
.replace(/\s+/g, '-')
|
|
161
|
-
.replace(/^[._]/, '')
|
|
162
|
-
.replace(/[^a-z\d\-~]+/g, '-');
|
|
150
|
+
return projectName.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^a-z\d\-~]+/g, "-");
|
|
163
151
|
}
|
|
164
152
|
function emptyDir(dir) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
fs.rmSync(path.resolve(dir, file), { recursive: true, force: true });
|
|
153
|
+
if (!fs.existsSync(dir)) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
for (const file of fs.readdirSync(dir)) {
|
|
157
|
+
if (file === ".git") {
|
|
158
|
+
continue;
|
|
173
159
|
}
|
|
160
|
+
fs.rmSync(path.resolve(dir, file), { recursive: true, force: true });
|
|
161
|
+
}
|
|
174
162
|
}
|
|
175
163
|
function copyDir(srcDir, destDir) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
164
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
165
|
+
for (const file of fs.readdirSync(srcDir)) {
|
|
166
|
+
const srcFile = path.resolve(srcDir, file);
|
|
167
|
+
const destFile = path.resolve(destDir, file);
|
|
168
|
+
copy(srcFile, destFile);
|
|
169
|
+
}
|
|
182
170
|
}
|
|
183
171
|
function copy(src, dest) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
172
|
+
const stat = fs.statSync(src);
|
|
173
|
+
if (stat.isDirectory()) {
|
|
174
|
+
copyDir(src, dest);
|
|
175
|
+
} else {
|
|
176
|
+
fs.copyFileSync(src, dest);
|
|
177
|
+
}
|
|
191
178
|
}
|
|
192
179
|
function run(command, args, cwd) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
});
|
|
202
|
-
child.on('error', reject);
|
|
180
|
+
return new Promise((resolve, reject) => {
|
|
181
|
+
const child = spawn(command, args, { cwd, stdio: "inherit" });
|
|
182
|
+
child.on("close", (code) => {
|
|
183
|
+
if (code !== 0) {
|
|
184
|
+
reject(new Error(`${command} ${args.join(" ")} failed`));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
resolve();
|
|
203
188
|
});
|
|
189
|
+
child.on("error", reject);
|
|
190
|
+
});
|
|
204
191
|
}
|
|
205
192
|
async function runWithLogger(command, args, cwd, packageManager) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
});
|
|
236
|
-
child.on('error', (err) => {
|
|
237
|
-
logger.error();
|
|
238
|
-
reject(err);
|
|
239
|
-
});
|
|
193
|
+
const logger = new InstallLogger(packageManager);
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
const child = spawn(command, args, {
|
|
196
|
+
cwd,
|
|
197
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
198
|
+
});
|
|
199
|
+
logger.start();
|
|
200
|
+
if (child.stdout) {
|
|
201
|
+
const stdoutReader = readline.createInterface({
|
|
202
|
+
input: child.stdout,
|
|
203
|
+
crlfDelay: Infinity
|
|
204
|
+
});
|
|
205
|
+
stdoutReader.on("line", (line) => logger.processLine(line, "stdout"));
|
|
206
|
+
}
|
|
207
|
+
if (child.stderr) {
|
|
208
|
+
const stderrReader = readline.createInterface({
|
|
209
|
+
input: child.stderr,
|
|
210
|
+
crlfDelay: Infinity
|
|
211
|
+
});
|
|
212
|
+
stderrReader.on("line", (line) => logger.processLine(line, "stderr"));
|
|
213
|
+
}
|
|
214
|
+
child.on("close", async (code) => {
|
|
215
|
+
if (code !== 0) {
|
|
216
|
+
logger.error();
|
|
217
|
+
reject(new Error(`${command} ${args.join(" ")} failed`));
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const stats = await logger.finish(cwd);
|
|
221
|
+
resolve(stats);
|
|
240
222
|
});
|
|
223
|
+
child.on("error", (err) => {
|
|
224
|
+
logger.error();
|
|
225
|
+
reject(err);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
241
228
|
}
|
|
242
229
|
function findViteConfigFile(projectDir) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
return null;
|
|
230
|
+
const candidates = ["vite.config.ts", "vite.config.js", "vite.config.mjs", "vite.config.cjs"];
|
|
231
|
+
for (const name of candidates) {
|
|
232
|
+
const p = path.join(projectDir, name);
|
|
233
|
+
if (fs.existsSync(p)) return p;
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
250
236
|
}
|
|
251
237
|
async function cloneViteTemplate(template, targetDir, packageManager, spinner) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
238
|
+
const errors = [];
|
|
239
|
+
for (let i = 0; i < TEMPLATE_SOURCES.length; i++) {
|
|
240
|
+
const source = TEMPLATE_SOURCES[i];
|
|
241
|
+
let tempTargetDir = null;
|
|
242
|
+
try {
|
|
243
|
+
if (i > 0) {
|
|
244
|
+
spinner.message(`Trying ${source.name}...`);
|
|
245
|
+
}
|
|
246
|
+
const { command, args } = source.getCloneCommand(template, targetDir, packageManager);
|
|
247
|
+
if (source.isGitBased) {
|
|
248
|
+
const tempCloneDir = path.join(process.cwd(), `.tmp-clone-${randomUUID()}`);
|
|
249
|
+
const cloneArgs = [...args.slice(0, -1), tempCloneDir];
|
|
250
|
+
spinner.stop();
|
|
251
|
+
await run(command, cloneArgs, process.cwd());
|
|
252
|
+
spinner.start("Processing template");
|
|
256
253
|
try {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
try {
|
|
274
|
-
// Extract the template folder from the cloned repo
|
|
275
|
-
const templateSrcPath = path.join(tempCloneDir, 'packages/create-vite', `template-${template}`);
|
|
276
|
-
if (!fs.existsSync(templateSrcPath)) {
|
|
277
|
-
throw new Error(`Template not found at packages/create-vite/template-${template}. The repository structure may have changed.`);
|
|
278
|
-
}
|
|
279
|
-
// Move the template files to a temp location with unique name
|
|
280
|
-
tempTargetDir = path.join(process.cwd(), `.tmp-template-${randomUUID()}`);
|
|
281
|
-
copyDir(templateSrcPath, tempTargetDir);
|
|
282
|
-
// Clean up the cloned repo
|
|
283
|
-
fs.rmSync(tempCloneDir, { recursive: true, force: true });
|
|
284
|
-
// Move the template to the final location
|
|
285
|
-
fs.renameSync(tempTargetDir, targetDir);
|
|
286
|
-
tempTargetDir = null; // Mark as successfully moved
|
|
287
|
-
}
|
|
288
|
-
catch (extractError) {
|
|
289
|
-
// Clean up temp clone directory on extraction error
|
|
290
|
-
if (fs.existsSync(tempCloneDir)) {
|
|
291
|
-
fs.rmSync(tempCloneDir, { recursive: true, force: true });
|
|
292
|
-
}
|
|
293
|
-
throw extractError;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
// For degit sources, command and args are already properly formatted
|
|
298
|
-
await run(command, args, process.cwd());
|
|
299
|
-
}
|
|
300
|
-
return;
|
|
254
|
+
const templateSrcPath = path.join(tempCloneDir, "packages/create-vite", `template-${template}`);
|
|
255
|
+
if (!fs.existsSync(templateSrcPath)) {
|
|
256
|
+
throw new Error(
|
|
257
|
+
`Template not found at packages/create-vite/template-${template}. The repository structure may have changed.`
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
tempTargetDir = path.join(process.cwd(), `.tmp-template-${randomUUID()}`);
|
|
261
|
+
copyDir(templateSrcPath, tempTargetDir);
|
|
262
|
+
fs.rmSync(tempCloneDir, { recursive: true, force: true });
|
|
263
|
+
fs.renameSync(tempTargetDir, targetDir);
|
|
264
|
+
tempTargetDir = null;
|
|
265
|
+
} catch (extractError) {
|
|
266
|
+
if (fs.existsSync(tempCloneDir)) {
|
|
267
|
+
fs.rmSync(tempCloneDir, { recursive: true, force: true });
|
|
268
|
+
}
|
|
269
|
+
throw extractError;
|
|
301
270
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
else {
|
|
320
|
-
// This is the last source, throw comprehensive error
|
|
321
|
-
const errorDetails = errors
|
|
322
|
-
.map(e => ` • ${e.source}: ${e.error}`)
|
|
323
|
-
.join('\n');
|
|
324
|
-
throw new Error(`Failed to clone template from all sources:\n${errorDetails}\n\nPlease check your network connection or try again later.`);
|
|
325
|
-
}
|
|
271
|
+
} else {
|
|
272
|
+
await run(command, args, process.cwd());
|
|
273
|
+
}
|
|
274
|
+
return;
|
|
275
|
+
} catch (error) {
|
|
276
|
+
if (tempTargetDir && fs.existsSync(tempTargetDir)) {
|
|
277
|
+
fs.rmSync(tempTargetDir, { recursive: true, force: true });
|
|
278
|
+
}
|
|
279
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
280
|
+
errors.push({ source: source.name, error: errorMsg });
|
|
281
|
+
if (i < TEMPLATE_SOURCES.length - 1) {
|
|
282
|
+
if (source.isGitBased) {
|
|
283
|
+
spinner.start(`${source.name} failed, trying ${TEMPLATE_SOURCES[i + 1].name}`);
|
|
284
|
+
} else {
|
|
285
|
+
spinner.message(`${source.name} failed, trying ${TEMPLATE_SOURCES[i + 1].name}...`);
|
|
326
286
|
}
|
|
287
|
+
} else {
|
|
288
|
+
const errorDetails = errors.map((e) => ` \u2022 ${e.source}: ${e.error}`).join("\n");
|
|
289
|
+
throw new Error(
|
|
290
|
+
`Failed to clone template from all sources:
|
|
291
|
+
${errorDetails}
|
|
292
|
+
|
|
293
|
+
Please check your network connection or try again later.`
|
|
294
|
+
);
|
|
295
|
+
}
|
|
327
296
|
}
|
|
297
|
+
}
|
|
328
298
|
}
|
|
329
299
|
function getDegitCommandForPM(pm) {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
300
|
+
switch (pm) {
|
|
301
|
+
case "pnpm":
|
|
302
|
+
return {
|
|
303
|
+
command: "pnpx",
|
|
304
|
+
args: (cmd, cmdArgs) => [cmd, ...cmdArgs]
|
|
305
|
+
};
|
|
306
|
+
case "npm":
|
|
307
|
+
return {
|
|
308
|
+
command: "npx",
|
|
309
|
+
args: (cmd, cmdArgs) => [cmd, ...cmdArgs]
|
|
310
|
+
};
|
|
311
|
+
case "yarn":
|
|
312
|
+
return {
|
|
313
|
+
command: "yarn",
|
|
314
|
+
args: (cmd, cmdArgs) => ["dlx", cmd, ...cmdArgs]
|
|
315
|
+
};
|
|
316
|
+
case "bun":
|
|
317
|
+
return {
|
|
318
|
+
command: "bunx",
|
|
319
|
+
args: (cmd, cmdArgs) => [cmd, ...cmdArgs]
|
|
320
|
+
};
|
|
321
|
+
}
|
|
352
322
|
}
|
|
353
323
|
function injectPluginIntoViteConfig(content, pluginPackage, pluginName) {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
324
|
+
const hasImport = new RegExp(`['"]${pluginPackage.replace(/\//g, "\\/")}['"]`).test(content);
|
|
325
|
+
const hasCall = content.includes(`${pluginName}(`);
|
|
326
|
+
let out = content;
|
|
327
|
+
if (!hasImport) {
|
|
328
|
+
const importMatches = Array.from(out.matchAll(/^import .+$/gm));
|
|
329
|
+
if (importMatches.length > 0) {
|
|
330
|
+
const last = importMatches[importMatches.length - 1];
|
|
331
|
+
const insertPos = (last.index ?? 0) + last[0].length;
|
|
332
|
+
out = `${out.slice(0, insertPos)}
|
|
333
|
+
import { ${pluginName} } from '${pluginPackage}'
|
|
334
|
+
${out.slice(insertPos)}`;
|
|
335
|
+
} else {
|
|
336
|
+
out = `import { ${pluginName} } from '${pluginPackage}'
|
|
337
|
+
${out}`;
|
|
368
338
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
const firstPluginLine = pluginLines[0];
|
|
385
|
-
const match = firstPluginLine.match(/^(\s+)/);
|
|
386
|
-
if (match)
|
|
387
|
-
indent = match[1];
|
|
388
|
-
}
|
|
389
|
-
// 在最后一个插件后添加新插件
|
|
390
|
-
const trimmedInner = inner.trimEnd();
|
|
391
|
-
const hasTrailingComma = trimmedInner.trim().endsWith(',');
|
|
392
|
-
const newPlugin = `${indent}${pluginName}(),`;
|
|
393
|
-
// 找到最后一个非空白行
|
|
394
|
-
const lastContentIndex = trimmedInner.lastIndexOf('\n');
|
|
395
|
-
if (lastContentIndex === -1) {
|
|
396
|
-
// 单行但包含换行符的情况
|
|
397
|
-
out = out.replace(full, `plugins: [\n${newPlugin}\n ]`);
|
|
398
|
-
}
|
|
399
|
-
else {
|
|
400
|
-
const beforeLast = trimmedInner.slice(0, lastContentIndex + 1);
|
|
401
|
-
const lastLine = trimmedInner.slice(lastContentIndex + 1);
|
|
402
|
-
if (!hasTrailingComma && lastLine.trim()) {
|
|
403
|
-
// 最后一行没有逗号,需要添加
|
|
404
|
-
out = out.replace(full, `plugins: [${beforeLast}${lastLine},\n${newPlugin}\n ]`);
|
|
405
|
-
}
|
|
406
|
-
else {
|
|
407
|
-
// 最后一行有逗号或为空
|
|
408
|
-
out = out.replace(full, `plugins: [${trimmedInner}\n${newPlugin}\n ]`);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
else {
|
|
413
|
-
// 单行格式
|
|
414
|
-
const compactInner = inner.trim();
|
|
415
|
-
const nextInner = compactInner ? `${compactInner}, ${pluginName}()` : `${pluginName}()`;
|
|
416
|
-
out = out.replace(full, `plugins: [${nextInner}]`);
|
|
417
|
-
}
|
|
339
|
+
}
|
|
340
|
+
if (!hasCall) {
|
|
341
|
+
const pluginsRegex = /plugins\s*:\s*\[([^\]]*(?:\[[^\]]*\][^\]]*)*)\]/;
|
|
342
|
+
const m = pluginsRegex.exec(out);
|
|
343
|
+
if (m && m.index !== void 0) {
|
|
344
|
+
const full = m[0];
|
|
345
|
+
const inner = m[1] || "";
|
|
346
|
+
if (inner.includes("\n")) {
|
|
347
|
+
const lines = inner.split("\n");
|
|
348
|
+
const pluginLines = lines.filter((line) => line.trim() && !line.trim().startsWith("//"));
|
|
349
|
+
let indent = " ";
|
|
350
|
+
if (pluginLines.length > 0) {
|
|
351
|
+
const firstPluginLine = pluginLines[0];
|
|
352
|
+
const match = firstPluginLine.match(/^(\s+)/);
|
|
353
|
+
if (match) indent = match[1];
|
|
418
354
|
}
|
|
355
|
+
const trimmedInner = inner.trimEnd();
|
|
356
|
+
const hasTrailingComma = trimmedInner.trim().endsWith(",");
|
|
357
|
+
const newPlugin = `${indent}${pluginName}(),`;
|
|
358
|
+
const lastContentIndex = trimmedInner.lastIndexOf("\n");
|
|
359
|
+
if (lastContentIndex === -1) {
|
|
360
|
+
out = out.replace(full, `plugins: [
|
|
361
|
+
${newPlugin}
|
|
362
|
+
]`);
|
|
363
|
+
} else {
|
|
364
|
+
const beforeLast = trimmedInner.slice(0, lastContentIndex + 1);
|
|
365
|
+
const lastLine = trimmedInner.slice(lastContentIndex + 1);
|
|
366
|
+
if (!hasTrailingComma && lastLine.trim()) {
|
|
367
|
+
out = out.replace(full, `plugins: [${beforeLast}${lastLine},
|
|
368
|
+
${newPlugin}
|
|
369
|
+
]`);
|
|
370
|
+
} else {
|
|
371
|
+
out = out.replace(full, `plugins: [${trimmedInner}
|
|
372
|
+
${newPlugin}
|
|
373
|
+
]`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
} else {
|
|
377
|
+
const compactInner = inner.trim();
|
|
378
|
+
const nextInner = compactInner ? `${compactInner}, ${pluginName}()` : `${pluginName}()`;
|
|
379
|
+
out = out.replace(full, `plugins: [${nextInner}]`);
|
|
380
|
+
}
|
|
419
381
|
}
|
|
420
|
-
|
|
382
|
+
}
|
|
383
|
+
return out;
|
|
421
384
|
}
|
|
422
385
|
function updatePluginComponentName(content, pluginName, componentName) {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
const newPluginCall = `${pluginName}({
|
|
386
|
+
const pluginCall = `${pluginName}()`;
|
|
387
|
+
const newPluginCall = `${pluginName}({
|
|
426
388
|
${componentName}: 'src/${componentName}/index.tsx',
|
|
427
389
|
})`;
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
390
|
+
if (content.includes(pluginCall)) {
|
|
391
|
+
return content.replace(pluginCall, newPluginCall);
|
|
392
|
+
}
|
|
393
|
+
return content;
|
|
394
|
+
}
|
|
395
|
+
function editFile(file, callback) {
|
|
396
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
397
|
+
fs.writeFileSync(file, callback(content), "utf-8");
|
|
398
|
+
}
|
|
399
|
+
function setupReactSWC(root, isTs) {
|
|
400
|
+
editFile(path.join(root, "package.json"), (content) => {
|
|
401
|
+
return content.replace(
|
|
402
|
+
/"@vitejs\/plugin-react": ".+?"/,
|
|
403
|
+
'"@vitejs/plugin-react-swc": "^4.2.2"'
|
|
404
|
+
);
|
|
405
|
+
});
|
|
406
|
+
editFile(path.join(root, `vite.config.${isTs ? "ts" : "js"}`), (content) => {
|
|
407
|
+
return content.replace("@vitejs/plugin-react", "@vitejs/plugin-react-swc");
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
function setupReactCompiler(root, isTs) {
|
|
411
|
+
editFile(path.join(root, "package.json"), (content) => {
|
|
412
|
+
const asObject = JSON.parse(content);
|
|
413
|
+
const devDepsEntries = Object.entries(asObject.devDependencies || {});
|
|
414
|
+
devDepsEntries.push(["babel-plugin-react-compiler", "^1.0.0"]);
|
|
415
|
+
devDepsEntries.sort();
|
|
416
|
+
asObject.devDependencies = Object.fromEntries(devDepsEntries);
|
|
417
|
+
return JSON.stringify(asObject, null, 2) + "\n";
|
|
418
|
+
});
|
|
419
|
+
editFile(path.join(root, `vite.config.${isTs ? "ts" : "js"}`), (content) => {
|
|
420
|
+
return content.replace(
|
|
421
|
+
" plugins: [react()],",
|
|
422
|
+
` plugins: [
|
|
423
|
+
react({
|
|
424
|
+
babel: {
|
|
425
|
+
plugins: [['babel-plugin-react-compiler']],
|
|
426
|
+
},
|
|
427
|
+
}),
|
|
428
|
+
],`
|
|
429
|
+
);
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
function printBanner() {
|
|
433
|
+
const version = __BUILD_INFO__.version;
|
|
434
|
+
const commit = __BUILD_INFO__.commit;
|
|
435
|
+
const branch = __BUILD_INFO__.branch;
|
|
436
|
+
const buildTimeUTC = __BUILD_INFO__.buildTime;
|
|
437
|
+
const [date, time] = buildTimeUTC.split(" ");
|
|
438
|
+
const utcDateTime = /* @__PURE__ */ new Date(`${date}T${time}:00Z`);
|
|
439
|
+
const year = utcDateTime.getFullYear();
|
|
440
|
+
const month = String(utcDateTime.getMonth() + 1).padStart(2, "0");
|
|
441
|
+
const day = String(utcDateTime.getDate()).padStart(2, "0");
|
|
442
|
+
const hours = String(utcDateTime.getHours()).padStart(2, "0");
|
|
443
|
+
const minutes = String(utcDateTime.getMinutes()).padStart(2, "0");
|
|
444
|
+
const localTime = `${year}-${month}-${day} ${hours}:${minutes}`;
|
|
445
|
+
const logo = `
|
|
446
|
+
${cyan(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557")}${yellow(" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557")} ${green(`v${version}`)}
|
|
447
|
+
${cyan(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551")}${yellow(" \u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557")} ${dim(`${commit === "unknown" ? "" : `${commit} on ${branch}`}`)}
|
|
448
|
+
${cyan(" \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551")}${yellow(" \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551")} ${dim(`${localTime}`)}
|
|
449
|
+
${cyan(" \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D")}${yellow(" \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551")}
|
|
450
|
+
${cyan(" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2588\u2554\u255D")}${yellow(" \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D")}
|
|
451
|
+
${cyan(" \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u255D")}${yellow(" \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D")}
|
|
452
|
+
`;
|
|
453
|
+
console.log(logo);
|
|
433
454
|
}
|
|
434
455
|
function addDevDependency(projectDir, pkgName, version) {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
456
|
+
const pkgPath = path.join(projectDir, "package.json");
|
|
457
|
+
const raw = fs.readFileSync(pkgPath, "utf-8");
|
|
458
|
+
const pkg = JSON.parse(raw);
|
|
459
|
+
const devDependencies = pkg["devDependencies"] ?? {};
|
|
460
|
+
if (!devDependencies[pkgName]) devDependencies[pkgName] = version;
|
|
461
|
+
pkg["devDependencies"] = devDependencies;
|
|
462
|
+
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
463
|
+
`);
|
|
443
464
|
}
|
|
444
465
|
async function init() {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
}
|
|
466
|
-
if (isEmpty(root)) {
|
|
467
|
-
return Promise.resolve(false);
|
|
468
|
-
}
|
|
469
|
-
return clack.confirm({
|
|
470
|
-
message: targetDir === '.'
|
|
471
|
-
? 'Current directory is not empty. Remove existing files and continue?'
|
|
472
|
-
: `Target directory "${targetDir}" is not empty. Remove existing files and continue?`,
|
|
473
|
-
});
|
|
474
|
-
},
|
|
475
|
-
componentName: ({ results }) => {
|
|
476
|
-
// Convert project name to PascalCase for component name
|
|
477
|
-
const projectName = results.projectName || 'dev-to-app';
|
|
478
|
-
const defaultComponentName = projectName
|
|
479
|
-
.split(/[-_\s]+/)
|
|
480
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
481
|
-
.join('');
|
|
482
|
-
return clack.text({
|
|
483
|
-
message: `First component name ${dim('\n│ Leave blank to default to project name.You can modify it in vite config later.\n')}`,
|
|
484
|
-
placeholder: defaultComponentName,
|
|
485
|
-
defaultValue: defaultComponentName,
|
|
486
|
-
});
|
|
487
|
-
},
|
|
488
|
-
}, {
|
|
489
|
-
onCancel: () => {
|
|
490
|
-
clack.cancel('Operation cancelled.');
|
|
491
|
-
process.exit(0);
|
|
492
|
-
},
|
|
493
|
-
});
|
|
494
|
-
const { shouldOverwrite, componentName } = project;
|
|
495
|
-
// 覆盖目录
|
|
496
|
-
if (shouldOverwrite) {
|
|
497
|
-
emptyDir(path.join(cwd, targetDir));
|
|
498
|
-
}
|
|
499
|
-
// 包管理器选择
|
|
500
|
-
if (!packageManager) {
|
|
501
|
-
const pmChoice = await clack.select({
|
|
502
|
-
message: 'Select a package manager:',
|
|
503
|
-
options: PACKAGE_MANAGERS.map(pm => ({
|
|
504
|
-
value: pm,
|
|
505
|
-
label: pm,
|
|
506
|
-
})),
|
|
507
|
-
});
|
|
508
|
-
if (clack.isCancel(pmChoice)) {
|
|
509
|
-
clack.cancel('Operation cancelled.');
|
|
510
|
-
process.exit(0);
|
|
466
|
+
const userAgent = process.env.npm_config_user_agent || "";
|
|
467
|
+
let packageManager = detectPackageManager(userAgent);
|
|
468
|
+
printBanner();
|
|
469
|
+
const cwd = process.cwd();
|
|
470
|
+
const argTargetDir = formatTargetDir(process.argv[2]);
|
|
471
|
+
let targetDir = argTargetDir || ".";
|
|
472
|
+
const getProjectName = () => targetDir === "." ? path.basename(path.resolve()) : targetDir;
|
|
473
|
+
const project = await clack.group(
|
|
474
|
+
{
|
|
475
|
+
projectName: () => clack.text({
|
|
476
|
+
message: `Project name ${dim("\n\u2502 Directory where your project will be created.\n")}`,
|
|
477
|
+
placeholder: "dev-to-app",
|
|
478
|
+
initialValue: argTargetDir,
|
|
479
|
+
defaultValue: argTargetDir || "dev-to-app"
|
|
480
|
+
}),
|
|
481
|
+
shouldOverwrite: ({ results }) => {
|
|
482
|
+
targetDir = formatTargetDir(results.projectName) || "dev-to-app";
|
|
483
|
+
const root2 = path.join(cwd, targetDir);
|
|
484
|
+
if (!fs.existsSync(root2)) {
|
|
485
|
+
return Promise.resolve(false);
|
|
511
486
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
clack.
|
|
532
|
-
clack.note(`We're working hard to add support for ${selectedFramework?.display}.\n\nFor now, please use React or stay tuned for updates!`, 'Roadmap');
|
|
533
|
-
process.exit(0);
|
|
534
|
-
}
|
|
535
|
-
// React 模板选择
|
|
536
|
-
const variant = await clack.select({
|
|
537
|
-
message: 'Select a variant:',
|
|
538
|
-
options: REACT_TEMPLATES.map(template => ({
|
|
539
|
-
value: template.name,
|
|
540
|
-
label: template.color(template.display),
|
|
541
|
-
})),
|
|
542
|
-
});
|
|
543
|
-
if (clack.isCancel(variant)) {
|
|
544
|
-
clack.cancel('Operation cancelled.');
|
|
545
|
-
process.exit(0);
|
|
546
|
-
}
|
|
547
|
-
const template = variant;
|
|
548
|
-
// 询问是否使用 Rolldown(实验性)
|
|
549
|
-
const shouldUseRolldown = await clack.confirm({
|
|
550
|
-
message: 'Use Rolldown for bundling? (Experimental)',
|
|
551
|
-
initialValue: false,
|
|
552
|
-
});
|
|
553
|
-
if (clack.isCancel(shouldUseRolldown)) {
|
|
554
|
-
clack.cancel('Operation cancelled.');
|
|
487
|
+
if (isEmpty(root2)) {
|
|
488
|
+
return Promise.resolve(false);
|
|
489
|
+
}
|
|
490
|
+
return clack.confirm({
|
|
491
|
+
message: targetDir === "." ? "Current directory is not empty. Remove existing files and continue?" : `Target directory "${targetDir}" is not empty. Remove existing files and continue?`
|
|
492
|
+
});
|
|
493
|
+
},
|
|
494
|
+
componentName: ({ results }) => {
|
|
495
|
+
const projectName = results.projectName || "dev-to-app";
|
|
496
|
+
const defaultComponentName = projectName.split(/[-_\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
497
|
+
return clack.text({
|
|
498
|
+
message: `First component name ${dim("\n\u2502 Leave blank to default to project name.You can modify it in vite config later.\n")}`,
|
|
499
|
+
placeholder: defaultComponentName,
|
|
500
|
+
defaultValue: defaultComponentName
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
onCancel: () => {
|
|
506
|
+
clack.cancel("Operation cancelled.");
|
|
555
507
|
process.exit(0);
|
|
508
|
+
}
|
|
556
509
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
const
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
const pluginPackage = '@dev-to/react-plugin';
|
|
570
|
-
const pluginName = 'devToReactPlugin';
|
|
571
|
-
addDevDependency(root, pluginPackage, 'latest');
|
|
572
|
-
// 注入插件到 vite.config
|
|
573
|
-
const viteConfigPath = findViteConfigFile(root);
|
|
574
|
-
if (viteConfigPath) {
|
|
575
|
-
let patched = fs.readFileSync(viteConfigPath, 'utf-8');
|
|
576
|
-
patched = injectPluginIntoViteConfig(patched, pluginPackage, pluginName);
|
|
577
|
-
patched = updatePluginComponentName(patched, pluginName, componentName);
|
|
578
|
-
fs.writeFileSync(viteConfigPath, patched);
|
|
579
|
-
}
|
|
580
|
-
spinner.stop('Project created');
|
|
581
|
-
// 询问是否立即安装
|
|
582
|
-
const shouldInstall = await clack.confirm({
|
|
583
|
-
message: 'Install dependencies and start dev server?',
|
|
584
|
-
initialValue: true,
|
|
510
|
+
);
|
|
511
|
+
const { shouldOverwrite, componentName } = project;
|
|
512
|
+
if (shouldOverwrite) {
|
|
513
|
+
emptyDir(path.join(cwd, targetDir));
|
|
514
|
+
}
|
|
515
|
+
if (!packageManager) {
|
|
516
|
+
const pmChoice = await clack.select({
|
|
517
|
+
message: "Select a package manager:",
|
|
518
|
+
options: PACKAGE_MANAGERS.map((pm) => ({
|
|
519
|
+
value: pm,
|
|
520
|
+
label: pm
|
|
521
|
+
}))
|
|
585
522
|
});
|
|
586
|
-
if (clack.isCancel(
|
|
587
|
-
|
|
523
|
+
if (clack.isCancel(pmChoice)) {
|
|
524
|
+
clack.cancel("Operation cancelled.");
|
|
525
|
+
process.exit(0);
|
|
588
526
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
527
|
+
packageManager = pmChoice;
|
|
528
|
+
}
|
|
529
|
+
const framework = await clack.select({
|
|
530
|
+
message: "Select a framework:",
|
|
531
|
+
options: FRAMEWORKS.map((fw) => ({
|
|
532
|
+
value: fw.name,
|
|
533
|
+
label: fw.supported ? fw.color(fw.display) : `${fw.color(fw.display)} ${dim("(Coming soon)")}`,
|
|
534
|
+
hint: fw.supported ? void 0 : "Not yet supported"
|
|
535
|
+
})),
|
|
536
|
+
initialValue: "react"
|
|
537
|
+
});
|
|
538
|
+
if (clack.isCancel(framework)) {
|
|
539
|
+
clack.cancel("Operation cancelled.");
|
|
540
|
+
process.exit(0);
|
|
541
|
+
}
|
|
542
|
+
const selectedFramework = FRAMEWORKS.find((fw) => fw.name === framework);
|
|
543
|
+
if (!selectedFramework?.supported) {
|
|
544
|
+
clack.outro(yellow(`\u26A0\uFE0F ${selectedFramework?.display} support is coming soon!`));
|
|
545
|
+
clack.note(
|
|
546
|
+
`We're working hard to add support for ${selectedFramework?.display}.
|
|
547
|
+
|
|
548
|
+
For now, please use React or stay tuned for updates!`,
|
|
549
|
+
"Roadmap"
|
|
550
|
+
);
|
|
551
|
+
process.exit(0);
|
|
552
|
+
}
|
|
553
|
+
const variant = await clack.select({
|
|
554
|
+
message: "Select a variant:",
|
|
555
|
+
options: REACT_TEMPLATES.map((template2) => ({
|
|
556
|
+
value: template2.name,
|
|
557
|
+
label: template2.color(template2.display)
|
|
558
|
+
}))
|
|
559
|
+
});
|
|
560
|
+
if (clack.isCancel(variant)) {
|
|
561
|
+
clack.cancel("Operation cancelled.");
|
|
562
|
+
process.exit(0);
|
|
563
|
+
}
|
|
564
|
+
const template = variant;
|
|
565
|
+
const shouldUseSWC = await clack.confirm({
|
|
566
|
+
message: "Use SWC for faster transpilation? (Optional)",
|
|
567
|
+
initialValue: false
|
|
568
|
+
});
|
|
569
|
+
if (clack.isCancel(shouldUseSWC)) {
|
|
570
|
+
clack.cancel("Operation cancelled.");
|
|
571
|
+
process.exit(0);
|
|
572
|
+
}
|
|
573
|
+
const shouldUseReactCompiler = await clack.confirm({
|
|
574
|
+
message: "Use React Compiler? (Experimental)",
|
|
575
|
+
initialValue: false
|
|
576
|
+
});
|
|
577
|
+
if (clack.isCancel(shouldUseReactCompiler)) {
|
|
578
|
+
clack.cancel("Operation cancelled.");
|
|
579
|
+
process.exit(0);
|
|
580
|
+
}
|
|
581
|
+
const shouldUseRolldown = await clack.confirm({
|
|
582
|
+
message: "Use Rolldown for bundling? (Experimental)",
|
|
583
|
+
initialValue: false
|
|
584
|
+
});
|
|
585
|
+
if (clack.isCancel(shouldUseRolldown)) {
|
|
586
|
+
clack.cancel("Operation cancelled.");
|
|
587
|
+
process.exit(0);
|
|
588
|
+
}
|
|
589
|
+
const root = path.join(cwd, targetDir);
|
|
590
|
+
const spinner = clack.spinner();
|
|
591
|
+
spinner.start("Scaffolding project");
|
|
592
|
+
await cloneViteTemplate(template, root, packageManager, spinner);
|
|
593
|
+
const pkgPath = path.join(root, "package.json");
|
|
594
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
595
|
+
pkg.name = toValidPackageName(getProjectName());
|
|
596
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
597
|
+
const isTs = template.includes("ts");
|
|
598
|
+
if (shouldUseSWC) {
|
|
599
|
+
spinner.message("Setting up SWC...");
|
|
600
|
+
setupReactSWC(root, isTs);
|
|
601
|
+
}
|
|
602
|
+
if (shouldUseReactCompiler) {
|
|
603
|
+
spinner.message("Setting up React Compiler...");
|
|
604
|
+
setupReactCompiler(root, isTs);
|
|
605
|
+
}
|
|
606
|
+
spinner.message("Adding @dev-to/react-plugin");
|
|
607
|
+
const pluginPackage = "@dev-to/react-plugin";
|
|
608
|
+
const pluginName = "devToReactPlugin";
|
|
609
|
+
addDevDependency(root, pluginPackage, "latest");
|
|
610
|
+
const viteConfigPath = findViteConfigFile(root);
|
|
611
|
+
if (viteConfigPath) {
|
|
612
|
+
let patched = fs.readFileSync(viteConfigPath, "utf-8");
|
|
613
|
+
patched = injectPluginIntoViteConfig(patched, pluginPackage, pluginName);
|
|
614
|
+
patched = updatePluginComponentName(patched, pluginName, componentName);
|
|
615
|
+
fs.writeFileSync(viteConfigPath, patched);
|
|
616
|
+
}
|
|
617
|
+
spinner.stop("Project created");
|
|
618
|
+
const shouldInstall = await clack.confirm({
|
|
619
|
+
message: "Install dependencies and start dev server?",
|
|
620
|
+
initialValue: true
|
|
621
|
+
});
|
|
622
|
+
if (clack.isCancel(shouldInstall)) {
|
|
623
|
+
}
|
|
624
|
+
if (shouldInstall) {
|
|
625
|
+
try {
|
|
626
|
+
const stats = await runWithLogger(packageManager, ["install"], root, packageManager);
|
|
627
|
+
displayInstallSummary(stats);
|
|
628
|
+
clack.log.info("Starting dev server...");
|
|
629
|
+
const devArgs = packageManager === "npm" ? ["run", "dev"] : ["dev"];
|
|
630
|
+
spawn(packageManager, devArgs, { cwd: root, stdio: "inherit" });
|
|
631
|
+
} catch (error) {
|
|
632
|
+
clack.log.error("Installation failed");
|
|
633
|
+
throw error;
|
|
610
634
|
}
|
|
635
|
+
} else {
|
|
636
|
+
const pkgManager = packageManager || "npm";
|
|
637
|
+
const cdPath = targetDir !== "." ? `cd ${targetDir}` : null;
|
|
638
|
+
const installCmd = PM_CONFIGS[pkgManager].install;
|
|
639
|
+
const devCmd = PM_CONFIGS[pkgManager].dev;
|
|
640
|
+
const nextSteps = [cdPath, installCmd, devCmd].filter(Boolean).join("\n ");
|
|
641
|
+
clack.note(nextSteps, "Next steps");
|
|
642
|
+
clack.outro("Done");
|
|
643
|
+
}
|
|
611
644
|
}
|
|
612
645
|
init().catch((e) => {
|
|
613
|
-
|
|
614
|
-
|
|
646
|
+
clack.log.error(red(e.message || e));
|
|
647
|
+
process.exit(1);
|
|
615
648
|
});
|
|
616
|
-
//# sourceMappingURL=index.js.map
|