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 | let entryFile = path.resolve(srcDir, "entry.js");
|
275 | let entryContext = fse.existsSync(entryFile) ? fse.readFileSync(entryFile, 'utf8') : "";
|
276 | fse.remove(path.resolve(srcDir), (err) => {
|
277 | if (!err) {
|
278 | decompress(downFile, path.resolve(srcDir)).then(() => {
|
279 | if (!fse.existsSync(entryFile) && entryContext) {
|
280 | fse.writeFileSync(entryFile, entryContext, 'utf8');
|
281 | }
|
282 | utils.editConfig(rundir, info.config);
|
283 | });
|
284 | }
|
285 | logger.success("【" + info.desc + "】演示模板设置成功。");
|
286 | });
|
287 | });
|
288 | } else {
|
289 | logger.fatal(`放弃设置模板操作!`);
|
290 | }
|
291 | }).catch(console.error);
|
292 | }
|
293 |
|
294 |
|
295 |
|
296 |
|
297 | function displayReleases() {
|
298 | let spinPod = ora('正在获取版本信息...');
|
299 | spinPod.start();
|
300 | templateRelease.fetchReleaseVersions((err, result) => {
|
301 | spinPod.stop();
|
302 | if (err) {
|
303 | logger.fatal(err);
|
304 | return;
|
305 | }
|
306 | let array = [];
|
307 | result.some(t => {
|
308 | if (!utils.leftExists(t, "2")) {
|
309 | return false;
|
310 | }
|
311 | array.push(t);
|
312 | if (array.length >= 10) {
|
313 | return true;
|
314 | }
|
315 | });
|
316 | if (array.length === 0) {
|
317 | logger.fatal("无可用版本!");
|
318 | } else {
|
319 | console.log("可用的版本:");
|
320 | array.forEach((t) => {
|
321 | console.log(chalk.green.underline(t));
|
322 | })
|
323 | }
|
324 | });
|
325 | }
|
326 |
|
327 |
|
328 |
|
329 |
|
330 |
|
331 | function changeAppKey(path) {
|
332 | let configPath = path + "/eeui.config.js";
|
333 | if (!fse.existsSync(configPath)) {
|
334 | return;
|
335 | }
|
336 | let config = require(configPath);
|
337 | let content = '';
|
338 | if (config === null || typeof config !== 'object') {
|
339 | return;
|
340 | }
|
341 | if (typeof config.appKey === 'undefined') {
|
342 | return;
|
343 | }
|
344 | let createRand = (len) => {
|
345 | len = len || 32;
|
346 | let $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678oOLl9gqVvUuI1';
|
347 | let maxPos = $chars.length;
|
348 | let pwd = '';
|
349 | for (let i = 0; i < len; i++) {
|
350 | pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
|
351 | }
|
352 | return pwd;
|
353 | };
|
354 | logger.info("正在创建appKey...");
|
355 | config.appKey = createRand(32);
|
356 | content += "/**\n * 配置文件\n * 参数详细说明:https://eeui.app/guide/config.html\n */\n";
|
357 | content += "module.exports = ";
|
358 | content += JSON.stringify(config, null, "\t");
|
359 | content += ";";
|
360 | fse.writeFileSync(configPath, content, 'utf8');
|
361 |
|
362 | let androidPath = path + "/platforms/android/eeuiApp/app/src/main/assets/eeui/config.json";
|
363 | if (fse.existsSync(androidPath)) {
|
364 | fse.writeFileSync(androidPath, JSON.stringify(config), 'utf8');
|
365 | }
|
366 | let iosPath = path + "/platforms/ios/eeuiApp/bundlejs/eeui/config.json";
|
367 | if (fse.existsSync(androidPath)) {
|
368 | fse.writeFileSync(iosPath, JSON.stringify(config), 'utf8');
|
369 | }
|
370 | }
|
371 |
|
372 | let args = yargs
|
373 | .command({
|
374 | command: "create [name]",
|
375 | desc: "创建一个eeui项目",
|
376 | handler: (argv) => {
|
377 | if (typeof argv.name === "string") {
|
378 | if (fse.existsSync(argv.name)) {
|
379 | logger.fatal(`目录“${argv.name}”已经存在。`);
|
380 | return;
|
381 | }
|
382 | }
|
383 | initProject(argv.name);
|
384 | }
|
385 | })
|
386 | .command({
|
387 | command: "lists",
|
388 | desc: "列出创建可用模板版本",
|
389 | handler: () => {
|
390 | displayReleases();
|
391 | }
|
392 | })
|
393 | .command({
|
394 | command: "setting",
|
395 | desc: "项目App设置(应用名称、版本等)",
|
396 | handler: () => {
|
397 | utils.verifyeeuiProject();
|
398 | setting.start();
|
399 | }
|
400 | })
|
401 | .command({
|
402 | command: "template",
|
403 | desc: "设置App模板(初始化演示模板)",
|
404 | handler: () => {
|
405 | utils.verifyeeuiProject();
|
406 | setTemplate(path.resolve(process.cwd()));
|
407 | }
|
408 | })
|
409 | .command({
|
410 | command: "update",
|
411 | desc: "项目主框架升级至最新版本",
|
412 | handler: () => {
|
413 | utils.verifyeeuiProject();
|
414 | utils.verifyeeuiTemplate();
|
415 | update.start();
|
416 | }
|
417 | })
|
418 | .command({
|
419 | command: "vue [pageName]",
|
420 | desc: "创建vue页面示例模板",
|
421 | handler: (argv) => {
|
422 | utils.verifyeeuiProject();
|
423 | utils.verifyeeuiTemplate();
|
424 | let pageName = utils.rightDelete(argv.pageName, ".vue").trim();
|
425 | if (pageName) {
|
426 | let dir = path.resolve(process.cwd(), "src");
|
427 | if (!fse.existsSync(dir)) {
|
428 | logger.fatal(`目录“src”不存在,当前目录非eeui项目。`);
|
429 | return;
|
430 | }
|
431 | let filePath = dir + "/pages/" + pageName + ".vue";
|
432 | if (fse.existsSync(filePath)) {
|
433 | logger.fatal(`文件“${pageName}.vue”已经存在。`);
|
434 | return;
|
435 | }
|
436 | let tmlPath = __dirname + "/lib/template/_template.vue";
|
437 | if (!fse.existsSync(tmlPath)) {
|
438 | logger.fatal(`模板文件不存在。`);
|
439 | return;
|
440 | }
|
441 | fse.copySync(tmlPath, filePath);
|
442 | logger.success(`模板文件“${pageName}.vue”成功创建。`);
|
443 | } else {
|
444 | logger.fatal(`请输入要创建的文件名称。`);
|
445 | }
|
446 | }
|
447 | })
|
448 | .command({
|
449 | command: "plugin [command] [name]",
|
450 | desc: "添加、删除、创建或发布插件",
|
451 | handler: (argv) => {
|
452 | utils.verifyeeuiProject();
|
453 | utils.verifyeeuiTemplate();
|
454 | let op = {};
|
455 | op.name = argv.name;
|
456 | op.rootDir = process.cwd();
|
457 | op.dir = path.basename(process.cwd());
|
458 | op.simple = argv.s === true;
|
459 | switch (argv.command) {
|
460 | case 'add':
|
461 | case 'install':
|
462 | case 'i':
|
463 | plugin.add(op);
|
464 | break;
|
465 | case 'del':
|
466 | case 'remove':
|
467 | case 'uninstall':
|
468 | case 'u':
|
469 | plugin.remove(op);
|
470 | break;
|
471 | case 'repair':
|
472 | case 'r':
|
473 | plugin.repair(op);
|
474 | break;
|
475 | case 'script':
|
476 | plugin.eeuiScript(argv.name, true);
|
477 | break;
|
478 | case 'unscript':
|
479 | plugin.eeuiScript(argv.name, false);
|
480 | break;
|
481 | case 'create':
|
482 | case 'c':
|
483 | create.create(op);
|
484 | break;
|
485 | case 'publish':
|
486 | case 'upload':
|
487 | case 'p':
|
488 | publish.publish(op);
|
489 | break;
|
490 | }
|
491 | }
|
492 | })
|
493 | .command({
|
494 | command: "repair",
|
495 | desc: "一键云修复文件(热更新)",
|
496 | handler: () => {
|
497 | utils.verifyeeuiProject();
|
498 | repair.start();
|
499 | }
|
500 | })
|
501 | .command({
|
502 | command: "icons [id]",
|
503 | desc: "一键设置图标资源",
|
504 | handler: (argv) => {
|
505 | utils.verifyeeuiProject();
|
506 | tools.icons(argv.id);
|
507 | }
|
508 | })
|
509 | .command({
|
510 | command: "launchimage [id]",
|
511 | desc: "一键设置启动图资源",
|
512 | handler: (argv) => {
|
513 | utils.verifyeeuiProject();
|
514 | tools.launchimage(argv.id);
|
515 | }
|
516 | })
|
517 | .command({
|
518 | command: "login",
|
519 | desc: "登录云中心",
|
520 | handler: () => {
|
521 | utils.login((data) => {
|
522 | logger.success(data.username + ' 登录成功!');
|
523 | });
|
524 | }
|
525 | })
|
526 | .command({
|
527 | command: "logout",
|
528 | desc: "登出云中心",
|
529 | handler: () => {
|
530 | utils.logout(() => {
|
531 | logger.success('退出成功!');
|
532 | });
|
533 | }
|
534 | })
|
535 | .command({
|
536 | command: "backup",
|
537 | desc: "备份项目开发文件",
|
538 | handler: () => {
|
539 | utils.verifyeeuiProject();
|
540 | backup.backup();
|
541 | }
|
542 | })
|
543 | .command({
|
544 | command: "recovery",
|
545 | desc: "恢复项目备份文件",
|
546 | handler: () => {
|
547 | utils.verifyeeuiProject();
|
548 | utils.verifyeeuiTemplate();
|
549 | backup.recovery();
|
550 | }
|
551 | })
|
552 | .command({
|
553 | command: "dev",
|
554 | desc: "编译构造",
|
555 | handler: (argv) => {
|
556 | utils.verifyeeuiProject();
|
557 | utils.verifyeeuiTemplate();
|
558 | plugin.eeuiScript(null, true, () => {
|
559 | buildApp.dev(argv.s === true);
|
560 | });
|
561 | }
|
562 | })
|
563 | .command({
|
564 | command: "build",
|
565 | desc: "编译构造并最小化输出结果",
|
566 | handler: (argv) => {
|
567 | utils.verifyeeuiProject();
|
568 | utils.verifyeeuiTemplate();
|
569 | plugin.eeuiScript(null, true, () => {
|
570 | buildApp.build(argv.s === true);
|
571 | });
|
572 | }
|
573 | })
|
574 | .command({
|
575 | command: "run [platform]",
|
576 | desc: "在你的设备上运行app (实验功能)",
|
577 | handler: (argv) => {
|
578 | utils.verifyeeuiProject();
|
579 | utils.verifyeeuiTemplate();
|
580 | let dir = path.basename(process.cwd());
|
581 | if (argv.platform === "ios") {
|
582 | runapp.runIOS({dir});
|
583 | } else if (argv.platform === "android") {
|
584 | runapp.runAndroid({dir});
|
585 | } else {
|
586 | inquirer.prompt(runQuestions).then((answers) => {
|
587 | let platform = JSON.parse(JSON.stringify(answers)).platform;
|
588 | if (platform === 'ios') {
|
589 | runapp.runIOS({dir});
|
590 | } else if (platform === 'android') {
|
591 | runapp.runAndroid({dir});
|
592 | }
|
593 | });
|
594 | }
|
595 | }
|
596 | })
|
597 | .version(() => {
|
598 | let text = "eeui-cli: " + chalk.underline(require('./package.json').version);
|
599 | if (utils.projectVersion()) {
|
600 | text += "\neeui-template: " + chalk.underline(utils.projectVersion());
|
601 | }
|
602 | return text;
|
603 | })
|
604 | .help()
|
605 | .alias({
|
606 | "h": "help",
|
607 | "v": "version",
|
608 | "s": "simple"
|
609 | })
|
610 | .strict(true)
|
611 | .argv;
|
612 |
|
613 |
|