lightshortcuts 1.0.0
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/README.md +59 -0
- package/bin/lsc.js +177 -0
- package/index.js +1 -0
- package/lib/LSC.js +151 -0
- package/lib/core/API.js +314 -0
- package/lib/core/apiUrl.js +19 -0
- package/lib/defaults.js +23 -0
- package/lib/helpers/bind.js +11 -0
- package/lib/helpers/loadModSync.js +31 -0
- package/lib/shortcuts.js +3 -0
- package/lib/utils.js +435 -0
- package/lsc.config.json +14 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Light快捷指令
|
|
2
|
+
|
|
3
|
+
仅适用于Light管控台的快捷指令,用于快速处理离线包的一些处理,如发布等。请向相关平台管理员获取地址和token。
|
|
4
|
+
|
|
5
|
+
#### 使用说明
|
|
6
|
+
|
|
7
|
+
1. 安装
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g lightshortcuts
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
2. 初始化发布配置
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
lsc init
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
之后将在当前路径下创建发布配置文件lsc.config.json,请打开文件,根据需要做相应修改:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"publishInfo": {
|
|
24
|
+
"token": "XXXXX", //登录token
|
|
25
|
+
"pkgid": "XXXXX", //离线包ID
|
|
26
|
+
"set_pkg_version": "1.0.1", //指定发布版本
|
|
27
|
+
"publish_app_arr": [3577, 3592], //发布到指定APP,默认开发版
|
|
28
|
+
"apps_name": {
|
|
29
|
+
"3592": "开发版_iOS",
|
|
30
|
+
"3577": "开发版_安卓"
|
|
31
|
+
},
|
|
32
|
+
"android_version_scope": "7.0.6.0", //Android端离线包兼容版本
|
|
33
|
+
"ios_version_scope": "7.0.6", //iOS端离线包兼容版本
|
|
34
|
+
"pkg_zip_name": "dist.zip",//离线包压缩包文件名
|
|
35
|
+
"pkg_dir": "./dist/", //离线包相对所在路径
|
|
36
|
+
"release_desc": "功能更新", //发布日志
|
|
37
|
+
"task_status": "0" //发布当前版本后,对上一版本的处理:0:发布(不处理),1:暂停,2:结束(下架)
|
|
38
|
+
},
|
|
39
|
+
"lightBaseURL": "https://xxx.xxx.xxx/lightadmin/pas-api"//Light管控台地址
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
3. 离线包发布
|
|
46
|
+
|
|
47
|
+
- 单项目发布:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
lsc publish #或简写为 lsc p
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- 批量发布。配置项(除pkgid,pkg_zip_name外)与单项目发布一样,批量发布将以离线包名称作为ID,以`ID.zip`作为压缩包文件名,请务必确保ID的准确性:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
lsc batch #或简写为 lsc b
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
|
package/bin/lsc.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { access, statSync, constants, writeFile, readdirSync } = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { Command } = require("commander");
|
|
6
|
+
const inquirer = require("inquirer");
|
|
7
|
+
const program = new Command();
|
|
8
|
+
const { LSC } = require("../index");
|
|
9
|
+
const { merge, getZipFileList } = require("../lib/utils");
|
|
10
|
+
const loadModSync = require("../lib/helpers/loadModSync");
|
|
11
|
+
|
|
12
|
+
// 读取发布配置文件
|
|
13
|
+
const defaultConfig = require("../lib/defaults");
|
|
14
|
+
const { checkSession } = require("../lib/core/API");
|
|
15
|
+
const { config } = require("process");
|
|
16
|
+
let confPath = path.join(process.cwd(), "./lsc.config.json");
|
|
17
|
+
|
|
18
|
+
program.version("0.0.1");
|
|
19
|
+
|
|
20
|
+
program.command("config").action(function () {
|
|
21
|
+
inquirer
|
|
22
|
+
.prompt([
|
|
23
|
+
{
|
|
24
|
+
type: "input",
|
|
25
|
+
message: "请输入token:",
|
|
26
|
+
name: "token",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: "input",
|
|
30
|
+
message: "请输入离线包id:",
|
|
31
|
+
name: "h5pkgid",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
type: "input",
|
|
35
|
+
message: "请输入离线包名称:",
|
|
36
|
+
name: "name",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: "input",
|
|
40
|
+
message: "请指定发布版本号:",
|
|
41
|
+
name: "version",
|
|
42
|
+
default: "1.0.1",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: "checkbox",
|
|
46
|
+
message: "请选择发布的APP:",
|
|
47
|
+
name: "apps",
|
|
48
|
+
choices: [
|
|
49
|
+
{
|
|
50
|
+
name: "开发版_iOS",
|
|
51
|
+
checked: true,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "开发版_安卓",
|
|
55
|
+
checked: true,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
validate(answer) {
|
|
59
|
+
if (answer.length < 1) {
|
|
60
|
+
return "请至少选择一项";
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
type: "input",
|
|
67
|
+
message: "离线包压缩包存放位置:",
|
|
68
|
+
name: "pkg_dir",
|
|
69
|
+
default: "./dist/",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
type: "input",
|
|
73
|
+
message: "离线包名称:",
|
|
74
|
+
name: "pkg_zip_name",
|
|
75
|
+
default: "dist.zip",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: "rawlist",
|
|
79
|
+
message: "是否处理上一版本离线包?",
|
|
80
|
+
name: "task_status",
|
|
81
|
+
default: "保留",
|
|
82
|
+
choices: ["保留", "暂停", "下架"],
|
|
83
|
+
},
|
|
84
|
+
])
|
|
85
|
+
.then((answer) => {
|
|
86
|
+
// 用户输入的结果最终会在这里输出
|
|
87
|
+
console.log(answer);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
program.command("init").action(function () {
|
|
92
|
+
let confPath = path.join(process.cwd(), "./lsc.config.json");
|
|
93
|
+
access(confPath, constants.F_OK, (err) => {
|
|
94
|
+
if (!err) {
|
|
95
|
+
// 存在配置文件,唤起交互输入
|
|
96
|
+
inquirer
|
|
97
|
+
.prompt([
|
|
98
|
+
{
|
|
99
|
+
type: "confirm",
|
|
100
|
+
message: "注意!!!配置文件已经存在,是否重新初始化?",
|
|
101
|
+
name: "initConfig",
|
|
102
|
+
default: false,
|
|
103
|
+
},
|
|
104
|
+
])
|
|
105
|
+
.then((answer) => {
|
|
106
|
+
if (answer.initConfig) {
|
|
107
|
+
writeConfig();
|
|
108
|
+
} else {
|
|
109
|
+
console.log("配置无变化");
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
} else {
|
|
113
|
+
writeConfig();
|
|
114
|
+
}
|
|
115
|
+
var writeConfig = function () {
|
|
116
|
+
// 写入文件内容(如果文件不存在会创建一个文件)
|
|
117
|
+
writeFile(
|
|
118
|
+
"./lsc.config.json",
|
|
119
|
+
JSON.stringify(defaultConfig),
|
|
120
|
+
{ flag: "w" },
|
|
121
|
+
function (err) {
|
|
122
|
+
if (err) {
|
|
123
|
+
throw err;
|
|
124
|
+
}
|
|
125
|
+
console.log("创建配置模板成功,请打开文件进行相应修改");
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// 单项目发布
|
|
133
|
+
program
|
|
134
|
+
.command("publish")
|
|
135
|
+
.alias("p")
|
|
136
|
+
.description("发布离线包")
|
|
137
|
+
.action(() => {
|
|
138
|
+
access(confPath, constants.F_OK, (err) => {
|
|
139
|
+
if (!err) {
|
|
140
|
+
try {
|
|
141
|
+
let lscConfig = require(confPath);
|
|
142
|
+
let totalConfig = merge(defaultConfig, lscConfig);
|
|
143
|
+
LSC(totalConfig);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.log("主程序异常", error);
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
// 不存在配置文件,唤起交互输入
|
|
149
|
+
console.log(
|
|
150
|
+
"不存在配置文件,请先进行配置或者使用如下命令创建:lsc init"
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// 批量离线包发布.已经包名作为pkgid
|
|
157
|
+
program
|
|
158
|
+
.command("batch")
|
|
159
|
+
.alias("b")
|
|
160
|
+
.action(async function () {
|
|
161
|
+
let currPath = path.join(process.cwd(), "./dist/");
|
|
162
|
+
let zipLists = await getZipFileList(currPath);
|
|
163
|
+
for (let i = 0; i < zipLists.length; i++) {
|
|
164
|
+
const e = zipLists[i],
|
|
165
|
+
s = e.split("."),
|
|
166
|
+
pkgid = s[0];
|
|
167
|
+
let lscConfig = require(confPath);
|
|
168
|
+
let totalConfig = merge(defaultConfig, lscConfig);
|
|
169
|
+
let batchConfig = merge(totalConfig, {
|
|
170
|
+
publishInfo: { pkgid, pkg_zip_name: e },
|
|
171
|
+
});
|
|
172
|
+
// console.log("batchConfig", batchConfig);
|
|
173
|
+
await LSC(batchConfig);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
program.parse(process.argv);
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./lib/LSC');
|
package/lib/LSC.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
const { statSync } = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { getPkgInfo, setPkgVersion, merge } = require("./utils");
|
|
4
|
+
const axios = require("axios").default;
|
|
5
|
+
|
|
6
|
+
let totalConfig = {};
|
|
7
|
+
var token,
|
|
8
|
+
pkgid = "ffff",
|
|
9
|
+
publish_app_arr,
|
|
10
|
+
set_pkg_version,
|
|
11
|
+
task_status,
|
|
12
|
+
release_desc,
|
|
13
|
+
android_version_scope,
|
|
14
|
+
ios_version_scope,
|
|
15
|
+
pkg_zip_name,
|
|
16
|
+
pkg_dir,
|
|
17
|
+
apps_name;
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
//引入API
|
|
21
|
+
const {
|
|
22
|
+
allOfflinePkgLists,
|
|
23
|
+
checkSession,
|
|
24
|
+
fileUpload,
|
|
25
|
+
addOfflineVersion,
|
|
26
|
+
addOfflineTask,
|
|
27
|
+
queryOfflineTask,
|
|
28
|
+
updateOfflineTask,
|
|
29
|
+
queryOfflinePkgList,
|
|
30
|
+
queryOfflineVersionlist,
|
|
31
|
+
syncPkgList,
|
|
32
|
+
} = require("./core/API");
|
|
33
|
+
|
|
34
|
+
//在APP上发布指定离线包主逻辑
|
|
35
|
+
async function publish(h5pkgid, app_id) {
|
|
36
|
+
try {
|
|
37
|
+
const __filePath = path.join(process.cwd(), `${pkg_dir}${pkg_zip_name}`);
|
|
38
|
+
let package_id = await fileUpload(__filePath);
|
|
39
|
+
|
|
40
|
+
let mvRes = await queryOfflineVersionlist(app_id, h5pkgid),
|
|
41
|
+
{ max_version } = mvRes.data;
|
|
42
|
+
|
|
43
|
+
let { id } = await getPkgInfo(allOfflinePkgLists, app_id, h5pkgid);
|
|
44
|
+
|
|
45
|
+
let file_size = await statSync(__filePath).size;
|
|
46
|
+
let pkg_id = id;
|
|
47
|
+
let relVersion = await setPkgVersion(set_pkg_version, max_version);
|
|
48
|
+
console.log("即将发布的版本号:", relVersion);
|
|
49
|
+
//创建离线发布版本
|
|
50
|
+
let aovRes = await addOfflineVersion({
|
|
51
|
+
app_id,
|
|
52
|
+
pkg_id,
|
|
53
|
+
version: relVersion,
|
|
54
|
+
android_version_scope,
|
|
55
|
+
ios_version_scope,
|
|
56
|
+
is_wifi: "0",
|
|
57
|
+
package_id,
|
|
58
|
+
file_name: pkg_zip_name,
|
|
59
|
+
file_size,
|
|
60
|
+
});
|
|
61
|
+
if (aovRes) {
|
|
62
|
+
//更新列表
|
|
63
|
+
let m = await queryOfflineVersionlist(app_id, h5pkgid),
|
|
64
|
+
{ id } = m.data.list[0];
|
|
65
|
+
let aotRes = await addOfflineTask({
|
|
66
|
+
app_id,
|
|
67
|
+
version_id: id,
|
|
68
|
+
pkg_id,
|
|
69
|
+
release_type: 0,
|
|
70
|
+
update_strategy: 0,
|
|
71
|
+
release_strategy: "",
|
|
72
|
+
release_desc,
|
|
73
|
+
});
|
|
74
|
+
console.info(`版本${relVersion}发布${aotRes ? "成功" : "失败"}`);
|
|
75
|
+
let ls = m.data.list.slice(1),
|
|
76
|
+
fs = ls.length > 0 ? ls.filter((val) => val.status === 1) : [],
|
|
77
|
+
prevVerInfo = fs[0];
|
|
78
|
+
if (fs.length > 0) {
|
|
79
|
+
const qotRes = await queryOfflineTask({
|
|
80
|
+
app_id,
|
|
81
|
+
version_id: prevVerInfo.id,
|
|
82
|
+
}),
|
|
83
|
+
e = qotRes.filter((val) => val.status == 0),
|
|
84
|
+
f = e[0];
|
|
85
|
+
// 处理上一版本的问题
|
|
86
|
+
if (task_status != 0) {
|
|
87
|
+
let sm = { 0: "发布", 1: "暂停", 2: "下架" };
|
|
88
|
+
const uotRes = await updateOfflineTask({
|
|
89
|
+
id: f.id,
|
|
90
|
+
status: task_status, //0发布,1暂停,2下架
|
|
91
|
+
app_id,
|
|
92
|
+
});
|
|
93
|
+
console.log(
|
|
94
|
+
`版本${prevVerInfo.version}${sm[task_status]}${
|
|
95
|
+
uotRes ? "成功" : "失败"
|
|
96
|
+
}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.log("发布失败:", error);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function Main(h5pkgid, ids) {
|
|
107
|
+
let tokenIsReady = await checkSession();
|
|
108
|
+
if (tokenIsReady) {
|
|
109
|
+
// console.info("会话有效,准备处理!");
|
|
110
|
+
let chkPkg = await syncPkgList(ids, h5pkgid);
|
|
111
|
+
if (chkPkg) {
|
|
112
|
+
await queryOfflinePkgList(ids);
|
|
113
|
+
for (let i = 0; i < ids.length; i++) {
|
|
114
|
+
console.log(`*************${apps_name[ids[i]]==undefined?"":apps_name[ids[i]]}:::${h5pkgid}*************`);
|
|
115
|
+
// console.log(apps_name[ids[i]]==undefined,ids[i])
|
|
116
|
+
await publish(h5pkgid, ids[i]);
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
console.warn("请联系Light管理员先在创建离线包!");
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
console.info("token已过期");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function LSC(config = {}) {
|
|
127
|
+
let { publishInfo, lightBaseURL } = config;
|
|
128
|
+
totalConfig = publishInfo;
|
|
129
|
+
|
|
130
|
+
token = publishInfo.token;
|
|
131
|
+
pkgid = publishInfo.pkgid;
|
|
132
|
+
publish_app_arr = publishInfo.publish_app_arr;
|
|
133
|
+
set_pkg_version = publishInfo.set_pkg_version;
|
|
134
|
+
task_status = publishInfo.task_status;
|
|
135
|
+
release_desc = publishInfo.release_desc;
|
|
136
|
+
android_version_scope = publishInfo.android_version_scope;
|
|
137
|
+
ios_version_scope = publishInfo.ios_version_scope;
|
|
138
|
+
pkg_zip_name = publishInfo.pkg_zip_name;
|
|
139
|
+
pkg_dir = publishInfo.pkg_dir;
|
|
140
|
+
apps_name= publishInfo.apps_name;
|
|
141
|
+
|
|
142
|
+
//全局配置
|
|
143
|
+
axios.defaults.baseURL = lightBaseURL;
|
|
144
|
+
axios.defaults.headers["cookie"] = `token=${token}`;
|
|
145
|
+
console.log(pkgid, publish_app_arr)
|
|
146
|
+
await Main(pkgid, publish_app_arr);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
module.exports = {
|
|
150
|
+
LSC,
|
|
151
|
+
};
|
package/lib/core/API.js
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const axios = require("axios").default;
|
|
3
|
+
const qs = require("qs");
|
|
4
|
+
const { createReadStream, access, statSync, constants } = require("fs");
|
|
5
|
+
const FormData = require("form-data");
|
|
6
|
+
const { getPkgInfo } = require("../utils");
|
|
7
|
+
const { ApiList } = require("./apiUrl");
|
|
8
|
+
|
|
9
|
+
// 存储APP的离线包
|
|
10
|
+
let allOfflinePkgLists = {};
|
|
11
|
+
|
|
12
|
+
// 添加响应拦截器
|
|
13
|
+
axios.interceptors.response.use(
|
|
14
|
+
function (response) {
|
|
15
|
+
// 2xx 范围内的状态码都会触发该函数。
|
|
16
|
+
return response;
|
|
17
|
+
},
|
|
18
|
+
function (error) {
|
|
19
|
+
// 超出 2xx 范围的状态码都会触发该函数。
|
|
20
|
+
return Promise.reject(error);
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
//校验会话有效性
|
|
25
|
+
const checkSession = async function (token) {
|
|
26
|
+
if(token){
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
axios
|
|
31
|
+
.get(ApiList.checkSession)
|
|
32
|
+
.then((res) => {
|
|
33
|
+
let { data } = res;
|
|
34
|
+
if (data.err_no === 0) {
|
|
35
|
+
resolve(true);
|
|
36
|
+
} else {
|
|
37
|
+
resolve(false);
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
.catch((err) => {
|
|
41
|
+
console.warn("检查会话报错!", err);
|
|
42
|
+
resolve(false);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// 上传文件,__filePath:绝对路径
|
|
48
|
+
const fileUpload = async function (__filePath) {
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const formData = new FormData();
|
|
51
|
+
formData.append("file", createReadStream(__filePath));
|
|
52
|
+
access(__filePath, constants.F_OK, (err) => {
|
|
53
|
+
if (err) {
|
|
54
|
+
console.log(`文件${__filePath}不存在`);
|
|
55
|
+
reject(`文件${__filePath}不存在`);
|
|
56
|
+
} else {
|
|
57
|
+
axios
|
|
58
|
+
.post(ApiList.fileUpload, formData, {
|
|
59
|
+
headers: formData.getHeaders(),
|
|
60
|
+
})
|
|
61
|
+
.then((res) => {
|
|
62
|
+
console.log("上传文件:", res.data);
|
|
63
|
+
let { err_no, data, err_msg } = res.data;
|
|
64
|
+
if (err_no == 0) {
|
|
65
|
+
resolve(data);
|
|
66
|
+
} else {
|
|
67
|
+
reject(res.data);
|
|
68
|
+
}
|
|
69
|
+
resolve(res);
|
|
70
|
+
})
|
|
71
|
+
.catch((e) => reject(e));
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// 查询APP下的离线包列表 √
|
|
78
|
+
const queryOfflinePkgList = async function (ids = ["3592", "3577"]) {
|
|
79
|
+
const getPkgListOf = function (appid) {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
axios
|
|
82
|
+
.get(`${ApiList.queryOfflinePkgList}?app_id=${appid}`)
|
|
83
|
+
.then((res) => {
|
|
84
|
+
let { err_no, err_info, data } = res.data;
|
|
85
|
+
if (err_no == 0) {
|
|
86
|
+
allOfflinePkgLists[appid] = data;
|
|
87
|
+
} else {
|
|
88
|
+
console.log(`${appid}报错:${err_info}`);
|
|
89
|
+
}
|
|
90
|
+
resolve(true);
|
|
91
|
+
})
|
|
92
|
+
.catch((e) => {
|
|
93
|
+
let { err_no, err_info } = e.response.data;
|
|
94
|
+
console.log(`${appid}采集报错:${err_info}`);
|
|
95
|
+
resolve(false);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// 批量查询
|
|
101
|
+
return new Promise((resolve, reject) => {
|
|
102
|
+
Promise.all(
|
|
103
|
+
ids.reduce((prev, next) => {
|
|
104
|
+
return prev.concat(getPkgListOf(next));
|
|
105
|
+
}, [])
|
|
106
|
+
).then((rs) => {
|
|
107
|
+
// console.log("批量查询查询情况:", rs);
|
|
108
|
+
resolve(allOfflinePkgLists);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// 同步APP的离线包列表,如果没有创建,则进行创建,以拉平两端离线包
|
|
114
|
+
async function syncPkgList(ids = [], h5app_id) {
|
|
115
|
+
await queryOfflinePkgList(ids);
|
|
116
|
+
|
|
117
|
+
let keys = Object.keys(allOfflinePkgLists);
|
|
118
|
+
let newAppPkgList = keys.reduce(
|
|
119
|
+
(acc, cur) => {
|
|
120
|
+
let a = allOfflinePkgLists[cur];
|
|
121
|
+
let b = a.filter((val) => val.h5app_id == h5app_id);
|
|
122
|
+
// acc[cur] = b;
|
|
123
|
+
if (b.length > 0) {
|
|
124
|
+
acc.name = b[0].name;
|
|
125
|
+
} else {
|
|
126
|
+
acc.none.push(cur);
|
|
127
|
+
}
|
|
128
|
+
return acc;
|
|
129
|
+
},
|
|
130
|
+
{ none: [], name: "" }
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
if (newAppPkgList["none"].length > 0 && newAppPkgList["name"] != "") {
|
|
135
|
+
Promise.all(
|
|
136
|
+
newAppPkgList["none"].reduce((prev, next) => {
|
|
137
|
+
return prev.concat(
|
|
138
|
+
addOfflineH5pkg(next, h5app_id, newAppPkgList["name"])
|
|
139
|
+
);
|
|
140
|
+
}, [])
|
|
141
|
+
).then((rs) => {
|
|
142
|
+
console.log("同步离线包信息:", rs);
|
|
143
|
+
resolve(true);
|
|
144
|
+
});
|
|
145
|
+
} else {
|
|
146
|
+
resolve(true);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
//查询版本信息√
|
|
152
|
+
const queryOfflineVersionlist = async function (app_id, h5app_id) {
|
|
153
|
+
let t = await getPkgInfo(allOfflinePkgLists, app_id, h5app_id),
|
|
154
|
+
{ id } = t;
|
|
155
|
+
//pkg_id
|
|
156
|
+
let qovRes = await axios.get(
|
|
157
|
+
`${ApiList.queryOfflineVersionlist}?app_id=${app_id}&pkg_id=${id}&page_no=1&page_size=10`
|
|
158
|
+
);
|
|
159
|
+
return qovRes.data;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// 如果未创建离线包,则创建之√
|
|
163
|
+
const addOfflineH5pkg = async function (app_id, h5app_id, name) {
|
|
164
|
+
// return new Promise((resolve, reject) => {
|
|
165
|
+
let copRes = await axios.get(
|
|
166
|
+
`${ApiList.checkOfflinePkgid}?app_id=${app_id}&h5app_id=${h5app_id}`
|
|
167
|
+
),
|
|
168
|
+
copFlag = copRes.data.data;
|
|
169
|
+
let conRes = await axios.get(
|
|
170
|
+
`${ApiList.checkOfflinePkgname}?app_id=${app_id}&name=${encodeURI(name)}`
|
|
171
|
+
),
|
|
172
|
+
conFlag = conRes.data.data;
|
|
173
|
+
if (copFlag || conFlag) {
|
|
174
|
+
console.log("已存在");
|
|
175
|
+
} else {
|
|
176
|
+
let aofRes = await axios({
|
|
177
|
+
method: "post",
|
|
178
|
+
url: ApiList.addOfflineH5pkg,
|
|
179
|
+
data: qs.stringify({
|
|
180
|
+
app_id,
|
|
181
|
+
h5app_id,
|
|
182
|
+
name,
|
|
183
|
+
pkg_type: 1,
|
|
184
|
+
}),
|
|
185
|
+
});
|
|
186
|
+
let { err_no, data } = aofRes.data;
|
|
187
|
+
console.log(`添加离线包:${h5app_id}-${name}`, aofRes.data);
|
|
188
|
+
if (err_no == 0 && data == 1) {
|
|
189
|
+
await queryOfflinePkgList([app_id]);
|
|
190
|
+
return true;
|
|
191
|
+
} else {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// });
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
//创建离线发布版本 √
|
|
199
|
+
const addOfflineVersion = async function (args) {
|
|
200
|
+
return new Promise((resolve, reject) => {
|
|
201
|
+
// 创建版本
|
|
202
|
+
axios({
|
|
203
|
+
method: "post",
|
|
204
|
+
url: ApiList.addOfflineVersion,
|
|
205
|
+
data: qs.stringify(args),
|
|
206
|
+
})
|
|
207
|
+
.then(function (res) {
|
|
208
|
+
// {"err_no":0,"err_info":"成功执行","data":1}
|
|
209
|
+
console.log("创建离线发布版本:", res.data);
|
|
210
|
+
const { err_no } = res.data;
|
|
211
|
+
if (err_no == 0) {
|
|
212
|
+
resolve(true);
|
|
213
|
+
} else {
|
|
214
|
+
resolve(false);
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
.catch(function (error) {
|
|
218
|
+
console.log("创建离线发布版本报错:", error);
|
|
219
|
+
resolve(false);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// 添加发布任务 √
|
|
225
|
+
const addOfflineTask = async function (args) {
|
|
226
|
+
return new Promise((resolve, reject) => {
|
|
227
|
+
// 创建版本
|
|
228
|
+
axios({
|
|
229
|
+
method: "post",
|
|
230
|
+
url: ApiList.addOfflineTask,
|
|
231
|
+
data: qs.stringify(args),
|
|
232
|
+
})
|
|
233
|
+
.then((res) => {
|
|
234
|
+
// {"err_no":0,"err_info":"成功执行","data":24303}
|
|
235
|
+
console.log("添加发布任务:", res.data);
|
|
236
|
+
const { err_no } = res.data;
|
|
237
|
+
if (err_no == 0) {
|
|
238
|
+
resolve(true);
|
|
239
|
+
} else {
|
|
240
|
+
resolve(false);
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
.catch((error) => {
|
|
244
|
+
console.log("添加发布任务报错:", error);
|
|
245
|
+
resolve(false);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
//查询某个版本的发布任务 √
|
|
251
|
+
const queryOfflineTask = async function (args) {
|
|
252
|
+
return new Promise((resolve, reject) => {
|
|
253
|
+
// 创建版本
|
|
254
|
+
axios
|
|
255
|
+
.get(
|
|
256
|
+
`${ApiList.queryOfflineTask}?${qs.stringify(
|
|
257
|
+
args
|
|
258
|
+
)}&page_no=1&page_size=5`
|
|
259
|
+
)
|
|
260
|
+
.then(function (res) {
|
|
261
|
+
// console.log("获取指定版本下的发布任务列表:", JSON.stringify(res.data));
|
|
262
|
+
const { err_no, data } = res.data;
|
|
263
|
+
if (err_no == 0) {
|
|
264
|
+
resolve(data.list);
|
|
265
|
+
} else {
|
|
266
|
+
resolve([]);
|
|
267
|
+
}
|
|
268
|
+
})
|
|
269
|
+
.catch(function (error) {
|
|
270
|
+
console.log("获取指定版本下的发布任务列表报错:", error);
|
|
271
|
+
reject(error);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
//更新发布任务状态√
|
|
277
|
+
const updateOfflineTask = async function (uotArgs = {}) {
|
|
278
|
+
return new Promise((resolve, reject) => {
|
|
279
|
+
// 创建版本
|
|
280
|
+
axios({
|
|
281
|
+
method: "post",
|
|
282
|
+
url: ApiList.updateOfflineTask,
|
|
283
|
+
data: qs.stringify(uotArgs),
|
|
284
|
+
})
|
|
285
|
+
.then(function (res) {
|
|
286
|
+
console.log("更新发布任务状态:", res.data);
|
|
287
|
+
const { err_no } = res.data;
|
|
288
|
+
if (err_no == 0) {
|
|
289
|
+
resolve(true);
|
|
290
|
+
} else {
|
|
291
|
+
resolve(false);
|
|
292
|
+
}
|
|
293
|
+
})
|
|
294
|
+
.catch(function (error) {
|
|
295
|
+
console.log("更新发布任务状态报错:", error);
|
|
296
|
+
resolve(false);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
module.exports = {
|
|
302
|
+
allOfflinePkgLists,
|
|
303
|
+
checkSession,
|
|
304
|
+
fileUpload,
|
|
305
|
+
queryOfflinePkgList,
|
|
306
|
+
getPkgInfo,
|
|
307
|
+
queryOfflineVersionlist,
|
|
308
|
+
addOfflineH5pkg,
|
|
309
|
+
addOfflineVersion,
|
|
310
|
+
addOfflineTask,
|
|
311
|
+
queryOfflineTask,
|
|
312
|
+
updateOfflineTask,
|
|
313
|
+
syncPkgList,
|
|
314
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
|
|
3
|
+
// API列表
|
|
4
|
+
const offlineURlPrefix = `/light/light-platform-client/v/appdiy/offline/`;
|
|
5
|
+
const ApiList = {
|
|
6
|
+
fileUpload: "/ltcommon/file/v/upload?bizType=h5_offline_src",
|
|
7
|
+
checkSession: "/light/light-platform-client/v/auth/check_session",
|
|
8
|
+
queryOfflinePkgList: offlineURlPrefix + "query_offline_pkglist1",
|
|
9
|
+
queryOfflineVersionlist: offlineURlPrefix + "query_offline_versionlist",
|
|
10
|
+
addOfflineH5pkg: offlineURlPrefix + "add_offline_h5pkg",
|
|
11
|
+
checkOfflinePkgid: offlineURlPrefix + "check_offline_pkgid",
|
|
12
|
+
checkOfflinePkgname: offlineURlPrefix + "check_offline_pkgname",
|
|
13
|
+
addOfflineVersion: offlineURlPrefix + "add_offline_version",
|
|
14
|
+
addOfflineTask: offlineURlPrefix + "add_offline_task",
|
|
15
|
+
queryOfflineTask: offlineURlPrefix + "query_offline_task",
|
|
16
|
+
updateOfflineTask: offlineURlPrefix + "update_offline_task",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
module.exports={ApiList}
|
package/lib/defaults.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var defaults = {
|
|
3
|
+
publishInfo: {
|
|
4
|
+
token: "XXXXX", //登录token
|
|
5
|
+
pkgid: "XXXXX", //离线包ID
|
|
6
|
+
set_pkg_version: "1.0.1", //指定发布版本
|
|
7
|
+
publish_app_arr: [3577, 3592], //发布到指定APP,默认开发版
|
|
8
|
+
apps_name: {
|
|
9
|
+
3592: "开发版_iOS",
|
|
10
|
+
3577: "开发版_安卓",
|
|
11
|
+
3559: "测试",
|
|
12
|
+
},
|
|
13
|
+
android_version_scope: "7.0.6.0", //Android端离线包兼容版本
|
|
14
|
+
ios_version_scope: "7.0.6", //iOS端离线包兼容版本
|
|
15
|
+
pkg_zip_name: "dist.zip",
|
|
16
|
+
pkg_dir: "./dist/", //离线包所在路径
|
|
17
|
+
release_desc: "功能更新", //发布日志
|
|
18
|
+
task_status: "0", //对上一版本的处理:0:发布,1:暂停,2:下架
|
|
19
|
+
},
|
|
20
|
+
lightBaseURL: "https://xxx.xxx.xxx/lightadmin/pas-api",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
module.exports = defaults;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"user strict"
|
|
2
|
+
var fs = require("fs");
|
|
3
|
+
var path = require("path");
|
|
4
|
+
|
|
5
|
+
var load = function (path, name) {
|
|
6
|
+
if (name) {
|
|
7
|
+
return require(path + name);
|
|
8
|
+
}
|
|
9
|
+
return require(path);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
module.exports = function (dir) {
|
|
13
|
+
patcher = {};
|
|
14
|
+
if (!/\.js$/.test(filename)) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
var name = path.basename(filename, ".js");
|
|
18
|
+
var _load = load.bind(null, "./" + dir + "/", name);
|
|
19
|
+
patcher.__defineGetter__(name, _load);
|
|
20
|
+
|
|
21
|
+
// fs.readdirSync(__dirname + "/" + dir).forEach(function (filename) {
|
|
22
|
+
// if (!/\.js$/.test(filename)) {
|
|
23
|
+
// return;
|
|
24
|
+
// }
|
|
25
|
+
// var name = path.basename(filename, ".js");
|
|
26
|
+
// var _load = load.bind(null, "./" + dir + "/", name);
|
|
27
|
+
// patcher.__defineGetter__(name, _load);
|
|
28
|
+
// });
|
|
29
|
+
|
|
30
|
+
return patcher;
|
|
31
|
+
};
|
package/lib/shortcuts.js
ADDED
package/lib/utils.js
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const { access, statSync, constants, writeFile, readdirSync } = require("fs");
|
|
3
|
+
|
|
4
|
+
var bind = require("./helpers/bind");
|
|
5
|
+
|
|
6
|
+
// utils is a library of generic helper functions non-specific to this project;
|
|
7
|
+
|
|
8
|
+
var toString = Object.prototype.toString;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Determine if a value is an Array
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} val The value to test
|
|
14
|
+
* @returns {boolean} True if value is an Array, otherwise false
|
|
15
|
+
*/
|
|
16
|
+
function isArray(val) {
|
|
17
|
+
return Array.isArray(val);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Determine if a value is undefined
|
|
22
|
+
*
|
|
23
|
+
* @param {Object} val The value to test
|
|
24
|
+
* @returns {boolean} True if the value is undefined, otherwise false
|
|
25
|
+
*/
|
|
26
|
+
function isUndefined(val) {
|
|
27
|
+
return typeof val === "undefined";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Determine if a value is a Buffer
|
|
32
|
+
*
|
|
33
|
+
* @param {Object} val The value to test
|
|
34
|
+
* @returns {boolean} True if value is a Buffer, otherwise false
|
|
35
|
+
*/
|
|
36
|
+
function isBuffer(val) {
|
|
37
|
+
return (
|
|
38
|
+
val !== null &&
|
|
39
|
+
!isUndefined(val) &&
|
|
40
|
+
val.constructor !== null &&
|
|
41
|
+
!isUndefined(val.constructor) &&
|
|
42
|
+
typeof val.constructor.isBuffer === "function" &&
|
|
43
|
+
val.constructor.isBuffer(val)
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Determine if a value is an ArrayBuffer
|
|
49
|
+
*
|
|
50
|
+
* @param {Object} val The value to test
|
|
51
|
+
* @returns {boolean} True if value is an ArrayBuffer, otherwise false
|
|
52
|
+
*/
|
|
53
|
+
function isArrayBuffer(val) {
|
|
54
|
+
return toString.call(val) === "[object ArrayBuffer]";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Determine if a value is a FormData
|
|
59
|
+
*
|
|
60
|
+
* @param {Object} val The value to test
|
|
61
|
+
* @returns {boolean} True if value is an FormData, otherwise false
|
|
62
|
+
*/
|
|
63
|
+
function isFormData(val) {
|
|
64
|
+
return toString.call(val) === "[object FormData]";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Determine if a value is a view on an ArrayBuffer
|
|
69
|
+
*
|
|
70
|
+
* @param {Object} val The value to test
|
|
71
|
+
* @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false
|
|
72
|
+
*/
|
|
73
|
+
function isArrayBufferView(val) {
|
|
74
|
+
var result;
|
|
75
|
+
if (typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView) {
|
|
76
|
+
result = ArrayBuffer.isView(val);
|
|
77
|
+
} else {
|
|
78
|
+
result = val && val.buffer && isArrayBuffer(val.buffer);
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Determine if a value is a String
|
|
85
|
+
*
|
|
86
|
+
* @param {Object} val The value to test
|
|
87
|
+
* @returns {boolean} True if value is a String, otherwise false
|
|
88
|
+
*/
|
|
89
|
+
function isString(val) {
|
|
90
|
+
return typeof val === "string";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Determine if a value is a Number
|
|
95
|
+
*
|
|
96
|
+
* @param {Object} val The value to test
|
|
97
|
+
* @returns {boolean} True if value is a Number, otherwise false
|
|
98
|
+
*/
|
|
99
|
+
function isNumber(val) {
|
|
100
|
+
return typeof val === "number";
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Determine if a value is an Object
|
|
105
|
+
*
|
|
106
|
+
* @param {Object} val The value to test
|
|
107
|
+
* @returns {boolean} True if value is an Object, otherwise false
|
|
108
|
+
*/
|
|
109
|
+
function isObject(val) {
|
|
110
|
+
return val !== null && typeof val === "object";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Determine if a value is a plain Object
|
|
115
|
+
*
|
|
116
|
+
* @param {Object} val The value to test
|
|
117
|
+
* @return {boolean} True if value is a plain Object, otherwise false
|
|
118
|
+
*/
|
|
119
|
+
function isPlainObject(val) {
|
|
120
|
+
if (toString.call(val) !== "[object Object]") {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
var prototype = Object.getPrototypeOf(val);
|
|
125
|
+
return prototype === null || prototype === Object.prototype;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Determine if a value is a Date
|
|
130
|
+
*
|
|
131
|
+
* @param {Object} val The value to test
|
|
132
|
+
* @returns {boolean} True if value is a Date, otherwise false
|
|
133
|
+
*/
|
|
134
|
+
function isDate(val) {
|
|
135
|
+
return toString.call(val) === "[object Date]";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Determine if a value is a File
|
|
140
|
+
*
|
|
141
|
+
* @param {Object} val The value to test
|
|
142
|
+
* @returns {boolean} True if value is a File, otherwise false
|
|
143
|
+
*/
|
|
144
|
+
function isFile(val) {
|
|
145
|
+
return toString.call(val) === "[object File]";
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Determine if a value is a Blob
|
|
150
|
+
*
|
|
151
|
+
* @param {Object} val The value to test
|
|
152
|
+
* @returns {boolean} True if value is a Blob, otherwise false
|
|
153
|
+
*/
|
|
154
|
+
function isBlob(val) {
|
|
155
|
+
return toString.call(val) === "[object Blob]";
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Determine if a value is a Function
|
|
160
|
+
*
|
|
161
|
+
* @param {Object} val The value to test
|
|
162
|
+
* @returns {boolean} True if value is a Function, otherwise false
|
|
163
|
+
*/
|
|
164
|
+
function isFunction(val) {
|
|
165
|
+
return toString.call(val) === "[object Function]";
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Determine if a value is a Stream
|
|
170
|
+
*
|
|
171
|
+
* @param {Object} val The value to test
|
|
172
|
+
* @returns {boolean} True if value is a Stream, otherwise false
|
|
173
|
+
*/
|
|
174
|
+
function isStream(val) {
|
|
175
|
+
return isObject(val) && isFunction(val.pipe);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Determine if a value is a URLSearchParams object
|
|
180
|
+
*
|
|
181
|
+
* @param {Object} val The value to test
|
|
182
|
+
* @returns {boolean} True if value is a URLSearchParams object, otherwise false
|
|
183
|
+
*/
|
|
184
|
+
function isURLSearchParams(val) {
|
|
185
|
+
return toString.call(val) === "[object URLSearchParams]";
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Trim excess whitespace off the beginning and end of a string
|
|
190
|
+
*
|
|
191
|
+
* @param {String} str The String to trim
|
|
192
|
+
* @returns {String} The String freed of excess whitespace
|
|
193
|
+
*/
|
|
194
|
+
function trim(str) {
|
|
195
|
+
return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, "");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Determine if we're running in a standard browser environment
|
|
200
|
+
*
|
|
201
|
+
* This allows axios to run in a web worker, and react-native.
|
|
202
|
+
* Both environments support XMLHttpRequest, but not fully standard globals.
|
|
203
|
+
*
|
|
204
|
+
* web workers:
|
|
205
|
+
* typeof window -> undefined
|
|
206
|
+
* typeof document -> undefined
|
|
207
|
+
*
|
|
208
|
+
* react-native:
|
|
209
|
+
* navigator.product -> 'ReactNative'
|
|
210
|
+
* nativescript
|
|
211
|
+
* navigator.product -> 'NativeScript' or 'NS'
|
|
212
|
+
*/
|
|
213
|
+
function isStandardBrowserEnv() {
|
|
214
|
+
if (
|
|
215
|
+
typeof navigator !== "undefined" &&
|
|
216
|
+
(navigator.product === "ReactNative" ||
|
|
217
|
+
navigator.product === "NativeScript" ||
|
|
218
|
+
navigator.product === "NS")
|
|
219
|
+
) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Iterate over an Array or an Object invoking a function for each item.
|
|
227
|
+
*
|
|
228
|
+
* If `obj` is an Array callback will be called passing
|
|
229
|
+
* the value, index, and complete array for each item.
|
|
230
|
+
*
|
|
231
|
+
* If 'obj' is an Object callback will be called passing
|
|
232
|
+
* the value, key, and complete object for each property.
|
|
233
|
+
*
|
|
234
|
+
* @param {Object|Array} obj The object to iterate
|
|
235
|
+
* @param {Function} fn The callback to invoke for each item
|
|
236
|
+
*/
|
|
237
|
+
function forEach(obj, fn) {
|
|
238
|
+
// Don't bother if no value provided
|
|
239
|
+
if (obj === null || typeof obj === "undefined") {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Force an array if not already something iterable
|
|
244
|
+
if (typeof obj !== "object") {
|
|
245
|
+
/*eslint no-param-reassign:0*/
|
|
246
|
+
obj = [obj];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (isArray(obj)) {
|
|
250
|
+
// Iterate over array values
|
|
251
|
+
for (var i = 0, l = obj.length; i < l; i++) {
|
|
252
|
+
fn.call(null, obj[i], i, obj);
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
// Iterate over object keys
|
|
256
|
+
for (var key in obj) {
|
|
257
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
258
|
+
fn.call(null, obj[key], key, obj);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Accepts varargs expecting each argument to be an object, then
|
|
266
|
+
* immutably merges the properties of each object and returns result.
|
|
267
|
+
*
|
|
268
|
+
* When multiple objects contain the same key the later object in
|
|
269
|
+
* the arguments list will take precedence.
|
|
270
|
+
*
|
|
271
|
+
* Example:
|
|
272
|
+
*
|
|
273
|
+
* ```js
|
|
274
|
+
* var result = merge({foo: 123}, {foo: 456});
|
|
275
|
+
* console.log(result.foo); // outputs 456
|
|
276
|
+
* ```
|
|
277
|
+
*
|
|
278
|
+
* @param {Object} obj1 Object to merge
|
|
279
|
+
* @returns {Object} Result of all merge properties
|
|
280
|
+
*/
|
|
281
|
+
function merge(/* obj1, obj2, obj3, ... */) {
|
|
282
|
+
var result = {};
|
|
283
|
+
function assignValue(val, key) {
|
|
284
|
+
if (isPlainObject(result[key]) && isPlainObject(val)) {
|
|
285
|
+
result[key] = merge(result[key], val);
|
|
286
|
+
} else if (isPlainObject(val)) {
|
|
287
|
+
result[key] = merge({}, val);
|
|
288
|
+
} else if (isArray(val)) {
|
|
289
|
+
result[key] = val.slice();
|
|
290
|
+
} else {
|
|
291
|
+
result[key] = val;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
for (var i = 0, l = arguments.length; i < l; i++) {
|
|
296
|
+
forEach(arguments[i], assignValue);
|
|
297
|
+
}
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Extends object a by mutably adding to it the properties of object b.
|
|
303
|
+
*
|
|
304
|
+
* @param {Object} a The object to be extended
|
|
305
|
+
* @param {Object} b The object to copy properties from
|
|
306
|
+
* @param {Object} thisArg The object to bind function to
|
|
307
|
+
* @return {Object} The resulting value of object a
|
|
308
|
+
*/
|
|
309
|
+
function extend(a, b, thisArg) {
|
|
310
|
+
forEach(b, function assignValue(val, key) {
|
|
311
|
+
if (thisArg && typeof val === "function") {
|
|
312
|
+
a[key] = bind(val, thisArg);
|
|
313
|
+
} else {
|
|
314
|
+
a[key] = val;
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
return a;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
|
|
322
|
+
*
|
|
323
|
+
* @param {string} content with BOM
|
|
324
|
+
* @return {string} content value without BOM
|
|
325
|
+
*/
|
|
326
|
+
function stripBOM(content) {
|
|
327
|
+
if (content.charCodeAt(0) === 0xfeff) {
|
|
328
|
+
content = content.slice(1);
|
|
329
|
+
}
|
|
330
|
+
return content;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const fmtTime = function () {
|
|
334
|
+
var date = new Date();
|
|
335
|
+
hh = `${date.getHours() < 10 ? "0" : ""}${date.getHours()}`;
|
|
336
|
+
min = `${date.getMinutes() < 10 ? "0" : ""}${date.getMinutes()}`;
|
|
337
|
+
sec = `${date.getSeconds() < 10 ? "0" : ""}${date.getSeconds()}`;
|
|
338
|
+
mymilliseconds = date.getMilliseconds();
|
|
339
|
+
return hh + ":" + min + ":" + sec + ":" + mymilliseconds;
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
//获取离线包信息√
|
|
343
|
+
async function getPkgInfo(pkgLists, appid, h5pkgid) {
|
|
344
|
+
let f = pkgLists[appid].filter((val) => val.h5app_id == h5pkgid);
|
|
345
|
+
return f[0];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
//设置要发布版本号
|
|
349
|
+
const setPkgVersion = function (set_pkg_version, max_version) {
|
|
350
|
+
const compare = (a, b) => {
|
|
351
|
+
if (a === b) {
|
|
352
|
+
return 0;
|
|
353
|
+
}
|
|
354
|
+
const aArr = a.split("."),
|
|
355
|
+
bArr = b.split(".");
|
|
356
|
+
for (let i = 0; i < Math.min(aArr.length, bArr.length); i++) {
|
|
357
|
+
if (parseInt(aArr[i]) < parseInt(bArr[i])) {
|
|
358
|
+
return -1;
|
|
359
|
+
}
|
|
360
|
+
if (parseInt(aArr[i]) > parseInt(bArr[i])) {
|
|
361
|
+
return 1;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (aArr.length < bArr.length) {
|
|
365
|
+
return -1;
|
|
366
|
+
}
|
|
367
|
+
if (aArr.length > bArr.length) {
|
|
368
|
+
return 1;
|
|
369
|
+
}
|
|
370
|
+
return 0;
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
let e = max_version.split("."),
|
|
374
|
+
p = e.slice(-1),
|
|
375
|
+
f = e.pop(),
|
|
376
|
+
n = parseInt(p) + 1,
|
|
377
|
+
newVersion = `${[...e, n].join(".")}`;
|
|
378
|
+
if (set_pkg_version && compare(set_pkg_version, max_version) == 1) {
|
|
379
|
+
// 如果指定发布版本号
|
|
380
|
+
newVersion = set_pkg_version;
|
|
381
|
+
}
|
|
382
|
+
return newVersion;
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
//获取指定文件夹下的离线包
|
|
386
|
+
//获取项目工程里的图片
|
|
387
|
+
const readFileList = function (path, filesList) {
|
|
388
|
+
var files = readdirSync(path);
|
|
389
|
+
files.forEach(function (item, index) {
|
|
390
|
+
var stat = statSync(path + item);
|
|
391
|
+
if (stat.isDirectory()) {
|
|
392
|
+
//递归读取文件
|
|
393
|
+
readFileList(path + item + "/", filesList);
|
|
394
|
+
} else {
|
|
395
|
+
if (item.includes(".zip")) {
|
|
396
|
+
filesList.push(item);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
};
|
|
401
|
+
//获取文件夹下的所有文件
|
|
402
|
+
const getZipFileList = async function (path) {
|
|
403
|
+
var filesList = [];
|
|
404
|
+
readFileList(path, filesList);
|
|
405
|
+
return filesList;
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
module.exports = {
|
|
409
|
+
fmtTime,
|
|
410
|
+
getPkgInfo,
|
|
411
|
+
setPkgVersion,
|
|
412
|
+
getZipFileList,
|
|
413
|
+
isArray: isArray,
|
|
414
|
+
isArrayBuffer: isArrayBuffer,
|
|
415
|
+
isBuffer: isBuffer,
|
|
416
|
+
isFormData: isFormData,
|
|
417
|
+
isArrayBufferView: isArrayBufferView,
|
|
418
|
+
isString: isString,
|
|
419
|
+
isNumber: isNumber,
|
|
420
|
+
isObject: isObject,
|
|
421
|
+
isPlainObject: isPlainObject,
|
|
422
|
+
isUndefined: isUndefined,
|
|
423
|
+
isDate: isDate,
|
|
424
|
+
isFile: isFile,
|
|
425
|
+
isBlob: isBlob,
|
|
426
|
+
isFunction: isFunction,
|
|
427
|
+
isStream: isStream,
|
|
428
|
+
isURLSearchParams: isURLSearchParams,
|
|
429
|
+
isStandardBrowserEnv: isStandardBrowserEnv,
|
|
430
|
+
forEach: forEach,
|
|
431
|
+
merge: merge,
|
|
432
|
+
extend: extend,
|
|
433
|
+
trim: trim,
|
|
434
|
+
stripBOM: stripBOM,
|
|
435
|
+
};
|
package/lsc.config.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"publishInfo": {
|
|
3
|
+
"token": "xxx.xxx.xxx",
|
|
4
|
+
"pkgid": "xxx.xxx.xxx",
|
|
5
|
+
"set_pkg_version": "1.2.9",
|
|
6
|
+
"publish_app_arr": [3592, 3577],
|
|
7
|
+
"android_version_scope": "7.0.7.0",
|
|
8
|
+
"ios_version_scope": "7.0.7",
|
|
9
|
+
"pkg_zip_name": "xxx.xxx.xxx.zip",
|
|
10
|
+
"pkg_dir": "./dist/",
|
|
11
|
+
"task_status": "2"
|
|
12
|
+
},
|
|
13
|
+
"lightBaseURL": "https://xxx.xxx.xxx/lightadmin/pas-api"
|
|
14
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lightshortcuts",
|
|
3
|
+
"dependencies": {
|
|
4
|
+
"axios": "^0.24.0",
|
|
5
|
+
"chalk": "^5.0.0",
|
|
6
|
+
"commander": "^8.3.0",
|
|
7
|
+
"filesize": "^8.0.6",
|
|
8
|
+
"form-data": "^4.0.0",
|
|
9
|
+
"inquirer": "^8.2.0",
|
|
10
|
+
"qs": "^6.10.3"
|
|
11
|
+
},
|
|
12
|
+
"version": "1.0.0",
|
|
13
|
+
"description": "Light离线包自动发布工具",
|
|
14
|
+
"main": "index.js",
|
|
15
|
+
"devDependencies": {},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"start": "lsc publish",
|
|
18
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
19
|
+
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"lsc": "./bin/lsc.js"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"offline",
|
|
25
|
+
"tools"
|
|
26
|
+
],
|
|
27
|
+
"author": "weizhen@idoseek.com",
|
|
28
|
+
"license": "ISC"
|
|
29
|
+
}
|