UNPKG

19.7 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3const yargs = require("yargs");
4const path = require("path");
5const fse = require("fs-extra");
6const chalk = require('chalk');
7const inquirer = require('inquirer');
8const ora = require('ora');
9const shelljs = require('shelljs');
10const decompress = require('decompress');
11const config = require('./config');
12const logger = require("./lib/utils/logger");
13const utils = require("./lib/utils");
14const project = require("./lib/utils/project");
15const backup = require("./lib/utils/backup");
16const runapp = require("./lib/run");
17const buildApp = require("./lib/builder/buildApp");
18const plugin = require('./lib/plugin');
19const create = require('./lib/plugin/create');
20const publish = require('./lib/plugin/publish');
21const update = require('./lib/utils/update');
22const repair = require('./lib/utils/repair');
23const tools = require('./lib/utils/tools');
24const setting = require('./lib/utils/setting');
25
26const TemplateRelease = require("./template-release");
27const templateRelease = new TemplateRelease(config.cacheDirName, config.templateReleaseUrl);
28const isWin = /^win/.test(process.platform);
29
30let 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
108let 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 * 创建 eeui 工程.
123 */
124function 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 */
234function 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 * @param rundir
260 */
261function 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 */
297function 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 * 生成appKey
329 * @param {string} path 文件路径.
330 */
331function 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
372let 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//发布模块: npm publish