@yuw-cli-dev/init 1.0.38 → 1.0.39
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/lib/index.js +262 -50
- package/package.json +9 -4
package/lib/index.js
CHANGED
|
@@ -5,13 +5,21 @@ const path = require('path');
|
|
|
5
5
|
const inquire = require('inquirer');
|
|
6
6
|
const fse = require('fs-extra');
|
|
7
7
|
const semver = require('semver');
|
|
8
|
+
const userHome = require('user-home');
|
|
8
9
|
const isValidFilename = require('valid-filename');
|
|
10
|
+
const glob = require('glob');
|
|
11
|
+
const ejs = require('ejs');
|
|
9
12
|
const Command = require('@yuw-cli-dev/command');
|
|
10
13
|
const log = require('@yuw-cli-dev/log');
|
|
14
|
+
const Package = require('@yuw-cli-dev/package');
|
|
15
|
+
const { spinnerStart, execSync } = require('@yuw-cli-dev/utils');
|
|
11
16
|
const getProjectTemplate = require('./getProjectTemplate');
|
|
12
17
|
|
|
13
18
|
const TYPE_PROJECT = 'project';
|
|
14
19
|
const TYPE_COMPONENT = 'component';
|
|
20
|
+
const TEMPLATE_TYPE_NORMAL = 'normal';
|
|
21
|
+
const TEMPLATE_TYPE_CUSTOM = 'custom';
|
|
22
|
+
const WHITE_COMMAND = ['npm', 'cnpm', 'yarn', 'pnpm'];
|
|
15
23
|
|
|
16
24
|
class InitCommand extends Command {
|
|
17
25
|
init() {
|
|
@@ -27,13 +35,201 @@ class InitCommand extends Command {
|
|
|
27
35
|
if (projectInfo) {
|
|
28
36
|
this.projectInfo = projectInfo;
|
|
29
37
|
await this.downloadTemplate();
|
|
38
|
+
await this.installTemplate();
|
|
30
39
|
}
|
|
31
40
|
} catch (e) {
|
|
32
41
|
log.error(e.message);
|
|
33
42
|
}
|
|
34
43
|
}
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
checkCommand(cmd) {
|
|
45
|
+
if (WHITE_COMMAND.includes(cmd)) {
|
|
46
|
+
return cmd;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async execCmd(command, errMsg) {
|
|
52
|
+
const installCmdArr = command.split(' ');
|
|
53
|
+
const installCmd = installCmdArr[0];
|
|
54
|
+
const checkedCmd = this.checkCommand(installCmd);
|
|
55
|
+
if (!checkedCmd) {
|
|
56
|
+
throw new Error('命令不存在,请检查:', installCmd);
|
|
57
|
+
}
|
|
58
|
+
const installArgs = installCmdArr.slice(1);
|
|
59
|
+
const ret = await execSync(checkedCmd, installArgs, {
|
|
60
|
+
cwd: process.cwd(),
|
|
61
|
+
stdio: 'inherit',
|
|
62
|
+
});
|
|
63
|
+
if (ret !== 0) {
|
|
64
|
+
throw new Error(errMsg);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async ejsRender(options) {
|
|
69
|
+
const dir = process.cwd();
|
|
70
|
+
const projectInfo = this.projectInfo;
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
glob(
|
|
73
|
+
'**',
|
|
74
|
+
{
|
|
75
|
+
cwd: dir,
|
|
76
|
+
nodir: true,
|
|
77
|
+
ignore: options && options.ignore,
|
|
78
|
+
},
|
|
79
|
+
(err, files) => {
|
|
80
|
+
if (err) {
|
|
81
|
+
reject(err);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
Promise.all(
|
|
85
|
+
files.map(file => {
|
|
86
|
+
const filePath = path.join(dir, file);
|
|
87
|
+
return new Promise((res, rej) => {
|
|
88
|
+
ejs.renderFile(
|
|
89
|
+
filePath,
|
|
90
|
+
{
|
|
91
|
+
className: projectInfo.projectName,
|
|
92
|
+
version: projectInfo.projectVersion,
|
|
93
|
+
},
|
|
94
|
+
{},
|
|
95
|
+
(e, result) => {
|
|
96
|
+
if (e) {
|
|
97
|
+
rej(e);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
fse.writeFileSync(filePath, result);
|
|
101
|
+
res();
|
|
102
|
+
},
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
}),
|
|
106
|
+
)
|
|
107
|
+
.then(() => {
|
|
108
|
+
resolve();
|
|
109
|
+
})
|
|
110
|
+
.catch(e => {
|
|
111
|
+
reject(e);
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async installTemplate() {
|
|
119
|
+
if (this.templateInfo) {
|
|
120
|
+
if (!this.templateInfo.type) {
|
|
121
|
+
this.templateInfo.type = TEMPLATE_TYPE_NORMAL;
|
|
122
|
+
}
|
|
123
|
+
if (this.templateInfo.type === TEMPLATE_TYPE_NORMAL) {
|
|
124
|
+
await this.installNormalTemplate();
|
|
125
|
+
} else if (this.templateInfo.type === TEMPLATE_TYPE_CUSTOM) {
|
|
126
|
+
await this.installCustomTemplate();
|
|
127
|
+
} else {
|
|
128
|
+
throw new Error('无法识别项目模板类型');
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
throw new Error('项目模板信息不存在');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 进入指定目录
|
|
135
|
+
process.chdir(path.resolve(process.cwd(), this.projectInfo.projectName));
|
|
136
|
+
|
|
137
|
+
const ignore = ['node_modules/**', 'public/**'];
|
|
138
|
+
await this.ejsRender({ ignore });
|
|
139
|
+
// 安装依赖并启动项目
|
|
140
|
+
const { installCommand, startCommand } = this.templateInfo;
|
|
141
|
+
if (installCommand) {
|
|
142
|
+
await this.execCmd(installCommand, '依赖安装过程中失败');
|
|
143
|
+
}
|
|
144
|
+
if (startCommand) {
|
|
145
|
+
await this.execCmd(startCommand, '启动项目过程中失败');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async installNormalTemplate() {
|
|
150
|
+
let spinner;
|
|
151
|
+
try {
|
|
152
|
+
const templatePath = path.resolve(
|
|
153
|
+
this.templateNpm.cacheFilePath,
|
|
154
|
+
'template',
|
|
155
|
+
);
|
|
156
|
+
const targetPath = path.resolve(
|
|
157
|
+
process.cwd(),
|
|
158
|
+
this.projectInfo.projectName,
|
|
159
|
+
);
|
|
160
|
+
log.verbose('templatePath', templatePath);
|
|
161
|
+
log.verbose('targetPath', targetPath);
|
|
162
|
+
|
|
163
|
+
spinner = spinnerStart('正在安装模板...');
|
|
164
|
+
fse.ensureDirSync(templatePath);
|
|
165
|
+
fse.ensureDirSync(targetPath);
|
|
166
|
+
fse.copySync(templatePath, targetPath);
|
|
167
|
+
} catch (e) {
|
|
168
|
+
throw e;
|
|
169
|
+
} finally {
|
|
170
|
+
spinner.stop(true);
|
|
171
|
+
log.success('模板安装完成');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async installCustomTemplate() {
|
|
175
|
+
log.info('正在安装自定义模板...');
|
|
176
|
+
}
|
|
177
|
+
async downloadTemplate() {
|
|
178
|
+
const { projectTemplate } = this.projectInfo;
|
|
179
|
+
const templateInfo = this.template.find(
|
|
180
|
+
item => item.npmName === projectTemplate,
|
|
181
|
+
);
|
|
182
|
+
log.verbose('templateInfo', templateInfo);
|
|
183
|
+
this.templateInfo = templateInfo;
|
|
184
|
+
|
|
185
|
+
const targetPath = path.resolve(
|
|
186
|
+
userHome,
|
|
187
|
+
process.env.CLI_HOME_PATH,
|
|
188
|
+
'template',
|
|
189
|
+
);
|
|
190
|
+
const storeDir = path.resolve(
|
|
191
|
+
userHome,
|
|
192
|
+
process.env.CLI_HOME_PATH,
|
|
193
|
+
'template',
|
|
194
|
+
'node_modules',
|
|
195
|
+
);
|
|
196
|
+
log.verbose('targetPath', targetPath);
|
|
197
|
+
log.verbose('storeDir', storeDir);
|
|
198
|
+
|
|
199
|
+
const templateNpm = new Package({
|
|
200
|
+
targetPath,
|
|
201
|
+
storeDir,
|
|
202
|
+
packageName: templateInfo.npmName,
|
|
203
|
+
packageVersion: templateInfo.version,
|
|
204
|
+
});
|
|
205
|
+
if (!(await templateNpm.exists())) {
|
|
206
|
+
const spinner = spinnerStart('正在下载模板...');
|
|
207
|
+
try {
|
|
208
|
+
await templateNpm.install();
|
|
209
|
+
} catch (e) {
|
|
210
|
+
throw e;
|
|
211
|
+
} finally {
|
|
212
|
+
spinner.stop(true);
|
|
213
|
+
if (await templateNpm.exists()) {
|
|
214
|
+
this.templateNpm = templateNpm;
|
|
215
|
+
log.success('模板下载完成');
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
const spinner = spinnerStart('正在更新模板...');
|
|
220
|
+
try {
|
|
221
|
+
await templateNpm.update();
|
|
222
|
+
} catch (error) {
|
|
223
|
+
throw error;
|
|
224
|
+
} finally {
|
|
225
|
+
spinner.stop(true);
|
|
226
|
+
if (await templateNpm.exists()) {
|
|
227
|
+
this.templateNpm = templateNpm;
|
|
228
|
+
log.verbose('templateNpm-----------------', this.templateNpm);
|
|
229
|
+
log.success('模板更新完成');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
37
233
|
}
|
|
38
234
|
async prepare() {
|
|
39
235
|
const template = await getProjectTemplate();
|
|
@@ -65,8 +261,16 @@ class InitCommand extends Command {
|
|
|
65
261
|
message: '是否确认清空当前目录下的文件?',
|
|
66
262
|
default: false,
|
|
67
263
|
});
|
|
264
|
+
let spinner;
|
|
68
265
|
if (ifContinue) {
|
|
69
|
-
|
|
266
|
+
try {
|
|
267
|
+
spinner = spinnerStart('正在清空目录...');
|
|
268
|
+
fse.emptyDirSync(localPath);
|
|
269
|
+
} catch (error) {
|
|
270
|
+
throw error;
|
|
271
|
+
} finally {
|
|
272
|
+
spinner.stop(true);
|
|
273
|
+
}
|
|
70
274
|
}
|
|
71
275
|
}
|
|
72
276
|
}
|
|
@@ -74,6 +278,11 @@ class InitCommand extends Command {
|
|
|
74
278
|
}
|
|
75
279
|
async getProjectInfo() {
|
|
76
280
|
let projectInfo = {};
|
|
281
|
+
let isProjectNameValid = false;
|
|
282
|
+
if (isValidFilename(this.projectName)) {
|
|
283
|
+
isProjectNameValid = true;
|
|
284
|
+
projectInfo.projectName = this.projectName;
|
|
285
|
+
}
|
|
77
286
|
const { type } = await inquire.prompt({
|
|
78
287
|
type: 'list',
|
|
79
288
|
name: 'type',
|
|
@@ -85,57 +294,60 @@ class InitCommand extends Command {
|
|
|
85
294
|
],
|
|
86
295
|
});
|
|
87
296
|
log.verbose('type', type);
|
|
297
|
+
const projectNamePrompt = {
|
|
298
|
+
type: 'input',
|
|
299
|
+
name: 'projectName',
|
|
300
|
+
message: '请输入项目名称',
|
|
301
|
+
default: '',
|
|
302
|
+
validate(v) {
|
|
303
|
+
const done = this.async();
|
|
304
|
+
setTimeout(() => {
|
|
305
|
+
if (!isValidFilename(v)) {
|
|
306
|
+
done('项目名称不合法');
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
done(null, true);
|
|
310
|
+
}, 0);
|
|
311
|
+
},
|
|
312
|
+
filter(v) {
|
|
313
|
+
return v;
|
|
314
|
+
},
|
|
315
|
+
};
|
|
88
316
|
if (type === TYPE_PROJECT) {
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
done(
|
|
103
|
-
|
|
104
|
-
},
|
|
105
|
-
filter(v) {
|
|
106
|
-
return v;
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
type: 'input',
|
|
111
|
-
name: 'projectVersion',
|
|
112
|
-
message: '请输入项目版本号',
|
|
113
|
-
default: '1.0.0',
|
|
114
|
-
validate(v) {
|
|
115
|
-
const done = this.async();
|
|
116
|
-
setTimeout(() => {
|
|
117
|
-
if (!!semver.valid(v) === false) {
|
|
118
|
-
done('项目版本号不合法');
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
done(null, true);
|
|
122
|
-
}, 0);
|
|
123
|
-
},
|
|
124
|
-
filter(v) {
|
|
125
|
-
if (!!semver.valid(v)) {
|
|
126
|
-
return semver.valid(v);
|
|
317
|
+
const promptArr = [];
|
|
318
|
+
if (!isProjectNameValid) {
|
|
319
|
+
promptArr.push(projectNamePrompt);
|
|
320
|
+
}
|
|
321
|
+
promptArr.push({
|
|
322
|
+
type: 'input',
|
|
323
|
+
name: 'projectVersion',
|
|
324
|
+
message: '请输入项目版本号',
|
|
325
|
+
default: '1.0.0',
|
|
326
|
+
validate(v) {
|
|
327
|
+
const done = this.async();
|
|
328
|
+
setTimeout(() => {
|
|
329
|
+
if (!!semver.valid(v) === false) {
|
|
330
|
+
done('项目版本号不合法');
|
|
331
|
+
return;
|
|
127
332
|
}
|
|
128
|
-
|
|
129
|
-
},
|
|
333
|
+
done(null, true);
|
|
334
|
+
}, 0);
|
|
130
335
|
},
|
|
131
|
-
{
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
336
|
+
filter(v) {
|
|
337
|
+
if (!!semver.valid(v)) {
|
|
338
|
+
return semver.valid(v);
|
|
339
|
+
}
|
|
340
|
+
return v;
|
|
136
341
|
},
|
|
137
|
-
|
|
138
|
-
|
|
342
|
+
});
|
|
343
|
+
promptArr.push({
|
|
344
|
+
type: 'list',
|
|
345
|
+
name: 'projectTemplate',
|
|
346
|
+
message: '请选择项目模板',
|
|
347
|
+
choices: this.createTemplateChoices(),
|
|
348
|
+
});
|
|
349
|
+
const project = await inquire.prompt(promptArr);
|
|
350
|
+
projectInfo = { ...projectInfo, ...project, type };
|
|
139
351
|
} else if (type === TYPE_COMPONENT) {
|
|
140
352
|
}
|
|
141
353
|
return projectInfo;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yuw-cli-dev/init",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.39",
|
|
4
4
|
"description": "yuw-cli-dev init",
|
|
5
5
|
"author": "yuwei <yuwei@huya.com>",
|
|
6
6
|
"homepage": "https://github.com/yousanjin/yuw-cli#readme",
|
|
@@ -28,12 +28,17 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@yuw-cli-dev/command": "file:../../models/command",
|
|
31
|
-
"@yuw-cli-dev/log": "^1.0.
|
|
32
|
-
"@yuw-cli-dev/
|
|
31
|
+
"@yuw-cli-dev/log": "^1.0.39",
|
|
32
|
+
"@yuw-cli-dev/package": "file:../../models/package",
|
|
33
|
+
"@yuw-cli-dev/request": "^1.0.39",
|
|
34
|
+
"@yuw-cli-dev/utils": "^1.0.39",
|
|
35
|
+
"ejs": "^4.0.1",
|
|
33
36
|
"fs-extra": "^9.0.1",
|
|
37
|
+
"glob": "^7.1.6",
|
|
34
38
|
"inquirer": "^7.3.3",
|
|
35
39
|
"semver": "^7.7.3",
|
|
40
|
+
"user-home": "^3.0.0",
|
|
36
41
|
"valid-filename": "^3.1.0"
|
|
37
42
|
},
|
|
38
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "7bcace542ccccc47f518b0537cbb0cce93229adf"
|
|
39
44
|
}
|