quan-cli 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.prettierrc.json +6 -0
- package/README.md +0 -0
- package/bin/index.js +13 -0
- package/lib/actions/index.js +17 -0
- package/lib/actions/initAction.js +123 -0
- package/lib/config/index.js +18 -0
- package/lib/config/package.js +7 -0
- package/lib/config/path.js +8 -0
- package/lib/index.js +60 -0
- package/lib/utils/file.js +20 -0
- package/lib/utils/index.js +20 -0
- package/lib/utils/loading.js +20 -0
- package/lib/utils/log.js +17 -0
- package/lib/utils/npm.js +29 -0
- package/package.json +51 -0
- package/src/actions/index.ts +1 -0
- package/src/actions/initAction.ts +143 -0
- package/src/config/index.ts +2 -0
- package/src/config/package.ts +12 -0
- package/src/config/path.ts +4 -0
- package/src/index.ts +56 -0
- package/src/utils/file.ts +12 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/loading.ts +17 -0
- package/src/utils/log.ts +16 -0
- package/src/utils/npm.ts +23 -0
- package/tsconfig.json +105 -0
package/.prettierrc.json
ADDED
package/README.md
ADDED
File without changes
|
package/bin/index.js
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
// const path = require('path');
|
3
|
+
// const execa = require('execa');
|
4
|
+
// const modulePath = path.resolve(__dirname, '../lib/index.ts');
|
5
|
+
// try {
|
6
|
+
// execa.commandSync('ts-node ' + modulePath, {
|
7
|
+
// stdio: 'inherit',
|
8
|
+
// });
|
9
|
+
// } catch (e) {
|
10
|
+
// process.exit(1);
|
11
|
+
// }
|
12
|
+
|
13
|
+
require('../lib/index.js');
|
@@ -0,0 +1,17 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
15
|
+
};
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
17
|
+
__exportStar(require("./initAction"), exports);
|
@@ -0,0 +1,123 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.InitAction = void 0;
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
8
|
+
const path_1 = __importDefault(require("path"));
|
9
|
+
//@ts-ignore
|
10
|
+
const userhome_1 = __importDefault(require("userhome"));
|
11
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
12
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
13
|
+
const axios_1 = __importDefault(require("axios"));
|
14
|
+
//@ts-ignore
|
15
|
+
const npminstall_1 = __importDefault(require("npminstall"));
|
16
|
+
const utils_1 = require("../utils");
|
17
|
+
const config_1 = require("../config");
|
18
|
+
class InitAction {
|
19
|
+
constructor(option) {
|
20
|
+
this.cwdPath = process.cwd();
|
21
|
+
this.homePath = (0, userhome_1.default)();
|
22
|
+
this.basePath = option.packagePath || this.cwdPath;
|
23
|
+
this.force = option.force || false;
|
24
|
+
this.origin = option.origin || utils_1.NpmUtil.getNpmRegistry(true);
|
25
|
+
this.initAction();
|
26
|
+
}
|
27
|
+
//初始化
|
28
|
+
async initAction() {
|
29
|
+
utils_1.log.debug('homePath', this.homePath);
|
30
|
+
utils_1.log.debug('工作路径', this.cwdPath);
|
31
|
+
utils_1.log.debug('要创建的目录', this.basePath);
|
32
|
+
//路径不存在
|
33
|
+
this.checkPath();
|
34
|
+
this.checkForce();
|
35
|
+
await this.checkPrompt();
|
36
|
+
await this.getTemplate();
|
37
|
+
await this.selectTemplate();
|
38
|
+
await this.getTemplateVersion();
|
39
|
+
await this.install();
|
40
|
+
}
|
41
|
+
//检查路径是否可用
|
42
|
+
checkPath() {
|
43
|
+
if (!fs_1.default.existsSync(this.basePath)) {
|
44
|
+
utils_1.log.error('error', '路径不存在,请重试!');
|
45
|
+
process.exit(1);
|
46
|
+
}
|
47
|
+
}
|
48
|
+
//force清空目录
|
49
|
+
checkForce() {
|
50
|
+
const isEmpty = utils_1.FileUtil.isEmptyDir(this.basePath);
|
51
|
+
if (!isEmpty && this.force) {
|
52
|
+
fs_extra_1.default.emptyDirSync(this.basePath);
|
53
|
+
}
|
54
|
+
}
|
55
|
+
//checkPrompt 清空目录
|
56
|
+
async checkPrompt() {
|
57
|
+
const isEmpty = utils_1.FileUtil.isEmptyDir(this.basePath);
|
58
|
+
if (!isEmpty) {
|
59
|
+
const isDelDir = await this.getDelPrompt();
|
60
|
+
if (!isDelDir) {
|
61
|
+
process.exit(1);
|
62
|
+
}
|
63
|
+
else {
|
64
|
+
fs_extra_1.default.emptyDirSync(this.basePath);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
async getDelPrompt() {
|
69
|
+
const { isDelDir } = await inquirer_1.default.prompt([
|
70
|
+
{
|
71
|
+
type: 'confirm',
|
72
|
+
name: 'isDelDir',
|
73
|
+
message: `是否清空${this.basePath}。注意(文件无法恢复,请谨慎操作)`,
|
74
|
+
},
|
75
|
+
]);
|
76
|
+
return isDelDir;
|
77
|
+
}
|
78
|
+
async getTemplateVersion() {
|
79
|
+
return await utils_1.NpmUtil.getLatestVersion(this.template.store, this.origin);
|
80
|
+
}
|
81
|
+
async install() {
|
82
|
+
const version = await this.getTemplateVersion();
|
83
|
+
utils_1.log.debug(`执行安装:${this.template.store},版本:${version}`);
|
84
|
+
utils_1.log.debug(path_1.default.resolve(this.homePath, config_1.paths.ROOT_PATH, config_1.paths.TARGET_PATH));
|
85
|
+
const loadingStart = (0, utils_1.loading)('正在下载模板...');
|
86
|
+
await (0, utils_1.sleep)(1500);
|
87
|
+
await (0, npminstall_1.default)({
|
88
|
+
root: path_1.default.resolve(this.homePath, config_1.paths.ROOT_PATH, config_1.paths.TARGET_PATH),
|
89
|
+
registry: this.origin,
|
90
|
+
pkgs: [{ name: this.template.store, version: version }],
|
91
|
+
});
|
92
|
+
this.copy(this.template.store, version);
|
93
|
+
loadingStart.stop(true);
|
94
|
+
utils_1.log.info('info', '模板下载完成');
|
95
|
+
}
|
96
|
+
//執行copy
|
97
|
+
copy(name, version) {
|
98
|
+
const packageName = utils_1.NpmUtil.getPackageDirName(name, version);
|
99
|
+
const originPath = path_1.default.resolve(this.homePath, config_1.paths.ROOT_PATH, config_1.paths.TARGET_PATH, 'node_modules', packageName, 'template');
|
100
|
+
const targetPath = this.basePath;
|
101
|
+
fs_extra_1.default.copySync(originPath, targetPath);
|
102
|
+
process.exit(1);
|
103
|
+
}
|
104
|
+
async selectTemplate() {
|
105
|
+
const { template } = await inquirer_1.default.prompt([
|
106
|
+
{
|
107
|
+
type: 'list',
|
108
|
+
name: 'template',
|
109
|
+
message: '请选择要下载的模板',
|
110
|
+
choices: this.templates.map((item) => item.name),
|
111
|
+
},
|
112
|
+
]);
|
113
|
+
this.template = this.templates.filter((item) => item.name === template)[0];
|
114
|
+
}
|
115
|
+
async getTemplate() {
|
116
|
+
const { data: { data }, } = await (0, axios_1.default)({
|
117
|
+
method: 'get',
|
118
|
+
url: 'http://101.42.154.196/quan-cli/getAllTemplate',
|
119
|
+
});
|
120
|
+
this.templates = data;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
exports.InitAction = InitAction;
|
@@ -0,0 +1,18 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
15
|
+
};
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
17
|
+
__exportStar(require("./package"), exports);
|
18
|
+
__exportStar(require("./path"), exports);
|
@@ -0,0 +1,7 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.pack = void 0;
|
4
|
+
const utils_1 = require("../utils");
|
5
|
+
const packJson = require('../../package');
|
6
|
+
utils_1.log.debug('package', JSON.stringify(packJson));
|
7
|
+
exports.pack = packJson;
|
@@ -0,0 +1,8 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.paths = void 0;
|
4
|
+
var paths;
|
5
|
+
(function (paths) {
|
6
|
+
paths["ROOT_PATH"] = ".quan-cli";
|
7
|
+
paths["TARGET_PATH"] = "dependencies";
|
8
|
+
})(paths = exports.paths || (exports.paths = {}));
|
package/lib/index.js
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
const commander_1 = require("commander");
|
7
|
+
const semver_1 = __importDefault(require("semver"));
|
8
|
+
const minimist_1 = __importDefault(require("minimist"));
|
9
|
+
const utils_1 = require("./utils");
|
10
|
+
const config_1 = require("./config");
|
11
|
+
const actions_1 = require("./actions");
|
12
|
+
class Cli {
|
13
|
+
constructor() {
|
14
|
+
this.program = commander_1.program;
|
15
|
+
this.init();
|
16
|
+
}
|
17
|
+
async init() {
|
18
|
+
this.checkEnv();
|
19
|
+
await this.checkCli();
|
20
|
+
this.registerCommand();
|
21
|
+
}
|
22
|
+
checkEnv() {
|
23
|
+
const args = (0, minimist_1.default)(process.argv.slice(2));
|
24
|
+
if (args.debug || args.d) {
|
25
|
+
(0, utils_1.setLogLevel)('debug');
|
26
|
+
}
|
27
|
+
}
|
28
|
+
async checkCli() {
|
29
|
+
const currentVersion = config_1.pack.version;
|
30
|
+
let lastVersion;
|
31
|
+
try {
|
32
|
+
lastVersion = await utils_1.NpmUtil.getLatestVersion('cyq-cli-template-vue3');
|
33
|
+
}
|
34
|
+
catch (e) {
|
35
|
+
lastVersion = '0.0.0';
|
36
|
+
}
|
37
|
+
if (!semver_1.default.lt(currentVersion, lastVersion)) {
|
38
|
+
utils_1.log.info('tip', `quan-cli最新版本${lastVersion} 当前版本${currentVersion}`);
|
39
|
+
utils_1.log.info('tip', `使用npm install quan-cli 更新`);
|
40
|
+
}
|
41
|
+
utils_1.log.debug('quan-cli当前版本', currentVersion);
|
42
|
+
utils_1.log.debug('quan-cli最新版本', lastVersion);
|
43
|
+
}
|
44
|
+
registerCommand() {
|
45
|
+
this.program.name(config_1.pack.name).version(config_1.pack.version).description(`quan-cli version:${config_1.pack.version}`);
|
46
|
+
this.program.option('-d, --debug', '开启debug模式(打印信息)');
|
47
|
+
this.program
|
48
|
+
.command('init [type]')
|
49
|
+
.description('quan-cli 初始化项目')
|
50
|
+
.option('-p, --packagePath <packagePath>', '指定init包的路径')
|
51
|
+
.option('-f, --force', '覆盖当前项目')
|
52
|
+
.option('-o, --origin <origin>', '指定初始化使用的npm源')
|
53
|
+
.action((str, option) => {
|
54
|
+
utils_1.log.debug('输入参数', option);
|
55
|
+
new actions_1.InitAction(option);
|
56
|
+
});
|
57
|
+
this.program.parse();
|
58
|
+
}
|
59
|
+
}
|
60
|
+
new Cli();
|
@@ -0,0 +1,20 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.FileUtil = void 0;
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
8
|
+
class FileUtil {
|
9
|
+
}
|
10
|
+
exports.FileUtil = FileUtil;
|
11
|
+
FileUtil.isEmptyDir = (path) => {
|
12
|
+
let fileList = fs_1.default.readdirSync(path);
|
13
|
+
fileList = fileList.filter((file) => ['node_modules', 'src', 'tsconfig.json', 'package.json', '.git', '.DS_Store'].includes(file));
|
14
|
+
if (fileList && fileList.length > 0) {
|
15
|
+
return false;
|
16
|
+
}
|
17
|
+
else {
|
18
|
+
return true;
|
19
|
+
}
|
20
|
+
};
|
@@ -0,0 +1,20 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
15
|
+
};
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
17
|
+
__exportStar(require("./file"), exports);
|
18
|
+
__exportStar(require("./log"), exports);
|
19
|
+
__exportStar(require("./npm"), exports);
|
20
|
+
__exportStar(require("./loading"), exports);
|
@@ -0,0 +1,20 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.sleep = exports.loading = void 0;
|
4
|
+
const cli_spinner_1 = require("cli-spinner");
|
5
|
+
const loading = (msg, spinnerString = '|/-\\') => {
|
6
|
+
const spinner = new cli_spinner_1.Spinner(`${msg}.. %s`);
|
7
|
+
spinner.setSpinnerString(spinnerString);
|
8
|
+
spinner.start();
|
9
|
+
return spinner;
|
10
|
+
};
|
11
|
+
exports.loading = loading;
|
12
|
+
const sleep = async (time = 0) => {
|
13
|
+
await new Promise((resolve) => {
|
14
|
+
let timer = setTimeout(() => {
|
15
|
+
resolve(undefined);
|
16
|
+
clearTimeout(timer);
|
17
|
+
}, time);
|
18
|
+
});
|
19
|
+
};
|
20
|
+
exports.sleep = sleep;
|
package/lib/utils/log.js
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.setLogLevel = exports.log = void 0;
|
7
|
+
const npmlog_1 = __importDefault(require("npmlog"));
|
8
|
+
exports.log = npmlog_1.default;
|
9
|
+
npmlog_1.default.level = process.env.LOG_LEVEL || 'info';
|
10
|
+
npmlog_1.default.heading = 'quan-cli';
|
11
|
+
npmlog_1.default.addLevel('success', 2000, { fg: 'green', bold: true });
|
12
|
+
npmlog_1.default.addLevel('debug', 1000, { fg: 'blue', bg: 'black' });
|
13
|
+
const setLogLevel = (level) => {
|
14
|
+
npmlog_1.default.level = level;
|
15
|
+
npmlog_1.default.debug(npmlog_1.default.level + '模式开启');
|
16
|
+
};
|
17
|
+
exports.setLogLevel = setLogLevel;
|
package/lib/utils/npm.js
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.NpmUtil = void 0;
|
7
|
+
const log_1 = require("./log");
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
9
|
+
class NpmUtil {
|
10
|
+
// 获取 registry 信息
|
11
|
+
static getNpmRegistry(isOriginal = false) {
|
12
|
+
return isOriginal ? 'https://registry.npmjs.org' : 'https://registry.npm.taobao.org';
|
13
|
+
}
|
14
|
+
//获取npm最新版本号
|
15
|
+
static async getLatestVersion(packageName, origin = NpmUtil.getNpmRegistry(true)) {
|
16
|
+
log_1.log.debug('链接' + origin + '/' + packageName);
|
17
|
+
const { data } = await (0, axios_1.default)({
|
18
|
+
method: 'get',
|
19
|
+
url: origin + '/' + packageName,
|
20
|
+
});
|
21
|
+
const version = data['dist-tags'].latest;
|
22
|
+
return version;
|
23
|
+
}
|
24
|
+
//获取npm包在文件中的名字
|
25
|
+
static getPackageDirName(packageName, version) {
|
26
|
+
return '_' + packageName + '@' + version + '@' + packageName;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
exports.NpmUtil = NpmUtil;
|
package/package.json
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
{
|
2
|
+
"name": "quan-cli",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "",
|
5
|
+
"main": "index.js",
|
6
|
+
"bin": {
|
7
|
+
"quan-cli": "./bin/index.js"
|
8
|
+
},
|
9
|
+
"scripts": {
|
10
|
+
"dev": "ts-node-dev --respawn --transpile-only src/index.ts ",
|
11
|
+
"build": "tsc --build",
|
12
|
+
"prettier": "prettier --write ."
|
13
|
+
},
|
14
|
+
"keywords": [],
|
15
|
+
"author": "",
|
16
|
+
"license": "ISC",
|
17
|
+
"type": "commonjs",
|
18
|
+
"devDependencies": {
|
19
|
+
"@types/axios": "^0.14.0",
|
20
|
+
"@types/cli-spinner": "^0.2.1",
|
21
|
+
"@types/commander": "^2.12.2",
|
22
|
+
"@types/execa": "^2.0.0",
|
23
|
+
"@types/fs-extra": "^9.0.13",
|
24
|
+
"@types/inquirer": "^8.2.1",
|
25
|
+
"@types/minimist": "^1.2.2",
|
26
|
+
"@types/node": "^18.6.3",
|
27
|
+
"@types/npmlog": "^4.1.4",
|
28
|
+
"@types/semver": "^7.3.10",
|
29
|
+
"npmlog": "^6.0.2",
|
30
|
+
"prettier": "^2.7.1",
|
31
|
+
"ts-loader": "^9.3.1",
|
32
|
+
"ts-node": "^10.9.1",
|
33
|
+
"ts-node-dev": "^2.0.0",
|
34
|
+
"webpack": "^5.74.0",
|
35
|
+
"webpack-cli": "^4.10.0"
|
36
|
+
},
|
37
|
+
"dependencies": {
|
38
|
+
"axios": "^0.27.2",
|
39
|
+
"cli-spinner": "^0.2.10",
|
40
|
+
"commander": "^9.4.0",
|
41
|
+
"execa": "^5.1.1",
|
42
|
+
"fs-extra": "^10.1.0",
|
43
|
+
"inquirer": "^8.2.4",
|
44
|
+
"minimist": "^1.2.6",
|
45
|
+
"npminstall": "^5.8.1",
|
46
|
+
"semver": "^7.3.7",
|
47
|
+
"ts-node": "^10.9.1",
|
48
|
+
"typescript": "^4.7.4",
|
49
|
+
"userhome": "^1.0.0"
|
50
|
+
}
|
51
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './initAction';
|
@@ -0,0 +1,143 @@
|
|
1
|
+
import fs from 'fs';
|
2
|
+
import path from 'path';
|
3
|
+
//@ts-ignore
|
4
|
+
import userhome from 'userhome';
|
5
|
+
import inquirer from 'inquirer';
|
6
|
+
import fse from 'fs-extra';
|
7
|
+
import axios from 'axios';
|
8
|
+
//@ts-ignore
|
9
|
+
import npminstall from 'npminstall';
|
10
|
+
import { log, FileUtil, NpmUtil, loading, sleep } from '../utils';
|
11
|
+
import { paths } from '../config';
|
12
|
+
|
13
|
+
export interface initCommand {
|
14
|
+
packagePath?: string;
|
15
|
+
force?: boolean;
|
16
|
+
origin?: string;
|
17
|
+
}
|
18
|
+
|
19
|
+
export interface template {
|
20
|
+
id: number;
|
21
|
+
name: string;
|
22
|
+
store: string;
|
23
|
+
}
|
24
|
+
|
25
|
+
export class InitAction {
|
26
|
+
cwdPath: string = process.cwd();
|
27
|
+
basePath!: string;
|
28
|
+
force: boolean;
|
29
|
+
templates!: template[];
|
30
|
+
template!: template;
|
31
|
+
origin!: string;
|
32
|
+
homePath: string = userhome();
|
33
|
+
constructor(option: initCommand) {
|
34
|
+
this.basePath = option.packagePath || this.cwdPath;
|
35
|
+
this.force = option.force || false;
|
36
|
+
this.origin = option.origin || NpmUtil.getNpmRegistry(true);
|
37
|
+
this.initAction();
|
38
|
+
}
|
39
|
+
//初始化
|
40
|
+
async initAction() {
|
41
|
+
log.debug('homePath', this.homePath);
|
42
|
+
log.debug('工作路径', this.cwdPath);
|
43
|
+
log.debug('要创建的目录', this.basePath);
|
44
|
+
//路径不存在
|
45
|
+
this.checkPath();
|
46
|
+
this.checkForce();
|
47
|
+
await this.checkPrompt();
|
48
|
+
await this.getTemplate();
|
49
|
+
await this.selectTemplate();
|
50
|
+
await this.getTemplateVersion();
|
51
|
+
await this.install();
|
52
|
+
}
|
53
|
+
//检查路径是否可用
|
54
|
+
checkPath() {
|
55
|
+
if (!fs.existsSync(this.basePath)) {
|
56
|
+
log.error('error', '路径不存在,请重试!');
|
57
|
+
process.exit(1);
|
58
|
+
}
|
59
|
+
}
|
60
|
+
//force清空目录
|
61
|
+
checkForce() {
|
62
|
+
const isEmpty = FileUtil.isEmptyDir(this.basePath);
|
63
|
+
if (!isEmpty && this.force) {
|
64
|
+
fse.emptyDirSync(this.basePath);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
//checkPrompt 清空目录
|
68
|
+
async checkPrompt() {
|
69
|
+
const isEmpty = FileUtil.isEmptyDir(this.basePath);
|
70
|
+
if (!isEmpty) {
|
71
|
+
const isDelDir = await this.getDelPrompt();
|
72
|
+
if (!isDelDir) {
|
73
|
+
process.exit(1);
|
74
|
+
} else {
|
75
|
+
fse.emptyDirSync(this.basePath);
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
async getDelPrompt() {
|
80
|
+
const { isDelDir } = await inquirer.prompt([
|
81
|
+
{
|
82
|
+
type: 'confirm',
|
83
|
+
name: 'isDelDir',
|
84
|
+
message: `是否清空${this.basePath}。注意(文件无法恢复,请谨慎操作)`,
|
85
|
+
},
|
86
|
+
]);
|
87
|
+
return isDelDir;
|
88
|
+
}
|
89
|
+
async getTemplateVersion() {
|
90
|
+
return await NpmUtil.getLatestVersion(this.template.store, this.origin);
|
91
|
+
}
|
92
|
+
async install() {
|
93
|
+
const version = await this.getTemplateVersion();
|
94
|
+
log.debug(`执行安装:${this.template.store},版本:${version}`);
|
95
|
+
log.debug(path.resolve(this.homePath, paths.ROOT_PATH, paths.TARGET_PATH));
|
96
|
+
const loadingStart = loading('正在下载模板...');
|
97
|
+
await sleep(1500);
|
98
|
+
await npminstall({
|
99
|
+
root: path.resolve(this.homePath, paths.ROOT_PATH, paths.TARGET_PATH),
|
100
|
+
registry: this.origin,
|
101
|
+
pkgs: [{ name: this.template.store, version: version }],
|
102
|
+
});
|
103
|
+
this.copy(this.template.store, version);
|
104
|
+
loadingStart.stop(true);
|
105
|
+
log.info('info', '模板下载完成');
|
106
|
+
}
|
107
|
+
//執行copy
|
108
|
+
copy(name: string, version: string) {
|
109
|
+
const packageName = NpmUtil.getPackageDirName(name, version);
|
110
|
+
const originPath = path.resolve(
|
111
|
+
this.homePath,
|
112
|
+
paths.ROOT_PATH,
|
113
|
+
paths.TARGET_PATH,
|
114
|
+
'node_modules',
|
115
|
+
packageName,
|
116
|
+
'template'
|
117
|
+
);
|
118
|
+
const targetPath = this.basePath;
|
119
|
+
fse.copySync(originPath, targetPath);
|
120
|
+
process.exit(1);
|
121
|
+
}
|
122
|
+
async selectTemplate() {
|
123
|
+
const { template } = await inquirer.prompt([
|
124
|
+
{
|
125
|
+
type: 'list',
|
126
|
+
name: 'template',
|
127
|
+
message: '请选择要下载的模板',
|
128
|
+
choices: this.templates.map((item) => item.name),
|
129
|
+
},
|
130
|
+
]);
|
131
|
+
this.template = this.templates.filter((item) => item.name === template)[0];
|
132
|
+
}
|
133
|
+
async getTemplate() {
|
134
|
+
|
135
|
+
const {
|
136
|
+
data: { data },
|
137
|
+
} = await axios({
|
138
|
+
method: 'get',
|
139
|
+
url: 'http://101.42.154.196/quan-cli/getAllTemplate',
|
140
|
+
});
|
141
|
+
this.templates = data;
|
142
|
+
}
|
143
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { log } from '../utils';
|
2
|
+
const packJson = require('../../package');
|
3
|
+
|
4
|
+
log.debug('package', JSON.stringify(packJson));
|
5
|
+
|
6
|
+
export interface Package {
|
7
|
+
name: string;
|
8
|
+
version: string;
|
9
|
+
author: string;
|
10
|
+
}
|
11
|
+
|
12
|
+
export const pack = packJson as Package;
|
package/src/index.ts
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
import { program, Command } from 'commander';
|
2
|
+
import semver from 'semver';
|
3
|
+
import minimist from 'minimist';
|
4
|
+
import { log, setLogLevel, NpmUtil } from './utils';
|
5
|
+
import { pack } from './config';
|
6
|
+
import { initCommand, InitAction } from './actions';
|
7
|
+
|
8
|
+
class Cli {
|
9
|
+
program: Command = program;
|
10
|
+
constructor() {
|
11
|
+
this.init();
|
12
|
+
}
|
13
|
+
async init() {
|
14
|
+
this.checkEnv();
|
15
|
+
await this.checkCli();
|
16
|
+
this.registerCommand();
|
17
|
+
}
|
18
|
+
checkEnv() {
|
19
|
+
const args = minimist(process.argv.slice(2));
|
20
|
+
if (args.debug || args.d) {
|
21
|
+
setLogLevel('debug');
|
22
|
+
}
|
23
|
+
}
|
24
|
+
async checkCli() {
|
25
|
+
const currentVersion = pack.version;
|
26
|
+
let lastVersion;
|
27
|
+
try {
|
28
|
+
lastVersion = await NpmUtil.getLatestVersion('cyq-cli-template-vue3');
|
29
|
+
} catch (e) {
|
30
|
+
lastVersion = '0.0.0';
|
31
|
+
}
|
32
|
+
if (!semver.lt(currentVersion, lastVersion)) {
|
33
|
+
log.info('tip', `quan-cli最新版本${lastVersion} 当前版本${currentVersion}`);
|
34
|
+
log.info('tip', `使用npm install quan-cli 更新`);
|
35
|
+
}
|
36
|
+
log.debug('quan-cli当前版本', currentVersion);
|
37
|
+
log.debug('quan-cli最新版本', lastVersion);
|
38
|
+
}
|
39
|
+
registerCommand() {
|
40
|
+
this.program.name(pack.name).version(pack.version).description(`quan-cli version:${pack.version}`);
|
41
|
+
this.program.option('-d, --debug', '开启debug模式(打印信息)');
|
42
|
+
this.program
|
43
|
+
.command('init [type]')
|
44
|
+
.description('quan-cli 初始化项目')
|
45
|
+
.option('-p, --packagePath <packagePath>', '指定init包的路径')
|
46
|
+
.option('-f, --force', '覆盖当前项目')
|
47
|
+
.option('-o, --origin <origin>', '指定初始化使用的npm源')
|
48
|
+
.action((str: string, option: initCommand) => {
|
49
|
+
log.debug('输入参数', option);
|
50
|
+
new InitAction(option);
|
51
|
+
});
|
52
|
+
this.program.parse();
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
new Cli();
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import fs from 'fs';
|
2
|
+
export class FileUtil {
|
3
|
+
static isEmptyDir = (path: string) => {
|
4
|
+
let fileList = fs.readdirSync(path);
|
5
|
+
fileList = fileList.filter((file) => ['node_modules', 'src', 'tsconfig.json', 'package.json', '.git', '.DS_Store'].includes(file));
|
6
|
+
if (fileList && fileList.length > 0) {
|
7
|
+
return false;
|
8
|
+
} else {
|
9
|
+
return true;
|
10
|
+
}
|
11
|
+
};
|
12
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { Spinner } from 'cli-spinner';
|
2
|
+
|
3
|
+
export const loading = (msg: string, spinnerString: string = '|/-\\') => {
|
4
|
+
const spinner = new Spinner(`${msg}.. %s`);
|
5
|
+
spinner.setSpinnerString(spinnerString);
|
6
|
+
spinner.start();
|
7
|
+
return spinner;
|
8
|
+
};
|
9
|
+
|
10
|
+
export const sleep = async (time: number = 0) => {
|
11
|
+
await new Promise((resolve) => {
|
12
|
+
let timer = setTimeout(() => {
|
13
|
+
resolve(undefined);
|
14
|
+
clearTimeout(timer);
|
15
|
+
}, time);
|
16
|
+
});
|
17
|
+
};
|
package/src/utils/log.ts
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
import log from 'npmlog';
|
2
|
+
|
3
|
+
export type customLogLevels = log.LogLevels | 'success' | 'debug';
|
4
|
+
|
5
|
+
log.level = process.env.LOG_LEVEL || 'info';
|
6
|
+
log.heading = 'quan-cli';
|
7
|
+
|
8
|
+
log.addLevel('success', 2000, { fg: 'green', bold: true });
|
9
|
+
log.addLevel('debug', 1000, { fg: 'blue', bg: 'black' });
|
10
|
+
|
11
|
+
const setLogLevel = (level: customLogLevels) => {
|
12
|
+
log.level = level;
|
13
|
+
log.debug(log.level + '模式开启');
|
14
|
+
};
|
15
|
+
|
16
|
+
export { log, setLogLevel };
|
package/src/utils/npm.ts
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
import { log } from './log';
|
2
|
+
import axios from 'axios';
|
3
|
+
export class NpmUtil {
|
4
|
+
// 获取 registry 信息
|
5
|
+
static getNpmRegistry(isOriginal = false): string {
|
6
|
+
return isOriginal ? 'https://registry.npmjs.org' : 'https://registry.npm.taobao.org';
|
7
|
+
}
|
8
|
+
//获取npm最新版本号
|
9
|
+
static async getLatestVersion(packageName: string, origin: string = NpmUtil.getNpmRegistry(true)) {
|
10
|
+
log.debug('链接' + origin + '/' + packageName);
|
11
|
+
const { data } = await axios({
|
12
|
+
method: 'get',
|
13
|
+
url: origin + '/' + packageName,
|
14
|
+
});
|
15
|
+
|
16
|
+
const version = data['dist-tags'].latest;
|
17
|
+
return version;
|
18
|
+
}
|
19
|
+
//获取npm包在文件中的名字
|
20
|
+
static getPackageDirName(packageName: string, version: string): string {
|
21
|
+
return '_' + packageName + '@' + version + '@' + packageName;
|
22
|
+
}
|
23
|
+
}
|
package/tsconfig.json
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
{
|
2
|
+
"compilerOptions": {
|
3
|
+
/* Visit https://aka.ms/tsconfig to read more about this file */
|
4
|
+
|
5
|
+
/* Projects */
|
6
|
+
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
7
|
+
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
8
|
+
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
9
|
+
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
10
|
+
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
11
|
+
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
12
|
+
|
13
|
+
/* Language and Environment */
|
14
|
+
"target": "es2018" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
15
|
+
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
16
|
+
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
17
|
+
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
18
|
+
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
19
|
+
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
20
|
+
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
21
|
+
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
22
|
+
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
23
|
+
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
24
|
+
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
25
|
+
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
26
|
+
|
27
|
+
/* Modules */
|
28
|
+
"module": "commonjs" /* Specify what module code is generated. */,
|
29
|
+
// "rootDir": "./", /* Specify the root folder within your source files. */
|
30
|
+
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
31
|
+
"baseUrl": "./" /* Specify the base directory to resolve non-relative module names. */,
|
32
|
+
"paths": {} /* Specify a set of entries that re-map imports to additional lookup locations. */,
|
33
|
+
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
34
|
+
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
35
|
+
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
36
|
+
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
37
|
+
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
38
|
+
// "resolveJsonModule": true, /* Enable importing .json files. */
|
39
|
+
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
40
|
+
|
41
|
+
/* JavaScript Support */
|
42
|
+
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
43
|
+
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
44
|
+
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
45
|
+
|
46
|
+
/* Emit */
|
47
|
+
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
48
|
+
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
49
|
+
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
50
|
+
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
51
|
+
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
52
|
+
"outDir": "./lib" /* Specify an output folder for all emitted files. */,
|
53
|
+
// "removeComments": true, /* Disable emitting comments. */
|
54
|
+
// "noEmit": true, /* Disable emitting files from a compilation. */
|
55
|
+
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
56
|
+
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
57
|
+
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
58
|
+
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
59
|
+
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
60
|
+
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
61
|
+
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
62
|
+
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
63
|
+
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
64
|
+
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
65
|
+
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
66
|
+
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
67
|
+
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
68
|
+
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
69
|
+
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
70
|
+
|
71
|
+
/* Interop Constraints */
|
72
|
+
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
73
|
+
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
74
|
+
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
75
|
+
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
76
|
+
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
77
|
+
|
78
|
+
/* Type Checking */
|
79
|
+
"strict": true /* Enable all strict type-checking options. */,
|
80
|
+
"noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied 'any' type. */,
|
81
|
+
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
82
|
+
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
83
|
+
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
84
|
+
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
85
|
+
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
86
|
+
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
87
|
+
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
88
|
+
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
89
|
+
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
90
|
+
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
91
|
+
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
92
|
+
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
93
|
+
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
94
|
+
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
95
|
+
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
96
|
+
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
97
|
+
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
98
|
+
|
99
|
+
/* Completeness */
|
100
|
+
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
101
|
+
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
102
|
+
},
|
103
|
+
"include": ["./src/**/*"],
|
104
|
+
"exclude": ["node_modules"]
|
105
|
+
}
|