vite-plugin-taro 0.2.2 → 0.3.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.
Files changed (133) hide show
  1. package/README.md +83 -106
  2. package/README.zh.md +83 -104
  3. package/dist/node/build-context.d.ts +25 -0
  4. package/dist/node/build-context.js +48 -0
  5. package/dist/node/css/css-pipeline.d.ts +21 -0
  6. package/dist/node/css/css-pipeline.js +129 -0
  7. package/dist/node/plugins/conditional-directives.d.ts +3 -0
  8. package/dist/node/plugins/conditional-directives.js +62 -0
  9. package/dist/node/plugins/taro-runtime.d.ts +5 -0
  10. package/dist/node/plugins/taro-runtime.js +18 -0
  11. package/dist/node/targets/h5/plugin.d.ts +6 -0
  12. package/dist/node/targets/h5/plugin.js +117 -0
  13. package/dist/node/targets/h5/virtual-modules.d.ts +5 -0
  14. package/dist/node/targets/h5/virtual-modules.js +60 -0
  15. package/dist/node/targets/wx/companion-assets.d.ts +18 -0
  16. package/dist/node/targets/wx/companion-assets.js +121 -0
  17. package/dist/node/targets/wx/dev-server/development-session.d.ts +31 -0
  18. package/dist/node/targets/wx/dev-server/development-session.js +202 -0
  19. package/dist/node/targets/wx/dev-server/full-build-scheduler.d.ts +14 -0
  20. package/dist/node/targets/wx/dev-server/full-build-scheduler.js +60 -0
  21. package/dist/node/targets/wx/dev-server/js-utils.d.ts +5 -0
  22. package/dist/node/targets/wx/dev-server/js-utils.js +28 -0
  23. package/dist/node/targets/wx/dev-server/output.d.ts +19 -0
  24. package/dist/node/targets/wx/dev-server/output.js +44 -0
  25. package/dist/node/targets/wx/dev-server/rolldown-runtime-source.d.ts +8 -0
  26. package/dist/node/targets/wx/dev-server/rolldown-runtime-source.js +95 -0
  27. package/dist/node/targets/wx/dev-server/update-server-state.d.ts +65 -0
  28. package/dist/node/targets/wx/dev-server/update-server-state.js +116 -0
  29. package/dist/node/targets/wx/dev-server/update-transport.d.ts +28 -0
  30. package/dist/node/targets/wx/dev-server/update-transport.js +212 -0
  31. package/dist/node/targets/wx/dev-server/vite-bundled-dev-adapter.d.ts +44 -0
  32. package/dist/node/targets/wx/dev-server/vite-bundled-dev-adapter.js +145 -0
  33. package/dist/node/targets/wx/development-files.d.ts +8 -0
  34. package/dist/node/targets/wx/development-files.js +8 -0
  35. package/dist/node/targets/wx/plugin.d.ts +6 -0
  36. package/dist/node/targets/wx/plugin.js +143 -0
  37. package/dist/node/targets/wx/react-refresh.d.ts +4 -0
  38. package/dist/node/targets/wx/react-refresh.js +38 -0
  39. package/dist/node/targets/wx/virtual-modules.d.ts +16 -0
  40. package/dist/node/targets/wx/virtual-modules.js +120 -0
  41. package/dist/node/utils/async.d.ts +8 -0
  42. package/dist/node/utils/async.js +16 -0
  43. package/dist/node/utils/filesystem.d.ts +13 -0
  44. package/dist/node/utils/filesystem.js +43 -0
  45. package/dist/{vite/utils.d.ts → node/utils/modules.d.ts} +2 -2
  46. package/dist/{vite/utils.js → node/utils/modules.js} +10 -3
  47. package/dist/node/utils/packages.d.ts +2 -0
  48. package/dist/node/utils/packages.js +8 -0
  49. package/dist/node/vite-plugin.d.ts +4 -0
  50. package/dist/node/vite-plugin.js +33 -0
  51. package/dist/options.d.ts +21 -0
  52. package/dist/{shim/h5.d.ts → runtime/h5/taro-runtime.d.ts} +3 -1
  53. package/dist/runtime/h5/taro-runtime.js +9 -0
  54. package/dist/runtime/taro/api.d.ts +5 -0
  55. package/dist/{virtual → runtime/taro}/api.js +3 -0
  56. package/dist/runtime/taro/components.d.ts +2 -0
  57. package/dist/runtime/taro/components.js +2 -0
  58. package/dist/runtime/wx/page-update.d.ts +6 -0
  59. package/dist/runtime/wx/page-update.js +157 -0
  60. package/dist/{shim/wx.d.ts → runtime/wx/taro-runtime.d.ts} +1 -0
  61. package/dist/{shim/wx.js → runtime/wx/taro-runtime.js} +1 -0
  62. package/dist/runtime/wx/update-client-state.d.ts +68 -0
  63. package/dist/runtime/wx/update-client-state.js +84 -0
  64. package/dist/runtime/wx/update-client.d.ts +1 -0
  65. package/dist/runtime/wx/update-client.js +170 -0
  66. package/dist/vite.d.ts +2 -2
  67. package/dist/vite.js +1 -1
  68. package/package.json +15 -11
  69. package/src/node/build-context.ts +63 -0
  70. package/src/node/css/css-pipeline.ts +147 -0
  71. package/src/node/plugins/conditional-directives.ts +76 -0
  72. package/src/node/plugins/taro-runtime.ts +22 -0
  73. package/src/node/targets/h5/plugin.ts +135 -0
  74. package/src/node/targets/h5/virtual-modules.ts +71 -0
  75. package/src/node/targets/wx/companion-assets.ts +160 -0
  76. package/src/node/targets/wx/dev-server/development-session.ts +253 -0
  77. package/src/node/targets/wx/dev-server/full-build-scheduler.ts +62 -0
  78. package/src/node/targets/wx/dev-server/js-utils.ts +33 -0
  79. package/src/node/targets/wx/dev-server/output.ts +56 -0
  80. package/src/node/targets/wx/dev-server/rolldown-runtime-source.ts +95 -0
  81. package/src/node/targets/wx/dev-server/update-server-state.ts +183 -0
  82. package/src/node/targets/wx/dev-server/update-transport.ts +261 -0
  83. package/src/node/targets/wx/dev-server/vite-bundled-dev-adapter.ts +203 -0
  84. package/src/node/targets/wx/development-files.ts +11 -0
  85. package/src/node/targets/wx/plugin.ts +163 -0
  86. package/src/node/targets/wx/react-refresh.ts +51 -0
  87. package/src/node/targets/wx/virtual-modules.ts +152 -0
  88. package/src/node/utils/async.ts +19 -0
  89. package/src/node/utils/filesystem.ts +45 -0
  90. package/src/{vite/utils.ts → node/utils/modules.ts} +11 -3
  91. package/src/node/utils/packages.ts +10 -0
  92. package/src/node/vite-plugin.ts +41 -0
  93. package/src/options.ts +25 -0
  94. package/src/runtime/h5/taro-css.d.ts +3 -0
  95. package/src/runtime/h5/taro-runtime.ts +10 -0
  96. package/src/{virtual → runtime/taro}/api.ts +3 -0
  97. package/src/runtime/taro/components.ts +2 -0
  98. package/src/runtime/wx/page-update.ts +230 -0
  99. package/src/runtime/wx/react-refresh.d.ts +20 -0
  100. package/src/{shim/wx.ts → runtime/wx/taro-runtime.ts} +1 -0
  101. package/src/runtime/wx/update-client-state.ts +138 -0
  102. package/src/runtime/wx/update-client.ts +222 -0
  103. package/src/vite.ts +2 -2
  104. package/dist/shim/h5.js +0 -5
  105. package/dist/virtual/api.d.ts +0 -3
  106. package/dist/virtual/components.d.ts +0 -1
  107. package/dist/virtual/components.js +0 -1
  108. package/dist/vite/constants.d.ts +0 -4
  109. package/dist/vite/constants.js +0 -8
  110. package/dist/vite/plugins.d.ts +0 -8
  111. package/dist/vite/plugins.js +0 -186
  112. package/dist/vite/tailwindcss.d.ts +0 -3
  113. package/dist/vite/tailwindcss.js +0 -35
  114. package/dist/vite/targets/h5.d.ts +0 -31
  115. package/dist/vite/targets/h5.js +0 -201
  116. package/dist/vite/targets/wx.d.ts +0 -74
  117. package/dist/vite/targets/wx.js +0 -362
  118. package/dist/vite/types.d.ts +0 -38
  119. package/dist/vite/virtual-modules.d.ts +0 -3
  120. package/dist/vite/virtual-modules.js +0 -13
  121. package/dist/vite/vite-plugin-taro.d.ts +0 -7
  122. package/dist/vite/vite-plugin-taro.js +0 -80
  123. package/src/shim/h5.ts +0 -6
  124. package/src/virtual/components.ts +0 -1
  125. package/src/vite/constants.ts +0 -12
  126. package/src/vite/plugins.ts +0 -218
  127. package/src/vite/tailwindcss.ts +0 -41
  128. package/src/vite/targets/h5.ts +0 -229
  129. package/src/vite/targets/wx.ts +0 -436
  130. package/src/vite/types.ts +0 -48
  131. package/src/vite/virtual-modules.ts +0 -14
  132. package/src/vite/vite-plugin-taro.ts +0 -106
  133. /package/dist/{vite/types.js → options.js} +0 -0
