xc-web-update-notice-umijs 1.0.0 → 1.0.1

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.
@@ -0,0 +1,188 @@
1
+ const path = require("path");
2
+ const fs = require("fs");
3
+ const {
4
+ createHash
5
+ } = require("crypto");
6
+ const KEY = "XcUpdateNoticeUmiPlugin";
7
+ function createEnvironmentHash(mode, version) {
8
+ if (mode === "hash") {
9
+ const hash = createHash("md5");
10
+ hash.update(Date.now().toString());
11
+ const result = hash.digest("hex");
12
+ return result;
13
+ } else if (mode === "custom") {
14
+ return version;
15
+ } else {
16
+ return "1.0.0";
17
+ }
18
+ }
19
+ module.exports = function (api) {
20
+ let history = [];
21
+ api.describe({
22
+ key: KEY,
23
+ config: {
24
+ schema(joi) {
25
+ history = getHistoryVersion();
26
+ return joi.object({
27
+ filename: joi.string().default("_version.json"),
28
+ interval: joi.number().default(5000),
29
+ keepVersions: joi.number().default(5),
30
+ publishDescription: joi.string().default(""),
31
+ isLogout: joi.boolean().default(false),
32
+ laterInterval: joi.number().default(10 * 60 * 1000),
33
+ // 文件指向定义
34
+ versionDir: joi.string().default("/"),
35
+ // 非生产环境不进行检查
36
+ isProd: joi.boolean().default(true),
37
+ // 生成版本号模式
38
+ versionMode: joi.string().valid("hash", "custom").default("hash"),
39
+ // 版本号(versionMode为custom时生效)
40
+ version: joi.string().default("1.0.0")
41
+ });
42
+ }
43
+ }
44
+ });
45
+
46
+ // 获取历史版本
47
+ const getHistoryVersion = () => {
48
+ const outputPath = path.join(api.paths.cwd, "dist");
49
+ const historyFile = path.join(outputPath, "_version-history.json");
50
+ let history = [];
51
+ if (fs.existsSync(historyFile)) {
52
+ try {
53
+ // 读取之前的上线版本信息
54
+ history = JSON.parse(fs.readFileSync(historyFile, "utf-8"));
55
+ } catch {
56
+ console.warn("[XcUpdateNoticeUmi] 历史版本解析失败");
57
+ }
58
+ }
59
+ return history;
60
+ };
61
+
62
+ // 构建结束后生成 _version.json
63
+ api.onBuildComplete(({
64
+ err
65
+ }) => {
66
+ if (err) return;
67
+ const userConfig = api.userConfig[KEY] || {};
68
+ const options = Object.assign({
69
+ filename: "_version.json",
70
+ keepVersions: 5,
71
+ publishDescription: "",
72
+ isLogout: false,
73
+ isProd: true,
74
+ versionMode: "hash"
75
+ }, userConfig);
76
+
77
+ // 非生产环境不检测
78
+ if (!options.isProd) return;
79
+ const outputPath = path.join(api.paths.cwd, "dist");
80
+ const file = path.join(outputPath, options.filename);
81
+ const historyFile = path.join(outputPath, "_version-history.json");
82
+ const hash = createEnvironmentHash(options.versionMode, options.version);
83
+ const current = {
84
+ hash,
85
+ buildTime: new Date().toLocaleString(),
86
+ publishDescription: options.publishDescription,
87
+ isLogout: options.isLogout
88
+ };
89
+ fs.writeFileSync(file, JSON.stringify(current, null, 2));
90
+ history.unshift(current);
91
+ history = history.slice(0, options.keepVersions);
92
+ fs.writeFileSync(historyFile, JSON.stringify(history, null, 2));
93
+ console.log(`[XcUpdateNoticeUmi] ✅ 生成成功 -> ${file}`);
94
+ });
95
+ api.addEntryCodeAhead(() => {
96
+ const userConfig = api.userConfig[KEY] || {};
97
+ const options = Object.assign({
98
+ filename: "_version.json",
99
+ interval: 5000,
100
+ laterInterval: 10 * 60 * 1000,
101
+ versionDir: "/",
102
+ isProd: true
103
+ }, userConfig);
104
+ if (!options.isProd) return "";
105
+ return generateCheckScript(options, history);
106
+ });
107
+ };
108
+ function generateCheckScript(options) {
109
+ return `
110
+ (function(){
111
+ if(window.__XC_UPDATE_INITED__) return;
112
+ window.__XC_UPDATE_INITED__ = true;
113
+ const versionUrl = '${options.versionDir}${options.filename}';
114
+ // 用户点击稍后更新后存储的版本信息,落后的版本
115
+ let laterInfo = []
116
+ // 是否处于弹窗中
117
+ let isDialog = false;
118
+ // 最新版本
119
+ let latestVersion = null;
120
+ // 落后版本是否需要退出登录
121
+ let lastIsLogout = false;
122
+ const interval = ${options.interval};
123
+ const laterInterval = ${options.laterInterval};
124
+ let timer = null;
125
+ let laterTimer = null;
126
+ // 客户端版本
127
+ let clientCurrentVersion = null;
128
+ const callbacks = [];
129
+ const _xcUpdate = {
130
+ onUpdate(fn, ver) {
131
+ clientCurrentVersion = ver
132
+ console.log('[xc-web-update-notice] 当前版本为', ver);
133
+ callbacks.push(fn);
134
+ },
135
+ updateLater() {
136
+ if (laterTimer) {
137
+ clearTimeout(laterTimer);
138
+ laterTimer = null;
139
+ }
140
+ laterInfo.push({
141
+ version: latestVersion,
142
+ isLater: true,
143
+ isLogout: lastIsLogout
144
+ })
145
+ if(isDialog) {
146
+ isDialog = false
147
+ }
148
+ laterTimer = setTimeout(() => {
149
+ laterTimer = null;
150
+ }, laterInterval);
151
+ }
152
+ };
153
+
154
+ async function checkUpdate() {
155
+ try {
156
+ const res = await fetch(versionUrl + '?_=' + Date.now());
157
+ const data = await res.json();
158
+ latestVersion = data.hash;
159
+ lastIsLogout = data.isLogout;
160
+
161
+ // 正常发布更新
162
+ if (clientCurrentVersion != data.hash && !laterInfo.length) {
163
+ if (isDialog) {
164
+ return;
165
+ }
166
+ isDialog = true;
167
+ callbacks.forEach(fn => fn({ oldHash: clientCurrentVersion, newHash: data.hash, isLogout: data.isLogout }));
168
+ }
169
+
170
+ // 稍后更新中的时候,再次发布新版本时更新
171
+ if(laterInfo.length && laterInfo[laterInfo.length-1].version !== data.hash) {
172
+ if (isDialog) {
173
+ return;
174
+ }
175
+ isDialog = true;
176
+ // 落后版本是否有需要退出登录的
177
+ const isLogout = laterInfo.find(item => item.isLogout)?.isLogout;
178
+ callbacks.forEach(fn => fn({ oldHash: clientCurrentVersion, newHash: data.hash, isLogout: data.isLogout || isLogout }));
179
+ }
180
+ } catch(e) {
181
+ console.warn('[xc-web-update-notice] 检查版本失败', e);
182
+ }
183
+ }
184
+ timer = setInterval(checkUpdate, interval);
185
+ window._xcUpdate = _xcUpdate;
186
+ })();
187
+ `;
188
+ }
@@ -0,0 +1,188 @@
1
+ const path = require("path");
2
+ const fs = require("fs");
3
+ const {
4
+ createHash
5
+ } = require("crypto");
6
+ const KEY = "XcUpdateNoticeUmiPlugin";
7
+ function createEnvironmentHash(mode, version) {
8
+ if (mode === "hash") {
9
+ const hash = createHash("md5");
10
+ hash.update(Date.now().toString());
11
+ const result = hash.digest("hex");
12
+ return result;
13
+ } else if (mode === "custom") {
14
+ return version;
15
+ } else {
16
+ return "1.0.0";
17
+ }
18
+ }
19
+ module.exports = function (api) {
20
+ let history = [];
21
+ api.describe({
22
+ key: KEY,
23
+ config: {
24
+ schema(joi) {
25
+ history = getHistoryVersion();
26
+ return joi.object({
27
+ filename: joi.string().default("_version.json"),
28
+ interval: joi.number().default(5000),
29
+ keepVersions: joi.number().default(5),
30
+ publishDescription: joi.string().default(""),
31
+ isLogout: joi.boolean().default(false),
32
+ laterInterval: joi.number().default(10 * 60 * 1000),
33
+ // 文件指向定义
34
+ versionDir: joi.string().default("/"),
35
+ // 非生产环境不进行检查
36
+ isProd: joi.boolean().default(true),
37
+ // 生成版本号模式
38
+ versionMode: joi.string().valid("hash", "custom").default("hash"),
39
+ // 版本号(versionMode为custom时生效)
40
+ version: joi.string().default("1.0.0")
41
+ });
42
+ }
43
+ }
44
+ });
45
+
46
+ // 获取历史版本
47
+ const getHistoryVersion = () => {
48
+ const outputPath = path.join(api.paths.cwd, "dist");
49
+ const historyFile = path.join(outputPath, "_version-history.json");
50
+ let history = [];
51
+ if (fs.existsSync(historyFile)) {
52
+ try {
53
+ // 读取之前的上线版本信息
54
+ history = JSON.parse(fs.readFileSync(historyFile, "utf-8"));
55
+ } catch {
56
+ console.warn("[XcUpdateNoticeUmi] 历史版本解析失败");
57
+ }
58
+ }
59
+ return history;
60
+ };
61
+
62
+ // 构建结束后生成 _version.json
63
+ api.onBuildComplete(({
64
+ err
65
+ }) => {
66
+ if (err) return;
67
+ const userConfig = api.userConfig[KEY] || {};
68
+ const options = Object.assign({
69
+ filename: "_version.json",
70
+ keepVersions: 5,
71
+ publishDescription: "",
72
+ isLogout: false,
73
+ isProd: true,
74
+ versionMode: "hash"
75
+ }, userConfig);
76
+
77
+ // 非生产环境不检测
78
+ if (!options.isProd) return;
79
+ const outputPath = path.join(api.paths.cwd, "dist");
80
+ const file = path.join(outputPath, options.filename);
81
+ const historyFile = path.join(outputPath, "_version-history.json");
82
+ const hash = createEnvironmentHash(options.versionMode, options.version);
83
+ const current = {
84
+ hash,
85
+ buildTime: new Date().toLocaleString(),
86
+ publishDescription: options.publishDescription,
87
+ isLogout: options.isLogout
88
+ };
89
+ fs.writeFileSync(file, JSON.stringify(current, null, 2));
90
+ history.unshift(current);
91
+ history = history.slice(0, options.keepVersions);
92
+ fs.writeFileSync(historyFile, JSON.stringify(history, null, 2));
93
+ console.log(`[XcUpdateNoticeUmi] ✅ 生成成功 -> ${file}`);
94
+ });
95
+ api.addEntryCodeAhead(() => {
96
+ const userConfig = api.userConfig[KEY] || {};
97
+ const options = Object.assign({
98
+ filename: "_version.json",
99
+ interval: 5000,
100
+ laterInterval: 10 * 60 * 1000,
101
+ versionDir: "/",
102
+ isProd: true
103
+ }, userConfig);
104
+ if (!options.isProd) return "";
105
+ return generateCheckScript(options, history);
106
+ });
107
+ };
108
+ function generateCheckScript(options) {
109
+ return `
110
+ (function(){
111
+ if(window.__XC_UPDATE_INITED__) return;
112
+ window.__XC_UPDATE_INITED__ = true;
113
+ const versionUrl = '${options.versionDir}${options.filename}';
114
+ // 用户点击稍后更新后存储的版本信息,落后的版本
115
+ let laterInfo = []
116
+ // 是否处于弹窗中
117
+ let isDialog = false;
118
+ // 最新版本
119
+ let latestVersion = null;
120
+ // 落后版本是否需要退出登录
121
+ let lastIsLogout = false;
122
+ const interval = ${options.interval};
123
+ const laterInterval = ${options.laterInterval};
124
+ let timer = null;
125
+ let laterTimer = null;
126
+ // 客户端版本
127
+ let clientCurrentVersion = null;
128
+ const callbacks = [];
129
+ const _xcUpdate = {
130
+ onUpdate(fn, ver) {
131
+ clientCurrentVersion = ver
132
+ console.log('[xc-web-update-notice] 当前版本为', ver);
133
+ callbacks.push(fn);
134
+ },
135
+ updateLater() {
136
+ if (laterTimer) {
137
+ clearTimeout(laterTimer);
138
+ laterTimer = null;
139
+ }
140
+ laterInfo.push({
141
+ version: latestVersion,
142
+ isLater: true,
143
+ isLogout: lastIsLogout
144
+ })
145
+ if(isDialog) {
146
+ isDialog = false
147
+ }
148
+ laterTimer = setTimeout(() => {
149
+ laterTimer = null;
150
+ }, laterInterval);
151
+ }
152
+ };
153
+
154
+ async function checkUpdate() {
155
+ try {
156
+ const res = await fetch(versionUrl + '?_=' + Date.now());
157
+ const data = await res.json();
158
+ latestVersion = data.hash;
159
+ lastIsLogout = data.isLogout;
160
+
161
+ // 正常发布更新
162
+ if (clientCurrentVersion != data.hash && !laterInfo.length) {
163
+ if (isDialog) {
164
+ return;
165
+ }
166
+ isDialog = true;
167
+ callbacks.forEach(fn => fn({ oldHash: clientCurrentVersion, newHash: data.hash, isLogout: data.isLogout }));
168
+ }
169
+
170
+ // 稍后更新中的时候,再次发布新版本时更新
171
+ if(laterInfo.length && laterInfo[laterInfo.length-1].version !== data.hash) {
172
+ if (isDialog) {
173
+ return;
174
+ }
175
+ isDialog = true;
176
+ // 落后版本是否有需要退出登录的
177
+ const isLogout = laterInfo.find(item => item.isLogout)?.isLogout;
178
+ callbacks.forEach(fn => fn({ oldHash: clientCurrentVersion, newHash: data.hash, isLogout: data.isLogout || isLogout }));
179
+ }
180
+ } catch(e) {
181
+ console.warn('[xc-web-update-notice] 检查版本失败', e);
182
+ }
183
+ }
184
+ timer = setInterval(checkUpdate, interval);
185
+ window._xcUpdate = _xcUpdate;
186
+ })();
187
+ `;
188
+ }
package/package.json CHANGED
@@ -1,19 +1,17 @@
1
1
  {
2
2
  "name": "xc-web-update-notice-umijs",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "前端版本更新通知 - UmiJS 插件",
5
- "main": "../../dist/cjs/umijs/index.js",
6
- "module": "../../dist/esm/umijs/index.js",
7
- "types": "../../dist/esm/umijs/index.d.ts",
5
+ "main": "./dist/cjs/index.js",
6
+ "module": "./dist/esm/index.js",
8
7
  "exports": {
9
8
  ".": {
10
- "require": "../../dist/cjs/umijs/index.js",
11
- "import": "../../dist/esm/umijs/index.js"
9
+ "require": "./dist/cjs/index.js",
10
+ "import": "./dist/esm/index.js"
12
11
  }
13
12
  },
14
13
  "files": [
15
- "../../dist/cjs/umijs",
16
- "../../dist/esm/umijs"
14
+ "dist"
17
15
  ],
18
16
  "keywords": [
19
17
  "umijs",
@@ -32,7 +30,7 @@
32
30
  "author": "nnn",
33
31
  "license": "ISC",
34
32
  "scripts": {
35
- "build": "babel index.js --out-dir ../../dist/cjs/umijs --env-name cjs --out-file-extension .js && babel index.js --out-dir ../../dist/esm/umijs --env-name esm --out-file-extension .js"
33
+ "build": "babel index.js --out-dir dist/cjs --env-name cjs --out-file-extension .js && babel index.js --out-dir dist/esm --env-name esm --out-file-extension .js"
36
34
  },
37
35
  "peerDependencies": {
38
36
  "umi": "^3.0.0 || ^4.0.0"