@cdfrd/publish-notification-plugin 0.1.3 → 0.1.5

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 CHANGED
@@ -29,20 +29,31 @@ createUpdateNotice()
29
29
 
30
30
  ## 微前端(qiankun)
31
31
 
32
- 每个应用各自接入 `vitePluginVersion()` + `createUpdateNotice`;Host 同步 `setActiveApp`。
32
+ 每个子应用各自接入 `vitePluginVersion()` + `createUpdateNotice`;Host 同步 `setActiveApp`。
33
33
 
34
34
  ```ts
35
- // host/main.ts
35
+ // vite.config.ts
36
+ import { defineConfig } from 'vite'
37
+ import { vitePluginVersion } from '@cdfrd/publish-notification-plugin/vite'
38
+
39
+ export default defineConfig({
40
+ plugins: [vitePluginVersion()],
41
+ })
42
+ ```
43
+
44
+ ```ts
45
+ // 主应用: host/main.ts
36
46
  import { createUpdateNotice, getUpdateNotice } from '@cdfrd/publish-notification-plugin'
37
47
 
38
48
  createUpdateNotice({
39
49
  microApp: { enabled: true, appId: 'host' },
40
- // 纯壳 Host:disableNotification: true,
50
+ // 主应用纯壳(可选):disableNotification: true,
41
51
  })
42
52
 
43
53
  registerMicroApps(apps, {
44
54
  beforeMount: [
45
55
  (app) => {
56
+ // 激活子应用(插件只检查激活子应用是否有版本,不检查其他子应用)
46
57
  getUpdateNotice()?.setActiveApp(app.name)
47
58
  return Promise.resolve()
48
59
  },
@@ -51,21 +62,151 @@ registerMicroApps(apps, {
51
62
  ```
52
63
 
53
64
  ```ts
54
- // order/main.ts
65
+ // 子应用:order/main.ts
55
66
  createUpdateNotice({
56
67
  microApp: { enabled: true, appId: 'order-center' },
57
68
  })
58
69
  ```
59
70
 
71
+
72
+
60
73
  ## 事后切换语言 / 主题
61
74
 
62
75
  ```ts
63
76
  import { getUpdateNotice } from '@cdfrd/publish-notification-plugin'
64
77
 
65
78
  getUpdateNotice()?.setLocale('en_US')
79
+ // 如果是 Antd UI 4.x + 5.x +6.x ,可以不用写下面这行
80
+ // Antd主题会自动适配,不需要额外配置
66
81
  getUpdateNotice()?.setTheme('dark')
67
82
  ```
68
83
 