@@ -0,0 +1,33 @@
1
+ import react from '@vitejs/plugin-react';
2
+ import { BuildContext } from './build-context.js';
3
+ import { createConditionalDirectivePlugin } from './plugins/conditional-directives.js';
4
+ import { createTaroRuntimePlugin } from './plugins/taro-runtime.js';
5
+ import { createH5TargetPlugins, createH5ViteConfig } from './targets/h5/plugin.js';
6
+ import { createWxTargetPlugins, createWxViteConfig } from './targets/wx/plugin.js';
7
+ /** Creates the Vite plugins for the selected Taro target. */
8
+ export default function vitePluginTaro(options) {
9
+ const context = new BuildContext(options);
10
+ const targetPlugins = context.project.target === 'wx' ? createWxTargetPlugins(context) : createH5TargetPlugins(context);
11
+ return [
12
+ createBuildCoordinator(context),
13
+ createConditionalDirectivePlugin(context),
14
+ createTaroRuntimePlugin(),
15
+ context.css.plugin,
16
+ ...react(),
17
+ ...targetPlugins
18
+ ];
19
+ }
20
+ /** Establishes build mode and resolved Vite state before target hooks consume the context. */
21
+ function createBuildCoordinator(context) {
22
+ return {
23
+ name: 'vite-plugin-taro:build',
24
+ enforce: 'pre',
25
+ config(_, environment) {
26
+ context.configure(environment);
27
+ return context.project.target === 'wx' ? createWxViteConfig(context) : createH5ViteConfig(context);
28
+ },
29
+ configResolved(config) {
30
+ context.resolve(config);
31
+ }
32
+ };
33
+ }
@@ -0,0 +1,21 @@
1
+ /** Plain JSON object emitted into Mini Program/Web config payloads. */
2
+ export type JsonObject = Record<string, unknown>;
3
+ /** Build target handled by this plugin. */
4
+ export type VitePluginTaroTarget = 'wx' | 'h5';
5
+ /** Describes one React-backed page shared by WeChat Mini Program and Web builds. */
6
+ export type VitePluginTaroPageOption = {
7
+ /** Taro route and output path without a file extension. */
8
+ path: string;
9
+ /** Page config merged into the generated target config. */
10
+ config: JsonObject;
11
+ };
12
+ /** Required build inputs for the Vite/Rolldown Taro renderer plugin. */
13
+ export interface VitePluginTaroOptions {
14
+ target: VitePluginTaroTarget;
15
+ app: string;
16
+ pages: VitePluginTaroPageOption[];
17
+ appJson: JsonObject;
18
+ projectConfigJson: JsonObject;
19
+ projectPrivateConfigJson: JsonObject;
20
+ sitemapJson: JsonObject;
21
+ }
@@ -1,4 +1,6 @@
1
- import '@tarojs/plugin-platform-h5/dist/runtime';
1
+ /** Runtime exports consumed by the generated H5 entry. */
2
+ import '@tarojs/components/global.css';
3
+ import '@tarojs/components/dist/taro-components/taro-components.css';
2
4
  export { createReactApp } from '@tarojs/plugin-framework-react/dist/runtime';
