vite-plugin-taro 0.6.2 → 0.6.3
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.en.md +6 -1
- package/README.md +6 -1
- package/dist/node/plugins/wx/dev/plugins.d.ts +2 -12
- package/dist/node/plugins/wx/dev/plugins.js +12 -66
- package/dist/node/plugins/wx/dev/react-refresh.d.ts +9 -3
- package/dist/node/plugins/wx/dev/react-refresh.js +35 -7
- package/dist/node/plugins/wx/dev/wx-dev-options.js +2 -3
- package/dist/node/plugins/wx/resolve/resolver.js +4 -4
- package/dist/node/plugins/wx/resolve/{specialize-bootstrap.d.ts → specialize-app-capsule.d.ts} +2 -2
- package/dist/node/plugins/wx/resolve/{specialize-bootstrap.js → specialize-app-capsule.js} +2 -2
- package/dist/runtime/wx/amphibious/bootstrap.d.ts +0 -2
- package/dist/runtime/wx/amphibious/bootstrap.js +0 -2
- package/dist/runtime/wx/capsule/app.d.ts +2 -2
- package/dist/runtime/wx/capsule/app.js +2 -2
- package/dist/runtime/wx/dev/dev-runtime.js +55 -79
- package/package.json +3 -3
- package/src/node/plugins/wx/dev/plugins.ts +12 -69
- package/src/node/plugins/wx/dev/react-refresh.ts +36 -7
- package/src/node/plugins/wx/dev/wx-dev-options.ts +2 -3
- package/src/node/plugins/wx/resolve/resolver.ts +4 -4
- package/src/node/plugins/wx/resolve/{specialize-bootstrap.ts → specialize-app-capsule.ts} +2 -2
- package/src/runtime/wx/amphibious/bootstrap.ts +0 -5
- package/src/runtime/wx/capsule/app.ts +5 -2
- package/src/runtime/wx/dev/dev-runtime.ts +71 -120
- package/src/runtime/wx/wechat.d.ts +1 -2
package/README.en.md
CHANGED
|
@@ -31,12 +31,17 @@ Continue with the [Quick Start guide](https://vpt.js.org/guides/quick-start/).
|
|
|
31
31
|
|
|
32
32
|
## Documentation
|
|
33
33
|
|
|
34
|
+
- [Quick Start](https://vpt.js.org/guides/quick-start/)
|
|
35
|
+
- [Styles](https://vpt.js.org/guides/styles/)
|
|
34
36
|
- [Automatic subpackages](https://vpt.js.org/guides/automatic-subpackages/)
|
|
35
37
|
- [Native WeChat components](https://vpt.js.org/guides/native-components/)
|
|
36
38
|
- [Hot module replacement](https://vpt.js.org/guides/hot-module-replacement/)
|
|
39
|
+
- [Configuration options](https://vpt.js.org/guides/configuration/)
|
|
40
|
+
- [Conditional compilation](https://vpt.js.org/guides/conditional-directives/)
|
|
37
41
|
- [Skyline mode](https://vpt.js.org/guides/skyline-mode/)
|
|
38
42
|
- [Migrate from Taro CLI](https://vpt.js.org/guides/migrate-from-taro/)
|
|
39
|
-
- [
|
|
43
|
+
- [Module system](https://vpt.js.org/references/module-system/)
|
|
44
|
+
- [Hot module replacement internals](https://vpt.js.org/references/hmr-implementation/)
|
|
40
45
|
- [Repository management](https://vpt.js.org/references/repository-management/)
|
|
41
46
|
|
|
42
47
|
## License
|
package/README.md
CHANGED
|
@@ -32,12 +32,17 @@ npm create vite-taro@latest my-app
|
|
|
32
32
|
|
|
33
33
|
## 文档
|
|
34
34
|
|
|
35
|
+
- [快速开始](https://vpt.js.org/guides/quick-start/)
|
|
36
|
+
- [样式](https://vpt.js.org/guides/styles/)
|
|
35
37
|
- [全自动分包](https://vpt.js.org/guides/automatic-subpackages/)
|
|
36
38
|
- [微信原生组件](https://vpt.js.org/guides/native-components/)
|
|
37
39
|
- [开发者工具热更新](https://vpt.js.org/guides/hot-module-replacement/)
|
|
40
|
+
- [配置选项](https://vpt.js.org/guides/configuration/)
|
|
41
|
+
- [条件编译](https://vpt.js.org/guides/conditional-directives/)
|
|
38
42
|
- [Skyline 模式](https://vpt.js.org/guides/skyline-mode/)
|
|
39
43
|
- [从 Taro CLI 迁移](https://vpt.js.org/guides/migrate-from-taro/)
|
|
40
|
-
- [
|
|
44
|
+
- [模块系统](https://vpt.js.org/references/module-system/)
|
|
45
|
+
- [热更新原理](https://vpt.js.org/references/hmr-implementation/)
|
|
41
46
|
- [仓库维护](https://vpt.js.org/references/repository-management/)
|
|
42
47
|
|
|
43
48
|
## 许可证
|
|
@@ -28,18 +28,8 @@ export declare function createWxDevelopmentPlugin(options: VptOptions, applicati
|
|
|
28
28
|
* only `assets/global.wxss`, which preserves the App heap. The serve-only plugin leaves production output unchanged.
|
|
29
29
|
*/
|
|
30
30
|
export declare function removeDevelopmentAppWxss(bundle: Record<string, unknown>): void;
|
|
31
|
-
/**
|
|
32
|
-
export declare function
|
|
33
|
-
code: string;
|
|
34
|
-
map: null;
|
|
35
|
-
};
|
|
36
|
-
/** Connects the shared WX dev runtime to the application graph's Taro runtime instance. */
|
|
37
|
-
export declare function injectTaroConnection(code: string): {
|
|
38
|
-
code: string;
|
|
39
|
-
map: null;
|
|
40
|
-
};
|
|
41
|
-
/** Activates development-only lifecycle handling for one plugin-owned Page capsule. */
|
|
42
|
-
export declare function injectPageHmr(code: string, route: string): {
|
|
31
|
+
/** Injects the retained snapshot at the exact native Page registration edge. */
|
|
32
|
+
export declare function injectPageShellHmr(code: string): {
|
|
43
33
|
code: string;
|
|
44
34
|
map: null;
|
|
45
35
|
};
|
|
@@ -2,11 +2,10 @@ import { normalizePath, transformWithOxc } from 'vite';
|
|
|
2
2
|
import { esTarget } from '../../../utils/constant.js';
|
|
3
3
|
import { memoize } from '../../../utils/memoize.js';
|
|
4
4
|
import { normalizeModuleId } from '../../../utils/modules.js';
|
|
5
|
-
import {
|
|
5
|
+
import { pageShellPath, rolldownRuntimeId } from '../module/module.js';
|
|
6
6
|
import { createWxDevHost } from './dev-host.js';
|
|
7
7
|
import { developmentAppWxssFileName } from './hmr-files.js';
|
|
8
8
|
import { createWxReactRefreshTransforms } from './react-refresh.js';
|
|
9
|
-
const taroRuntimeId = '@tarojs/runtime';
|
|
10
9
|
/** Selects the sole Vite environment that owns the physical Mini Program development project. */
|
|
11
10
|
export function isWxClientEnvironment(environment) {
|
|
12
11
|
return environment.name === 'client';
|
|
@@ -28,9 +27,7 @@ export function createWxDevelopmentPlugin(options, applicationEntryIds) {
|
|
|
28
27
|
*/
|
|
29
28
|
let host = null;
|
|
30
29
|
// Portable hook filters stay broad; these exact identities exclude similarly named user modules.
|
|
31
|
-
const
|
|
32
|
-
const normalizedPageCapsulePath = normalizePath(pageCapsulePath);
|
|
33
|
-
const normalizedTaroRuntimePath = normalizePath(taroRuntimePath);
|
|
30
|
+
const normalizedPageShellPath = normalizePath(pageShellPath);
|
|
34
31
|
return [
|
|
35
32
|
{
|
|
36
33
|
name: 'vpt:wx-dev',
|
|
@@ -107,41 +104,15 @@ export function createWxDevelopmentPlugin(options, applicationEntryIds) {
|
|
|
107
104
|
}
|
|
108
105
|
},
|
|
109
106
|
{
|
|
110
|
-
name: 'vpt:wx-
|
|
107
|
+
name: 'vpt:wx-page-shell-hmr',
|
|
111
108
|
apply: 'serve',
|
|
112
109
|
transform: {
|
|
113
110
|
order: 'post',
|
|
114
|
-
filter: { id: /\/runtime\/wx\/
|
|
111
|
+
filter: { id: /\/runtime\/wx\/native\/page\.js(?:\?|$)/ },
|
|
115
112
|
handler(code, id) {
|
|
116
|
-
if (normalizeModuleId(id) !==
|
|
113
|
+
if (normalizeModuleId(id) !== normalizedPageShellPath)
|
|
117
114
|
return;
|
|
118
|
-
return
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
name: 'vpt:wx-page-hmr',
|
|
124
|
-
apply: 'serve',
|
|
125
|
-
transform: {
|
|
126
|
-
order: 'post',
|
|
127
|
-
filter: { id: /\/runtime\/wx\/capsule\/page\.js(?:\?|$)/ },
|
|
128
|
-
handler(code, id) {
|
|
129
|
-
if (normalizeModuleId(id) !== normalizedPageCapsulePath)
|
|
130
|
-
return;
|
|
131
|
-
return injectPageHmr(code, getPageRoute(id));
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
name: 'vpt:wx-taro-hmr',
|
|
137
|
-
apply: 'serve',
|
|
138
|
-
transform: {
|
|
139
|
-
order: 'post',
|
|
140
|
-
filter: { id: /\/runtime\/wx\/capsule\/taro-runtime\.js(?:\?|$)/ },
|
|
141
|
-
handler(code, id) {
|
|
142
|
-
if (normalizeModuleId(id) !== normalizedTaroRuntimePath)
|
|
143
|
-
return;
|
|
144
|
-
return injectTaroConnection(code);
|
|
115
|
+
return injectPageShellHmr(code);
|
|
145
116
|
}
|
|
146
117
|
}
|
|
147
118
|
},
|
|
@@ -166,42 +137,17 @@ export function createWxDevelopmentPlugin(options, applicationEntryIds) {
|
|
|
166
137
|
export function removeDevelopmentAppWxss(bundle) {
|
|
167
138
|
delete bundle[developmentAppWxssFileName];
|
|
168
139
|
}
|
|
169
|
-
/**
|
|
170
|
-
export function
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
/** Connects the shared WX dev runtime to the application graph's Taro runtime instance. */
|
|
177
|
-
export function injectTaroConnection(code) {
|
|
178
|
-
if (!/\bCurrent\b/.test(code) || !/\bdocument\b/.test(code) || !/\binjectPageInstance\b/.test(code)) {
|
|
179
|
-
throw new Error('WX Taro runtime must expose Current, document, and injectPageInstance for HMR');
|
|
140
|
+
/** Injects the retained snapshot at the exact native Page registration edge. */
|
|
141
|
+
export function injectPageShellHmr(code) {
|
|
142
|
+
const registration = 'Page(pageConfig)';
|
|
143
|
+
if (!code.includes(registration)) {
|
|
144
|
+
throw new Error('WX native Page shell must register pageConfig');
|
|
180
145
|
}
|
|
181
|
-
const taroImport = `import { Current as __vptCurrent, document as __vptDocument, injectPageInstance as __vptInjectPageInstance } from ${JSON.stringify(taroRuntimeId)};`;
|
|
182
146
|
return {
|
|
183
|
-
code:
|
|
147
|
+
code: code.replace(registration, 'Page(__rolldown_runtime__.injectPageHmr(pageConfig))'),
|
|
184
148
|
map: null
|
|
185
149
|
};
|
|
186
150
|
}
|
|
187
|
-
/** Activates development-only lifecycle handling for one plugin-owned Page capsule. */
|
|
188
|
-
export function injectPageHmr(code, route) {
|
|
189
|
-
if (!/\bconst\s+config\s*=/.test(code) || !/\bexport\s+default\s+config\b/.test(code)) {
|
|
190
|
-
throw new Error('WX Page capsule must declare and default-export config before HMR injection');
|
|
191
|
-
}
|
|
192
|
-
return {
|
|
193
|
-
code: `${code}\n__rolldown_runtime__.injectPageHmr(config, ${JSON.stringify(route)});`,
|
|
194
|
-
map: null
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
/** Reads the stable route carried by every specialized Page capsule ID. */
|
|
198
|
-
function getPageRoute(id) {
|
|
199
|
-
const queryIndex = id.indexOf('?');
|
|
200
|
-
const route = queryIndex < 0 ? null : new URLSearchParams(id.slice(queryIndex + 1)).get('route');
|
|
201
|
-
if (!route)
|
|
202
|
-
throw new Error(`WX Page capsule is missing its route: ${id}`);
|
|
203
|
-
return route;
|
|
204
|
-
}
|
|
205
151
|
/*
|
|
206
152
|
* memoize owns a mutable one-entry-by-input cache. The assembled runtime source is byte-identical across complete generations
|
|
207
153
|
* because both the Rolldown base and injected implementation are immutable for the server lifetime. Reusing its lowered result
|
|
@@ -4,8 +4,9 @@ import type { Plugin } from 'vite';
|
|
|
4
4
|
*
|
|
5
5
|
* @vitejs/plugin-react's generated refresh code assumes the web HTML preamble and a browser
|
|
6
6
|
* global scope; wx has neither. Each transform adapts one piece of that contract:
|
|
7
|
-
* - the refresh runtime module (id-filtered): the
|
|
7
|
+
* - the refresh runtime module (id-filtered): the runtime reads and assigns
|
|
8
8
|
* `window` protocol globals (rewritten to `global`) and must inject itself at evaluation;
|
|
9
|
+
* - React Reconciler: its renderer injection statically depends on the refresh runtime, fixing cold-start order;
|
|
9
10
|
* - react-family modules (filtered on free references): the DevTools hook is read as a free
|
|
10
11
|
* variable, which the WeChat runtime scope never resolves against `global` — every free
|
|
11
12
|
* reference becomes an explicit member access;
|
|
@@ -13,10 +14,15 @@ import type { Plugin } from 'vite';
|
|
|
13
14
|
* needed because the transform generates local wrappers over the imported refresh
|
|
14
15
|
* runtime, so the guard that checks for the global is removed.
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
17
|
+
* Filters route every adaptation directly. The Reconciler intentionally receives the renderer dependency first and the
|
|
18
|
+
* global-hook rewrite second; modules outside these explicit domains never reach a handler.
|
|
18
19
|
*/
|
|
19
20
|
export declare function createWxReactRefreshTransforms(): Plugin[];
|
|
21
|
+
/** Makes renderer hook injection statically depend on the refresh runtime. */
|
|
22
|
+
export declare function injectReactRefreshRendererDependency(code: string): {
|
|
23
|
+
code: string;
|
|
24
|
+
map: null;
|
|
25
|
+
};
|
|
20
26
|
export declare function transformRefreshRuntime({ code, id }: {
|
|
21
27
|
code: string;
|
|
22
28
|
id: string;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { isReferenceIdentifier } from 'oxc-walker';
|
|
2
2
|
import { memoize } from '../../../utils/memoize.js';
|
|
3
|
+
import { normalizeModuleId } from '../../../utils/modules.js';
|
|
3
4
|
import { transformWithOxcWalker } from '../../../utils/oxc-transform.js';
|
|
5
|
+
import { reactReconcilerRoot } from '../module/module.js';
|
|
4
6
|
/** The React DevTools hook protocol name; free references must target `global` in wx. */
|
|
5
7
|
const reactDevtoolsHookProtocol = '__REACT_DEVTOOLS_GLOBAL_HOOK__';
|
|
6
8
|
/**
|
|
@@ -10,7 +12,7 @@ const reactDevtoolsHookProtocol = '__REACT_DEVTOOLS_GLOBAL_HOOK__';
|
|
|
10
12
|
* - `__getReactRefreshIgnoredExports` is an optional extension point read while validating a refresh boundary.
|
|
11
13
|
* Leaving that read on the nonexistent `window` crashes every update validation pass.
|
|
12
14
|
*
|
|
13
|
-
* Keeping this list explicit prevents the adapter from rewriting unrelated browser accesses if the
|
|
15
|
+
* Keeping this list explicit prevents the adapter from rewriting unrelated browser accesses if the refresh runtime
|
|
14
16
|
* gains new code. Any future React Refresh protocol addition therefore requires a deliberate compatibility decision.
|
|
15
17
|
*/
|
|
16
18
|
const refreshRuntimeWindowGlobals = ['__registerBeforePerformReactRefresh', '__getReactRefreshIgnoredExports'];
|
|
@@ -19,8 +21,9 @@ const refreshRuntimeWindowGlobals = ['__registerBeforePerformReactRefresh', '__g
|
|
|
19
21
|
*
|
|
20
22
|
* @vitejs/plugin-react's generated refresh code assumes the web HTML preamble and a browser
|
|
21
23
|
* global scope; wx has neither. Each transform adapts one piece of that contract:
|
|
22
|
-
* - the refresh runtime module (id-filtered): the
|
|
24
|
+
* - the refresh runtime module (id-filtered): the runtime reads and assigns
|
|
23
25
|
* `window` protocol globals (rewritten to `global`) and must inject itself at evaluation;
|
|
26
|
+
* - React Reconciler: its renderer injection statically depends on the refresh runtime, fixing cold-start order;
|
|
24
27
|
* - react-family modules (filtered on free references): the DevTools hook is read as a free
|
|
25
28
|
* variable, which the WeChat runtime scope never resolves against `global` — every free
|
|
26
29
|
* reference becomes an explicit member access;
|
|
@@ -28,8 +31,8 @@ const refreshRuntimeWindowGlobals = ['__registerBeforePerformReactRefresh', '__g
|
|
|
28
31
|
* needed because the transform generates local wrappers over the imported refresh
|
|
29
32
|
* runtime, so the guard that checks for the global is removed.
|
|
30
33
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
34
|
+
* Filters route every adaptation directly. The Reconciler intentionally receives the renderer dependency first and the
|
|
35
|
+
* global-hook rewrite second; modules outside these explicit domains never reach a handler.
|
|
33
36
|
*/
|
|
34
37
|
export function createWxReactRefreshTransforms() {
|
|
35
38
|
return [
|
|
@@ -38,13 +41,27 @@ export function createWxReactRefreshTransforms() {
|
|
|
38
41
|
apply: 'serve',
|
|
39
42
|
transform: {
|
|
40
43
|
order: 'post',
|
|
41
|
-
// The
|
|
44
|
+
// The refresh runtime module is id-filtered, so no code scan is needed.
|
|
42
45
|
filter: { id: /^\/@react-refresh(?:\?|$)/ },
|
|
43
46
|
handler(code, id) {
|
|
44
47
|
return fixRefreshRuntime({ code, id });
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
},
|
|
51
|
+
{
|
|
52
|
+
name: 'vpt:wx-react-refresh-renderer-dependency',
|
|
53
|
+
apply: 'serve',
|
|
54
|
+
transform: {
|
|
55
|
+
order: 'post',
|
|
56
|
+
filter: { id: /\/react-reconciler\/cjs\/react-reconciler\.development\.js(?:\?|$)/ },
|
|
57
|
+
handler(code, id) {
|
|
58
|
+
const rendererId = `${reactReconcilerRoot}/cjs/react-reconciler.development.js`;
|
|
59
|
+
if (normalizeModuleId(id) !== rendererId)
|
|
60
|
+
return;
|
|
61
|
+
return injectReactRefreshRendererDependency(code);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
48
65
|
{
|
|
49
66
|
name: 'vpt:wx-react-devtools-hook',
|
|
50
67
|
apply: 'serve',
|
|
@@ -86,7 +103,8 @@ export function createWxReactRefreshTransforms() {
|
|
|
86
103
|
* The protocol name is unique, but only reference identifiers are rewritten. Declaration
|
|
87
104
|
* keys and explicit members such as `global.__REACT_DEVTOOLS_GLOBAL_HOOK__` must remain
|
|
88
105
|
* untouched; rewriting those would either produce invalid syntax or double-prefix the hook.
|
|
89
|
-
* The
|
|
106
|
+
* The Reconciler's injected static dependency orders hook injection before renderer initialization independently of Rolldown's
|
|
107
|
+
* final chunking decision.
|
|
90
108
|
*/
|
|
91
109
|
function createReactDevtoolsHookVisitor(editor) {
|
|
92
110
|
return function enter(node, parent) {
|
|
@@ -133,7 +151,7 @@ function createRefreshRuntimeVisitor(editor) {
|
|
|
133
151
|
node.property.type === 'Identifier' &&
|
|
134
152
|
refreshRuntimeWindowGlobals.some((globalName) => globalName === node.property.name)) {
|
|
135
153
|
// `global` is the shared wx App heap used by the dev runtime and hook injection. Only
|
|
136
|
-
// replacing the object range preserves the
|
|
154
|
+
// replacing the object range preserves the upstream runtime byte-for-byte otherwise
|
|
137
155
|
// and prevents unrelated `window` expressions from being silently adapted.
|
|
138
156
|
editor.overwrite(node.object.start, node.object.end, 'global');
|
|
139
157
|
}
|
|
@@ -169,6 +187,16 @@ function createRefreshPreambleGuardVisitor(editor) {
|
|
|
169
187
|
this.skip();
|
|
170
188
|
};
|
|
171
189
|
}
|
|
190
|
+
/** Makes renderer hook injection statically depend on the refresh runtime. */
|
|
191
|
+
export function injectReactRefreshRendererDependency(code) {
|
|
192
|
+
if (!/\bhook\.inject\(internals\)/.test(code)) {
|
|
193
|
+
throw new Error('React Reconciler must inject its renderer into the DevTools hook');
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
code: `import '/@react-refresh'\n${code}`,
|
|
197
|
+
map: null
|
|
198
|
+
};
|
|
199
|
+
}
|
|
172
200
|
export function transformRefreshRuntime({ code, id }) {
|
|
173
201
|
return transformWithOxcWalker({
|
|
174
202
|
code,
|
|
@@ -105,7 +105,7 @@ function ensureSingleOutput(rolldownOptions) {
|
|
|
105
105
|
/**
|
|
106
106
|
* Prepends entry banners after Rolldown's analysis so their native requires remain physical dependencies rather than chunk
|
|
107
107
|
* graph edges. The App initializes the runtime identity, while every Page explicitly applies the watched patch data before its
|
|
108
|
-
*
|
|
108
|
+
* native Page registration.
|
|
109
109
|
*/
|
|
110
110
|
function createEntryBanner(pageFiles) {
|
|
111
111
|
return (chunk) => {
|
|
@@ -114,8 +114,7 @@ function createEntryBanner(pageFiles) {
|
|
|
114
114
|
}
|
|
115
115
|
if (pageFiles.has(chunk.name)) {
|
|
116
116
|
const patchesPath = path.posix.relative(path.posix.dirname(chunk.fileName), 'hmr/patches.js');
|
|
117
|
-
|
|
118
|
-
return `__rolldown_runtime__.applyPatches(require('${patchesPath}'), ${JSON.stringify(route)});\n`;
|
|
117
|
+
return `__rolldown_runtime__.applyPatches(require('${patchesPath}'));\n`;
|
|
119
118
|
}
|
|
120
119
|
return '';
|
|
121
120
|
};
|
|
@@ -3,11 +3,11 @@ import { normalizeModuleId, resolveAppComponentPath, resolvePageComponentPath }
|
|
|
3
3
|
import { createAppConfig } from '../../../utils/project-config.js';
|
|
4
4
|
import { appComponentId } from '../../client/constant.js';
|
|
5
5
|
import { appCapsulePath, appShellFileName, appShellPath, bootstrapPath, componentCapsulePath, componentShellFileName, componentShellPath, pageCapsuleId, pageCapsulePath, pageComponentId, pageShellPath, transportPath, vitePreloadId } from '../module/module.js';
|
|
6
|
-
import {
|
|
6
|
+
import { specializeAppCapsule } from './specialize-app-capsule.js';
|
|
7
7
|
import { specializePageCapsule } from './specialize-page-capsule.js';
|
|
8
8
|
/** Creates the resolver and source specializer for the wx module graph. */
|
|
9
9
|
export function createResolver(options) {
|
|
10
|
-
const
|
|
10
|
+
const normalizedAppCapsulePath = normalizePath(appCapsulePath);
|
|
11
11
|
const normalizedPageCapsulePath = normalizePath(pageCapsulePath);
|
|
12
12
|
// Construct output input and application traversal roots together once so style order cannot drift from route order.
|
|
13
13
|
const entryGraph = createEntryGraph(options.pages);
|
|
@@ -42,8 +42,8 @@ export function createResolver(options) {
|
|
|
42
42
|
},
|
|
43
43
|
specialize(code, id, sourcemap = true) {
|
|
44
44
|
const normalizedId = normalizeModuleId(id);
|
|
45
|
-
if (normalizedId ===
|
|
46
|
-
return
|
|
45
|
+
if (normalizedId === normalizedAppCapsulePath) {
|
|
46
|
+
return specializeAppCapsule({ code, id, appConfig: createAppConfig(options), sourcemap });
|
|
47
47
|
}
|
|
48
48
|
if (normalizedId === normalizedPageCapsulePath) {
|
|
49
49
|
return specializePageCapsule({
|
package/dist/node/plugins/wx/resolve/{specialize-bootstrap.d.ts → specialize-app-capsule.d.ts}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { VptJsonObject } from '../../../../options.ts';
|
|
2
2
|
import { type AstTransformResult } from '../../../utils/transform.ts';
|
|
3
|
-
/** Specializes the
|
|
4
|
-
export declare function
|
|
3
|
+
/** Specializes the App capsule with its normalized native configuration. */
|
|
4
|
+
export declare function specializeAppCapsule({ code, id, appConfig, sourcemap }: {
|
|
5
5
|
code: string;
|
|
6
6
|
id: string;
|
|
7
7
|
appConfig: VptJsonObject;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as types from '@babel/types';
|
|
2
2
|
import { replaceWithAst } from '../../../utils/transform.js';
|
|
3
3
|
const appConfigPlaceholder = '__VPT_APP_CONFIG__';
|
|
4
|
-
/** Specializes the
|
|
5
|
-
export function
|
|
4
|
+
/** Specializes the App capsule with its normalized native configuration. */
|
|
5
|
+
export function specializeAppCapsule({ code, id, appConfig, sourcemap = true }) {
|
|
6
6
|
return replaceWithAst(code, id, {
|
|
7
7
|
[appConfigPlaceholder]: types.valueToNode(appConfig)
|
|
8
8
|
}, sourcemap);
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
// Install the minimal SystemJS loader and its synchronous-import extension before any native entry requests a capsule.
|
|
2
2
|
import '../systemjs/system-core.js';
|
|
3
3
|
import { transport } from './transport.js';
|
|
4
|
-
/** Shares one App configuration object between the specialized bootstrap and App capsule. */
|
|
5
|
-
export const appConfig = __VPT_APP_CONFIG__;
|
|
6
4
|
// WX has no modulepreload transport. Genuine application import() boundaries retain System.import() and may load
|
|
7
5
|
// asynchronous subpackage or top-level-await graphs through this identity wrapper.
|
|
8
6
|
export const __vitePreload = (load) => load();
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const
|
|
2
|
-
export default
|
|
1
|
+
declare const config: any;
|
|
2
|
+
export default config;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// biome-ignore assist/source/organizeImports: Taro must initialize before the App component.
|
|
2
2
|
import { createReactApp, ReactDOM } from './taro-runtime.js';
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import { appConfig } from '../amphibious/bootstrap.js';
|
|
5
4
|
// @ts-expect-error: The wx build resolves this private App component.
|
|
6
5
|
import AppComponent from '\0vpt:app-component';
|
|
7
|
-
|
|
6
|
+
const config = createReactApp(AppComponent, React, ReactDOM, __VPT_APP_CONFIG__);
|
|
7
|
+
export default config;
|
|
@@ -5,16 +5,13 @@
|
|
|
5
5
|
// The `DevRuntime` base class is injected into the chunk by Rolldown's dev-mode
|
|
6
6
|
// transform, so the WX host only extends it.
|
|
7
7
|
//
|
|
8
|
-
// Every Page explicitly passes the inert hmr/patches.js export here before
|
|
8
|
+
// Every Page explicitly passes the inert hmr/patches.js export here before native Page registration. The runtime applies that
|
|
9
9
|
// cumulative suffix synchronously, reports its successful application frontier, and ignores sequences replayed by other Pages.
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const pageHmrStateKey = Symbol('vpt.pageHmrState');
|
|
11
|
+
/** Calls a native lifecycle with the same Page bound to `this` and the same arguments. */
|
|
12
|
+
function forward(handler, page, args) {
|
|
12
13
|
if (typeof handler === 'function')
|
|
13
|
-
handler.apply(
|
|
14
|
-
}
|
|
15
|
-
/** Narrows the untyped element returned across the Taro connection boundary. */
|
|
16
|
-
function isTaroRoot(value) {
|
|
17
|
-
return typeof value === 'object' && value !== null && 'ctx' in value;
|
|
14
|
+
handler.apply(page, args);
|
|
18
15
|
}
|
|
19
16
|
/** Shared no-op CSS contract because physical rebuilds replace styles wholesale. */
|
|
20
17
|
const hotContextInternals = Object.freeze({
|
|
@@ -168,92 +165,76 @@ class WxDevRuntime extends DevRuntime {
|
|
|
168
165
|
return;
|
|
169
166
|
this.session = { ...info, appliedSeq: 0 };
|
|
170
167
|
}
|
|
171
|
-
/**
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
168
|
+
/** Tracks the mounted native Page and prepares its static config for HMR re-registration. */
|
|
169
|
+
injectPageHmr(config) {
|
|
170
|
+
const existingState = config[pageHmrStateKey];
|
|
171
|
+
if (existingState) {
|
|
172
|
+
const mountedPage = existingState.mountedPage;
|
|
173
|
+
/*
|
|
174
|
+
* The static config can outlive a native Page instance. Before its first ordinary onLoad, or after a real onUnload,
|
|
175
|
+
* there is no mounted Page or current view-model to carry into another registration. Leave the lifecycle gate
|
|
176
|
+
* unarmed and preserve the config's existing initial data so a future real onLoad still enters Taro normally.
|
|
177
|
+
*/
|
|
178
|
+
if (!mountedPage) {
|
|
179
|
+
return config;
|
|
180
|
+
}
|
|
181
|
+
/*
|
|
182
|
+
* Arm the lifecycle wrappers on this exact static config before it is passed back to `Page(config)`. DevTools then
|
|
183
|
+
* triggers an unload/load/show sequence for that native re-registration: unload and load observe `true` and return
|
|
184
|
+
* before entering Taro, preserving the mounted React tree and its original Page connection; show consumes the
|
|
185
|
+
* one-shot gate by restoring `false`. Ordinary navigation never enters this branch, and every Page config owns an
|
|
186
|
+
* independent state object, so no route map, global phase, or Page identity comparison participates in the decision.
|
|
187
|
+
*/
|
|
188
|
+
existingState.isReregistering = true;
|
|
189
|
+
/*
|
|
190
|
+
* `Page(config)` reads `config.data` as the initial native view-model for this registration. Supplying the mounted
|
|
191
|
+
* Page's latest data before that call prevents the temporary Page used for re-registration callbacks from starting
|
|
192
|
+
* empty. This is an O(1) reference assignment in vpt: it does not clone the recursive data tree, call `setData`, move
|
|
193
|
+
* React state, or rebind Taro. The ordinary Taro lifecycle remains suppressed until re-registration onShow, so its
|
|
194
|
+
* React tree and output connection stay attached to `mountedPage`; every later registration reads its latest data.
|
|
195
|
+
*/
|
|
196
|
+
config.data = mountedPage.data;
|
|
197
|
+
return config;
|
|
181
198
|
}
|
|
182
|
-
this.taro = {
|
|
183
|
-
current,
|
|
184
|
-
document,
|
|
185
|
-
injectPageInstance,
|
|
186
|
-
pageReplacements: new Map()
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
/** Injects snapshot-preserving behavior into one route-specific Taro Page configuration. */
|
|
190
|
-
injectPageHmr(config, route) {
|
|
191
199
|
const originalOnUnload = config.onUnload;
|
|
192
200
|
const originalOnLoad = config.onLoad;
|
|
193
201
|
const originalOnShow = config.onShow;
|
|
194
|
-
const
|
|
202
|
+
const state = {
|
|
203
|
+
isReregistering: false,
|
|
204
|
+
mountedPage: undefined
|
|
205
|
+
};
|
|
206
|
+
/*
|
|
207
|
+
* Attach the one mutable HMR state object to this exact static config. The lifecycle wrappers below close over the same
|
|
208
|
+
* object, while a later `injectPageHmr(config)` call finds it through the symbol and knows wrapping is already complete.
|
|
209
|
+
* A symbol cannot collide with WeChat or Taro's string-named Page options, and config-local ownership avoids route maps,
|
|
210
|
+
* a runtime-wide WeakMap, or state shared by two Page configs. The state becomes unreachable together with the config.
|
|
211
|
+
*/
|
|
212
|
+
config[pageHmrStateKey] = state;
|
|
195
213
|
config.onUnload = function (...args) {
|
|
196
|
-
|
|
197
|
-
if (taro.pageReplacements.has(route)) {
|
|
198
|
-
taro.pageReplacements.set(route, {
|
|
199
|
-
$taroPath: this.$taroPath,
|
|
200
|
-
$taroParams: this.$taroParams,
|
|
201
|
-
// WeChat owns this serializable view-model. Keeping its reference is O(1),
|
|
202
|
-
// unlike cloning the complete recursive projection before every edit.
|
|
203
|
-
data: this.data
|
|
204
|
-
});
|
|
214
|
+
if (state.isReregistering) {
|
|
205
215
|
return;
|
|
206
216
|
}
|
|
207
217
|
forward(originalOnUnload, this, args);
|
|
218
|
+
state.mountedPage = undefined;
|
|
208
219
|
};
|
|
209
220
|
config.onLoad = function (...args) {
|
|
210
|
-
|
|
211
|
-
const snapshot = taro.pageReplacements.get(route);
|
|
212
|
-
if (snapshot) {
|
|
213
|
-
// Replace the transaction before native work so exceptions cannot retain the
|
|
214
|
-
// large data snapshot while the route waits for its synthetic onShow.
|
|
215
|
-
taro.pageReplacements.set(route, null);
|
|
216
|
-
// Snapshot paint is the first bridge operation and removes the empty-page gap.
|
|
217
|
-
this.setData(snapshot.data);
|
|
218
|
-
this.$taroPath = snapshot.$taroPath;
|
|
219
|
-
this.$taroParams = snapshot.$taroParams;
|
|
220
|
-
runtime.bindPage(this, snapshot.$taroPath);
|
|
221
|
+
if (state.isReregistering) {
|
|
221
222
|
return;
|
|
222
223
|
}
|
|
223
224
|
forward(originalOnLoad, this, args);
|
|
225
|
+
state.mountedPage = this;
|
|
224
226
|
};
|
|
225
227
|
config.onShow = function (...args) {
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
+
if (state.isReregistering) {
|
|
229
|
+
state.isReregistering = false;
|
|
228
230
|
return;
|
|
229
231
|
}
|
|
230
232
|
forward(originalOnShow, this, args);
|
|
231
233
|
};
|
|
234
|
+
return config;
|
|
232
235
|
}
|
|
233
|
-
/**
|
|
234
|
-
|
|
235
|
-
if (!this.taro)
|
|
236
|
-
throw new Error('[vpt] WX HMR used before the Taro runtime was connected');
|
|
237
|
-
return this.taro;
|
|
238
|
-
}
|
|
239
|
-
/** Returns a retained Taro root after normal Page mount has created it. */
|
|
240
|
-
findRoot(path) {
|
|
241
|
-
const root = this.requireTaro().document.getElementById(path);
|
|
242
|
-
return isTaroRoot(root) ? root : undefined;
|
|
243
|
-
}
|
|
244
|
-
/** Rebinds a retained Taro tree to one replacement native Page without repainting. */
|
|
245
|
-
bindPage(instance, path) {
|
|
246
|
-
const taro = this.requireTaro();
|
|
247
|
-
taro.injectPageInstance(instance, path);
|
|
248
|
-
taro.current.page = instance;
|
|
249
|
-
const pageElement = this.findRoot(path);
|
|
250
|
-
if (!pageElement) {
|
|
251
|
-
throw new Error(`[vpt] retained Taro page not found: ${path}`);
|
|
252
|
-
}
|
|
253
|
-
pageElement.ctx = instance;
|
|
254
|
-
}
|
|
255
|
-
/** Applies one Page-delivered payload and arms its route for native replacement. */
|
|
256
|
-
applyPatches(payload, route) {
|
|
236
|
+
/** Applies one Page-delivered payload before its native shell registers the static route configuration. */
|
|
237
|
+
applyPatches(payload) {
|
|
257
238
|
// The initial physical dependency exports undefined until the host has a patch range.
|
|
258
239
|
if (!payload)
|
|
259
240
|
return;
|
|
@@ -262,11 +243,6 @@ class WxDevRuntime extends DevRuntime {
|
|
|
262
243
|
console.warn('[vpt] patches for a stale build');
|
|
263
244
|
return;
|
|
264
245
|
}
|
|
265
|
-
// A replayed payload still causes DevTools to replace this physical Page. Arm the
|
|
266
|
-
// route independently of whether this App heap already applied its patch sequence.
|
|
267
|
-
if (route) {
|
|
268
|
-
this.requireTaro().pageReplacements.set(route, null);
|
|
269
|
-
}
|
|
270
246
|
// Apply synchronously: the page's imports below the require resolve against the
|
|
271
247
|
// freshly registered modules, so the re-executed Page evaluates with the new code.
|
|
272
248
|
if (this.applyPatchBatch(session, payload.patches)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-taro",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"author": "sep2",
|
|
5
5
|
"description": "Vite 8 plugin for building one React/Taro codebase for WeChat Mini Program and H5 targets.",
|
|
6
6
|
"type": "module",
|
|
@@ -75,8 +75,8 @@
|
|
|
75
75
|
"rxjs": "^7.8.2",
|
|
76
76
|
"tailwindcss": "^4.3.3",
|
|
77
77
|
"weapp-tailwindcss": "^5.2.11",
|
|
78
|
-
"@tarojs/plugin-framework-react": "npm:vite-plugin-taro-plugin-framework-react@0.6.
|
|
79
|
-
"@tarojs/react": "npm:vite-plugin-taro-react@0.6.
|
|
78
|
+
"@tarojs/plugin-framework-react": "npm:vite-plugin-taro-plugin-framework-react@0.6.3",
|
|
79
|
+
"@tarojs/react": "npm:vite-plugin-taro-react@0.6.3"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
82
|
"react": "^19.0.0",
|
|
@@ -3,13 +3,11 @@ import type { VptOptions } from '../../../../options.ts'
|
|
|
3
3
|
import { esTarget } from '../../../utils/constant.ts'
|
|
4
4
|
import { memoize } from '../../../utils/memoize.ts'
|
|
5
5
|
import { normalizeModuleId } from '../../../utils/modules.ts'
|
|
6
|
-
import {
|
|
6
|
+
import { pageShellPath, rolldownRuntimeId } from '../module/module.ts'
|
|
7
7
|
import { createWxDevHost, type WxDevHost } from './dev-host.ts'
|
|
8
8
|
import { developmentAppWxssFileName } from './hmr-files.ts'
|
|
9
9
|
import { createWxReactRefreshTransforms } from './react-refresh.ts'
|
|
10
10
|
|
|
11
|
-
const taroRuntimeId = '@tarojs/runtime'
|
|
12
|
-
|
|
13
11
|
/** Selects the sole Vite environment that owns the physical Mini Program development project. */
|
|
14
12
|
export function isWxClientEnvironment(environment: Readonly<{ name: string }>): boolean {
|
|
15
13
|
return environment.name === 'client'
|
|
@@ -32,9 +30,7 @@ export function createWxDevelopmentPlugin(options: VptOptions, applicationEntryI
|
|
|
32
30
|
*/
|
|
33
31
|
let host: WxDevHost | null = null
|
|
34
32
|
// Portable hook filters stay broad; these exact identities exclude similarly named user modules.
|
|
35
|
-
const
|
|
36
|
-
const normalizedPageCapsulePath = normalizePath(pageCapsulePath)
|
|
37
|
-
const normalizedTaroRuntimePath = normalizePath(taroRuntimePath)
|
|
33
|
+
const normalizedPageShellPath = normalizePath(pageShellPath)
|
|
38
34
|
|
|
39
35
|
return [
|
|
40
36
|
{
|
|
@@ -116,38 +112,14 @@ export function createWxDevelopmentPlugin(options: VptOptions, applicationEntryI
|
|
|
116
112
|
}
|
|
117
113
|
},
|
|
118
114
|
{
|
|
119
|
-
name: 'vpt:wx-
|
|
120
|
-
apply: 'serve',
|
|
121
|
-
transform: {
|
|
122
|
-
order: 'post',
|
|
123
|
-
filter: { id: /\/runtime\/wx\/capsule\/app\.js(?:\?|$)/ },
|
|
124
|
-
handler(code, id) {
|
|
125
|
-
if (normalizeModuleId(id) !== normalizedAppCapsulePath) return
|
|
126
|
-
return injectReactRefreshBootstrap(code)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
name: 'vpt:wx-page-hmr',
|
|
132
|
-
apply: 'serve',
|
|
133
|
-
transform: {
|
|
134
|
-
order: 'post',
|
|
135
|
-
filter: { id: /\/runtime\/wx\/capsule\/page\.js(?:\?|$)/ },
|
|
136
|
-
handler(code, id) {
|
|
137
|
-
if (normalizeModuleId(id) !== normalizedPageCapsulePath) return
|
|
138
|
-
return injectPageHmr(code, getPageRoute(id))
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
name: 'vpt:wx-taro-hmr',
|
|
115
|
+
name: 'vpt:wx-page-shell-hmr',
|
|
144
116
|
apply: 'serve',
|
|
145
117
|
transform: {
|
|
146
118
|
order: 'post',
|
|
147
|
-
filter: { id: /\/runtime\/wx\/
|
|
119
|
+
filter: { id: /\/runtime\/wx\/native\/page\.js(?:\?|$)/ },
|
|
148
120
|
handler(code, id) {
|
|
149
|
-
if (normalizeModuleId(id) !==
|
|
150
|
-
return
|
|
121
|
+
if (normalizeModuleId(id) !== normalizedPageShellPath) return
|
|
122
|
+
return injectPageShellHmr(code)
|
|
151
123
|
}
|
|
152
124
|
}
|
|
153
125
|
},
|
|
@@ -174,48 +146,19 @@ export function removeDevelopmentAppWxss(bundle: Record<string, unknown>): void
|
|
|
174
146
|
delete bundle[developmentAppWxssFileName]
|
|
175
147
|
}
|
|
176
148
|
|
|
177
|
-
/**
|
|
178
|
-
export function
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/** Connects the shared WX dev runtime to the application graph's Taro runtime instance. */
|
|
186
|
-
export function injectTaroConnection(code: string): { code: string; map: null } {
|
|
187
|
-
if (!/\bCurrent\b/.test(code) || !/\bdocument\b/.test(code) || !/\binjectPageInstance\b/.test(code)) {
|
|
188
|
-
throw new Error('WX Taro runtime must expose Current, document, and injectPageInstance for HMR')
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const taroImport = `import { Current as __vptCurrent, document as __vptDocument, injectPageInstance as __vptInjectPageInstance } from ${JSON.stringify(taroRuntimeId)};`
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
code: `${code}\n${taroImport}\n__rolldown_runtime__.connectTaro(__vptCurrent, __vptDocument, __vptInjectPageInstance);`,
|
|
195
|
-
map: null
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/** Activates development-only lifecycle handling for one plugin-owned Page capsule. */
|
|
200
|
-
export function injectPageHmr(code: string, route: string): { code: string; map: null } {
|
|
201
|
-
if (!/\bconst\s+config\s*=/.test(code) || !/\bexport\s+default\s+config\b/.test(code)) {
|
|
202
|
-
throw new Error('WX Page capsule must declare and default-export config before HMR injection')
|
|
149
|
+
/** Injects the retained snapshot at the exact native Page registration edge. */
|
|
150
|
+
export function injectPageShellHmr(code: string): { code: string; map: null } {
|
|
151
|
+
const registration = 'Page(pageConfig)'
|
|
152
|
+
if (!code.includes(registration)) {
|
|
153
|
+
throw new Error('WX native Page shell must register pageConfig')
|
|
203
154
|
}
|
|
204
155
|
|
|
205
156
|
return {
|
|
206
|
-
code:
|
|
157
|
+
code: code.replace(registration, 'Page(__rolldown_runtime__.injectPageHmr(pageConfig))'),
|
|
207
158
|
map: null
|
|
208
159
|
}
|
|
209
160
|
}
|
|
210
161
|
|
|
211
|
-
/** Reads the stable route carried by every specialized Page capsule ID. */
|
|
212
|
-
function getPageRoute(id: string): string {
|
|
213
|
-
const queryIndex = id.indexOf('?')
|
|
214
|
-
const route = queryIndex < 0 ? null : new URLSearchParams(id.slice(queryIndex + 1)).get('route')
|
|
215
|
-
if (!route) throw new Error(`WX Page capsule is missing its route: ${id}`)
|
|
216
|
-
return route
|
|
217
|
-
}
|
|
218
|
-
|
|
219
162
|
/*
|
|
220
163
|
* memoize owns a mutable one-entry-by-input cache. The assembled runtime source is byte-identical across complete generations
|
|
221
164
|
* because both the Rolldown base and injected implementation are immutable for the server lifetime. Reusing its lowered result
|
|
@@ -2,7 +2,9 @@ import { isReferenceIdentifier, type WalkerEnter } from 'oxc-walker'
|
|
|
2
2
|
import type { RolldownMagicString } from 'rolldown'
|
|
3
3
|
import type { Plugin } from 'vite'
|
|
4
4
|
import { memoize } from '../../../utils/memoize.ts'
|
|
5
|
+
import { normalizeModuleId } from '../../../utils/modules.ts'
|
|
5
6
|
import { transformWithOxcWalker } from '../../../utils/oxc-transform.ts'
|
|
7
|
+
import { reactReconcilerRoot } from '../module/module.ts'
|
|
6
8
|
|
|
7
9
|
/** The React DevTools hook protocol name; free references must target `global` in wx. */
|
|
8
10
|
const reactDevtoolsHookProtocol = '__REACT_DEVTOOLS_GLOBAL_HOOK__'
|
|
@@ -14,7 +16,7 @@ const reactDevtoolsHookProtocol = '__REACT_DEVTOOLS_GLOBAL_HOOK__'
|
|
|
14
16
|
* - `__getReactRefreshIgnoredExports` is an optional extension point read while validating a refresh boundary.
|
|
15
17
|
* Leaving that read on the nonexistent `window` crashes every update validation pass.
|
|
16
18
|
*
|
|
17
|
-
* Keeping this list explicit prevents the adapter from rewriting unrelated browser accesses if the
|
|
19
|
+
* Keeping this list explicit prevents the adapter from rewriting unrelated browser accesses if the refresh runtime
|
|
18
20
|
* gains new code. Any future React Refresh protocol addition therefore requires a deliberate compatibility decision.
|
|
19
21
|
*/
|
|
20
22
|
const refreshRuntimeWindowGlobals = ['__registerBeforePerformReactRefresh', '__getReactRefreshIgnoredExports'] as const
|
|
@@ -24,8 +26,9 @@ const refreshRuntimeWindowGlobals = ['__registerBeforePerformReactRefresh', '__g
|
|
|
24
26
|
*
|
|
25
27
|
* @vitejs/plugin-react's generated refresh code assumes the web HTML preamble and a browser
|
|
26
28
|
* global scope; wx has neither. Each transform adapts one piece of that contract:
|
|
27
|
-
* - the refresh runtime module (id-filtered): the
|
|
29
|
+
* - the refresh runtime module (id-filtered): the runtime reads and assigns
|
|
28
30
|
* `window` protocol globals (rewritten to `global`) and must inject itself at evaluation;
|
|
31
|
+
* - React Reconciler: its renderer injection statically depends on the refresh runtime, fixing cold-start order;
|
|
29
32
|
* - react-family modules (filtered on free references): the DevTools hook is read as a free
|
|
30
33
|
* variable, which the WeChat runtime scope never resolves against `global` — every free
|
|
31
34
|
* reference becomes an explicit member access;
|
|
@@ -33,8 +36,8 @@ const refreshRuntimeWindowGlobals = ['__registerBeforePerformReactRefresh', '__g
|
|
|
33
36
|
* needed because the transform generates local wrappers over the imported refresh
|
|
34
37
|
* runtime, so the guard that checks for the global is removed.
|
|
35
38
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
39
|
+
* Filters route every adaptation directly. The Reconciler intentionally receives the renderer dependency first and the
|
|
40
|
+
* global-hook rewrite second; modules outside these explicit domains never reach a handler.
|
|
38
41
|
*/
|
|
39
42
|
export function createWxReactRefreshTransforms(): Plugin[] {
|
|
40
43
|
return [
|
|
@@ -43,13 +46,26 @@ export function createWxReactRefreshTransforms(): Plugin[] {
|
|
|
43
46
|
apply: 'serve',
|
|
44
47
|
transform: {
|
|
45
48
|
order: 'post',
|
|
46
|
-
// The
|
|
49
|
+
// The refresh runtime module is id-filtered, so no code scan is needed.
|
|
47
50
|
filter: { id: /^\/@react-refresh(?:\?|$)/ },
|
|
48
51
|
handler(code, id) {
|
|
49
52
|
return fixRefreshRuntime({ code, id })
|
|
50
53
|
}
|
|
51
54
|
}
|
|
52
55
|
},
|
|
56
|
+
{
|
|
57
|
+
name: 'vpt:wx-react-refresh-renderer-dependency',
|
|
58
|
+
apply: 'serve',
|
|
59
|
+
transform: {
|
|
60
|
+
order: 'post',
|
|
61
|
+
filter: { id: /\/react-reconciler\/cjs\/react-reconciler\.development\.js(?:\?|$)/ },
|
|
62
|
+
handler(code, id) {
|
|
63
|
+
const rendererId = `${reactReconcilerRoot}/cjs/react-reconciler.development.js`
|
|
64
|
+
if (normalizeModuleId(id) !== rendererId) return
|
|
65
|
+
return injectReactRefreshRendererDependency(code)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
53
69
|
{
|
|
54
70
|
name: 'vpt:wx-react-devtools-hook',
|
|
55
71
|
apply: 'serve',
|
|
@@ -92,7 +108,8 @@ export function createWxReactRefreshTransforms(): Plugin[] {
|
|
|
92
108
|
* The protocol name is unique, but only reference identifiers are rewritten. Declaration
|
|
93
109
|
* keys and explicit members such as `global.__REACT_DEVTOOLS_GLOBAL_HOOK__` must remain
|
|
94
110
|
* untouched; rewriting those would either produce invalid syntax or double-prefix the hook.
|
|
95
|
-
* The
|
|
111
|
+
* The Reconciler's injected static dependency orders hook injection before renderer initialization independently of Rolldown's
|
|
112
|
+
* final chunking decision.
|
|
96
113
|
*/
|
|
97
114
|
function createReactDevtoolsHookVisitor(editor: RolldownMagicString): WalkerEnter {
|
|
98
115
|
return function enter(node, parent) {
|
|
@@ -146,7 +163,7 @@ function createRefreshRuntimeVisitor(editor: RolldownMagicString): WalkerEnter {
|
|
|
146
163
|
refreshRuntimeWindowGlobals.some((globalName) => globalName === node.property.name)
|
|
147
164
|
) {
|
|
148
165
|
// `global` is the shared wx App heap used by the dev runtime and hook injection. Only
|
|
149
|
-
// replacing the object range preserves the
|
|
166
|
+
// replacing the object range preserves the upstream runtime byte-for-byte otherwise
|
|
150
167
|
// and prevents unrelated `window` expressions from being silently adapted.
|
|
151
168
|
editor.overwrite(node.object.start, node.object.end, 'global')
|
|
152
169
|
}
|
|
@@ -187,6 +204,18 @@ function createRefreshPreambleGuardVisitor(editor: RolldownMagicString): WalkerE
|
|
|
187
204
|
}
|
|
188
205
|
}
|
|
189
206
|
|
|
207
|
+
/** Makes renderer hook injection statically depend on the refresh runtime. */
|
|
208
|
+
export function injectReactRefreshRendererDependency(code: string): { code: string; map: null } {
|
|
209
|
+
if (!/\bhook\.inject\(internals\)/.test(code)) {
|
|
210
|
+
throw new Error('React Reconciler must inject its renderer into the DevTools hook')
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
code: `import '/@react-refresh'\n${code}`,
|
|
215
|
+
map: null
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
190
219
|
export function transformRefreshRuntime({ code, id }: { code: string; id: string }) {
|
|
191
220
|
return transformWithOxcWalker({
|
|
192
221
|
code,
|
|
@@ -145,7 +145,7 @@ function ensureSingleOutput(rolldownOptions: BundledDevRolldownOptions): OutputO
|
|
|
145
145
|
/**
|
|
146
146
|
* Prepends entry banners after Rolldown's analysis so their native requires remain physical dependencies rather than chunk
|
|
147
147
|
* graph edges. The App initializes the runtime identity, while every Page explicitly applies the watched patch data before its
|
|
148
|
-
*
|
|
148
|
+
* native Page registration.
|
|
149
149
|
*/
|
|
150
150
|
function createEntryBanner(pageFiles: ReadonlySet<string>): (chunk: { name: string; fileName: string }) => string {
|
|
151
151
|
return (chunk) => {
|
|
@@ -154,8 +154,7 @@ function createEntryBanner(pageFiles: ReadonlySet<string>): (chunk: { name: stri
|
|
|
154
154
|
}
|
|
155
155
|
if (pageFiles.has(chunk.name)) {
|
|
156
156
|
const patchesPath = path.posix.relative(path.posix.dirname(chunk.fileName), 'hmr/patches.js')
|
|
157
|
-
|
|
158
|
-
return `__rolldown_runtime__.applyPatches(require('${patchesPath}'), ${JSON.stringify(route)});\n`
|
|
157
|
+
return `__rolldown_runtime__.applyPatches(require('${patchesPath}'));\n`
|
|
159
158
|
}
|
|
160
159
|
return ''
|
|
161
160
|
}
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
transportPath,
|
|
19
19
|
vitePreloadId
|
|
20
20
|
} from '../module/module.ts'
|
|
21
|
-
import {
|
|
21
|
+
import { specializeAppCapsule } from './specialize-app-capsule.ts'
|
|
22
22
|
import { specializePageCapsule } from './specialize-page-capsule.ts'
|
|
23
23
|
|
|
24
24
|
/** Resolves one exact plugin-private ID using its importer and configured project root. */
|
|
@@ -26,7 +26,7 @@ type PrivateIdResolver = (importer: string | undefined, projectRoot: string) =>
|
|
|
26
26
|
|
|
27
27
|
/** Creates the resolver and source specializer for the wx module graph. */
|
|
28
28
|
export function createResolver(options: VptOptions) {
|
|
29
|
-
const
|
|
29
|
+
const normalizedAppCapsulePath = normalizePath(appCapsulePath)
|
|
30
30
|
const normalizedPageCapsulePath = normalizePath(pageCapsulePath)
|
|
31
31
|
|
|
32
32
|
// Construct output input and application traversal roots together once so style order cannot drift from route order.
|
|
@@ -70,8 +70,8 @@ export function createResolver(options: VptOptions) {
|
|
|
70
70
|
specialize(code: string, id: string, sourcemap = true) {
|
|
71
71
|
const normalizedId = normalizeModuleId(id)
|
|
72
72
|
|
|
73
|
-
if (normalizedId ===
|
|
74
|
-
return
|
|
73
|
+
if (normalizedId === normalizedAppCapsulePath) {
|
|
74
|
+
return specializeAppCapsule({ code, id, appConfig: createAppConfig(options), sourcemap })
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
if (normalizedId === normalizedPageCapsulePath) {
|
|
@@ -4,8 +4,8 @@ import { type AstTransformResult, replaceWithAst } from '../../../utils/transfor
|
|
|
4
4
|
|
|
5
5
|
const appConfigPlaceholder = '__VPT_APP_CONFIG__'
|
|
6
6
|
|
|
7
|
-
/** Specializes the
|
|
8
|
-
export function
|
|
7
|
+
/** Specializes the App capsule with its normalized native configuration. */
|
|
8
|
+
export function specializeAppCapsule({
|
|
9
9
|
code,
|
|
10
10
|
id,
|
|
11
11
|
appConfig,
|
|
@@ -2,11 +2,6 @@
|
|
|
2
2
|
import '../systemjs/system-core.js'
|
|
3
3
|
import { transport } from './transport.ts'
|
|
4
4
|
|
|
5
|
-
declare const __VPT_APP_CONFIG__: Record<string, unknown>
|
|
6
|
-
|
|
7
|
-
/** Shares one App configuration object between the specialized bootstrap and App capsule. */
|
|
8
|
-
export const appConfig = __VPT_APP_CONFIG__
|
|
9
|
-
|
|
10
5
|
// WX has no modulepreload transport. Genuine application import() boundaries retain System.import() and may load
|
|
11
6
|
// asynchronous subpackage or top-level-await graphs through this identity wrapper.
|
|
12
7
|
export const __vitePreload = <Value>(load: () => Value): Value => load()
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
// biome-ignore assist/source/organizeImports: Taro must initialize before the App component.
|
|
2
2
|
import { createReactApp, ReactDOM } from './taro-runtime.ts'
|
|
3
3
|
import React from 'react'
|
|
4
|
-
import { appConfig } from '../amphibious/bootstrap.ts'
|
|
5
4
|
|
|
6
5
|
// @ts-expect-error: The wx build resolves this private App component.
|
|
7
6
|
import AppComponent from '\0vpt:app-component'
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
declare const __VPT_APP_CONFIG__: Record<string, unknown>
|
|
9
|
+
|
|
10
|
+
const config = createReactApp(AppComponent, React, ReactDOM, __VPT_APP_CONFIG__)
|
|
11
|
+
|
|
12
|
+
export default config
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// The `DevRuntime` base class is injected into the chunk by Rolldown's dev-mode
|
|
6
6
|
// transform, so the WX host only extends it.
|
|
7
7
|
//
|
|
8
|
-
// Every Page explicitly passes the inert hmr/patches.js export here before
|
|
8
|
+
// Every Page explicitly passes the inert hmr/patches.js export here before native Page registration. The runtime applies that
|
|
9
9
|
// cumulative suffix synchronously, reports its successful application frontier, and ignores sequences replayed by other Pages.
|
|
10
10
|
|
|
11
11
|
import type { DevRuntime as RolldownDevRuntime } from 'rolldown/experimental/runtime-types'
|
|
@@ -54,54 +54,30 @@ type HmrUpdate = Readonly<{
|
|
|
54
54
|
|
|
55
55
|
type AcceptCallback = (moduleExports: unknown) => void
|
|
56
56
|
|
|
57
|
-
type PageSnapshot = Readonly<{
|
|
58
|
-
$taroPath: string
|
|
59
|
-
$taroParams: Record<string, unknown>
|
|
60
|
-
data: Record<string, unknown>
|
|
61
|
-
}>
|
|
62
|
-
|
|
63
57
|
type NativePage = {
|
|
64
|
-
$taroPath: string
|
|
65
|
-
$taroParams: Record<string, unknown>
|
|
66
58
|
data: Record<string, unknown>
|
|
67
|
-
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const pageHmrStateKey: unique symbol = Symbol('vpt.pageHmrState')
|
|
62
|
+
|
|
63
|
+
type PageHmrState = {
|
|
64
|
+
/** True only across the unload/load/show sequence triggered by one native re-registration. */
|
|
65
|
+
isReregistering: boolean
|
|
66
|
+
/** The Page bound to `this` by ordinary onLoad, retained until ordinary onUnload. */
|
|
67
|
+
mountedPage: NativePage | undefined
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
type HmrPageConfig = {
|
|
71
|
+
data: Record<string, unknown>
|
|
71
72
|
onUnload?: unknown
|
|
72
73
|
onLoad?: unknown
|
|
73
74
|
onShow?: unknown
|
|
75
|
+
[pageHmrStateKey]?: PageHmrState
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
/** Immutable bridge references plus replacement transactions owned by one Taro singleton. */
|
|
81
|
-
type TaroState = {
|
|
82
|
-
/** Taro's existing current-page source of truth; replacement onLoad switches its receiver. */
|
|
83
|
-
readonly current: { page: NativePage | null }
|
|
84
|
-
/** Taro's virtual document, used to find the retained root by its preserved unique path. */
|
|
85
|
-
readonly document: { getElementById(path: string): unknown }
|
|
86
|
-
/** Replaces Taro's native lifecycle receiver without remounting the retained React subtree. */
|
|
87
|
-
readonly injectPageInstance: (instance: unknown, path: string) => void
|
|
88
|
-
/**
|
|
89
|
-
* Short-lived route transactions. Absence means ordinary lifecycle; null means armed
|
|
90
|
-
* before onUnload or snapshot-consumed before onShow; a snapshot exists only between
|
|
91
|
-
* replacement onUnload and onLoad. The large data reference is therefore released in
|
|
92
|
-
* onLoad, while the null marker remains just long enough to suppress synthetic onShow.
|
|
93
|
-
*/
|
|
94
|
-
readonly pageReplacements: Map<string, PageSnapshot | null>
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** Forwards a native lifecycle with its original Page receiver. */
|
|
98
|
-
function forward(handler: unknown, receiver: unknown, args: unknown[]): void {
|
|
99
|
-
if (typeof handler === 'function') handler.apply(receiver, args)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** Narrows the untyped element returned across the Taro connection boundary. */
|
|
103
|
-
function isTaroRoot(value: unknown): value is TaroRoot {
|
|
104
|
-
return typeof value === 'object' && value !== null && 'ctx' in value
|
|
78
|
+
/** Calls a native lifecycle with the same Page bound to `this` and the same arguments. */
|
|
79
|
+
function forward(handler: unknown, page: unknown, args: unknown[]): void {
|
|
80
|
+
if (typeof handler === 'function') handler.apply(page, args)
|
|
105
81
|
}
|
|
106
82
|
|
|
107
83
|
/** Shared no-op CSS contract because physical rebuilds replace styles wholesale. */
|
|
@@ -285,109 +261,90 @@ class WxDevRuntime extends DevRuntime {
|
|
|
285
261
|
this.session = { ...info, appliedSeq: 0 }
|
|
286
262
|
}
|
|
287
263
|
|
|
288
|
-
/**
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return
|
|
303
|
-
}
|
|
264
|
+
/** Tracks the mounted native Page and prepares its static config for HMR re-registration. */
|
|
265
|
+
injectPageHmr(config: HmrPageConfig): HmrPageConfig {
|
|
266
|
+
const existingState = config[pageHmrStateKey]
|
|
267
|
+
|
|
268
|
+
if (existingState) {
|
|
269
|
+
const mountedPage = existingState.mountedPage
|
|
270
|
+
/*
|
|
271
|
+
* The static config can outlive a native Page instance. Before its first ordinary onLoad, or after a real onUnload,
|
|
272
|
+
* there is no mounted Page or current view-model to carry into another registration. Leave the lifecycle gate
|
|
273
|
+
* unarmed and preserve the config's existing initial data so a future real onLoad still enters Taro normally.
|
|
274
|
+
*/
|
|
275
|
+
if (!mountedPage) {
|
|
276
|
+
return config
|
|
277
|
+
}
|
|
304
278
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
279
|
+
/*
|
|
280
|
+
* Arm the lifecycle wrappers on this exact static config before it is passed back to `Page(config)`. DevTools then
|
|
281
|
+
* triggers an unload/load/show sequence for that native re-registration: unload and load observe `true` and return
|
|
282
|
+
* before entering Taro, preserving the mounted React tree and its original Page connection; show consumes the
|
|
283
|
+
* one-shot gate by restoring `false`. Ordinary navigation never enters this branch, and every Page config owns an
|
|
284
|
+
* independent state object, so no route map, global phase, or Page identity comparison participates in the decision.
|
|
285
|
+
*/
|
|
286
|
+
existingState.isReregistering = true
|
|
287
|
+
/*
|
|
288
|
+
* `Page(config)` reads `config.data` as the initial native view-model for this registration. Supplying the mounted
|
|
289
|
+
* Page's latest data before that call prevents the temporary Page used for re-registration callbacks from starting
|
|
290
|
+
* empty. This is an O(1) reference assignment in vpt: it does not clone the recursive data tree, call `setData`, move
|
|
291
|
+
* React state, or rebind Taro. The ordinary Taro lifecycle remains suppressed until re-registration onShow, so its
|
|
292
|
+
* React tree and output connection stay attached to `mountedPage`; every later registration reads its latest data.
|
|
293
|
+
*/
|
|
294
|
+
config.data = mountedPage.data
|
|
295
|
+
|
|
296
|
+
return config
|
|
310
297
|
}
|
|
311
|
-
}
|
|
312
298
|
|
|
313
|
-
/** Injects snapshot-preserving behavior into one route-specific Taro Page configuration. */
|
|
314
|
-
injectPageHmr(config: HmrPageConfig, route: string): void {
|
|
315
299
|
const originalOnUnload = config.onUnload
|
|
316
300
|
const originalOnLoad = config.onLoad
|
|
317
301
|
const originalOnShow = config.onShow
|
|
318
|
-
|
|
302
|
+
|
|
303
|
+
const state: PageHmrState = {
|
|
304
|
+
isReregistering: false,
|
|
305
|
+
mountedPage: undefined
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/*
|
|
309
|
+
* Attach the one mutable HMR state object to this exact static config. The lifecycle wrappers below close over the same
|
|
310
|
+
* object, while a later `injectPageHmr(config)` call finds it through the symbol and knows wrapping is already complete.
|
|
311
|
+
* A symbol cannot collide with WeChat or Taro's string-named Page options, and config-local ownership avoids route maps,
|
|
312
|
+
* a runtime-wide WeakMap, or state shared by two Page configs. The state becomes unreachable together with the config.
|
|
313
|
+
*/
|
|
314
|
+
config[pageHmrStateKey] = state
|
|
319
315
|
|
|
320
316
|
config.onUnload = function (this: NativePage, ...args: unknown[]) {
|
|
321
|
-
|
|
322
|
-
if (taro.pageReplacements.has(route)) {
|
|
323
|
-
taro.pageReplacements.set(route, {
|
|
324
|
-
$taroPath: this.$taroPath,
|
|
325
|
-
$taroParams: this.$taroParams,
|
|
326
|
-
// WeChat owns this serializable view-model. Keeping its reference is O(1),
|
|
327
|
-
// unlike cloning the complete recursive projection before every edit.
|
|
328
|
-
data: this.data
|
|
329
|
-
})
|
|
317
|
+
if (state.isReregistering) {
|
|
330
318
|
return
|
|
331
319
|
}
|
|
320
|
+
|
|
332
321
|
forward(originalOnUnload, this, args)
|
|
322
|
+
state.mountedPage = undefined
|
|
333
323
|
}
|
|
334
324
|
|
|
335
325
|
config.onLoad = function (this: NativePage, ...args: unknown[]) {
|
|
336
|
-
|
|
337
|
-
const snapshot = taro.pageReplacements.get(route)
|
|
338
|
-
if (snapshot) {
|
|
339
|
-
// Replace the transaction before native work so exceptions cannot retain the
|
|
340
|
-
// large data snapshot while the route waits for its synthetic onShow.
|
|
341
|
-
taro.pageReplacements.set(route, null)
|
|
342
|
-
|
|
343
|
-
// Snapshot paint is the first bridge operation and removes the empty-page gap.
|
|
344
|
-
this.setData(snapshot.data)
|
|
345
|
-
this.$taroPath = snapshot.$taroPath
|
|
346
|
-
this.$taroParams = snapshot.$taroParams
|
|
347
|
-
runtime.bindPage(this, snapshot.$taroPath)
|
|
326
|
+
if (state.isReregistering) {
|
|
348
327
|
return
|
|
349
328
|
}
|
|
329
|
+
|
|
350
330
|
forward(originalOnLoad, this, args)
|
|
331
|
+
state.mountedPage = this
|
|
351
332
|
}
|
|
352
333
|
|
|
353
334
|
config.onShow = function (this: NativePage, ...args: unknown[]) {
|
|
354
|
-
if (
|
|
355
|
-
|
|
335
|
+
if (state.isReregistering) {
|
|
336
|
+
state.isReregistering = false
|
|
356
337
|
return
|
|
357
338
|
}
|
|
358
339
|
|
|
359
340
|
forward(originalOnShow, this, args)
|
|
360
341
|
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/** Returns the Taro connection or fails at the first incorrectly ordered use. */
|
|
364
|
-
private requireTaro(): TaroState {
|
|
365
|
-
if (!this.taro) throw new Error('[vpt] WX HMR used before the Taro runtime was connected')
|
|
366
|
-
return this.taro
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/** Returns a retained Taro root after normal Page mount has created it. */
|
|
370
|
-
private findRoot(path: string): TaroRoot | undefined {
|
|
371
|
-
const root = this.requireTaro().document.getElementById(path)
|
|
372
|
-
return isTaroRoot(root) ? root : undefined
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/** Rebinds a retained Taro tree to one replacement native Page without repainting. */
|
|
376
|
-
private bindPage(instance: NativePage, path: string): void {
|
|
377
|
-
const taro = this.requireTaro()
|
|
378
|
-
taro.injectPageInstance(instance, path)
|
|
379
|
-
taro.current.page = instance
|
|
380
|
-
|
|
381
|
-
const pageElement = this.findRoot(path)
|
|
382
|
-
if (!pageElement) {
|
|
383
|
-
throw new Error(`[vpt] retained Taro page not found: ${path}`)
|
|
384
|
-
}
|
|
385
342
|
|
|
386
|
-
|
|
343
|
+
return config
|
|
387
344
|
}
|
|
388
345
|
|
|
389
|
-
/** Applies one Page-delivered payload
|
|
390
|
-
applyPatches(payload: PatchPayload | undefined
|
|
346
|
+
/** Applies one Page-delivered payload before its native shell registers the static route configuration. */
|
|
347
|
+
applyPatches(payload: PatchPayload | undefined): void {
|
|
391
348
|
// The initial physical dependency exports undefined until the host has a patch range.
|
|
392
349
|
if (!payload) return
|
|
393
350
|
|
|
@@ -397,12 +354,6 @@ class WxDevRuntime extends DevRuntime {
|
|
|
397
354
|
return
|
|
398
355
|
}
|
|
399
356
|
|
|
400
|
-
// A replayed payload still causes DevTools to replace this physical Page. Arm the
|
|
401
|
-
// route independently of whether this App heap already applied its patch sequence.
|
|
402
|
-
if (route) {
|
|
403
|
-
this.requireTaro().pageReplacements.set(route, null)
|
|
404
|
-
}
|
|
405
|
-
|
|
406
357
|
// Apply synchronously: the page's imports below the require resolve against the
|
|
407
358
|
// freshly registered modules, so the re-executed Page evaluates with the new code.
|
|
408
359
|
if (this.applyPatchBatch(session, payload.patches)) {
|
|
@@ -8,8 +8,7 @@ type WeChatGlobal = object
|
|
|
8
8
|
/** The App-global Rolldown dev runtime; present only in wx development builds. */
|
|
9
9
|
declare const __rolldown_runtime__:
|
|
10
10
|
| {
|
|
11
|
-
|
|
12
|
-
injectPageHmr(config: object, route: string): void
|
|
11
|
+
injectPageHmr(config: object): object
|
|
13
12
|
}
|
|
14
13
|
| undefined
|
|
15
14
|
|