84
+
85
+
86
+ ## `UpdateNoticeOptions`
87
+
88
+ `createUpdateNotice(options?)` 的运行时配置。
89
+
90
+
91
+ | 字段 | 类型 | 默认 | 说明 |
92
+ | --------------------- | -------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
93
+ | `versionBase` | `string` | 自动推导 | `version.json` URL 前缀,最终请求 `{versionBase}update-notice/version.json`。优先级:显式 `versionBase` → Vite `import.meta.env.BASE_URL` → `''`。非根路径 / CDN 部署时建议显式配置 |
94
+ | `check` | `CheckConfig` | 见下表 | 版本检查触发策略 |
95
+ | `microApp` | `MicroAppConfig` | — | 微前端场景配置 |
96
+ | `themeAdapter` | `ThemeAdapterConfig` | `{ type: 'antd' }` | 默认 UI 主题适配 |
97
+ | `placement` | `'topLeft' | 'topRight' | 'bottomLeft' |
98
+ | `forcedUpdate` | `boolean` | `false` | `true` 时强制更新:无「稍后」/关闭,展示倒计时与「立即刷新」,倒计时结束自动刷新;页面无遮罩仍可操作 |
99
+ | `locale` | `LocaleConfig` | — | 按语言覆盖内置通知文案;整站当前语言由 `setLocale` 控制,内置默认 `zh_CN` |
100
+ | `disableNotification` | `boolean` | `false` | `true` 时完全关闭该应用的检查 / 默认 UI / `web_update_notice`。纯壳 Host 常用。与 `hiddenDefaultNotice` **互斥**,不得同时为 `true` |
101
+ | `hiddenDefaultNotice` | `boolean` | `false` | `true` 时关闭默认 UI,仍检查并派发 `web_update_notice`,供业务自定义 UI。与 `disableNotification` **互斥**,不得同时为 `true` |
102
+
103
+
104
+ 通知模式三选一:两者均不开启(默认 UI)/ 仅 `disableNotification` / 仅 `hiddenDefaultNotice`。
105
+
106
+ ### `check`
107
+
108
+
109
+ | 字段 | 类型 | 默认 | 说明 |
110
+ | ----------------- | --------- | ---------------- | ------------------------------ |
111
+ | `interval` | `number` | `10 * 60 * 1000` | 轮询间隔(毫秒);`<= 0` 不轮询 |
112
+ | `onWindowActive` | `boolean` | `true` | 窗口重新获得焦点时检查 |
113
+ | `onTabVisible` | `boolean` | `true` | 标签页从隐藏变为可见时检查 |
114
+ | `onResourceError` | `boolean` | `true` | 静态资源加载失败时检查(用于发现发版后旧 chunk 失效) |
115
+
116
+
117
+
118
+
119
+ ### `microApp`
120
+
121
+
122
+ | 字段 | 类型 | 默认 | 说明 |
123
+ | --------- | --------- | --- | ------------------------------------------- |
124
+ | `enabled` | `boolean` | — | 是否启用微前端口径;为 `true` 时需配合 Host `setActiveApp` |
125
+ | `appId` | `string` | — | 当前应用 ID;仅当该 ID 为 active app 时才会检查 / 通知 |
126
+
127
+
128
+
129
+
130
+ ### `themeAdapter`
131
+
132
+
133
+ | 字段 | 类型 | 默认 | 说明 |
134
+ | -------------- | ------- | -------------------------------- | -------- |
135
+ | `type` | `'antd' | 'cssVariable'` | `'antd'` |
136
+ | `primaryColor` | `string | { light: string; dark: string }` | — |
137
+
138
+
139
+
140
+
141
+ ### `locale`
142
+
143
+ 按语言覆盖文案,键为 `'zh_CN' | 'en_US'`,值为部分字段:
144
+
145
+
146
+ | 字段 | 说明 |
147
+ | ------------------- | ---------- |
148
+ | `title` | 通知标题 |
149
+ | `description` | 可选更新时的说明文案 |
150
+ | `buttonText` | 「立即刷新」按钮文案 |
151
+ | `dismissButtonText` | 「稍后」按钮文案 |
152
+ | `forcedDescription` | 强制更新正文 |
153
+
154
+
155
+ ```ts
156
+ createUpdateNotice({
157
+ versionBase: '/admin/',
158
+ check: { interval: 5 * 60 * 1000 },
159
+ placement: 'bottomRight',
160
+ forcedUpdate: false,
161
+ themeAdapter: { type: 'antd' },
162
+ locale: {
163
+ zh_CN: { title: '发现新版本', buttonText: '立即刷新' },
164
+ en_US: { title: 'Update available', buttonText: 'Refresh' },
165
+ },
166
+ })
167
+ ```
168
+
169
+
170
+
171
+ ## `VersionPluginOptions`
172
+
173
+ 构建侧配置,传给 `vitePluginVersion(options?)` / `defaultPluginVersion(options?)`。只生成 `{outDir}/update-notice/version.json`,不注入 HTML / 脚本。Runtime **不**配置这些字段。
174
+
175
+
176
+ | 字段 | 类型 | 默认 | 说明 |
177
+ | ------------- | ------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------ |
178
+ | `versionType` | `'timestamp' | 'gitcommit' | 'version'` |
179
+ | `outDir` | `string` | 见下 | 产物根目录,最终写出 `{outDir}/update-notice/version.json`。`vitePluginVersion` 未配置时读 Vite `build.outDir`;`defaultPluginVersion` 未配置时为 `'dist'` |
180
+
181
+
182
+
183
+
184
+ ### `versionType`
185
+
186
+
187
+ | 值 | 说明 |
188
+ | ----------- | ------------------------------------------------------------- |
189
+ | `timestamp` | 构建时间戳(`Date.now()`),默认推荐 |
190
+ | `gitcommit` | 短 Git Commit(`git rev-parse --short HEAD`);获取失败时降级为 timestamp |
191
+ | `version` | 当前 `package.json` 的 `version`;获取失败时降级为 timestamp |
192
+
193
+
194
+ ```ts
195
+ // vite.config.ts
196
+ vitePluginVersion({
197
+ versionType: 'gitcommit',
198
+ // outDir 一般可省略,自动用 Vite build.outDir
199
+ })
200
+
201
+ // 其他构建工具
202
+ await defaultPluginVersion({
203
+ versionType: 'version',
204
+ outDir: 'build',
205
+ })
206
+ ```
207
+
208
+
209
+
69
210
  ## 本地示例
70
211
 
71
212
  ```bash