3
5
  export { createBrowserHistory, createHashHistory, createRouter, handleAppMount } from '@tarojs/router';
4
6
  export { window } from '@tarojs/runtime';
@@ -0,0 +1,9 @@
1
+ /** Runtime exports consumed by the generated H5 entry. */
2
+ import '@tarojs/components/global.css';
3
+ import '@tarojs/components/dist/taro-components/taro-components.css';
4
+ // Import the CSS files directly instead of @tarojs/plugin-platform-h5/dist/runtime:
5
+ // that runtime adds only taro-components.css plus optional polyfills, while generated H5 entries also need global.css.
6
+ // @ts-expect-error Taro exposes createReactApp from this runtime-only deep entry without types.
7
+ export { createReactApp } from '@tarojs/plugin-framework-react/dist/runtime';
8
+ export { createBrowserHistory, createHashHistory, createRouter, handleAppMount } from '@tarojs/router';
9
+ export { window } from '@tarojs/runtime';
@@ -0,0 +1,5 @@
1
+ /** Physical runtime module backing the public `virtual:taro/api` facade. */
2
+ import '@tarojs/plugin-framework-react/dist/runtime';
3
+ import Taro from '@tarojs/taro';
4
+ export * from '@tarojs/taro';
5
+ export default Taro;
@@ -1,3 +1,6 @@
1
+ /** Physical runtime module backing the public `virtual:taro/api` facade. */
2
+ // Ensure Taro's React runtime registers its `initNativeApi` hook before @tarojs/taro is initialized.
3
+ import '@tarojs/plugin-framework-react/dist/runtime';
1
4
  import { hooks } from '@tarojs/runtime';
