vite-plugin-taro 0.7.10 → 0.7.12-beta.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 (35) hide show
  1. package/dist/index.js +123 -123
  2. package/dist/node/plugins/deps/create-deps-plugin.d.ts +8 -0
  3. package/dist/node/plugins/mini/dev/inject-dev-page-component.d.ts +1 -1
  4. package/dist/node/plugins/mini/dev/modes/devtools/devtools-hmr-mode.d.ts +2 -4
  5. package/dist/node/plugins/mini/mini-contract.d.ts +1 -1
  6. package/dist/node/plugins/mini/module/module.d.ts +6 -6
  7. package/dist/node/plugins/mini/output/create-transport-output.d.ts +1 -1
  8. package/dist/node/plugins/mini/render/native.d.ts +2 -2
  9. package/dist/node/tests/create-test-project.d.ts +2 -0
  10. package/dist/node/tests/project-temp-dir.d.ts +2 -0
  11. package/dist/options.d.ts +5 -0
  12. package/dist/runtime/mini/amphibious/bootstrap.d.ts +1 -0
  13. package/dist/runtime/mini/amphibious/bootstrap.js +2 -0
  14. package/dist/runtime/mini/amphibious/get-page-query.d.ts +2 -0
  15. package/dist/runtime/mini/amphibious/get-page-query.js +16 -0
  16. package/dist/runtime/mini/capsule/create-vpt-page-config.d.ts +23 -0
  17. package/dist/runtime/mini/capsule/create-vpt-page-config.js +28 -0
  18. package/dist/runtime/mini/capsule/page.d.ts +3 -1
  19. package/dist/runtime/mini/capsule/page.js +2 -13
  20. package/dist/runtime/mini/capsule/prerender-to-data.d.ts +3 -0
  21. package/dist/runtime/mini/capsule/prerender-to-data.js +39 -0
  22. package/dist/runtime/mini/capsule/taro-runtime.d.ts +2 -1
  23. package/dist/runtime/mini/capsule/taro-runtime.js +2 -1
  24. package/dist/runtime/mini/dev/mini-hmr-runtime.d.ts +8 -1
  25. package/dist/runtime/mini/dev/mini-hmr-runtime.js +29 -2
  26. package/dist/runtime/mini/dev/modes/devtools/devtools-runtime.d.ts +1 -1
  27. package/dist/runtime/mini/dev/modes/devtools/devtools-runtime.js +1 -1
  28. package/dist/runtime/mini/dev/modes/devtools/{page-hmr.d.ts → inject-page-hmr.d.ts} +2 -1
  29. package/dist/runtime/mini/dev/modes/devtools/{page-hmr.js → inject-page-hmr.js} +15 -14
  30. package/dist/runtime/mini/native/app.js +1 -0
  31. package/dist/runtime/mini/native/component.js +1 -0
  32. package/dist/runtime/mini/native/custom-wrapper.js +1 -0
  33. package/dist/runtime/mini/native/page.js +1 -1
  34. package/package.json +5 -5
  35. package/dist/node/utils/assert-runtime-versions.d.ts +0 -3
@@ -0,0 +1,8 @@
1
+ import { type Logger, type Plugin } from 'vite';
2
+ declare const dependencyNames: readonly ['vite', 'rolldown', 'vite-plugin-taro-runtime'];
3
+ type DependencyVersions = Readonly<Record<(typeof dependencyNames)[number], string>>;
4
+ /** Reports untested dependency versions through Vite's configured logger without blocking startup. */
5
+ export declare function createDepsPlugin(): Plugin;
6
+ /** Report every mismatch independently so one untested dependency cannot hide another. */
7
+ export declare function warnDependencyVersions(actualVersions: DependencyVersions, logger: Pick<Logger, 'warn'>): void;
8
+ export {};
@@ -1,7 +1,7 @@
1
1
  import type { AstTransformResult } from '../../../utils/transform.ts';
2
2
  /**
3
3
  * In serve, a native Page may load its original physical capsule after HMR has already installed a newer component factory.
4
- * Taro captures the component at createPageConfig() time; without this lookup, that Page first mounts with stale source even
4
+ * The VPT factory passes the component to Taro at createVptPageConfig() time; without this lookup, that Page first mounts with stale source even
5
5
  * though the patch was acknowledged. Replace only the component argument, leaving the production capsule untouched.
6
6
  */
