vite-plugin-taro 0.5.5 → 0.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ import type { PluginOption } from 'vite';
2
+ import type { VitePluginTaroOptions } from '../../../../options.ts';
3
+ /**
4
+ * Adds the serve-only bundled-development plugin set for the wx target: the dev adapter,
5
+ * Page HMR activation, and React Refresh adaptation transforms.
6
+ */
7
+ export declare function createWxDevelopmentPlugin(options: VitePluginTaroOptions): PluginOption[];
8
+ /** Ensures the Refresh hook exists before React's renderer evaluates and injects itself. */
9
+ export declare function injectReactRefreshBootstrap(code: string): {
10
+ code: string;
11
+ map: null;
12
+ };
13
+ /** Connects the shared WX dev runtime to the application graph's Taro runtime instance. */
14
+ export declare function injectTaroConnection(code: string): {
15
+ code: string;
16
+ map: null;
17
+ };
18
+ /** Activates development-only lifecycle handling for one plugin-owned Page capsule. */
19
+ export declare function injectPageHmr(code: string, route: string): {
20
+ code: string;
21
+ map: null;
22
+ };
@@ -0,0 +1,158 @@
1
+ import { transformWithOxc } from 'vite';
2
+ import { esTarget } from '../../../utils/constant.js';
3
+ import { memoize } from '../../../utils/memoize.js';
4
+ import { normalizeModuleId } from '../../../utils/modules.js';
5
+ import { appCapsulePath, pageCapsulePath, rolldownRuntimeId, taroRuntimePath } from '../module.js';
6
+ import { createWxDevHost } from './dev-host.js';
7
+ import { createWxReactRefreshTransforms } from './react-refresh.js';
8
+ const taroRuntimeId = '@tarojs/runtime';
9
+ /**
10
+ * Adds the serve-only bundled-development plugin set for the wx target: the dev adapter,
11
+ * Page HMR activation, and React Refresh adaptation transforms.
12
+ */
13
+ export function createWxDevelopmentPlugin(options) {
14
+ let host = null;
15
+ // Portable hook filters stay broad; these exact identities exclude similarly named user modules.
16
+ const normalizedAppCapsulePath = normalizeModuleId(appCapsulePath);
17
+ const normalizedPageCapsulePath = normalizeModuleId(pageCapsulePath);
18
+ const normalizedTaroRuntimePath = normalizeModuleId(taroRuntimePath);
19
+ return [
20
+ {
21
+ name: 'vpt:wx-dev',
22
+ apply: 'serve',
23
+ config() {
24
+ return {
25
+ build: {
26
+ // Disable maps in resolved environment config as well as final output so Oxc and Babel skip producing
27
+ // intermediate maps that Rolldown would discard.
28
+ sourcemap: false
29
+ },
30
+ experimental: {
31
+ // Ask Vite to resolve its bundled-development graph and expose the private adapter instance. The wx
32
+ // configureServer hook replaces only its startup method with the directly writing DevEngine.
33
+ bundledDev: true
34
+ }
35
+ };
36
+ },
37
+ configureServer: {
38
+ // Install after Vite and user plugins have finished configuring the environment, but before server.listen()
39
+ // asks bundledDev to create its hard-coded skip-write DevEngine.
40
+ order: 'post',
41
+ async handler(server) {
42
+ host = await createWxDevHost({ server, options });
43
+ }
44
+ },
45
+ closeBundle() {
46
+ return host?.close();
47
+ }
48
+ },
49
+ {
50
+ name: 'vpt:wx-runtime-lowering',
51
+ apply: 'serve',
52
+ transform: {
53
+ order: 'post',
54
+ // The dev-mode transform assembles the runtime chunk (Rolldown's base runtime
55
+ // plus our injected implement) as this module's transform output, which
56
+ // bypasses the build's es2018 lowering. Real-device engines and WeChat's
57
+ // upload parser predate class fields and nullish operators, so the assembled
58
+ // runtime is lowered here — the only module that needs it. The exact id
59
+ // filter needs no code scan; the id must stay in sync with rolldownRuntimeId
60
+ // in module.ts (kept as a regex for the Rolldown-side filter).
61
+ filter: { id: /^\0rolldown\/runtime\.js(?:\?|$)/ },
62
+ handler(code) {
63
+ // The `setPublicClassFields` assumption emits plain `this.x = ...`
64
+ // assignments instead of external helpers, whose references the later
65
+ // minifier would mangle.
66
+ return fixRolldownRuntime(code);
67
+ }
68
+ }
69
+ },
70
+ {
71
+ name: 'vpt:wx-react-refresh-bootstrap',
72
+ apply: 'serve',
73
+ transform: {
74
+ order: 'post',
75
+ filter: { id: /\/runtime\/wx\/capsule\/app\.js(?:\?|$)/ },
76
+ handler(code, id) {
77
+ if (normalizeModuleId(id) !== normalizedAppCapsulePath)
78
+ return;
79
+ return injectReactRefreshBootstrap(code);
80
+ }
81
+ }
82
+ },
83
+ {
84
+ name: 'vpt:wx-page-hmr',
85
+ apply: 'serve',
86
+ transform: {
87
+ order: 'post',
88
+ filter: { id: /\/runtime\/wx\/capsule\/page\.js(?:\?|$)/ },
89
+ handler(code, id) {
90
+ if (normalizeModuleId(id) !== normalizedPageCapsulePath)
91
+ return;
92
+ return injectPageHmr(code, getPageRoute(id));
93
+ }
94
+ }
95
+ },
96
+ {
97
+ name: 'vpt:wx-taro-hmr',
98
+ apply: 'serve',
99
+ transform: {
100
+ order: 'post',
101
+ filter: { id: /\/runtime\/wx\/capsule\/taro-runtime\.js(?:\?|$)/ },
102
+ handler(code, id) {
103
+ if (normalizeModuleId(id) !== normalizedTaroRuntimePath)
104
+ return;
105
+ return injectTaroConnection(code);
106
+ }
107
+ }
108
+ },
109
+ ...createWxReactRefreshTransforms()
110
+ ];
111
+ }
112
+ /** Ensures the Refresh hook exists before React's renderer evaluates and injects itself. */
113
+ export function injectReactRefreshBootstrap(code) {
114
+ return {
115
+ code: `import ${JSON.stringify('/@react-refresh')};\n${code}`,
116
+ map: null
117
+ };
118
+ }
119
+ /** Connects the shared WX dev runtime to the application graph's Taro runtime instance. */
120
+ export function injectTaroConnection(code) {
121
+ if (!/\bCurrent\b/.test(code) || !/\bdocument\b/.test(code) || !/\binjectPageInstance\b/.test(code)) {
122
+ throw new Error('WX Taro runtime must expose Current, document, and injectPageInstance for HMR');
123
+ }
124
+ const taroImport = `import { Current as __vptCurrent, document as __vptDocument, injectPageInstance as __vptInjectPageInstance } from ${JSON.stringify(taroRuntimeId)};`;
125
+ return {
126
+ code: `${code}\n${taroImport}\n__rolldown_runtime__.connectTaro(__vptCurrent, __vptDocument, __vptInjectPageInstance);`,
127
+ map: null
128
+ };
129
+ }
130
+ /** Activates development-only lifecycle handling for one plugin-owned Page capsule. */
131
+ export function injectPageHmr(code, route) {
132
+ if (!/\bconst\s+config\s*=/.test(code) || !/\bexport\s+default\s+config\b/.test(code)) {
133
+ throw new Error('WX Page capsule must declare and default-export config before HMR injection');
134
+ }
135
+ return {
136
+ code: `${code}\n__rolldown_runtime__.injectPageHmr(config, ${JSON.stringify(route)});`,
137
+ map: null
138
+ };
139
+ }
140
+ /** Reads the stable route carried by every specialized Page capsule ID. */
141
+ function getPageRoute(id) {
142
+ const queryIndex = id.indexOf('?');
143
+ const route = queryIndex < 0 ? null : new URLSearchParams(id.slice(queryIndex + 1)).get('route');
144
+ if (!route)
145
+ throw new Error(`WX Page capsule is missing its route: ${id}`);
146
+ return route;
147
+ }
148
+ // The assembled runtime chunk is byte-identical on every build (the base runtime and the
149
+ // bundled implement are immutable for the server's lifetime), so the lowering runs once
150
+ // and every build reuses it.
151
+ const fixRolldownRuntime = memoize((code) => {
152
+ return transformWithOxc(code, rolldownRuntimeId, {
153
+ lang: 'js',
154
+ target: esTarget,
155
+ sourcemap: false,
156
+ assumptions: { setPublicClassFields: true }
157
+ });
158
+ });
@@ -5,8 +5,7 @@ import type { Plugin } from 'vite';
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
7
  * - the refresh runtime module (id-filtered): the vendored runtime reads and assigns
