UNPKG

19.3 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 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 */
292function 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 * 生成appKey
324 * @param {string} path 文件路径.
325 */
326function 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
367let 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//发布模块: npm publish