@@ -74,4 +215,4 @@ pnpm build
74
215
  pnpm dev:vite # Host http://localhost:7100 · 单体 http://localhost:7103
75
216
  ```
76
217
 
77
- 更多示例见 [docs/examples.md](./docs/examples.md)。
218
+ 更多示例见 [docs/examples.md](./docs/examples.md)。
package/dist/index.cjs CHANGED
@@ -216,7 +216,9 @@ function resolveVersionBase(explicit) {
216
216
  }
217
217
  function buildVersionJsonUrl(versionBase) {
218
218
  const base = resolveVersionBase(versionBase);
219
- return `${base}${DIRECTORY_NAME}/${JSON_FILE_NAME}.json`;
219
+ const path = `${DIRECTORY_NAME}/${JSON_FILE_NAME}.json`;
220
+ if (!base) return `/${path}`;
221
+ return `${base}${path}`;
220
222
  }
221
223
 
222
224
  // src/core/UpdateNoticeRuntime.ts
@@ -317,6 +319,7 @@ var UpdateNoticeRuntime = class {
317
319
  const check = this.options.check ?? {};
318
320
  const checkInterval = check.interval ?? DEFAULT_CHECK_INTERVAL;
319
321
  const onWindowActive = check.onWindowActive !== false;
322
+ const onTabVisible = check.onTabVisible !== false;
320
323
  const onResourceError = check.onResourceError !== false;
321
324
  const checkImmediately = !this.options.microApp?.enabled;
322
325
  const run = () => this.checkUpdate();
@@ -335,7 +338,7 @@ var UpdateNoticeRuntime = class {
335
338
  if (document.visibilityState === "visible") {
336
339
  startPolling();
337
340
  flushPending();
338
- if (onWindowActive) run();
341
+ if (onTabVisible) run();
339
342
  } else if (this.intervalId) {
340
343
  clearInterval(this.intervalId);
341
344
  this.intervalId = void 0;
@@ -397,7 +400,7 @@ var PRESET_LOCALE = {
397
400
  };
398
401
  function resolveNotificationCopy(options, locale) {
399
402
  const builtin = PRESET_LOCALE[locale] ?? PRESET_LOCALE.zh_CN;
400
- const fromData = options.locale?.data?.[locale] ?? {};
403
+ const fromData = options.locale?.[locale] ?? {};
401
404
  return {
402
405
  title: fromData.title ?? builtin.title,
403
406
  description: fromData.description ?? builtin.description,
@@ -417,8 +420,8 @@ var DEFAULT_NOTICE_CSS = `
417
420
  --update-icon-color: rgba(0, 0, 0, 0.45);
418
421
  --update-icon-hover: rgba(0, 0, 0, 0.88);
419
422
  --update-btn-hover-bg: rgba(0, 0, 0, 0.06);
420
- --update-primary: #1677ff;
421
- --update-primary-hover: #4096ff;
423
+ --update-primary: #4096ff;
424
+ --update-primary-hover: #69b1ff;
422
425
  --update-mask: rgba(0, 0, 0, 0.45);
423
426
  --update-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12),
424
427
  0 9px 28px 8px rgba(0, 0, 0, 0.05);