8
- * `window` protocol globals (rewritten to `global`) and must inject itself at evaluation
9
- * — the preamble's `injectIntoGlobalHook` call has no HTML home in wx;
8
+ * `window` protocol globals (rewritten to `global`) and must inject itself at evaluation;
10
9
  * - react-family modules (filtered on free references): the DevTools hook is read as a free
11
10
  * variable, which the WeChat runtime scope never resolves against `global` — every free
12
11
  * reference becomes an explicit member access;
@@ -20,8 +20,7 @@ const refreshRuntimeWindowGlobals = ['__registerBeforePerformReactRefresh', '__g
20
20
  * @vitejs/plugin-react's generated refresh code assumes the web HTML preamble and a browser
21
21
  * global scope; wx has neither. Each transform adapts one piece of that contract:
22
22
  * - the refresh runtime module (id-filtered): the vendored runtime reads and assigns
23
- * `window` protocol globals (rewritten to `global`) and must inject itself at evaluation
24
- * — the preamble's `injectIntoGlobalHook` call has no HTML home in wx;
23
+ * `window` protocol globals (rewritten to `global`) and must inject itself at evaluation;
25
24
  * - react-family modules (filtered on free references): the DevTools hook is read as a free
26
25
  * variable, which the WeChat runtime scope never resolves against `global` — every free
27
26
  * reference becomes an explicit member access;
@@ -87,7 +86,7 @@ export function createWxReactRefreshTransforms() {
87
86
  * The protocol name is unique, but only reference identifiers are rewritten. Declaration
88
87
  * keys and explicit members such as `global.__REACT_DEVTOOLS_GLOBAL_HOOK__` must remain
89
88
  * untouched; rewriting those would either produce invalid syntax or double-prefix the hook.
90
- * The hook itself is created on `global` by the dev runtime chunk in `dev-runtime.ts`.
89
+ * The eagerly evaluated refresh runtime creates the hook on `global` before the renderer loads.
91
90
  */
92
91
  function createReactDevtoolsHookVisitor(editor) {
93
92
  return function enter(node, parent) {
@@ -127,18 +126,17 @@ function createRefreshRuntimeVisitor(editor) {
127
126
  // unimplemented in wx.
128
127
  editor.append('\ninjectIntoGlobalHook(global);');
129
128
  return function enter(node) {
130
- if (node.type !== 'MemberExpression' ||
131
- node.computed ||
132
- node.object.type !== 'Identifier' ||
133
- node.object.name !== 'window' ||
134
- node.property.type !== 'Identifier' ||
135
- !refreshRuntimeWindowGlobals.some((globalName) => globalName === node.property.name)) {
136
- return;
129
+ if (node.type === 'MemberExpression' &&
130
+ !node.computed &&
131
+ node.object.type === 'Identifier' &&
132
+ node.object.name === 'window' &&
133
+ node.property.type === 'Identifier' &&
134
+ refreshRuntimeWindowGlobals.some((globalName) => globalName === node.property.name)) {
135
+ // `global` is the shared wx App heap used by the dev runtime and hook injection. Only
136
+ // replacing the object range preserves the vendored runtime byte-for-byte otherwise
137
+ // and prevents unrelated `window` expressions from being silently adapted.
138
+ editor.overwrite(node.object.start, node.object.end, 'global');
137
139
  }
138
- // `global` is the shared wx App heap used by the dev runtime and hook injection. Only
139
- // replacing the object range preserves the vendored runtime byte-for-byte otherwise
140
- // and prevents unrelated `window` expressions from being silently adapted.
141
- editor.overwrite(node.object.start, node.object.end, 'global');
142
140
  };
143
141
  }
144
142
  /**
@@ -93,7 +93,8 @@ function createEntryBanner(pageFiles) {
93
93
  }
94
94
  if (pageFiles.has(chunk.name)) {
95
95
  const patchesPath = path.posix.relative(path.posix.dirname(chunk.fileName), 'hmr/patches.js');
96
- return `__rolldown_runtime__.applyPatches(require('${patchesPath}'));\n`;
96
+ const route = chunk.name.slice(0, -'.js'.length);
97
+ return `__rolldown_runtime__.applyPatches(require('${patchesPath}'), ${JSON.stringify(route)});\n`;
97
98
  }
98
99
  return '';
99
100
  };
@@ -27,6 +27,8 @@ export declare const pageComponentId = "\0vpt:page-component";
27
27
  export declare const pageCapsuleId = "\0vpt:page-capsule";
28
28
  /** Provides the Page capsule source specialized through a stable route query. */
29
29
  export declare const pageCapsulePath: string;
30
+ /** Identifies the Taro facade shared by the App, Page, and recursive Component capsules. */
31
+ export declare const taroRuntimePath: string;
30
32
  /** Identifies the reusable synchronous native Page shell source. */
31
33
  export declare const pageShellPath: string;
32
34
  export type WxChunk = Rolldown.PreRenderedChunk | Rolldown.RenderedChunk;
@@ -29,6 +29,8 @@ export const pageComponentId = '\0vpt:page-component';
29
29
  export const pageCapsuleId = '\0vpt:page-capsule';
30
30
  /** Provides the Page capsule source specialized through a stable route query. */
31
31
  export const pageCapsulePath = resolvePackageFile('dist/runtime/wx/capsule/page.js');
32
+ /** Identifies the Taro facade shared by the App, Page, and recursive Component capsules. */
33
+ export const taroRuntimePath = resolvePackageFile('dist/runtime/wx/capsule/taro-runtime.js');
32
34
  /** Identifies the reusable synchronous native Page shell source. */
33
35
  export const pageShellPath = resolvePackageFile('dist/runtime/wx/native/page.js');
34
36
  // These fixed source identities describe entry roles independently from the final execution kind. A capsule entry may,
@@ -1,7 +1,7 @@
1
1
  import { esTarget } from '../../utils/constant.js';
2
2
  import { packageRequire } from '../../utils/packages.js';
3
3
  import { clientTaroNativeId } from '../client/constant.js';
4
- import { createWxDevelopmentPlugin } from './dev/plugin.js';
4
+ import { createWxDevelopmentPlugin } from './dev/plugins.js';
5
5
  import { getWxExecutionKind, isTransportModule } from './module.js';
6
6
  import { compileNativeComponentInterface } from './native/compile-native-component-interface.js';
7
7
  import { getNativeComponentAssetBytes } from './native/native-component-assets.js';
@@ -2,67 +2,6 @@
2
2
  import './app.js';
3
3
  // @ts-expect-error: The wx build replaces this private import with the configured Page component.
4
4
  import PageComponent from '\0vpt:page-component';
5
- import { Current, createPageConfig, document, injectPageInstance } from './taro-runtime.js';
5
+ import { createPageConfig } from './taro-runtime.js';
6
6
  const config = createPageConfig(PageComponent, __VITE_PLUGIN_TARO_PAGE_PATH__, { root: { cn: [] } }, __VITE_PLUGIN_TARO_PAGE_CONFIG__);
7
7
  export default config;
8
- /** Forwards a call with the native page instance as `this`, matching Taro's invocation. */
9
- function forward(handler, receiver, args) {
10
- ;
11
- handler?.apply(receiver, args);
12
- }
13
- // DevTools re-executes the page and replays the replacement lifecycle on every edit. Taro's
14
- // onUnload unmounts the React tree and onLoad mounts a fresh one, destroying state. While
15
- // the runtime says a patch was just delivered, the pair is intercepted instead: onUnload
16
- // captures the surviving page identity, and onLoad restores it onto the replacement native
17
- // instance and rebinds the retained tree to it (pageElement.ctx + a full re-render). The
18
- // capsule module is cached across re-executions, so this closure holds the capture.
19
- // Ordinary navigation (no patch) passes through unchanged. The wrappers are regular
20
- // functions so the native page instance (`this`) reaches the original handlers.
21
- if (typeof __rolldown_runtime__ !== 'undefined') {
22
- const originalOnUnload = config.onUnload;
23
- const originalOnLoad = config.onLoad;
24
- const originalOnShow = config.onShow;
25
- let captured;
26
- config.onUnload = function (...args) {
27
- if (__rolldown_runtime__.isHotReloading()) {
28
- const instance = this;
29
- captured = {
30
- $taroPath: instance.$taroPath,
31
- $taroParams: instance.$taroParams
32
- };
33
- return;
34
- }
35
- forward(originalOnUnload, this, args);
36
- };
37
- config.onLoad = function (...args) {
38
- if (__rolldown_runtime__.isHotReloading() && captured) {
39
- // Restore the surviving page identity onto the replacement native instance: the
40
- // re-loaded $taroPath embeds a fresh timestamp, so without this the tree and the
41
- // native side would disagree on the page key.
42
- const instance = this;
43
- instance.$taroPath = captured.$taroPath;
44
- instance.$taroParams = captured.$taroParams;
45
- // Rebind the retained tree to the replacement receiver and resync the native
46
- // data: updateChildNodes enqueues the full hydrated node tree (root.cn) — the
47
- // surviving tree's mutations were consumed long ago, so without it the payload
48
- // queue is empty and the new receiver renders nothing.
49
- injectPageInstance(instance, captured.$taroPath);
50
- Current.page = instance;
51
- const pageElement = document.getElementById(captured.$taroPath);
52
- pageElement.ctx = instance;
53
- pageElement.updateChildNodes();
54
- pageElement.performUpdate(true);
55
- return;
56
- }
57
- forward(originalOnLoad, this, args);
58
- };
59
- config.onShow = function (...args) {
60
- if (__rolldown_runtime__.isHotReloading()) {
61
- // The window ends at the first show; the user's onShow must not re-run on a
62
- // synthetic re-execution (it could reset state).
63
- __rolldown_runtime__.clearHotReloading();
64
- return;
65
- }
66
- forward(originalOnShow, this, args);
67
- };
68
- }