1 | #!/usr/bin/env node
|
2 |
|
3 | const yargs = require("yargs");
|
4 | const path = require("path");
|
5 | const fse = require("fs-extra");
|
6 | const chalk = require('chalk');
|
7 | const inquirer = require('inquirer');
|
8 | const ora = require('ora');
|
9 | const shelljs = require('shelljs');
|
10 | const decompress = require('decompress');
|
11 | const config = require('./config');
|
12 | const logger = require("./lib/utils/logger");
|
13 | const utils = require("./lib/utils");
|
14 | const project = require("./lib/utils/project");
|
15 | const backup = require("./lib/utils/backup");
|
16 | const runapp = require("./lib/run");
|
17 | const buildApp = require("./lib/builder/buildApp");
|
18 | const plugin = require('./lib/plugin');
|
19 | const create = require('./lib/plugin/create');
|
20 | const publish = require('./lib/plugin/publish');
|
21 | const update = require('./lib/utils/update');
|
22 | const repair = require('./lib/utils/repair');
|
23 | const tools = require('./lib/utils/tools');
|
24 | const setting = require('./lib/utils/setting');
|
25 |
|
26 | const TemplateRelease = require("./template-release");
|
27 | const templateRelease = new TemplateRelease(config.cacheDirName, config.templateReleaseUrl);
|
28 | const isWin = /^win/.test(process.platform);
|
29 |
|
30 | let questions = (inputName, releaseLists) => {
|
31 | let applicationid = "";
|
32 | return [{
|
33 | type: 'input',
|
34 | name: 'name',
|
35 | default: () => {
|
36 | if (typeof inputName !== 'string') inputName = "";
|
37 | return inputName.trim() ? inputName.trim() : 'eeui-demo';
|
38 | },
|
39 | message: "请输入项目名称",
|
40 | validate: (value) => {
|
41 | let pass = value.match(/^[0-9a-z\-_]+$/i);
|
42 | if (!pass) {
|
43 | return '输入格式错误,请重新输入。';
|
44 | }
|
45 | if (fse.existsSync(value)) {
|
46 | return '目录[' + value + ']已经存在,请重新输入。';
|
47 | }
|
48 | return true;
|
49 | }
|
50 | }, {
|
51 | type: 'input',
|
52 | name: 'appName',
|
53 | default: () => {
|
54 | return 'eeui演示';
|
55 | },
|
56 | message: "请输入App名称",
|
57 | validate: (value) => {
|
58 | return value !== ''
|
59 | }
|
60 | }, {
|
61 | type: 'input',
|
62 | name: 'applicationID',
|
63 | default: () => {
|
64 | return 'app.eeui.demo';
|
65 | },
|
66 | message: "请输入Android应用ID",
|
67 | validate: (value) => {
|
68 | let pass = value.match(/^[a-z][a-z0-9_]+([.][a-z][a-z0-9_]+){2,4}$/i);
|
69 | if (pass) {
|
70 | applicationid = value;
|
71 | return true;
|
72 | }
|
73 | return '输入格式错误,请重新输入。';
|
74 | }
|
75 | }, {
|
76 | type: 'input',
|
77 | name: 'bundleIdentifier',
|
78 | default: () => {
|
79 | return applicationid;
|
80 | },
|
81 | message: "请输入iOS应用ID",
|
82 | validate: (value) => {
|
83 | let pass = value.match(/^[a-z][a-z0-9_]+([.][a-z][a-z0-9_]+){2,4}$/i);
|
84 | if (pass) {
|
85 | return true;
|
86 | }
|
87 | return '输入格式错误,请重新输入。';
|
88 | }
|
89 | }, {
|
90 | type: 'list',
|
91 | name: 'release',
|
92 | message: "请选择框架版本",
|
93 | choices: releaseLists
|
94 | }, {
|
95 | type: 'list',
|
96 | name: 'location',
|
97 | message: "请选择下载服务器",
|
98 | choices: [{
|
99 | name: "Github服务器",
|
100 | value: "github"
|
101 | }, {
|
102 | name: "EEUI官网服务器",
|
103 | value: "eeui"
|
104 | }]
|
105 | }];
|
106 | };
|
107 |
|
108 | let runQuestions = [{
|
109 | type: 'list',
|
110 | name: 'platform',
|
111 | message: '您可以安装或更新eeui SDK',
|
112 | choices: [{
|
113 | name: "ios",
|
114 | value: "ios"
|
115 | }, {
|
116 | name: "android",
|
117 | value: "android"
|
118 | }]
|
119 | }];
|
120 |
|
121 |
|
122 |
|
123 |
|
124 | function initProject(createName) {
|
125 | let spinFetch = ora('正在下载版本列表...');
|
126 | spinFetch.start();
|
127 | templateRelease.fetchReleaseVersions((err, result) => {
|
128 | spinFetch.stop();
|
129 | if (err) {
|
130 | logger.fatal(err);
|
131 | return;
|
132 | }
|
133 |
|
134 | let lists = [];
|
135 | result.some(t => {
|
136 | if (!utils.leftExists(t, "2")) {
|
137 | return false;
|
138 | }
|
139 | lists.push({
|
140 | name: t,
|
141 | value: t
|
142 | });
|
143 | if (lists.length >= 10) {
|
144 | return true;
|
145 | }
|
146 | });
|
147 |
|
148 | if (lists.length === 0) {
|
149 | logger.fatal("没有找到可用的版本。");
|
150 | return;
|
151 | }
|
152 |
|
153 | inquirer.prompt(questions(createName, lists)).then((answers) => {
|
154 | let _answers = JSON.parse(JSON.stringify(answers));
|
155 | let rundir = path.resolve(process.cwd(), _answers.name);
|
156 |
|
157 | if (fse.existsSync(_answers.name)) {
|
158 | logger.fatal(`目录[${_answers.name}]已经存在。`);
|
159 | return;
|
160 | }
|
161 |
|
162 | let release = _answers.release === 'latest' ? '' : _answers.release;
|
163 | templateRelease.fetchRelease(release, _answers.location, (error, releasePath) => {
|
164 | if (error) {
|
165 | logger.fatal(error);
|
166 | return;
|
167 | }
|
168 |
|
169 | let nextStep = (callback) => {
|
170 | logger.info("正在复制模板文件...");
|
171 |
|
172 | fse.copySync(releasePath, _answers.name);
|
173 | project.initConfig(rundir, _answers);
|
174 | changeAppKey(rundir);
|
175 |
|
176 | if (shelljs.which('pod')) {
|
177 | let tempPath = process.cwd();
|
178 | let spinPod = ora('pod install...');
|
179 | spinPod.start();
|
180 | shelljs.cd(rundir + '/platforms/ios/eeuiApp');
|
181 | shelljs.exec('pod install', {silent: true}, (code, stdout, stderr) => {
|
182 | shelljs.cd(tempPath);
|
183 | spinPod.stop();
|
184 | if (code !== 0) {
|
185 | logger.warn("运行pod install错误:" + code + ",请稍后手动运行!");
|
186 | }
|
187 | callback();
|
188 | });
|
189 | } else {
|
190 | if (isWin) {
|
191 | logger.warn('未检测到系统安装pod,请安装pod后手动执行pod install!');
|
192 | }
|
193 | callback();
|
194 | }
|
195 | };
|
196 |
|
197 | let finalLog = () => {
|
198 | logger.success("创建项目完成。");
|
199 | logger.sep();
|
200 | logger.info("您可以运行一下命令开始。");
|
201 | logger.info(chalk.white(`1. cd ${_answers.name}`));
|
202 | logger.info(chalk.white(`2. npm install`));
|
203 | logger.info(chalk.white(`3. npm run dev`));
|
204 | };
|
205 |
|
206 | initDemo((error, downFile, info) => {
|
207 | if (error) {
|
208 | logger.warn(error);
|
209 | nextStep(() => {
|
210 | finalLog();
|
211 | });
|
212 | return;
|
213 | }
|
214 | nextStep(() => {
|
215 | let srcDir = rundir + "/src";
|
216 | fse.remove(path.resolve(srcDir), (err) => {
|
217 | if (!err) {
|
218 | decompress(downFile, path.resolve(srcDir)).then(() => {
|
219 | utils.editConfig(rundir, info.config);
|
220 | });
|
221 | }
|
222 | finalLog();
|
223 | });
|
224 | });
|
225 | });
|
226 | });
|
227 | });
|
228 | });
|
229 | }
|
230 |
|
231 |
|
232 |
|
233 |
|
234 | function initDemo(callback) {
|
235 | utils.getOnlineDemoLists((error, demoLists) => {
|
236 | if (error) {
|
237 | typeof callback === 'function' && callback(error);
|
238 | return;
|
239 | }
|
240 | inquirer.prompt([{
|
241 | type: 'list',
|
242 | name: 'demoInfo',
|
243 | message: "请选择初始化模板",
|
244 | choices: demoLists
|
245 | }]).then(answers => {
|
246 | utils.downOnlineDemo(answers.demoInfo.tree, (error, downFile) => {
|
247 | if (error) {
|
248 | typeof callback === 'function' && callback(error);
|
249 | return;
|
250 | }
|
251 | typeof callback === 'function' && callback('', downFile, answers.demoInfo);
|
252 | })
|
253 | }).catch(console.error);
|
254 | });
|
255 | }
|
256 |
|
257 |
|
258 |
|
259 |
|
260 |
|
261 | function setTemplate(rundir) {
|
262 | inquirer.prompt([{
|
263 | type: 'confirm',
|
264 | message: `此操作将重置src开发目录,是否继续操作?`,
|
265 | name: 'ok'
|
266 | }]).then(answers => {
|
267 | if (answers.ok) {
|
268 | initDemo((error, downFile, info) => {
|
269 | if (error) {
|
270 | logger.warn(error);
|
271 | return;
|
272 | }
|
273 | let srcDir = rundir + "/src";
|
274 | fse.remove(path.resolve(srcDir), (err) => {
|
275 | if (!err) {
|
276 | decompress(downFile, path.resolve(srcDir)).then(() => {
|
277 | utils.editConfig(rundir, info.config);
|
278 | });
|
279 | }
|
280 | logger.success("【" + info.desc + "】演示模板设置成功。");
|
281 | });
|
282 | });
|
283 | } else {
|
284 | logger.fatal(`放弃设置模板操作!`);
|
285 | }
|
286 | }).catch(console.error);
|
287 | }
|
288 |
|
289 |
|
290 |
|
291 |
|
292 | function displayReleases() {
|
293 | let spinPod = ora('正在获取版本信息...');
|
294 | spinPod.start();
|
295 | templateRelease.fetchReleaseVersions((err, result) => {
|
296 | spinPod.stop();
|
297 | if (err) {
|
298 | logger.fatal(err);
|
299 | return;
|
300 | }
|
301 | let array = [];
|
302 | result.some(t => {
|
303 | if (!utils.leftExists(t, "2")) {
|
304 | return false;
|
305 | }
|
306 | array.push(t);
|
307 | if (array.length >= 10) {
|
308 | return true;
|
309 | }
|
310 | });
|
311 | if (array.length === 0) {
|
312 | logger.fatal("无可用版本!");
|
313 | } else {
|
314 | console.log("可用的版本:");
|
315 | array.forEach((t) => {
|
316 | console.log(chalk.green.underline(t));
|
317 | })
|
318 | }
|
319 | });
|
320 | }
|
321 |
|
322 |
|
323 |
|
324 |
|
325 |
|
326 | function changeAppKey(path) {
|
327 | let configPath = path + "/eeui.config.js";
|
328 | if (!fse.existsSync(configPath)) {
|
329 | return;
|
330 | }
|
331 | let config = require(configPath);
|
332 | let content = '';
|
333 | if (config === null || typeof config !== 'object') {
|
334 | return;
|
335 | }
|
336 | if (typeof config.appKey === 'undefined') {
|
337 | return;
|
338 | }
|
339 | let createRand = (len) => {
|
340 | len = len || 32;
|
341 | let $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678oOLl9gqVvUuI1';
|
342 | let maxPos = $chars.length;
|
343 | let pwd = '';
|
344 | for (let i = 0; i < len; i++) {
|
345 | pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
|
346 | }
|
347 | return pwd;
|
348 | };
|
349 | logger.info("正在创建appKey...");
|
350 | config.appKey = createRand(32);
|
351 | content += "/**\n * 配置文件\n * 参数详细说明:https://eeui.app/guide/config.html\n */\n";
|
352 | content += "module.exports = ";
|
353 | content += JSON.stringify(config, null, "\t");
|
354 | content += ";";
|
355 | fse.writeFileSync(configPath, content, 'utf8');
|
356 |
|
357 | let androidPath = path + "/platforms/android/eeuiApp/app/src/main/assets/eeui/config.json";
|
358 | if (fse.existsSync(androidPath)) {
|
359 | fse.writeFileSync(androidPath, JSON.stringify(config), 'utf8');
|
360 | }
|
361 | let iosPath = path + "/platforms/ios/eeuiApp/bundlejs/eeui/config.json";
|
362 | if (fse.existsSync(androidPath)) {
|
363 | fse.writeFileSync(iosPath, JSON.stringify(config), 'utf8');
|
364 | }
|
365 | }
|
366 |
|
367 | let args = yargs
|
368 | .command({
|
369 | command: "create [name]",
|
370 | desc: "创建一个eeui项目",
|
371 | handler: (argv) => {
|
372 | if (typeof argv.name === "string") {
|
373 | if (fse.existsSync(argv.name)) {
|
374 | logger.fatal(`目录“${argv.name}”已经存在。`);
|
375 | return;
|
376 | }
|
377 | }
|
378 | initProject(argv.name);
|
379 | }
|
380 | })
|
381 | .command({
|
382 | command: "lists",
|
383 | desc: "列出创建可用模板版本",
|
384 | handler: () => {
|
385 | displayReleases();
|
386 | }
|
387 | })
|
388 | .command({
|
389 | command: "setting",
|
390 | desc: "项目App设置(应用名称、版本等)",
|
391 | handler: () => {
|
392 | utils.verifyeeuiProject();
|
393 | setting.start();
|
394 | }
|
395 | })
|
396 | .command({
|
397 | command: "template",
|
398 | desc: "设置App模板(初始化演示模板)",
|
399 | handler: () => {
|
400 | utils.verifyeeuiProject();
|
401 | setTemplate(path.resolve(process.cwd()));
|
402 | }
|
403 | })
|
404 | .command({
|
405 | command: "update",
|
406 | desc: "项目主框架升级至最新版本",
|
407 | handler: () => {
|
408 | utils.verifyeeuiProject();
|
409 | utils.verifyeeuiTemplate();
|
410 | update.start();
|
411 | }
|
412 | })
|
413 | .command({
|
414 | command: "vue [pageName]",
|
415 | desc: "创建vue页面示例模板",
|
416 | handler: (argv) => {
|
417 | utils.verifyeeuiProject();
|
418 | utils.verifyeeuiTemplate();
|
419 | let pageName = utils.rightDelete(argv.pageName, ".vue").trim();
|
420 | if (pageName) {
|
421 | let dir = path.resolve(process.cwd(), "src");
|
422 | if (!fse.existsSync(dir)) {
|
423 | logger.fatal(`目录“src”不存在,当前目录非eeui项目。`);
|
424 | return;
|
425 | }
|
426 | let filePath = dir + "/pages/" + pageName + ".vue";
|
427 | if (fse.existsSync(filePath)) {
|
428 | logger.fatal(`文件“${pageName}.vue”已经存在。`);
|
429 | return;
|
430 | }
|
431 | let tmlPath = __dirname + "/lib/template/_template.vue";
|
432 | if (!fse.existsSync(tmlPath)) {
|
433 | logger.fatal(`模板文件不存在。`);
|
434 | return;
|
435 | }
|
436 | fse.copySync(tmlPath, filePath);
|
437 | logger.success(`模板文件“${pageName}.vue”成功创建。`);
|
438 | } else {
|
439 | logger.fatal(`请输入要创建的文件名称。`);
|
440 | }
|
441 | }
|
442 | })
|
443 | .command({
|
444 | command: "plugin [command] [name]",
|
445 | desc: "添加、删除、创建或发布插件",
|
446 | handler: (argv) => {
|
447 | utils.verifyeeuiProject();
|
448 | utils.verifyeeuiTemplate();
|
449 | let op = {};
|
450 | op.name = argv.name;
|
451 | op.rootDir = process.cwd();
|
452 | op.dir = path.basename(process.cwd());
|
453 | op.simple = argv.s === true;
|
454 | switch (argv.command) {
|
455 | case 'add':
|
456 | case 'install':
|
457 | case 'i':
|
458 | plugin.add(op);
|
459 | break;
|
460 | case 'del':
|
461 | case 'remove':
|
462 | case 'uninstall':
|
463 | case 'u':
|
464 | plugin.remove(op);
|
465 | break;
|
466 | case 'repair':
|
467 | case 'r':
|
468 | plugin.repair(op);
|
469 | break;
|
470 | case 'script':
|
471 | plugin.eeuiScript(argv.name, true);
|
472 | break;
|
473 | case 'unscript':
|
474 | plugin.eeuiScript(argv.name, false);
|
475 | break;
|
476 | case 'create':
|
477 | case 'c':
|
478 | create.create(op);
|
479 | break;
|
480 | case 'publish':
|
481 | case 'upload':
|
482 | case 'p':
|
483 | publish.publish(op);
|
484 | break;
|
485 | }
|
486 | }
|
487 | })
|
488 | .command({
|
489 | command: "repair",
|
490 | desc: "一键云修复文件(热更新)",
|
491 | handler: () => {
|
492 | utils.verifyeeuiProject();
|
493 | repair.start();
|
494 | }
|
495 | })
|
496 | .command({
|
497 | command: "icons [id]",
|
498 | desc: "一键设置图标资源",
|
499 | handler: (argv) => {
|
500 | utils.verifyeeuiProject();
|
501 | tools.icons(argv.id);
|
502 | }
|
503 | })
|
504 | .command({
|
505 | command: "launchimage [id]",
|
506 | desc: "一键设置启动图资源",
|
507 | handler: (argv) => {
|
508 | utils.verifyeeuiProject();
|
509 | tools.launchimage(argv.id);
|
510 | }
|
511 | })
|
512 | .command({
|
513 | command: "login",
|
514 | desc: "登录云中心",
|
515 | handler: () => {
|
516 | utils.login((data) => {
|
517 | logger.success(data.username + ' 登录成功!');
|
518 | });
|
519 | }
|
520 | })
|
521 | .command({
|
522 | command: "logout",
|
523 | desc: "登出云中心",
|
524 | handler: () => {
|
525 | utils.logout(() => {
|
526 | logger.success('退出成功!');
|
527 | });
|
528 | }
|
529 | })
|
530 | .command({
|
531 | command: "backup",
|
532 | desc: "备份项目开发文件",
|
533 | handler: () => {
|
534 | utils.verifyeeuiProject();
|
535 | backup.backup();
|
536 | }
|
537 | })
|
538 | .command({
|
539 | command: "recovery",
|
540 | desc: "恢复项目备份文件",
|
541 | handler: () => {
|
542 | utils.verifyeeuiProject();
|
543 | utils.verifyeeuiTemplate();
|
544 | backup.recovery();
|
545 | }
|
546 | })
|
547 | .command({
|
548 | command: "dev",
|
549 | desc: "编译构造",
|
550 | handler: (argv) => {
|
551 | utils.verifyeeuiProject();
|
552 | utils.verifyeeuiTemplate();
|
553 | plugin.eeuiScript(null, true, () => {
|
554 | buildApp.dev(argv.s === true);
|
555 | });
|
556 | }
|
557 | })
|
558 | .command({
|
559 | command: "build",
|
560 | desc: "编译构造并最小化输出结果",
|
561 | handler: (argv) => {
|
562 | utils.verifyeeuiProject();
|
563 | utils.verifyeeuiTemplate();
|
564 | plugin.eeuiScript(null, true, () => {
|
565 | buildApp.build(argv.s === true);
|
566 | });
|
567 | }
|
568 | })
|
569 | .command({
|
570 | command: "run [platform]",
|
571 | desc: "在你的设备上运行app (实验功能)",
|
572 | handler: (argv) => {
|
573 | utils.verifyeeuiProject();
|
574 | utils.verifyeeuiTemplate();
|
575 | let dir = path.basename(process.cwd());
|
576 | if (argv.platform === "ios") {
|
577 | runapp.runIOS({dir});
|
578 | } else if (argv.platform === "android") {
|
579 | runapp.runAndroid({dir});
|
580 | } else {
|
581 | inquirer.prompt(runQuestions).then((answers) => {
|
582 | let platform = JSON.parse(JSON.stringify(answers)).platform;
|
583 | if (platform === 'ios') {
|
584 | runapp.runIOS({dir});
|
585 | } else if (platform === 'android') {
|
586 | runapp.runAndroid({dir});
|
587 | }
|
588 | });
|
589 | }
|
590 | }
|
591 | })
|
592 | .version(() => {
|
593 | let text = "eeui-cli: " + chalk.underline(require('./package.json').version);
|
594 | if (utils.projectVersion()) {
|
595 | text += "\neeui-template: " + chalk.underline(utils.projectVersion());
|
596 | }
|
597 | return text;
|
598 | })
|
599 | .help()
|
600 | .alias({
|
601 | "h": "help",
|
602 | "v": "version",
|
603 | "s": "simple"
|
604 | })
|
605 | .strict(true)
|
606 | .argv;
|
607 |
|
608 |
|