rigjs 2.0.0-alpha → 2.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/built/index.js +371 -34
- package/demo/.env.oem1 +4 -0
- package/demo/.env.oem2 +4 -0
- package/demo/babel.config.js +5 -0
- package/demo/cicd.rig.json5 +12 -7
- package/demo/jsconfig.json +19 -0
- package/demo/package.json +45 -14
- package/demo/package.rig.json5 +0 -1
- package/demo/public/favicon.ico +0 -0
- package/demo/public/index.html +17 -0
- package/demo/src/App.vue +28 -0
- package/demo/src/assets/logo.png +0 -0
- package/demo/src/components/HelloWorld.vue +58 -0
- package/demo/src/main.js +8 -0
- package/demo/vue.config.js +8 -0
- package/demo/yarn.lock +6057 -21
- package/lib/build/index.ts +5 -1
- package/lib/classes/cicd/CICD.ts +5 -3
- package/lib/classes/cicd/Deploy/AliDeploy.ts +46 -0
- package/lib/classes/cicd/Deploy/CDN.ts +126 -0
- package/lib/define/index.ts +1 -1
- package/lib/deploy/index.ts +54 -36
- package/lib/init/cicd.rig.json5 +5 -13
- package/lib/init/index.js +28 -5
- package/lib/rig/index.ts +2 -1
- package/package.json +8 -2
- package/demo/test.js +0 -18
package/lib/build/index.ts
CHANGED
|
@@ -2,16 +2,20 @@ import fsHelper from '../utils/fsHelper';
|
|
|
2
2
|
import CICD from '@/classes/cicd/CICD';
|
|
3
3
|
import CICDCmd from '@/classes/cicd/CICDCmd';
|
|
4
4
|
import shell from 'shelljs';
|
|
5
|
+
import path from 'path';
|
|
5
6
|
|
|
6
7
|
export default async (cmd: any) => {
|
|
7
8
|
//create cicd object
|
|
8
9
|
const cicd = CICD.createByDefault(cmd);
|
|
9
10
|
//construct cmd object
|
|
10
11
|
const cicdCmd = new CICDCmd(cmd, cicd);
|
|
12
|
+
console.log(cicd)
|
|
11
13
|
//build by cicdCmd and cicdConfig
|
|
12
14
|
console.log(cicdCmd.endpoints);
|
|
13
15
|
for (let i = 0; i < cicdCmd.endpoints.length; i++) {
|
|
14
16
|
const ep = cicdCmd.endpoints[i];
|
|
15
|
-
|
|
17
|
+
const cmdStr = `cross-env PUBLIC_PATH=${path.join('/',ep.deployDir)} ${ep.build}`;
|
|
18
|
+
console.log(cmdStr);
|
|
19
|
+
shell.exec(cmdStr);
|
|
16
20
|
}
|
|
17
21
|
}
|
package/lib/classes/cicd/CICD.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import DirLevel from '@/classes/cicd/DirLevel';
|
|
2
2
|
import Endpoint, { EndpointDict} from '@/classes/cicd/Endpoint';
|
|
3
3
|
import fs from 'fs';
|
|
4
|
-
import {Dir} from 'fs';
|
|
5
|
-
import fsHelper from '@/utils/fsHelper';
|
|
6
|
-
import CICDCmd from '@/classes/cicd/CICDCmd';
|
|
7
4
|
|
|
8
5
|
const JSON5 = require('json5');
|
|
9
6
|
import qs from 'querystring';
|
|
@@ -25,9 +22,14 @@ interface DeployTarget {
|
|
|
25
22
|
id: string;
|
|
26
23
|
type: CloudType;
|
|
27
24
|
bucket: string;
|
|
25
|
+
region: string;
|
|
28
26
|
access_key: string;
|
|
29
27
|
access_secret: string;
|
|
30
28
|
root_path: '/';
|
|
29
|
+
uri_rewrite: {
|
|
30
|
+
original: string;
|
|
31
|
+
final?: string;
|
|
32
|
+
};
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
/**
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import aliOSS from "ali-oss";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
class AliOSS {
|
|
4
|
+
ossClient: aliOSS;
|
|
5
|
+
constructor(accessKeyId: string, accessKeySecret: string, region: string, bucket: string) {
|
|
6
|
+
this.ossClient = new aliOSS({
|
|
7
|
+
region,
|
|
8
|
+
accessKeyId: accessKeyId,
|
|
9
|
+
accessKeySecret: accessKeySecret,
|
|
10
|
+
bucket,
|
|
11
|
+
timeout: 600000,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private async progress(p: number, filePath: string, ossPath: string) {
|
|
16
|
+
// 上传进度。
|
|
17
|
+
process.stdout.clearLine(-1);
|
|
18
|
+
process.stdout.cursorTo(0);
|
|
19
|
+
process.stdout.write(
|
|
20
|
+
`progress: ${p.toFixed(2)}%, Upload '${filePath}' To OSS_PATH:${ossPath}`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public async putStreamFiles(
|
|
25
|
+
filesList: string[],
|
|
26
|
+
ossBasePath: string,
|
|
27
|
+
dir: string
|
|
28
|
+
) {
|
|
29
|
+
for (let i = 0; i < filesList.length; i++) {
|
|
30
|
+
const filePath = filesList[i].split("dist\\")[1];
|
|
31
|
+
const ossPath =
|
|
32
|
+
ossBasePath + filePath.replace(/\\/g, "/").replace(dir, "");
|
|
33
|
+
const fileResult = await this.ossClient.putStream(
|
|
34
|
+
ossPath,
|
|
35
|
+
fs.createReadStream(filesList[i])
|
|
36
|
+
);
|
|
37
|
+
if (fileResult.res.status === 200) {
|
|
38
|
+
const p = ((i + 1) * 100) / filesList.length;
|
|
39
|
+
this.progress(p, filesList[i], ossPath);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
console.log("\n");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default AliOSS;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import moment from "dayjs";
|
|
2
|
+
import qs from "qs";
|
|
3
|
+
import crypto from "crypto";
|
|
4
|
+
import axios from "axios";
|
|
5
|
+
import * as uuid from "uuid";
|
|
6
|
+
|
|
7
|
+
type TFlag = "break" | "enhance_break" | null;
|
|
8
|
+
|
|
9
|
+
class CDN {
|
|
10
|
+
AccessKeySecret: string;
|
|
11
|
+
AccessKeyId: string;
|
|
12
|
+
constructor(AccessKeyId: string, AccessKeySecret: string) {
|
|
13
|
+
this.AccessKeySecret = AccessKeySecret;
|
|
14
|
+
this.AccessKeyId = AccessKeyId;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 访问CDN通用接口
|
|
18
|
+
* @param {接口名称} actionName
|
|
19
|
+
* @param {各接口定制化参数} paramObj
|
|
20
|
+
* @returns
|
|
21
|
+
*/
|
|
22
|
+
private async getCdnData(actionName: string, paramObj: Object) {
|
|
23
|
+
let config = {
|
|
24
|
+
Action: actionName,
|
|
25
|
+
Format: "JSON",
|
|
26
|
+
Version: "2018-05-10",
|
|
27
|
+
AccessKeyId: this.AccessKeyId,
|
|
28
|
+
SignatureMethod: "HMAC-SHA1",
|
|
29
|
+
Timestamp: moment().toDate().toISOString(),
|
|
30
|
+
SignatureVersion: "1.0",
|
|
31
|
+
SignatureNonce: uuid.v1(),
|
|
32
|
+
};
|
|
33
|
+
config = Object.assign(config, paramObj);
|
|
34
|
+
let paramConfig = qs.stringify(config, {
|
|
35
|
+
sort: (a: number, b: number) => {
|
|
36
|
+
return a < b ? -1 : 1;
|
|
37
|
+
},
|
|
38
|
+
charset: "utf-8",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const strSign = `GET&%2F&${encodeURIComponent(paramConfig)}`;
|
|
42
|
+
console.log(`strSign: ${strSign}`);
|
|
43
|
+
const hmacSha1 = crypto.createHmac("sha1", this.AccessKeySecret);
|
|
44
|
+
hmacSha1.update(strSign);
|
|
45
|
+
const signature = hmacSha1.digest("base64");
|
|
46
|
+
paramConfig += `&Signature=${signature}`;
|
|
47
|
+
|
|
48
|
+
const url = `http://cdn.aliyuncs.com?${paramConfig}`;
|
|
49
|
+
console.log(url);
|
|
50
|
+
|
|
51
|
+
const res = await axios.create().get(url);
|
|
52
|
+
return res.data;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 改写回源URI接口
|
|
57
|
+
* @param {加速域名} domainName
|
|
58
|
+
* @param {需要重写的url 数组} sourceUrls
|
|
59
|
+
* @param {重写目标url 数组} targetUrls
|
|
60
|
+
* @param {改写操作执行规则 数组 值为null、break或enhance_break} flags
|
|
61
|
+
* @returns
|
|
62
|
+
*/
|
|
63
|
+
public async setRWriteUri(
|
|
64
|
+
domainName: string,
|
|
65
|
+
sourceUrls: string[],
|
|
66
|
+
targetUrls: string[],
|
|
67
|
+
flags: TFlag[]
|
|
68
|
+
) {
|
|
69
|
+
if (sourceUrls.length !== targetUrls.length) {
|
|
70
|
+
throw new Error(`sourceUrls's length not equal targetUrls's length`);
|
|
71
|
+
}
|
|
72
|
+
const Functions: Object[] = [];
|
|
73
|
+
sourceUrls.forEach((item, index) => {
|
|
74
|
+
Functions.push({
|
|
75
|
+
functionArgs: [
|
|
76
|
+
{
|
|
77
|
+
argName: "source_url",
|
|
78
|
+
argValue: item,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
argName: "target_url",
|
|
82
|
+
argValue: targetUrls[index],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
argName: "flag",
|
|
86
|
+
argValue: flags[index],
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
functionName: "back_to_origin_url_rewrite",
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const data = await this.getCdnData("BatchSetCdnDomainConfig", {
|
|
94
|
+
DomainNames: domainName,
|
|
95
|
+
Functions: JSON.stringify(Functions),
|
|
96
|
+
});
|
|
97
|
+
return data;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 刷新节点上的文件内容
|
|
102
|
+
* @param {刷新URL, 格式为加速域名或刷新的文件或目录。多个URL之间使用换行符(\n)或(\r\n)分隔} objectPath
|
|
103
|
+
* @param {刷新的类型 File: 文件; Directory: 目录} objectType
|
|
104
|
+
*/
|
|
105
|
+
public async refreshCache(objectPath: string, objectType: string) {
|
|
106
|
+
const data = await this.getCdnData("RefreshObjectCaches", {
|
|
107
|
+
ObjectPath: objectPath,
|
|
108
|
+
ObjectType: objectType ? objectType : undefined,
|
|
109
|
+
});
|
|
110
|
+
return data;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 预热源站内容到缓存节点
|
|
115
|
+
* @param {预热URL,格式为加速域名或预热的文件 多个URL之间使用换行符(\n)或(\r\n)分隔 单条长度最长为1024个字符} objectPath
|
|
116
|
+
* @returns
|
|
117
|
+
*/
|
|
118
|
+
async pushCache(objectPath: string) {
|
|
119
|
+
const data = await this.getCdnData("PushObjectCache", {
|
|
120
|
+
ObjectPath: objectPath,
|
|
121
|
+
});
|
|
122
|
+
return data;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export default CDN;
|
package/lib/define/index.ts
CHANGED
|
@@ -13,7 +13,7 @@ const replaceDefine = (target:string,define?:Define)=>{
|
|
|
13
13
|
if (define){
|
|
14
14
|
const namePieces = dir.split('.');
|
|
15
15
|
const fileType = namePieces[namePieces.length - 1];
|
|
16
|
-
if (['js','
|
|
16
|
+
if (['js','ts'].indexOf(fileType)>=0){
|
|
17
17
|
let file = fs.readFileSync(path.join(target, dir)).toString();
|
|
18
18
|
const replaceArr = Object.keys(define);
|
|
19
19
|
for (let replace of replaceArr){
|
package/lib/deploy/index.ts
CHANGED
|
@@ -1,42 +1,60 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import aliOSS from "ali-oss";
|
|
4
|
-
import fsHelper from "../utils/fsHelper";
|
|
5
3
|
import CICD from "@/classes/cicd/CICD";
|
|
6
4
|
import CICDCmd from "@/classes/cicd/CICDCmd";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// } else {
|
|
26
|
-
// filesList.push(curPath);
|
|
27
|
-
// }
|
|
28
|
-
// });
|
|
29
|
-
// }
|
|
30
|
-
// };
|
|
31
|
-
//
|
|
5
|
+
import AliOSS from "@/classes/cicd/Deploy/AliDeploy";
|
|
6
|
+
import CDN from "@/classes/cicd/Deploy/CDN";
|
|
7
|
+
|
|
8
|
+
let filesList: string[] = [];
|
|
9
|
+
const traverseFolder = (url: string) => {
|
|
10
|
+
if (fs.existsSync(url)) {
|
|
11
|
+
const files = fs.readdirSync(url);
|
|
12
|
+
files.forEach((file) => {
|
|
13
|
+
const curPath = path.join(url, file);
|
|
14
|
+
if (fs.statSync(curPath).isDirectory()) {
|
|
15
|
+
traverseFolder(curPath);
|
|
16
|
+
} else {
|
|
17
|
+
filesList.push(curPath);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
32
23
|
export default async (cmd: any) => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
24
|
+
try {
|
|
25
|
+
//create cicd object
|
|
26
|
+
const cicd = CICD.createByDefault(cmd);
|
|
27
|
+
//construct cmd object
|
|
28
|
+
const cicdCmd = new CICDCmd(cmd, cicd);
|
|
29
|
+
|
|
30
|
+
const target = Array.isArray(cicdCmd.cicd.target)
|
|
31
|
+
? cicdCmd.cicd.target[0]
|
|
32
|
+
: cicdCmd.cicd.target;
|
|
33
|
+
|
|
34
|
+
const aliOss = new AliOSS(target.access_key, target.access_secret, target.region, target.bucket);
|
|
35
|
+
const cdn = new CDN(target.access_key, target.access_secret);
|
|
36
|
+
for (let i = 0; i < cicdCmd.endpoints.length; i++) {
|
|
37
|
+
const distPath = path.join(
|
|
38
|
+
process.cwd(),
|
|
39
|
+
cicd.source.root_path,
|
|
40
|
+
cicdCmd.endpoints[i].dir
|
|
41
|
+
);
|
|
42
|
+
traverseFolder(distPath);
|
|
43
|
+
await aliOss.putStreamFiles(
|
|
44
|
+
filesList,
|
|
45
|
+
cicdCmd.endpoints[i].deployDir.replace(/\\/g, "/"),
|
|
46
|
+
cicdCmd.endpoints[i].dir
|
|
47
|
+
);
|
|
38
48
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
49
|
+
// await cdn.setRWriteUri(
|
|
50
|
+
// cicdCmd.endpoints[i].domain,
|
|
51
|
+
// [""],
|
|
52
|
+
// [""],
|
|
53
|
+
// ["break"]
|
|
54
|
+
// );
|
|
55
|
+
filesList = [];
|
|
56
|
+
}
|
|
57
|
+
} catch (e) {
|
|
58
|
+
throw e;
|
|
59
|
+
}
|
|
60
|
+
};
|
package/lib/init/cicd.rig.json5
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
tree_schema: ''
|
|
2
|
+
tree_schema: '{app}/{env}/{oem}',
|
|
3
3
|
source: {
|
|
4
|
-
|
|
4
|
+
root_path: 'dist',
|
|
5
5
|
},
|
|
6
|
-
target: {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
access_token: '',
|
|
10
|
-
root_path: '/',
|
|
11
|
-
},
|
|
12
|
-
predefines: {},//relpace special string in your bundle,so you can use the same bundle in different enviroments without building multiple times.
|
|
13
|
-
endpoints: [],
|
|
14
|
-
alias: []
|
|
6
|
+
target: {},
|
|
7
|
+
endpoints: {},
|
|
8
|
+
groups: [],
|
|
15
9
|
}
|
|
16
|
-
|
|
17
|
-
|
package/lib/init/index.js
CHANGED
|
@@ -45,16 +45,39 @@ const load = async () => {
|
|
|
45
45
|
console.log(chalk.green('rig_helper.js already exists~'));
|
|
46
46
|
} else {
|
|
47
47
|
console.log(chalk.green('create rig_helper.js'));
|
|
48
|
-
let
|
|
49
|
-
|
|
48
|
+
let template = `const json5 = require('json5');
|
|
49
|
+
const fs = require('fs');
|
|
50
|
+
const getPkgs = () => {
|
|
51
|
+
\tlet pkgArr = json5.parse(fs.readFileSync('./package.rig.json5'));
|
|
52
|
+
\tlet flatPkgArr = pkgArr.map((item, index) => {
|
|
53
|
+
\t\treturn item.name;
|
|
54
|
+
\t});
|
|
55
|
+
\tconsole.log(flatPkgArr);
|
|
56
|
+
\treturn flatPkgArr;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
\tgetPkgs
|
|
61
|
+
}
|
|
62
|
+
`
|
|
63
|
+
fs.writeFileSync(`${process.cwd()}/rig_helper.js`, template);
|
|
50
64
|
}
|
|
51
65
|
if (fs.existsSync(`${process.cwd()}/cicd.rig.json5`)) {
|
|
52
66
|
console.log(chalk.green('cicd.rig.json5 already exists~'));
|
|
53
67
|
} else {
|
|
54
68
|
console.log(chalk.green('create cicd.rig.json5'));
|
|
55
|
-
let
|
|
56
|
-
|
|
57
|
-
|
|
69
|
+
let template = `{
|
|
70
|
+
tree_schema: '{app}/{env}/{oem}',
|
|
71
|
+
source: {
|
|
72
|
+
root_path: 'dist',
|
|
73
|
+
},
|
|
74
|
+
target: {},
|
|
75
|
+
endpoints: {},
|
|
76
|
+
groups: [],
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
fs.writeFileSync(`${process.cwd()}/cicd.rig.json5`, template);
|
|
80
|
+
|
|
58
81
|
}
|
|
59
82
|
//检查rigs是否存在
|
|
60
83
|
if (fs.existsSync('./rigs') && fs.lstatSync('./rigs').isDirectory()) {
|
package/lib/rig/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rigjs",
|
|
3
|
-
"version": "2.0.0-alpha",
|
|
3
|
+
"version": "2.0.0-alpha.2",
|
|
4
4
|
"description": "A multi-repos dev tool based on yarn and git.Rig is inspired by cocoapods. Not like those monorepo solutions,rig is a tool for organizing multi-repos.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"modular",
|
|
@@ -29,19 +29,25 @@
|
|
|
29
29
|
"envmake": "cd demo && node ../lib/rig/index.js --env prod_view_zhs",
|
|
30
30
|
"t": "node lib/rig/index.js tag",
|
|
31
31
|
"deliver": "npm publish --registry=https://registry.npmjs.org",
|
|
32
|
+
"deliver:alpha": "npm publish --registry=https://registry.npmjs.org --tag alpha",
|
|
32
33
|
"build": "esbuild lib/rig/index.ts --platform=node --bundle --minify --outfile=built/index.js --external:shelljs"
|
|
33
34
|
},
|
|
34
35
|
"dependencies": {
|
|
35
36
|
"@types/ali-oss": "^6.16.3",
|
|
36
37
|
"@types/json5": "^2.2.0",
|
|
38
|
+
"@types/qs": "^6.9.7",
|
|
37
39
|
"@types/shelljs": "^0.8.11",
|
|
40
|
+
"@types/uuid": "^8.3.4",
|
|
38
41
|
"ali-oss": "^6.17.1",
|
|
42
|
+
"axios": "^0.26.1",
|
|
39
43
|
"chalk": "^4.1.0",
|
|
40
44
|
"commander": "6.1.0",
|
|
45
|
+
"dayjs": "^1.11.0",
|
|
41
46
|
"inquirer": "7.3.3",
|
|
42
47
|
"json5": "2.1.3",
|
|
43
48
|
"ora": "^5.1.0",
|
|
44
|
-
"shelljs": "^0.8.4"
|
|
49
|
+
"shelljs": "^0.8.4",
|
|
50
|
+
"uuid": "^8.3.2"
|
|
45
51
|
},
|
|
46
52
|
"devDependencies": {
|
|
47
53
|
"@types/node": "^17.0.21"
|
package/demo/test.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// const fs = require('fs');
|
|
2
|
-
// let pkgStr = fs.readFileSync('package.json').toString();
|
|
3
|
-
// let pkg = JSON.parse(pkgStr);
|
|
4
|
-
// console.log(pkg);
|
|
5
|
-
// pkg.info = 'test';
|
|
6
|
-
// // fs.('package.json');
|
|
7
|
-
// fs.writeFileSync('package.json', JSON.stringify(pkg,null,2));
|
|
8
|
-
// pkgStr = fs.readFileSync('package.json').toString();
|
|
9
|
-
// pkg = JSON.parse(pkgStr);
|
|
10
|
-
// console.log(pkg);
|
|
11
|
-
//
|
|
12
|
-
console.log(/^([a-z]+-){1,3}([0-9]+)(\.([0-9]+)){3}$/.test('a-a-1.2.1.1'));
|
|
13
|
-
//cmd
|
|
14
|
-
// console.log(/^(\.([0-9]+)){3}$/.test('a-a-1.2.1.1'));
|
|
15
|
-
console.log('a-a-1.2.1.1'.split(/(?<=[a-z])-(?=[0-9])/));
|
|
16
|
-
|
|
17
|
-
//抓取版本
|
|
18
|
-
|