7
7
  export declare function injectDevPageComponent({ capsuleCode, componentId, capsuleId }: {
@@ -21,10 +21,8 @@ export declare function renderInitialDevtoolsPatches(): string;
21
21
  */
22
22
  export declare function renderDevtoolsPatches(buildId: string, patches: readonly PatchUpdate[]): string;
23
23
  /**
24
- * Injects Page handoff at the exact native registration edge rather than wrapping arbitrary user `Page` calls.
25
- *
26
- * The native shell has one stable `Page(pageConfig)` contract. Failing when that contract changes is intentional: silently
27
- * skipping this transform would let DevTools unload the live Taro Page and destroy React state while appearing to support HMR.
24
+ * Prepares the capsule's final config immediately before the native shell registers it.
25
+ * The shared shell owns this one registration call; no platform choice or import-source parsing belongs in HMR.
28
26
  */
29
27
  export declare function injectPageShellHmr(code: string): {
30
28
  code: string;
@@ -6,7 +6,7 @@ export type TaroContract = {
6
6
  componentsReactPath: string;
7
7
  targetRuntimePath: string;
8
8
  };
9
- /** Target-specific development runtimes; shared Mini entry IDs live beside their source modules. */
9
+ /** Development runtimes selected once by the target. */
10
10
  export type RuntimeContract = {
11
11
  devtoolsHmrRuntime: string;
12
12
  interpreterHmrRuntime: string;
@@ -1,5 +1,5 @@
1
1
  import { type Rolldown } from 'vite';
2
- /** Installs SystemJS, transport, and polyfills before native entries load capsules. */
2
+ /** Initializes SystemJS, transport, polyfills and native query capture before application capsules. */
3
3
  export declare const miniBootstrapId: string;
4
4
  /** Registers the native App using its generated configuration capsule. */
5
5
  export declare const miniAppShellId: string;
@@ -20,14 +20,14 @@ export declare const miniTaroRuntimeId: string;
20
20
  export declare const rolldownRuntimeId = "\0rolldown/runtime.js";
21
21
  /** Identifies the virtual binding shared by native files, SystemJS capsules and HMR factories. */
22
22
  export declare const vptGlobalBindingId = "\0vpt:global-binding";
23
- /** Generates the selected core-js imports loaded by bootstrap from the native polyfill chunk. */
23
+ /** Generates the selected core-js imports loaded before SystemJS from the native polyfill chunk. */
24
24
  export declare const miniPolyfillsId = "\0vpt:mini-polyfills";
25
- /** External bootstrap dependency emitted only after the bundled graph is finalized. */
25
+ /** External loader dependency emitted only after the bundled graph is finalized. */
26
26
  export declare const miniTransportId = "\0vpt:mini-transport";
27
- export declare const miniTransportOutputPath = "common/vpt/transport.js";
27
+ export declare const miniTransportOutputPath = "common/vpt-transport.js";
28
28
  /** Resolves the shared Taro facade's target initialization side effect. */
29
29
  export declare const taroTargetRuntimeId = "\0vpt:taro-target-runtime";
30
- /** Redirects Vite's injected browser preload helper to the bootstrap identity loader. */
30
+ /** Redirects Vite's injected browser preload helper to the SystemJS entry's identity loader. */
31
31
  export declare const vitePreloadId = "\0vite/preload-helper.js";
32
32
  /** Forces the native App shell entry to emit at the required root path. */
33
33
  export declare const appShellFileName = "app.js";
@@ -48,5 +48,5 @@ export declare const miniPolyfillSourceFilter: RegExp;
48
48
  export declare function isMiniPolyfillModule(moduleId: string): boolean;
49
49
  /** Groups framework modules by resolved package roots; Rolldown includes their dependencies. */
50
50
  export declare function isMiniFrameworkVendorModule(moduleId: string): boolean;
51
- /** Classifies each chunk by its compiler-owned modules; ordinary application chunks are normal capsules. O(M) time. */
51
+ /** Classifies compiler-owned entry facades and modules; ordinary application chunks are normal capsules. O(M) time. */
52
52
  export declare function classifyMiniModule(chunk: MiniChunk): MiniChunkKind;
@@ -5,7 +5,7 @@ import type { PackageLocation } from '../placer/placement.ts';
5
5
  * identities: the package-neutral final filename is the SystemJS registration ID, while the package-qualified filename
6
6
  * supplies the literal native require path. Preliminary hash placeholders must not escape into this late-generated file.
7
7
  *
8
- * Transport itself executes only as native CommonJS in common/vpt/, separate from the bundled chunk namespace. Together
8
+ * Transport itself executes only as native CommonJS in common/vpt-transport.js, outside the bundled graph. Together
9
9
  * with bootstrap it publishes native namespaces to SystemJS without re-evaluating their module bodies.
10
10
  *
11
11
  * Do not generate this table in renderChunk: that hook runs before Rolldown's final minifier/code generator, which can rewrite
@@ -7,8 +7,8 @@ import type { AstTransformResult } from '../../../utils/transform.ts';
7
7
  *
8
8
  * 1. Static ESM imports are hoisted into source-order `require` calls. Named imports remain property reads from the required
9
9
  * namespace so they observe current values. Default and namespace imports receive Babel-compatible CommonJS interop.
10
- * 2. An import whose target owns a capsule entry is not passed to native `require`. It becomes
11
- * bootstrap's `System.importSync(logicalChunkId)`, synchronously linking the capsule before the native lifecycle call.
10
+ * 2. An import targeting any capsule is not passed to native `require`. It becomes
11
+ * the loader's `System.importSync(logicalChunkId)`, synchronously linking the capsule before the native lifecycle call.
12
12
  * 3. Local exports are published at declaration and mutation points. Imported re-exports use getters, while assignments and
13
13
  * updates notify every alias without changing expression completion values or accidentally matching shadowed bindings.
14
14
  * 4. ESM top-level `this` becomes `undefined`. Direct imported calls and tags are explicitly unbound so converting an import
@@ -0,0 +1,2 @@
1
+ /** Creates an isolated consumer project under the repository-root tmp/ directory, outside node_modules. */
2
+ export declare function createTestProject(prefix: string): Promise<string>;
@@ -0,0 +1,2 @@
1
+ /** Shared scratch root; each test owns and removes only its own files beneath it. */
2
+ export declare const projectTempDir: string;
package/dist/options.d.ts CHANGED
@@ -35,6 +35,11 @@ export type VptPageOption = {
35
35
  * It does not translate configuration names between platforms.
36
36
  */
37
37
  config?: VptPageConfig;
38
+ /**
39
+ * Registers a native Page data factory for synchronous prerendering; disabled unless true.
40
+ * Requires host data-factory support (WX glass-easel). Rendering and effects may run before native onLoad.
41
+ */
42
+ prerender?: boolean;
38
43
  };
39
44
  /** Configures vpt for one build target. */
40
45
  export interface VptOptions {
@@ -1,3 +1,4 @@
1
1
  import '\0vpt:mini-polyfills';
2
+ export { getPageQuery } from './get-page-query.ts';
2
3
  export declare const System: any;
3
4
  export declare const __vitePreload: <Value>(load: () => Value) => Value;
@@ -8,6 +8,8 @@ import '\0vpt:mini-polyfills';
8
8
  import { transport } from '\0vpt:mini-transport';
9
9
  // Install the minimal SystemJS loader and its synchronous-import extension before any native entry requests a capsule.
10
10
  import { System as createdSystem } from '../systemjs/system-core.js';
11
+ // Install native query capture before App/Page capsules evaluate.
12
+ export { getPageQuery } from './get-page-query.js';
11
13
  // install the System on globalThis
12
14
  globalThis.System = createdSystem;
13
15
  // Publish the installed loader as an explicit native dependency; callers need no ambient globalThis binding.
@@ -0,0 +1,2 @@
1
+ /** Reads the complete native query for the Page instance currently being created. */
2
+ export declare function getPageQuery(): Record<string, unknown>;
@@ -0,0 +1,16 @@
1
+ /// <reference types="vite/client" />
2
+ // WX Page shells share one listener and the latest query supplied by native routing.
3
+ let pageQuery;
4
+ if (typeof wx !== 'undefined' && typeof wx.onBeforePageLoad === 'function') {
5
+ const capturePageQuery = ({ query }) => {
6
+ pageQuery = query;
7
+ };
8
+ wx.onBeforePageLoad(capturePageQuery);
9
+ if (import.meta.hot) {
10
+ import.meta.hot.dispose(() => wx.offBeforePageLoad(capturePageQuery));
11
+ }
12
+ }
13
+ /** Reads the complete native query for the Page instance currently being created. */
14
+ export function getPageQuery() {
15
+ return pageQuery;
16
+ }
@@ -0,0 +1,23 @@
1
+ import { createPageConfig as createTaroPageConfig, type MiniElementData } from 'vite-plugin-taro-runtime/runtime/mini';
2
+ export type PageData = {
3
+ app: {
4
+ nn: string;
5
+ cn: MiniElementData['cn'];
6
+ };
7
+ page: {
8
+ cn: MiniElementData['cn'];
9
+ };
10
+ };
11
+ export type VptPageOptions = {
12
+ path: string;
13
+ config: Record<string, unknown>;
14
+ prerender: boolean;
15
+ };
16
+ /** Prepares the original Taro config for native registration, capturing the seed when prerendering. */
17
+ export declare function createVptPageConfig(component: Parameters<typeof createTaroPageConfig>[0], vptPageOptions: {
18
+ path: string;
19
+ config: Record<string, unknown>;
20
+ prerender: boolean;
21
+ }): import("vite-plugin-taro-runtime/runtime/mini").PageInstance & {
22
+ __vpt_meta: {};
23
+ };
@@ -0,0 +1,28 @@
1
+ import { createPageConfig as createTaroPageConfig } from 'vite-plugin-taro-runtime/runtime/mini';
2
+ import { prerenderToData } from './prerender-to-data.js';
3
+ /** Prepares the original Taro config for native registration, capturing the seed when prerendering. */
4
+ export function createVptPageConfig(component, vptPageOptions) {
5
+ const { path: routePath, config: pageConfig, prerender } = vptPageOptions;
6
+ /*
7
+ * Generated Page templates invoke Taro's unchanged recursive component, whose input is one compact node selected by i.nn.
8
+ * App JSX does not have that cardinality: it may return one or many top-level hosts, and the private Page outlet may occur at
9
+ * any depth within them. vpt_fragment is therefore a native-template-only collection adapter. Its fixed nn selects a transparent
10
+ * template that iterates cn while one surrounding component owns the Page-content boundary. Runtime projection markers relay
11
+ * that content only through the App branch containing the outlet. Without the fragment, each App root would need a separate
12
+ * recursive component and potential Page-content copy, or the component would need an App-specific collection mode.
13
+ *
14
+ * This record is not a Taro host: it has no Fiber, event source, ref, lifecycle, native element, or keyed parent collection.
15
+ * It consequently needs no sid. Only cn is seeded and updated; nn remains the stable generic-template discriminator.
16
+ */
17
+ const initialData = { app: { nn: 'vpt_fragment', cn: [] }, page: { cn: [] } };
18
+ const config = Object.assign(createTaroPageConfig(component, routePath, initialData, pageConfig), {
19
+ __vpt_meta: {}
20
+ });
21
+ // Keep the one-shot prerender identity out of native registration.
22
+ Object.defineProperty(config, '__vpt_meta', { enumerable: false });
23
+ if (prerender) {
24
+ // Keep render inputs in the closure; native onLoad only needs the prepared identity.
25
+ config.data = () => prerenderToData(config, component, routePath, initialData);
26
+ }
27
+ return config;
28
+ }
@@ -1,3 +1,5 @@
1
1
  import './app.ts';
2
- declare const config: import("vite-plugin-taro-runtime/runtime/mini").PageInstance;
2
+ declare const config: import("vite-plugin-taro-runtime/runtime/mini").PageInstance & {
3
+ __vpt_meta: {};
4
+ };
3
5
  export default config;
@@ -2,17 +2,6 @@
2
2
  import './app.js';
3
3
  // @ts-expect-error: The Mini Program build replaces this private import with the configured Page component.
4
4
  import PageComponent from '\0vpt:page-component';
5
- import { createPageConfig } from './taro-runtime.js';
6
- /*
7
- * Generated Page templates invoke Taro's unchanged recursive component, whose input is one compact node selected by i.nn.
8
- * App JSX does not have that cardinality: it may return one or many top-level hosts, and the private Page outlet may occur at
9
- * any depth within them. vpt_fragment is therefore a native-template-only collection adapter. Its fixed nn selects a transparent
10
- * template that iterates cn while one surrounding component owns the Page-content boundary. Runtime projection markers relay
11
- * that content only through the App branch containing the outlet. Without the fragment, each App root would need a separate
12
- * recursive component and potential Page-content copy, or the component would need an App-specific collection mode.
13
- *
14
- * This record is not a Taro host: it has no Fiber, event source, ref, lifecycle, native element, or keyed parent collection.
15
- * It consequently needs no sid. Only cn is seeded and updated; nn remains the stable generic-template discriminator.
16
- */
17
- const config = createPageConfig(PageComponent, __VPT_PAGE_PATH__, { app: { nn: 'vpt_fragment', cn: [] }, page: { cn: [] } }, __VPT_PAGE_CONFIG__);
5
+ import { createVptPageConfig } from './taro-runtime.js';
6
+ const config = createVptPageConfig(PageComponent, __VPT_PAGE_OPTIONS__);
18
7
  export default config;
@@ -0,0 +1,3 @@
1
+ import type { createVptPageConfig, PageData } from './create-vpt-page-config.ts';
2
+ /** Commits one Page into the existing App root and returns its initial native data without dispatching lifecycles. */
3
+ export declare function prerenderToData(config: ReturnType<typeof createVptPageConfig>, component: Parameters<typeof createVptPageConfig>[0], route: string, initialData: PageData): PageData;
@@ -0,0 +1,39 @@
1
+ import { flushSync } from 'vite-plugin-taro-runtime/react';
2
+ import { addLeadingSlash, Current, document, getOnHideEventKey, getOnReadyEventKey, getOnShowEventKey, getPath, hydrate, incrementId } from 'vite-plugin-taro-runtime/runtime/mini';
3
+ import { getPageQuery } from '../amphibious/bootstrap.js';
4
+ // One counter distinguishes native instances even when their route, query and creation timestamp match.
5
+ const pageId = incrementId();
6
+ /** Commits one Page into the existing App root and returns its initial native data without dispatching lifecycles. */
7
+ export function prerenderToData(config, component, route, initialData) {
8
+ if (!Current.app?.mount) {
9
+ return initialData;
10
+ }
11
+ const params = { ...getPageQuery(), $taroTimestamp: Date.now() };
12
+ const path = getPath(route, { ...params, $vptPage: pageId() });
13
+ // Retain this instance identity until native onLoad consumes it without remounting the Page.
14
+ config.__vpt_meta.prerenderIdentity = { path, params };
15
+ Current.router = {
16
+ params,
17
+ path: addLeadingSlash(route),
18
+ $taroPath: path,
19
+ onReady: getOnReadyEventKey(route),
20
+ onShow: getOnShowEventKey(route),
21
+ onHide: getOnHideEventKey(route)
22
+ };
23
+ flushSync(() => {
24
+ Current.app.mount(component, path, () => { });
25
+ });
26
+ const page = document.getElementById(path);
27
+ const app = page?.parentNode?._root;
28
+ // Cold App startup or Suspense without a committed fallback keeps the seed until a later React commit.
29
+ if (!page || !app) {
30
+ return initialData;
31
+ }
32
+ // Both hosts are elements; hydrate's shared return type also includes text nodes.
33
+ const appData = hydrate(app);
34
+ const pageData = hydrate(page);
35
+ return {
36
+ app: { ...initialData.app, cn: appData.cn },
37
+ page: { ...initialData.page, cn: pageData.cn }
38
+ };
39
+ }
@@ -4,4 +4,5 @@ import '\0vpt:taro-target-runtime';
4
4
  import 'vite-plugin-taro-runtime/plugin-html/runtime';
5
5
  export { createReactApp } from 'vite-plugin-taro-runtime/plugin-framework-react/runtime';
6
6
  export { default as ReactDOM } from 'vite-plugin-taro-runtime/react';
7
- export { createPageConfig, createRecursiveComponentConfig } from 'vite-plugin-taro-runtime/runtime/mini';
7
+ export { createRecursiveComponentConfig } from 'vite-plugin-taro-runtime/runtime/mini';
8
+ export { createVptPageConfig } from './create-vpt-page-config.ts';
@@ -8,8 +8,9 @@ import '\0vpt:taro-target-runtime';
8
8
  import 'vite-plugin-taro-runtime/plugin-html/runtime';
9
9
  export { createReactApp } from 'vite-plugin-taro-runtime/plugin-framework-react/runtime';
10
10
  export { default as ReactDOM } from 'vite-plugin-taro-runtime/react';
11
- export { createPageConfig, createRecursiveComponentConfig } from 'vite-plugin-taro-runtime/runtime/mini';
11
+ export { createRecursiveComponentConfig } from 'vite-plugin-taro-runtime/runtime/mini';
12
12
  import { customWrapperCache } from 'vite-plugin-taro-runtime/runtime/mini';
13
+ export { createVptPageConfig } from './create-vpt-page-config.js';
13
14
  // DevTools HMR runs in the bootstrap chunk, so publish the application graph's cache once on the language global.
14
15
  if (process.env.NODE_ENV === 'development') {
15
16
  Reflect.set(globalThis, Symbol.for('customWrapperCache'), customWrapperCache);
@@ -26,7 +26,10 @@ declare class MiniHotContext {
26
26
  /** Stable module identity and shared sparse index used only when this context accepts. */
27
27
  private readonly moduleId;
28
28
  private readonly acceptingContexts;
29
- constructor(moduleId: string, acceptingContexts: Map<string, MiniHotContext>);
29
+ private readonly disposeCallbacks;
30
+ constructor(moduleId: string, acceptingContexts: Map<string, MiniHotContext>, disposeCallbacks: Map<string, Array<() => void>>);
31
+ /** Cleanup registration is independent of accepting updates; passive dependencies ay own native listenemrs. */
32
+ dispose(callback: () => void): void;
30
33
  accept(callback?: AcceptCallback): void;
31
34
  /** Invokes this old context's callbacks; invalidation aborts the update synchronously. */
32
35
  runAccept(moduleExports: unknown): void;
@@ -67,6 +70,7 @@ export declare class MiniHmrRuntime extends DevRuntime {
67
70
  * the new one. Passive contexts are never retained, so space is O(number of boundaries).
68
71
  */
69
72
  private readonly moduleHotContexts;
73
+ private readonly moduleDisposeCallbacks;
70
74
  /** Immutable platform socket constructor retained until the running App initializes. */
71
75
  private readonly connectSocket;
72
76
  constructor(connectSocket: ConnectMiniSocket);
@@ -76,6 +80,9 @@ export declare class MiniHmrRuntime extends DevRuntime {
76
80
  * calling accept registers the new context after the apply plan captured old callbacks.
77
81
  */
78
82
  createModuleHotContext(moduleId: string): MiniHotContext;
83
+ /** Native listener cleanup runs before the old exports are evicted, including cold-capsule replacement. */
84
+ removeModuleCache(moduleId: string): void;
85
+ private disposeModule;
79
86
  /** Resolves a cold Page against patches installed before its physical capsule first executed. */
80
87
  resolvePageComponent(moduleId: string, baseline: ElementType): ElementType;
81
88
  /** Computes accepting boundaries and every executed module that must be re-armed. */
@@ -29,9 +29,21 @@ class MiniHotContext {
29
29
  /** Stable module identity and shared sparse index used only when this context accepts. */
30
30
  moduleId;
31
31
  acceptingContexts;
32
- constructor(moduleId, acceptingContexts) {
32
+ disposeCallbacks;
33
+ constructor(moduleId, acceptingContexts, disposeCallbacks) {
33
34
  this.moduleId = moduleId;
34
35
  this.acceptingContexts = acceptingContexts;
36
+ this.disposeCallbacks = disposeCallbacks;
37
+ }
38
+ /** Cleanup registration is independent of accepting updates; passive dependencies ay own native listenemrs. */
39
+ dispose(callback) {
40
+ const callbacks = this.disposeCallbacks.get(this.moduleId);
41
+ if (callbacks) {
42
+ callbacks.push(callback);
43
+ }
44
+ else {
45
+ this.disposeCallbacks.set(this.moduleId, [callback]);
46
+ }
35
47
  }
36
48
  accept(callback) {
37
49
  if (!this.acceptCallbacks) {
@@ -76,6 +88,8 @@ export class MiniHmrRuntime extends DevRuntime {
76
88
  * the new one. Passive contexts are never retained, so space is O(number of boundaries).
77
89
  */
78
90
  moduleHotContexts = new Map();
91
+ // Retain only modules that registered cleanup; eviction consumes their callbacks before replacement factories execute.
92
+ moduleDisposeCallbacks = new Map();
79
93
  /** Immutable platform socket constructor retained until the running App initializes. */
80
94
  connectSocket;
81
95
  constructor(connectSocket) {
@@ -94,8 +108,21 @@ export class MiniHmrRuntime extends DevRuntime {
94
108
  */
95
109
  createModuleHotContext(moduleId) {
96
110
  // A new execution supersedes the old boundary before it decides whether to accept.
111
+ this.disposeModule(moduleId);
97
112
  this.moduleHotContexts.delete(moduleId);
98
- return new MiniHotContext(moduleId, this.moduleHotContexts);
113
+ return new MiniHotContext(moduleId, this.moduleHotContexts, this.moduleDisposeCallbacks);
114
+ }
115
+ /** Native listener cleanup runs before the old exports are evicted, including cold-capsule replacement. */
116
+ removeModuleCache(moduleId) {
117
+ this.disposeModule(moduleId);
118
+ super.removeModuleCache(moduleId);
119
+ }
120
+ disposeModule(moduleId) {
121
+ const callbacks = this.moduleDisposeCallbacks.get(moduleId);
122
+ this.moduleDisposeCallbacks.delete(moduleId);
123
+ for (const callback of callbacks ?? []) {
124
+ callback();
125
+ }
99
126
  }
100
127
  /** Resolves a cold Page against patches installed before its physical capsule first executed. */
101
128
  resolvePageComponent(moduleId, baseline) {
@@ -1,5 +1,5 @@
1
1
  import { type ConnectMiniSocket, MiniHmrRuntime, type RuntimePatch } from '../../mini-hmr-runtime.ts';
2
- import { injectPageHmr as injectDevtoolsPageHmr } from './page-hmr.ts';
2
+ import { injectPageHmr as injectDevtoolsPageHmr } from './inject-page-hmr.ts';
3
3
  /**
4
4
  * One physical DevTools patch. DevTools compiles `factory` as native project JavaScript; invoking it registers updated Rolldown
5
5
  * factories but does not execute application modules, allowing the shared runtime to install a complete batch before one render.
@@ -4,7 +4,7 @@
4
4
  * what allows module and React state to survive repeated native Page registration.
5
5
  */
6
6
  import { MiniHmrRuntime } from '../../mini-hmr-runtime.js';
7
- import { injectPageHmr as injectDevtoolsPageHmr } from './page-hmr.js';
7
+ import { injectPageHmr as injectDevtoolsPageHmr } from './inject-page-hmr.js';
8
8
  /** Adapts physical DevTools patch modules and Page replacement onto the shared Mini Program HMR runtime. */
9
9
  class DevtoolsHmrRuntime extends MiniHmrRuntime {
10
10
  /** Applies one Page-delivered payload before the native shell imports its route capsule. */
@@ -1,5 +1,6 @@
1
1
  type NativePage = {
2
2
  data: Record<string, unknown>;
3
+ groupUpdates?: unknown;
3
4
  };
4
5
  declare const pageHmrStateKey: unique symbol;
5
6
  type PageHmrState = {
@@ -9,7 +10,7 @@ type PageHmrState = {
9
10
  mountedPage: NativePage | undefined;
10
11
  };
11
12
  type HmrPageConfig = {
12
- data: Record<string, unknown>;
13
+ data: Record<string, unknown> | (() => Record<string, unknown>);
13
14
  onUnload?: unknown;
14
15
  onLoad?: unknown;
15
16
  onShow?: unknown;
@@ -1,10 +1,15 @@
1
1
  // A symbol cannot collide with native or Taro string-named Page options, and it lets a later injection recover the exact
2
2
  // config-local state without a route map or runtime-global WeakMap.
3
3
  const pageHmrStateKey = Symbol('vpt.pageHmrState');
4
+ /** Detects the active glass-easel framework through its native instance API, not compiler configuration. */
5
+ function isGlassEasel(page) {
6
+ return typeof page.groupUpdates === 'function';
7
+ }
4
8
  /** Calls a native lifecycle with the same Page bound to `this` and the same arguments. */
5
9
  function forward(handler, page, args) {
6
- if (typeof handler === 'function')
10
+ if (typeof handler === 'function') {
7
11
  handler.apply(page, args);
12
+ }
8
13
  }
9
14
  /** Tracks the mounted native Page and prepares its static config for DevTools re-registration. */
10
15
  export function injectPageHmr(config) {
@@ -15,20 +20,15 @@ export function injectPageHmr(config) {
15
20
  * The static config can outlive a native Page instance. Before its first ordinary onLoad, or after a real onUnload,
16
21
  * there is no mounted Page or current view-model to carry into another registration. Leave the lifecycle gate unarmed
17
22
  * and preserve the config's existing initial data so a future real onLoad still enters Taro normally.
23
+ * Glass-easel retains native instances without replaying lifecycles, so it needs neither suppression nor snapshot handoff.
18
24
  */
19
- if (!mountedPage) {
25
+ if (!mountedPage || isGlassEasel(mountedPage)) {
20
26
  return config;
21
27
  }
22
- /*
23
- * Arm the lifecycle wrappers on this exact static config before it is passed back to `Page(config)`. DevTools then
24
- * triggers an unload/load/show sequence for that native re-registration: unload and load observe `true` and return before
25
- * entering Taro, preserving the mounted React tree and its original Page connection; show consumes the one-shot gate by
26
- * restoring `false`. Ordinary navigation never enters this branch, and every Page config owns an independent state, so
27
- * no route map, global phase, or Page identity comparison participates in the decision.
28
- */
28
+ // Legacy hosts replay unload/load/show; onShow ends that native cycle.
29
29
  existingState.isReregistering = true;
30
30
  /*
31
- * `Page(config)` reads `config.data` as the initial native view-model for this registration. The Page owns current app
31
+ * Native registration reads `config.data` as the initial view-model for this registration. The Page owns current app
32
32
  * and ordinary Page fields, while each mounted CustomWrapper owns the current snapshot below its native boundary. Join
33
33
  * those already-serialized snapshots before registration so the native host never publishes the stale initial placeholder tree.
34
34
  * This lifecycle handoff performs no setData call and runs before React Refresh can publish another logical tree.
@@ -43,14 +43,16 @@ export function injectPageHmr(config) {
43
43
  /*
44
44
  * This mutable config-local state spans ordinary Page mount/unmount and one DevTools re-registration lifecycle. Lifecycle
45
45
  * wrappers close over this exact object, while a later `injectPageHmr(config)` retrieves it through the symbol and avoids
46
- * wrapping twice. Config-local ownership avoids route maps, runtime-global phase flags, and state shared by independent Page
46
+ * wrapping twice. Only legacy hosts need the re-registration lifecycle gate.
47
+ * Config-local ownership avoids route maps, runtime-global phase flags, and state shared by independent Page
47
48
  * registrations; the state becomes unreachable together with its static config.
48
49
  */
49
50
  const state = {
50
51
  isReregistering: false,
51
52
  mountedPage: undefined
52
53
  };
53
- config[pageHmrStateKey] = state;
54
+ // Handoff state belongs to the static config, not the properties copied into native registration.
55
+ Object.defineProperty(config, pageHmrStateKey, { value: state });
54
56
  config.onUnload = function (...args) {
55
57
  if (state.isReregistering) {
56
58
  return;
@@ -67,8 +69,7 @@ export function injectPageHmr(config) {
67
69
  };
68
70
  config.onShow = function (...args) {
69
71
  if (state.isReregistering) {
70
- // DevTools' synthetic unload/load/show cycle ends here. Consume the gate exactly once so the next real navigation
71
- // forwards ordinary Taro lifecycles instead of being mistaken for the same registration.
72
+ // DevTools' synthetic unload/load/show cycle ends here; the next real navigation forwards lifecycles.
72
73
  state.isReregistering = false;
73
74
  return;
74
75
  }
@@ -1,3 +1,4 @@
1
+ // Initialize the VPT runtime before loading the native configuration capsule.
1
2
  import '../amphibious/bootstrap.js';
2
3
  import appConfig from '../capsule/app.js';
3
4
  App(appConfig);
@@ -1,3 +1,4 @@
1
+ // Initialize the VPT runtime before loading the native configuration capsule.
1
2
  import '../amphibious/bootstrap.js';
2
3
  import { componentConfig } from '../capsule/component.js';
3
4
  Component(componentConfig);
@@ -1,3 +1,4 @@
1
+ // Initialize the VPT runtime before loading the native configuration capsule.
1
2
  import '../amphibious/bootstrap.js';
2
3
  import { customWrapperConfig } from '../capsule/component.js';
3
4
  Component(customWrapperConfig);
@@ -1,4 +1,4 @@
1
+ // biome-ignore assist/source/organizeImports: VPT runtime must initialize before the Page capsule.
1
2
  import '../amphibious/bootstrap.js';
2
- // @ts-expect-error: The active Mini contract resolves the route-specific Page capsule.
3
3
  import pageConfig from '\0vpt:page-capsule';
4
4
  Page(pageConfig);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-taro",
3
- "version": "0.7.10",
3
+ "version": "0.7.12-beta.0",
4
4
  "author": "sep2",
5
5
  "description": "Vite 8 plugin for building one React/Taro codebase for WeChat, Alipay, TikTok, and H5 targets.",
6
6
  "type": "module",
@@ -64,10 +64,10 @@
64
64
  "core-js": "^3.50.0",
65
65
  "lightningcss": "1.33.0",
66
66
  "react-reconciler": "0.34.0",
67
- "rolldown": "1.2.11",
67
+ "rolldown": "1.2.12",
68
68
  "sval": "0.6.12",
69
69
  "tailwindcss": "^4.3.3",
70
- "vite-plugin-taro-runtime": "0.7.10"
70
+ "vite-plugin-taro-runtime": "0.7.12-beta.0"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "react": "^19.3.0",
@@ -98,8 +98,8 @@
98
98
  },
99
99
  "scripts": {
100
100
  "build": "node ../../scripts/sync-plugin-readme.ts && node ./scripts/build.ts",
101
- "test": "node --test --test-concurrency=2 --test-reporter=dot",
102
- "test:coverage": "c8 node --test --test-concurrency=2",
101
+ "test": "node --test --test-concurrency=4 --test-reporter=dot",
102
+ "test:coverage": "c8 node --test --test-concurrency=4",
103
103
  "typecheck": "tsc --project tsconfig.json --noEmit"
104
104
  }
105
105
  }
@@ -1,3 +0,0 @@
1
- /** Verifies that Vite uses the versions pinned by VPT. */
2
- export declare function assertRuntimeVersions(): void;
3
- export declare function assertRuntimeVersionValues(actualViteVersion: string, actualRolldownVersion: string): void;