2
5
  import Taro from '@tarojs/taro';
3
6
  if (hooks.isExist('initNativeApi')) {
@@ -0,0 +1,2 @@
1
+ /** Physical runtime module backing the public `virtual:taro/components` facade. */
2
+ export * from '@tarojs/components';
@@ -0,0 +1,2 @@
1
+ /** Physical runtime module backing the public `virtual:taro/components` facade. */
2
+ export * from '@tarojs/components';
@@ -0,0 +1,6 @@
1
+ type WxPageConfig = Record<string, unknown>;
2
+ /** Registers a native route once per App Service runtime generation. */
3
+ export declare function registerWxPage(route: string, register: () => void): void;
4
+ /** Retains Taro's live root while filtering DevTools' synthetic update lifecycles. */
5
+ export declare function decorateWxPageConfig(config: WxPageConfig): WxPageConfig;
6
+ export {};
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Coordinates page-side update execution after the Rolldown and Taro runtimes exist.
3
+ *
4
+ * This module is intentionally page-only: it suppresses DevTools' synthetic lifecycles, preserves the Taro root,
5
+ * commits React Refresh, and relaunches stale component families. HTTP transport remains in update-client.ts.
6
+ */
7
+ import { document } from '@tarojs/runtime';
8
+ import { validateRefreshBoundaryAndEnqueueUpdate } from '/@react-refresh';
9
+ const wxRuntimeGlobal = globalThis;
10
+ wxRuntimeGlobal.__VITE_PLUGIN_TARO_WX_PAGE_UPDATE__ ??= { ready: false };
11
+ const bridge = wxRuntimeGlobal.__VITE_PLUGIN_TARO_WX_PAGE_UPDATE__;
12
+ const refreshBoundary = { default: function WxRefreshBoundary() { } };
13
+ const registeredRoutes = new Set();
14
+ const ignoredPages = new WeakSet();
15
+ let pageRuntimeState = { suppressLifecycles: false };
16
+ /** Registers a native route once per App Service runtime generation. */
17
+ export function registerWxPage(route, register) {
18
+ if (registeredRoutes.has(route))
19
+ return;
20
+ register();
21
+ registeredRoutes.add(route);
22
+ }
23
+ /** Retains Taro's live root while filtering DevTools' synthetic update lifecycles. */
24
+ export function decorateWxPageConfig(config) {
25
+ const onLoad = getLifecycle(config, 'onLoad');
26
+ config.onLoad = function (...args) {
27
+ if (pageRuntimeState.suppressLifecycles) {
28
+ ignoredPages.add(this);
29
+ this.$taroPath = pageRuntimeState.activePage?.$taroPath;
30
+ this.$taroParams = pageRuntimeState.activePage?.$taroParams;
31
+ dispatchPageRuntime({ type: 'page-activated', page: this });
32
+ return;
33
+ }
34
+ const result = onLoad?.apply(this, args);
35
+ dispatchPageRuntime({ type: 'page-activated', page: this });
36
+ return result;
37
+ };
38
+ wrapLifecycle(config, 'onReady', applyPendingPageUpdate);
39
+ wrapLifecycle(config, 'onShow', function () {
40
+ dispatchPageRuntime({ type: 'page-activated', page: this });
41
+ });
42
+ wrapLifecycle(config, 'onHide');
43
+ wrapLifecycle(config, 'onUnload', function () {
44
+ dispatchPageRuntime({ type: 'page-unloaded', page: this });
45
+ });
46
+ return config;
47
+ }
48
+ function wrapLifecycle(config, name, after) {
49
+ const original = getLifecycle(config, name);
50
+ config[name] = function (...args) {
51
+ if (pageRuntimeState.suppressLifecycles)
52
+ return;
53
+ if (ignoredPages.has(this)) {
54
+ if (name === 'onUnload')
55
+ ignoredPages.delete(this);
56
+ return;
57
+ }
58
+ const result = original?.apply(this, args);
59
+ after?.apply(this, args);
60
+ return result;
61
+ };
62
+ }
63
+ function getLifecycle(config, name) {
64
+ return typeof config[name] === 'function' ? config[name] : undefined;
65
+ }
66
+ function applyPendingPageUpdate() {
67
+ bridge.ready = true;
68
+ const pendingUpdate = bridge.pendingUpdate;
69
+ if (pendingUpdate) {
70
+ delete bridge.pendingUpdate;
71
+ pendingUpdate();
72
+ }
73
+ wxRuntimeGlobal.__VITE_PLUGIN_TARO_WX_UPDATE_CLIENT__?.routeReady();
74
+ }
75
+ function getActiveTaroRoot() {
76
+ const page = pageRuntimeState.activePage;
77
+ if (!page?.$taroPath)
78
+ return;
79
+ return document.getElementById(page.$taroPath) ?? undefined;
80
+ }
81
+ bridge.enqueueRefresh = () => {
82
+ validateRefreshBoundaryAndEnqueueUpdate('vite-plugin-taro-wx', refreshBoundary, refreshBoundary);
83
+ };
84
+ bridge.beginUpdate = () => {
85
+ bridge.blockRefreshRegistration = false;
86
+ wxRuntimeGlobal.__rolldown_runtime__?.beginPatch?.();
87
+ dispatchPageRuntime({ type: 'update-started', root: getActiveTaroRoot() });
88
+ refreshTaroRoot(pageRuntimeState.pendingUpdate?.root, pageRuntimeState.pendingUpdate?.page);
89
+ runInNextNativeTask(() => dispatchPageRuntime({ type: 'suppression-ended' }));
90
+ };
91
+ bridge.endUpdate = () => {
92
+ wxRuntimeGlobal.__rolldown_runtime__?.endPatch?.();
93
+ bridge.blockRefreshRegistration = true;
94
+ bridge.enqueueRefresh?.();
95
+ };
96
+ bridge.afterRefresh = (update) => {
97
+ bridge.blockRefreshRegistration = false;
98
+ const pendingUpdate = pageRuntimeState.pendingUpdate;
99
+ dispatchPageRuntime({ type: 'refresh-finished' });
100
+ const page = pendingUpdate?.page;
101
+ const root = pendingUpdate?.root;
102
+ const stale = Boolean(update?.staleFamilies?.size);
103
+ if (stale) {
104
+ wxRuntimeGlobal.__VITE_PLUGIN_TARO_WX_UPDATE_CLIENT__?.refreshCompleted(true);
105
+ relaunchActiveRoute(page);
106
+ return;
107
+ }
108
+ runInNextNativeTask(() => {
109
+ refreshTaroRoot(root, pageRuntimeState.activePage ?? page);
110
+ wxRuntimeGlobal.__VITE_PLUGIN_TARO_WX_UPDATE_CLIENT__?.refreshCompleted(false);
111
+ });
112
+ };
113
+ function dispatchPageRuntime(event) {
114
+ pageRuntimeState = transitionPageRuntime(pageRuntimeState, event);
115
+ }
116
+ function transitionPageRuntime(state, event) {
117
+ switch (event.type) {
118
+ case 'page-activated':
119
+ return { ...state, activePage: event.page };
120
+ case 'page-unloaded':
121
+ return state.activePage === event.page ? { ...state, activePage: undefined } : state;
122
+ case 'update-started':
123
+ return {
124
+ ...state,
125
+ pendingUpdate: { page: state.activePage, root: event.root },
126
+ suppressLifecycles: true
127
+ };
128
+ case 'suppression-ended':
129
+ return { ...state, suppressLifecycles: false };
130
+ case 'refresh-finished':
131
+ return { ...state, pendingUpdate: undefined };
132
+ }
133
+ }
134
+ function refreshTaroRoot(root, page) {
135
+ if (!root)
136
+ return;
137
+ root.ctx = page ?? null;
138
+ root.updateChildNodes();
139
+ root.performUpdate(true);
140
+ }
141
+ function relaunchActiveRoute(page) {
142
+ const route = wxRuntimeGlobal.getCurrentPages().at(-1)?.route;
143
+ if (!route)
144
+ return;
145
+ const query = Object.entries(page?.$taroParams ?? {})
146
+ .filter(([key, value]) => key !== '$taroTimestamp' && value !== undefined)
147
+ .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
148
+ .join('&');
149
+ runInNextNativeTask(() => wxRuntimeGlobal.wx.reLaunch({ url: `/${route}${query ? `?${query}` : ''}` }));
150
+ }
151
+ /**
152
+ * DevTools dispatches synthetic page lifecycles after the changed module returns. A macrotask—not a microtask—runs
153
+ * after that native turn and also avoids navigation or Taro root mutation from inside React Refresh's callback.
154
+ */
155
+ function runInNextNativeTask(task) {
156
+ setTimeout(task);
157
+ }
@@ -1,3 +1,4 @@
1
+ /** Runtime exports consumed by generated WX App, page, and recursive-component entries. */
1
2
  import '@tarojs/plugin-platform-weapp/dist/runtime.js';
2
3
  export { createReactApp } from '@tarojs/plugin-framework-react/dist/runtime';
3
4
  export { default as ReactDOM } from '@tarojs/react';
@@ -1,3 +1,4 @@
1
+ /** Runtime exports consumed by generated WX App, page, and recursive-component entries. */
1
2
  import '@tarojs/plugin-platform-weapp/dist/runtime.js';
2
3
  // @ts-expect-error Taro exposes createReactApp from this runtime-only deep entry without types.
3
4
  export { createReactApp } from '@tarojs/plugin-framework-react/dist/runtime';
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Pure App-side stop-and-wait protocol state machine.
3
+ *
4
+ * It knows build versions and sequencing only; wx.request, literal code execution, and page lifecycle effects live in
5
+ * update-client.ts and page-update.ts.
6
+ */
7
+ type WxUpdateClientPhase = 'stopped' | 'registering' | 'polling' | 'applying' | 'refreshing' | 'relaunching';
8
+ type WxUpdateClientState = {
9
+ buildId: string;
10
+ version: number;
11
+ phase: WxUpdateClientPhase;
12
+ targetVersion?: number;
13
+ pendingBatch?: {
14
+ fromVersion: number;
15
+ targetVersion: number;
16
+ };
17
+ };
18
+ export type WxUpdateClientEvent = {
19
+ type: 'started';
20
+ } | {
21
+ type: 'registration-completed';
22
+ } | {
23
+ type: 'poll-completed';
24
+ } | {
25
+ type: 'batch-observed';
26
+ buildId: string;
27
+ fromVersion: number;
28
+ targetVersion: number;
29
+ } | {
30
+ type: 'batch-executed';
31
+ targetVersion: number;
32
+ } | {
33
+ type: 'batch-failed';
34
+ } | {
35
+ type: 'refresh-completed';
36
+ stale: boolean;
37
+ } | {
38
+ type: 'route-ready';
39
+ } | {
40
+ type: 'transport-failed';
41
+ };
42
+ export type WxUpdateClientCommand = {
43
+ type: 'register';
44
+ version: number;
45
+ } | {
46
+ type: 'poll';
47
+ version: number;
48
+ } | {
49
+ type: 'apply-batch';
50
+ fromVersion: number;
51
+ targetVersion: number;
52
+ } | {
53
+ type: 'report-version';
54
+ version: number;
55
+ reason: 'applied' | 'batch-mismatch';
56
+ } | {
57
+ type: 'request-full-build';
58
+ reason: 'batch-execution-failed';
59
+ } | {
60
+ type: 'retry-transport';
61
+ };
62
+ type WxUpdateClientTransition = {
63
+ state: WxUpdateClientState;
64
+ commands: WxUpdateClientCommand[];
65
+ };
66
+ export declare function createWxUpdateClientState(buildId: string): WxUpdateClientState;
67
+ export declare function transitionWxUpdateClient(state: WxUpdateClientState, event: WxUpdateClientEvent): WxUpdateClientTransition;
68
+ export {};
@@ -0,0 +1,84 @@
1
+ export function createWxUpdateClientState(buildId) {
2
+ return { buildId, version: 0, phase: 'stopped' };
3
+ }
4
+ export function transitionWxUpdateClient(state, event) {
5
+ switch (event.type) {
6
+ case 'started':
7
+ if (state.phase !== 'stopped')
8
+ return transition(state);
9
+ return command({ ...state, phase: 'registering' }, { type: 'register', version: state.version });
10
+ case 'registration-completed': {
11
+ if (state.phase !== 'registering')
12
+ return transition(state);
13
+ const pending = state.pendingBatch;
14
+ if (!pending)
15
+ return command({ ...state, phase: 'polling' }, { type: 'poll', version: state.version });
16
+ return command({ ...state, phase: 'applying', targetVersion: pending.targetVersion, pendingBatch: undefined }, { type: 'apply-batch', ...pending });
17
+ }
18
+ case 'poll-completed':
19
+ if (state.phase !== 'polling')
20
+ return transition(state);
21
+ return command(state, { type: 'poll', version: state.version });
22
+ case 'batch-observed':
23
+ return observeBatch(state, event);
24
+ case 'batch-executed':
25
+ if (state.phase !== 'applying' || state.targetVersion !== event.targetVersion) {
26
+ return command(state, { type: 'report-version', version: state.version, reason: 'batch-mismatch' });
27
+ }
28
+ return transition({ ...state, phase: 'refreshing', version: event.targetVersion });
29
+ case 'batch-failed':
30
+ return command({ ...state, phase: 'polling', targetVersion: undefined }, { type: 'request-full-build', reason: 'batch-execution-failed' });
31
+ case 'refresh-completed':
32
+ if (state.phase !== 'refreshing')
33
+ return transition(state);
34
+ if (event.stale)
35
+ return transition({ ...state, phase: 'relaunching' });
36
+ return applied(state);
37
+ case 'route-ready':
38
+ if (state.phase !== 'relaunching')
39
+ return transition(state);
40
+ return applied(state);
41
+ case 'transport-failed':
42
+ return command(state, { type: 'retry-transport' });
43
+ }
44
+ }
45
+ function observeBatch(state, batch) {
46
+ const valid = batch.buildId === state.buildId &&
47
+ Number.isInteger(batch.fromVersion) &&
48
+ Number.isInteger(batch.targetVersion) &&
49
+ batch.fromVersion >= 0 &&
50
+ batch.targetVersion > batch.fromVersion;
51
+ if (state.phase === 'registering') {
52
+ if (!valid || batch.fromVersion !== state.version || batch.targetVersion <= state.version) {
53
+ return transition(state);
54
+ }
55
+ return transition({
56
+ ...state,
57
+ pendingBatch: { fromVersion: batch.fromVersion, targetVersion: batch.targetVersion }
58
+ });
59
+ }
60
+ if (!valid) {
61
+ return command(state, { type: 'report-version', version: state.version, reason: 'batch-mismatch' });
62
+ }
63
+ if (batch.targetVersion <= state.version) {
64
+ return command(state, { type: 'report-version', version: state.version, reason: 'applied' });
65
+ }
66
+ if (state.phase !== 'polling' || batch.fromVersion !== state.version) {
67
+ return command(state, { type: 'report-version', version: state.version, reason: 'batch-mismatch' });
68
+ }
69
+ return command({ ...state, phase: 'applying', targetVersion: batch.targetVersion }, {
70
+ type: 'apply-batch',
71
+ fromVersion: batch.fromVersion,
72
+ targetVersion: batch.targetVersion
73
+ });
74
+ }
75
+ function applied(state) {
76
+ const appliedState = { ...state, phase: 'polling', targetVersion: undefined };
77
+ return command(appliedState, { type: 'report-version', version: state.version, reason: 'applied' });
78
+ }
79
+ function transition(state) {
80
+ return { state, commands: [] };
81
+ }
82
+ function command(state, value) {
83
+ return { state, commands: [value] };
84
+ }
@@ -0,0 +1 @@
1
+ export declare function startWxUpdateClient(): void;
@@ -0,0 +1,170 @@
1
+ /**
2
+ * App-owned adapter for the WX update protocol.
3
+ *
4
+ * It transports metadata with wx.request and executes literal batches delivered through update.js. React/Taro page
5
+ * preservation is delegated to page-update.ts so the App never depends on the mutable update file.
6
+ */
7
+ import { createWxUpdateClientState, transitionWxUpdateClient } from './update-client-state.js';
8
+ const wxClientGlobal = globalThis;
9
+ const transportRetryDelay = 500;
10
+ const rebuildPollDelay = 1_000;
11
+ const updateFileWatchdogDelay = 2_000;
12
+ export function startWxUpdateClient() {
13
+ if (wxClientGlobal.__VITE_PLUGIN_TARO_WX_UPDATE_CLIENT__)
14
+ return;
15
+ const control = wxClientGlobal.__VITE_PLUGIN_TARO_WX_CONTROL__;
16
+ if (!control)
17
+ throw new Error('vite-plugin-taro could not find its WX update control configuration.');
18
+ let state = createWxUpdateClientState(control.buildId);
19
+ const sessionId = createSessionId();
20
+ let requestGeneration = 0;
21
+ let activeRequest;
22
+ let scheduledRequest;
23
+ let pendingBatchApply;
24
+ const dispatch = (event, apply) => {
25
+ const transition = transitionWxUpdateClient(state, event);
26
+ state = transition.state;
27
+ for (const command of transition.commands)
28
+ execute(command, apply);
29
+ };
30
+ const send = (action, version) => {
31
+ if (scheduledRequest)
32
+ clearTimeout(scheduledRequest);
33
+ scheduledRequest = undefined;
34
+ activeRequest?.abort();
35
+ const generation = ++requestGeneration;
36
+ activeRequest = wxClientGlobal.wx.request({
37
+ url: control.url,
38
+ method: 'POST',
39
+ data: {
40
+ token: control.token,
41
+ action,
42
+ buildId: state.buildId,
43
+ sessionId,
44
+ version
45
+ },
46
+ timeout: 30_000,
47
+ success(result) {
48
+ if (generation !== requestGeneration)
49
+ return;
50
+ activeRequest = undefined;
51
+ const response = parseResponse(result.data);
52
+ if (action === 'register' && response.type === 'registered') {
53
+ dispatch({ type: 'registration-completed' });
54
+ return;
55
+ }
56
+ if (action === 'poll' && (response.type === 'idle' || response.type === 'changed')) {
57
+ dispatch({ type: 'poll-completed' });
58
+ return;
59
+ }
60
+ if (response.type === 'batch-published') {
61
+ // If DevTools misses the update.js event, report the unchanged version and republish with a new nonce.
62
+ scheduleRequest(updateFileWatchdogDelay);
63
+ return;
64
+ }
65
+ if (response.type === 'rebuilding') {
66
+ scheduleRequest(rebuildPollDelay);
67
+ return;
68
+ }
69
+ dispatch({ type: 'transport-failed' });
70
+ },
71
+ fail() {
72
+ if (generation !== requestGeneration)
73
+ return;
74
+ activeRequest = undefined;
75
+ dispatch({ type: 'transport-failed' });
76
+ }
77
+ });
78
+ };
79
+ /** Keeps exactly one delayed metadata request and discards it when protocol state advances. */
80
+ const scheduleRequest = (delay) => {
81
+ if (scheduledRequest)
82
+ clearTimeout(scheduledRequest);
83
+ const expectedPhase = state.phase;
84
+ const expectedVersion = state.version;
85
+ scheduledRequest = setTimeout(() => {
86
+ scheduledRequest = undefined;
87
+ if (state.phase !== expectedPhase || state.version !== expectedVersion)
88
+ return;
89
+ send(state.phase === 'registering' ? 'register' : 'poll', state.version);
90
+ }, delay);
91
+ };
92
+ const execute = (command, apply) => {
93
+ switch (command.type) {
94
+ case 'register':
95
+ send('register', command.version);
96
+ return;
97
+ case 'poll':
98
+ case 'report-version':
99
+ send('poll', command.version);
100
+ return;
101
+ case 'apply-batch': {
102
+ const batchApply = apply ?? pendingBatchApply;
103
+ pendingBatchApply = undefined;
104
+ if (!batchApply) {
105
+ dispatch({ type: 'batch-failed' });
106
+ return;
107
+ }
108
+ const applyUpdate = () => {
109
+ const bridge = wxClientGlobal.__VITE_PLUGIN_TARO_WX_PAGE_UPDATE__;
110
+ if (!bridge) {
111
+ dispatch({ type: 'batch-failed' });
112
+ return;
113
+ }
114
+ bridge.beginUpdate?.();
115
+ try {
116
+ batchApply();
117
+ dispatch({ type: 'batch-executed', targetVersion: command.targetVersion });
118
+ }
119
+ catch {
120
+ dispatch({ type: 'batch-failed' });
121
+ }
122
+ finally {
123
+ bridge.endUpdate?.();
124
+ }
125
+ };
126
+ const bridge = wxClientGlobal.__VITE_PLUGIN_TARO_WX_PAGE_UPDATE__;
127
+ if (bridge?.ready)
128
+ applyUpdate();
129
+ else if (bridge)
130
+ bridge.pendingUpdate = applyUpdate;
131
+ else
132
+ dispatch({ type: 'batch-failed' });
133
+ return;
134
+ }
135
+ case 'request-full-build':
136
+ send('rebuild', state.version);
137
+ return;
138
+ case 'retry-transport':
139
+ scheduleRequest(transportRetryDelay);
140
+ }
141
+ };
142
+ wxClientGlobal.__VITE_PLUGIN_TARO_WX_UPDATE_CLIENT__ = {
143
+ receiveBatch(metadata, apply) {
144
+ dispatch({ type: 'batch-observed', ...metadata }, apply);
145
+ if (state.pendingBatch?.fromVersion === metadata.fromVersion &&
146
+ state.pendingBatch.targetVersion === metadata.targetVersion) {
147
+ pendingBatchApply = apply;
148
+ }
149
+ else if (!state.pendingBatch && state.phase !== 'applying') {
150
+ pendingBatchApply = undefined;
151
+ }
152
+ },
153
+ refreshCompleted(stale) {
154
+ dispatch({ type: 'refresh-completed', stale });
155
+ },
156
+ routeReady() {
157
+ dispatch({ type: 'route-ready' });
158
+ }
159
+ };
160
+ dispatch({ type: 'started' });
161
+ }
162
+ function createSessionId() {
163
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
164
+ }
165
+ function parseResponse(value) {
166
+ if (typeof value !== 'object' || value === null || !('type' in value) || typeof value.type !== 'string') {
167
+ return { type: 'invalid' };
168
+ }
169
+ return { type: value.type };
170
+ }
package/dist/vite.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export type { VitePluginTaroOptions, VitePluginTaroPageOption, VitePluginTaroTarget } from './vite/types.ts';
2
- export { default } from './vite/vite-plugin-taro.ts';
1
+ export { default } from './node/vite-plugin.ts';
2
+ export type { VitePluginTaroOptions, VitePluginTaroPageOption, VitePluginTaroTarget } from './options.ts';
package/dist/vite.js CHANGED
@@ -1 +1 @@
1
- export { default } from './vite/vite-plugin-taro.js';
1
+ export { default } from './node/vite-plugin.js';