@@ -431,8 +434,8 @@ var DEFAULT_NOTICE_CSS = `
431
434
  --update-icon-color: rgba(255, 255, 255, 0.45);
432
435
  --update-icon-hover: rgba(255, 255, 255, 0.85);
433
436
  --update-btn-hover-bg: rgba(255, 255, 255, 0.08);
434
- --update-primary: #1668dc;
435
- --update-primary-hover: #3c89e8;
437
+ --update-primary: #3c89e8;
438
+ --update-primary-hover: #65a9f3;
436
439
  --update-mask: rgba(0, 0, 0, 0.65);
437
440
  --update-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.32), 0 3px 6px -4px rgba(0, 0, 0, 0.48),
438
441
  0 9px 28px 8px rgba(0, 0, 0, 0.2);
@@ -743,9 +746,9 @@ function tryResolveAntdPrimaryColor(options) {
743
746
  }
744
747
  function resolveCssVariablePrimary(config, theme) {
745
748
  const primary = config?.primaryColor;
746
- if (!primary) return DEFAULT_PRIMARY_COLOR;
749
+ if (!primary) return "";
747
750
  if (typeof primary === "string") return primary;
748
- return primary[theme] || primary.light || DEFAULT_PRIMARY_COLOR;
751
+ return primary[theme] || primary.light || "";
749
752
  }
750
753
  function scheduleFrame(cb) {
751
754
  if (typeof requestAnimationFrame === "function") {
@@ -1051,10 +1054,45 @@ function el(tag, className, text) {
1051
1054
  if (text !== void 0) node.textContent = text;
1052
1055
  return node;
1053
1056
  }
1057
+ function lightenPrimaryForHover(primary, amount = 0.22) {
1058
+ const trimmed = primary.trim();
1059
+ const hex = trimmed.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
1060
+ let r;
1061
+ let g;
1062
+ let b;
1063
+ if (hex) {
1064
+ const h = hex[1];
1065
+ if (h.length === 3) {
1066
+ r = parseInt(h[0] + h[0], 16);
1067
+ g = parseInt(h[1] + h[1], 16);
1068
+ b = parseInt(h[2] + h[2], 16);
1069
+ } else {
1070
+ r = parseInt(h.slice(0, 2), 16);
1071
+ g = parseInt(h.slice(2, 4), 16);
1072
+ b = parseInt(h.slice(4, 6), 16);
1073
+ }
1074
+ } else {
1075
+ const rgb = trimmed.match(
1076
+ /^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*[\d.]+)?\s*\)$/i
1077
+ );
1078
+ if (!rgb) return primary;
1079
+ r = Number(rgb[1]);
1080
+ g = Number(rgb[2]);
1081
+ b = Number(rgb[3]);
1082
+ }
1083
+ const mix = (c) => Math.round(c + (255 - c) * amount);
1084
+ const toHex = (c) => mix(c).toString(16).padStart(2, "0");
1085
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
1086
+ }
1054
1087
  function applyPrimaryToNode(node, primary) {
1055
- if (!primary) return;
1056
- node.style.setProperty("--update-primary", primary);
1057
- node.style.setProperty("--update-primary-hover", primary);
1088
+ const el2 = node;
1089
+ if (!primary) {
1090
+ el2.style.removeProperty("--update-primary");
1091
+ el2.style.removeProperty("--update-primary-hover");
1092
+ return;
1093
+ }
1094
+ el2.style.setProperty("--update-primary", primary);
1095
+ el2.style.setProperty("--update-primary-hover", lightenPrimaryForHover(primary));
1058
1096
  }
1059
1097
  function applyTheme(theme) {
1060
1098
  ensureDefaultStyles();
@@ -1115,14 +1153,14 @@ function showDefaultNotice(options) {
1115
1153
  const placement = options.options.placement ?? "topRight";
1116
1154
  const forced = options.options.forcedUpdate === true;
1117
1155
  const theme = options.theme ?? "light";
1118
- const primary = options.primaryColor || currentPrimary;
1156
+ const primary = options.primaryColor ?? currentPrimary;
1119
1157
  const wrap = document.createElement("div");
1120
1158
  wrap.className = forced ? "cdfrd-update-notice cdfrd-update-notice--forced" : "cdfrd-update-notice";
1121
1159
  wrap.setAttribute("data-update-notice-theme", theme);
1122
1160
  if (!forced) {
1123
1161
  wrap.style.cssText = PLACEMENT_STYLES[placement] ?? PLACEMENT_STYLES.topRight;
1124
1162
  }
1125
- if (primary) applyPrimaryToNode(wrap, primary);
1163
+ applyPrimaryToNode(wrap, primary);
1126
1164
  const panel = el("div", "cdfrd-update-notice-panel");
1127
1165
  if (forced) {
1128
1166
  panel.appendChild(el("div", "cdfrd-update-notice-title", copy.forcedTitle));
@@ -1207,7 +1245,6 @@ var UpdateNoticeManager = class _UpdateNoticeManager {
1207
1245
  validateOptions(options);
1208
1246
  if (isFirstCreate) {
1209
1247
  this.themeAdapter = options.themeAdapter;
1210
- if (options.locale?.default) this.locale = options.locale.default;
1211
1248
  applyTheme(this.theme);
1212
1249
  this.startThemeWatch();
1213
1250
  }