vite-plugin-taro 0.6.6 → 0.6.8

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.
@@ -28,11 +28,11 @@ export function createH5TargetPlugins(options) {
28
28
  logLevel: 'warn'
29
29
  }),
30
30
  ...createH5SupportPlugins(),
31
- createH5TargetPlugin(options)
31
+ createH5Plugin(options)
32
32
  ];
33
33
  }
34
34
  /** Configures H5 resolution and supplies the specialized physical application entry. */
35
- function createH5TargetPlugin(options) {
35
+ function createH5Plugin(options) {
36
36
  const moduleResolver = createModuleResolver(options);
37
37
  return {
38
38
  name: 'vpt:h5',
@@ -42,6 +42,10 @@ function createH5TargetPlugin(options) {
42
42
  resolve: {
43
43
  mainFields: ['main:h5', 'browser', 'module', 'jsnext:main', 'jsnext'],
44
44
  alias: [
45
+ {
46
+ find: /^@tarojs\/runtime$/,
47
+ replacement: packageRequire.resolve('@tarojs/runtime/dist/runtime.esm.js')
48
+ },
45
49
  {
46
50
  find: /^@tarojs\/components$/,
47
51
  replacement: packageRequire.resolve('@tarojs/components/lib/react')
@@ -53,10 +57,13 @@ function createH5TargetPlugin(options) {
53
57
  ]
54
58
  },
55
59
  optimizeDeps: {
56
- // The compiler-owned H5 app and Taro facade are injected after Vite's initial HTML scan. Prebundle
57
- // the facade's platform backend as one boundary so its CommonJS implementation details receive
58
- // interop without duplicating their package list. ReactDOM needs the same treatment for the H5 app.
59
- include: ['@tarojs/plugin-platform-h5/dist/runtime/apis', 'react-dom/client'],
60
+ /*
61
+ * The compiler-owned H5 app and Taro facade are injected after Vite's initial HTML scan, so declare their
62
+ * optimization entries explicitly. The platform backend needs CommonJS interop, ReactDOM is imported by
63
+ * the hidden app, and @tarojs/runtime must be a first-class entry so subsequently discovered Taro packages
64
+ * share its Current singleton instead of embedding private copies in their optimized chunks.
65
+ */
66
+ include: ['@tarojs/plugin-platform-h5/dist/runtime/apis', '@tarojs/runtime', 'react-dom/client'],
60
67
  // Dependency optimization is its own Rolldown build and does not run application transform plugins.
61
68
  // Register the same adapter there so optimized Taro components cannot embed Stencil's original client.
62
69
  rolldownOptions: {
@@ -4,11 +4,8 @@ import { toRootRelativePath } from './relative-root.js';
4
4
  /** Creates every configured native JSON asset. */
5
5
  export function createJsonAssets({ options, subpackages, nativeComponents }) {
6
6
  return [
7
- createJsonAsset('app.json', {
8
- ...createAppConfig(options),
9
- ...(subpackages.length > 0 ? { subPackages: subpackages } : {})
10
- }),
11
- ...options.pages.map((page) => createJsonAsset(`${page.path}.json`, createPageJson(page, nativeComponents))),
7
+ createJsonAsset('app.json', createAppJson({ options: options, subpackages: subpackages, nativeComponents: nativeComponents })),
8
+ ...options.pages.map((page) => createJsonAsset(`${page.path}.json`, createPageJson(page))),
12
9
  createJsonAsset('project.config.json', options.projectConfigJson),
13
10
  ...(options.projectPrivateConfigJson
14
11
  ? [createJsonAsset('project.private.config.json', options.projectPrivateConfigJson)]
@@ -16,20 +13,43 @@ export function createJsonAssets({ options, subpackages, nativeComponents }) {
16
13
  ...(options.sitemapJson ? [createJsonAsset('sitemap.json', options.sitemapJson)] : [])
17
14
  ];
18
15
  }
19
- /** Creates Page JSON with generated Taro and native component registrations. */
20
- function createPageJson(page, nativeComponents) {
21
- const usingComponents = isJsonObject(page.config.usingComponents) ? page.config.usingComponents : {};
16
+ /** Creates App JSON with globally inherited native registrations and generated subpackages. */
17
+ function createAppJson({ options, subpackages, nativeComponents }) {
18
+ const appConfig = createAppConfig(options);
19
+ const nativeUsingComponents = nativeComponents.map(({ name, componentPath }) => [name, componentPath]);
22
20
  // Cross-package components require a placeholder while WeChat downloads their generated subpackage. Paths are
23
21
  // root-absolute, so remove the leading slash before testing the output-relative subpackage prefix.
24
22
  // https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/async.html
25
23
  // https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/placeholder.html
26
- const placeholderEntries = nativeComponents.flatMap(({ name, componentPath }) => isGeneratedSubpackageFile(componentPath.slice(1)) ? [[name, 'view']] : []);
24
+ const componentPlaceholders = nativeComponents.flatMap(({ name, componentPath }) => isGeneratedSubpackageFile(componentPath.slice(1)) ? [[name, 'view']] : []);
25
+ return {
26
+ ...appConfig,
27
+ ...(nativeUsingComponents.length > 0
28
+ ? {
29
+ usingComponents: {
30
+ ...(isJsonObject(appConfig.usingComponents) ? appConfig.usingComponents : {}),
31
+ ...Object.fromEntries(nativeUsingComponents)
32
+ }
33
+ }
34
+ : {}),
35
+ ...(componentPlaceholders.length > 0
36
+ ? {
37
+ componentPlaceholder: {
38
+ ...(isJsonObject(appConfig.componentPlaceholder) ? appConfig.componentPlaceholder : {}),
39
+ ...Object.fromEntries(componentPlaceholders)
40
+ }
41
+ }
42
+ : {}),
43
+ ...(subpackages.length > 0 ? { subPackages: subpackages } : {})
44
+ };
45
+ }
46
+ /** Preserves configured Page JSON and registers only Taro's local recursive component entry. */
47
+ function createPageJson(page) {
48
+ const usingComponents = isJsonObject(page.config.usingComponents) ? page.config.usingComponents : {};
27
49
  return {
28
50
  ...page.config,
29
- ...(placeholderEntries.length > 0 ? { componentPlaceholder: Object.fromEntries(placeholderEntries) } : {}),
30
51
  usingComponents: {
31
52
  ...usingComponents,
32
- ...Object.fromEntries(nativeComponents.map(({ name, componentPath }) => [name, componentPath])),
33
53
  comp: toRootRelativePath(page.path, 'comp')
34
54
  }
35
55
  };
@@ -9,7 +9,7 @@ const taroComponentsModulePath = packageRequire.resolve('@tarojs/plugin-platform
9
9
  export function createTemplateAssets(bundle, options, nativeComponents) {
10
10
  const templateBuilder = createTemplateBuilder();
11
11
  return [
12
- createAsset('base.wxml', templateBuilder.buildTemplate(collectTemplateComponentConfig(bundle, nativeComponents))),
12
+ createAsset('base.wxml', templateBuilder.buildBaseTemplate(collectTemplateComponentConfig(bundle, nativeComponents))),
13
13
  createAsset('utils.wxs', templateBuilder.buildXScript()),
14
14
  createAsset('comp.wxml', templateBuilder.buildBaseComponentTemplate('.wxml')),
15
15
  createAsset('comp.json', renderJson(createComponentJson())),
@@ -22,7 +22,106 @@ export function createTemplateAssets(bundle, options, nativeComponents) {
22
22
  ])
23
23
  ];
24
24
  }
25
- /** Creates the Taro WeChat template builder without invoking its Webpack integration. */
25
+ /**
26
+ * Adapts Taro's stock template builder to the WXML half of WX App wrapping without changing its recursive renderer.
27
+ *
28
+ * End-to-end contract
29
+ * -------------------
30
+ * React and Taro retain one in-memory ownership tree:
31
+ *
32
+ * App React root
33
+ * -> App host records
34
+ * -> vpt_page_outlet host at App {children}
35
+ * -> independently scheduled Taro Page roots
36
+ *
37
+ * The patched WX document makes the singleton App host a TaroRootElement. App host mutations therefore batch under app.*
38
+ * and fan out to every mounted native Page. Each Page root remains its own TaroRootElement and emits only page.*. The React
39
+ * host renderer marks the outlet and its App ancestors with an ordinary compact `vo` prop after each commit, while patched
40
+ * hydrate() stops at vpt_page_outlet. Page roots remain attached for React Context, lifecycle, events, removal, refs, effects,
41
+ * and HMR without entering app data.
42
+ *
43
+ * Build-time output
44
+ * -----------------
45
+ * createTemplateAssets still asks one builder for the normal five products: shared base.wxml, utils.wxs, comp.wxml,
46
+ * comp.json, and each Page WXML. This adapter specializes only the two products that own the native join:
47
+ *
48
+ * - shared base.wxml receives branch-local slot forwarding plus vpt_fragment and vpt_page_outlet template definitions;
49
+ * - each Page WXML replaces Taro's root:root entry with one generic comp bound to app and one caller-owned taro_tmpl bound
50
+ * to page as that component's default slot.
51
+ *
52
+ * utils.wxs remains Taro's normal compact-node dispatcher. comp.wxml and comp.json remain Taro's generic depth-reset
53
+ * component and still know only i, l, virtual-host behavior, and eh event dispatch. There is one shared template namespace,
54
+ * no Page-specific base file, no App/Page mode property, and no Page object threaded through App template data.
55
+ *
56
+ * First native Page
57
+ * -----------------
58
+ * createPageConfig starts the native Page with:
59
+ *
60
+ * app = { nn: 'vpt_fragment', cn: [] }
61
+ * page = { cn: [] }
62
+ *
63
+ * nn lets unchanged comp dispatch one input object even though App JSX may produce one or many root hosts. The record
64
+ * is WXML-only rather than a Taro host, so it needs no sid. After React commits the Page root below the outlet, the framework
65
+ * queues a lazy hydrate(AppRoot).cn value beside the Page root's already-pending page.* payloads. Taro drains both through the
66
+ * native Page's existing first setData, making App wrapping and Page content appear atomically.
67
+ *
68
+ * Native WXML execution
69
+ * ---------------------
70
+ * Data/slot ownership and named-template ownership intentionally travel through different scopes:
71
+ *
72
+ * Page WXML (owns app, page, Page eh, and Page-content light DOM)
73
+ * -> <comp i="{{app}}"> (crosses into a virtual custom-component scope)
74
+ * -> comp.wxml (owns App eh, imports unchanged utils.wxs and shared base.wxml)
75
+ * -> tmpl_0_vpt_fragment (iterates the real App compact roots in app.cn)
76
+ * -> stock Taro templates (render App hosts and recurse through their cn arrays)
77
+ * -> depth-reset <comp i="{{i}}" l="{{l}}">
78
+ * -> forwards <slot /> only when this compact subtree root has i.vo
79
+ * -> tmpl_0_vpt_page_outlet at React's exact {children} position
80
+ * -> <slot />
81
+ * -> caller-owned <template is="taro_tmpl" data="{{root:page}}" />
82
+ * -> stock Taro templates render this native Page's page.cn records
83
+ *
84
+ * The virtual comp and both private templates add no native layout node. `vo` is part of the existing compact node i, not a
85
+ * component property or template context. App events execute through comp.eh; slotted Page events retain the native Page's
86
+ * eh. Both resolve the original Taro sid through the same event source.
87
+ *
88
+ * Named-template scope
89
+ * --------------------
90
+ * Slots transfer caller-owned light DOM only. They do not transfer the caller's named-template table or WXS modules. App
91
+ * dispatch runs inside comp.wxml, so vpt_fragment and vpt_page_outlet must live in base.wxml imported by that component.
92
+ * Putting those definitions in buildPageTemplate produces Page WXML that compiles, but component runtime dispatch fails with
93
+ * `Template tmpl_0_vpt_fragment not found`. Shared base.wxml makes the names visible in the root and every depth-reset comp
94
+ * scope and emits them once rather than once per Page.
95
+ *
96
+ * Projection-spine ownership
97
+ * --------------------------
98
+ * React's host renderer runs after the final commit tree exists and caches the outlet-to-root Taro host ancestor array. It
99
+ * skips the unchanged root-side suffix, gives old leaf-side nodes the ordinary host prop vo=false, and gives new ones vo=true.
100
+ * Taro's existing lazy structural hydration runs later and therefore serializes those props without projection-specific
101
+ * scheduler or hydrate behavior. If React replaces the outlet host while moving it, the renderer finds the unique new marker
102
+ * once and caches its new ancestor array. At a depth reset, i.vo makes forwarding an O(1) local decision; WXML never searches
103
+ * descendants, and Page trees instantiate no unnamed slot.
104
+ *
105
+ * Steady-state updates and navigation
106
+ * -----------------------------------
107
+ * A page.* setData updates only the caller-owned Page template inside the slot. app is not passed through that template and
108
+ * comp.i does not change, so Page updates cannot invalidate App recursion. Ordinary app.* payloads and outlet-spine marker
109
+ * changes remain in Taro's granular batch before it fans out to every retained native Page. Adding or removing a React Page
110
+ * root mutates the outlet only in memory, and the runtime suppresses that marker's native child update. A newly pushed Page
111
+ * receives the latest complete App snapshot in its initial batch, while existing and hidden Pages require no navigation
112
+ * synchronization.
113
+ *
114
+ * Method responsibilities
115
+ * -----------------------
116
+ * 1. buildBaseTemplate wraps Taro's buildTemplate output, preserves every stock host template, guards the slot at Taro's
117
+ * existing depth-reset call site with i.vo, and adds the two private definitions to the shared namespace.
118
+ * 2. buildPageTemplate owns only the Page boundary: app binding, page binding, and the single Page-content slot.
119
+ * 3. buildXScript delegates unchanged because routing metadata already travels inside compact node i.
120
+ * 4. buildBaseComponentTemplate delegates unchanged so recursive comp remains generic and feature-independent.
121
+ *
122
+ * H5 never calls this WX output builder. Its App continues to receive ordinary Fragment children and none of these native
123
+ * data roots, templates, custom-component boundaries, or slot rules enter the browser build.
124
+ */
26
125
  function createTemplateBuilder() {
27
126
  const platform = new WxPlatform({
28
127
  helper: {
@@ -32,7 +131,96 @@ function createTemplateBuilder() {
32
131
  registerPlatform() { }
33
132
  }, {}, {});
34
133
  platform.modifyTemplate({});
35
- return platform.template;
134
+ const taroTemplateBuilder = platform.template;
135
+ /**
136
+ * Replaces one pinned Taro fragment so an upstream template change cannot silently break the coordinated
137
+ * framework/runtime/WXML boundary or partially apply the feature.
138
+ */
139
+ function replaceExactlyOnce(source, current, replacement, description) {
140
+ const firstIndex = source.indexOf(current);
141
+ const duplicateIndex = firstIndex === -1 ? -1 : source.indexOf(current, firstIndex + current.length);
142
+ if (firstIndex === -1 || duplicateIndex !== -1) {
143
+ throw new Error(`Expected one ${description}, found ${firstIndex === -1 ? 0 : 'multiple'}`);
144
+ }
145
+ return `${source.slice(0, firstIndex)}${replacement}${source.slice(firstIndex + current.length)}`;
146
+ }
147
+ return {
148
+ buildBaseTemplate: (componentConfig) => {
149
+ const source = taroTemplateBuilder.buildTemplate(componentConfig);
150
+ /*
151
+ * Taro inserts recursive comp only when template depth resets. App {children} may be below any number of those
152
+ * boundaries, so each boundary reads the renderer-maintained vo marker on its compact subtree root before forwarding
153
+ * the caller's default slot. No App/Page mode or Page data is threaded through template scopes: Page recursion
154
+ * and unrelated App branches have no marker, while the outlet spine carries the one Page-owned slot.
155
+ */
156
+ const slotTransparentRecursion = replaceExactlyOnce(source, '<comp i="{{i}}" l="{{l}}" />', `<comp i="{{i}}" l="{{l}}"><slot wx:if="{{i.vo}}" /></comp>`, 'recursive comp call');
157
+ /*
158
+ * These definitions belong in shared base.wxml rather than buildPageTemplate. Although native data is Page-owned,
159
+ * the dynamic calls that render App records execute after crossing into comp.wxml's component scope. WXML slots
160
+ * transfer caller-owned light DOM, not the caller's named-template table or WXS modules. comp.wxml can therefore
161
+ * resolve only its own definitions and those imported from base.wxml.
162
+ *
163
+ * Defining the two names in Page WXML is not merely redundant: WeChat accepts that Page file at compile time, then
164
+ * comp.wxml's runtime dispatch fails with `Template tmpl_0_vpt_fragment not found` because component template
165
+ * resolution never searches the caller Page. Shared base.wxml is already imported by comp.wxml, makes both names
166
+ * visible at every depth-reset component scope, and emits them once instead of once per generated Page.
167
+ *
168
+ * Page WXML can give generic comp one i object, whereas App output is a root collection. vpt_fragment bridges
169
+ * those contracts without becoming a native or Taro host: its nn is only a template discriminator and its
170
+ * template emits each real cn record directly. Keeping the collection behind one comp is important because that
171
+ * component owns exactly one Page slot regardless of whether App rendered zero, one, or many top-level hosts.
172
+ * The synthetic record itself is not keyed or event-addressable, so it deliberately has no sid. Its cn items are
173
+ * different: they are real hydrated Taro elements, text nodes, or the outlet, and every one has Taro's stable sid.
174
+ * wx:key="sid" matches Taro's stock root.cn loop so insertion/reordering preserves native-component instances,
175
+ * sibling identity, and event-source routing instead of reusing children only by array position.
176
+ *
177
+ * vpt_page_outlet is the matching terminal. The patched runtime retains Page roots below that marker in memory
178
+ * and serializes no children into app data, while React's host renderer marks its compact ancestor spine with vo.
179
+ * The slot inserts the parent Page's separate page data at the same visual position and adds no native layout
180
+ * wrapper.
181
+ */
182
+ return `${slotTransparentRecursion}
183
+ <template name="tmpl_0_vpt_fragment">
184
+ <template
185
+ is="{{xs.a(0, item.nn, '')}}"
186
+ data="{{i:item,c:1,l:xs.f('',item.nn)}}"
187
+ wx:for="{{i.cn}}"
188
+ wx:key="sid"
189
+ />
190
+ </template>
191
+ <template name="tmpl_0_vpt_page_outlet"><slot /></template>
192
+ `;
193
+ },
194
+ buildXScript: () => {
195
+ // Alias selection and compact paths are unchanged; both App and Page records use Taro's normal node vocabulary,
196
+ // and projection ownership already travels on the current i object as vo.
197
+ return taroTemplateBuilder.buildXScript();
198
+ },
199
+ buildBaseComponentTemplate: (ext) => {
200
+ /*
201
+ * Keep comp generic. i is the current compact node dispatched through i.nn. l is Taro's lineage of selected
202
+ * special/native aliases: xs.f records bounded/nestable ancestors and xs.a uses that history when choosing a
203
+ * generated template level on non-recursive WXML platforms. The current comp.wxml restarts its local lineage from
204
+ * i.nn, but l remains part of Taro's intentional depth-reset component contract and platform variants may consume
205
+ * it. Preserve that upstream binding; only the new Page-root comp starts with the property's empty default. Slot
206
+ * forwarding lives at this base.wxml call site, and vo already belongs to i, so comp still needs no App/Page mode,
207
+ * projection property, or Page data.
208
+ */
209
+ return taroTemplateBuilder.buildBaseComponentTemplate(ext);
210
+ },
211
+ buildPageTemplate: (baseTempPath, page) => {
212
+ const source = taroTemplateBuilder.buildPageTemplate(baseTempPath, page);
213
+ /*
214
+ * The native Page owns both data bindings. app is the transparent single-node adapter rendered by unchanged
215
+ * comp; the caller-owned taro_tmpl still reads only page and becomes comp's one default slot. A page.* update
216
+ * therefore cannot enter App template scopes or a component property, while App recursion can place the Page
217
+ * exactly at {children} by consuming the slot at vpt_page_outlet. The root call omits l because this native comp
218
+ * starts a fresh lineage scope and its generic property already defaults to the empty string. It also emits no id:
219
+ * no runtime lookup, event dispatch, ref, or selector addresses this virtual boundary.
220
+ */
221
+ return replaceExactlyOnce(source, '<template is="taro_tmpl" data="{{root:root}}" />', `<comp i="{{app}}"><template is="taro_tmpl" data="{{root:page}}" /></comp>`, 'Page template entry');
222
+ }
223
+ };
36
224
  }
37
225
  /** Creates template metadata from reachable Taro hosts and native component JSX fields. */
38
226
  function collectTemplateComponentConfig(bundle, nativeComponents) {
@@ -2,8 +2,8 @@ import { normalizePath } from 'vite';
2
2
  import { getWxExecutionKind, isTransportModule } from '../module/module.js';
3
3
  import { getNativeComponentAssetBytes } from '../native/native-component-assets.js';
4
4
  import { createPlacement } from './placement.js';
5
- const pnpmFrameworkPackagePattern = /\/node_modules\/\.pnpm\/(?:@tarojs\+|react(?:-dom|-reconciler)?@|scheduler@)/;
6
- const workspaceFrameworkPackagePattern = /\/packages\/(?:taro-react|taro-plugin-framework-react)\//;
5
+ const pnpmFrameworkPackagePattern = /\/node_modules\/\.pnpm\/(?:@tarojs\+|vite-plugin-taro-runtime@|react(?:-dom|-reconciler)?@|scheduler@)/;
6
+ const workspaceFrameworkPackagePattern = /\/packages\/(?:taro-react|taro-plugin-framework-react|taro-runtime)\//;
7
7
  /** Selects the explicit React/Taro roots whose complete dependency closure forms the framework vendor chunk. */
8
8
  export function isWxFrameworkVendorModule(moduleId) {
9
9
  const normalizedId = normalizePath(moduleId);
@@ -31,6 +31,10 @@ function createWxPlugin(options, resolver, placement) {
31
31
  oxc: { target: esTarget },
32
32
  resolve: {
33
33
  alias: [
34
+ {
35
+ find: /^@tarojs\/runtime$/,
36
+ replacement: packageRequire.resolve('@tarojs/runtime/dist/index.js')
37
+ },
34
38
  {
35
39
  find: /^@tarojs\/components$/,
36
40
  replacement: packageRequire.resolve('@tarojs/plugin-platform-weapp/dist/components-react')
@@ -127,7 +131,7 @@ function createWxPlugin(options, resolver, placement) {
127
131
  }
128
132
  /** Creates the build-time constants required by Taro's legacy feature gates. */
129
133
  function createTaroDefines() {
130
- const taroVersion = String(packageRequire('@tarojs/runtime/package.json').version);
134
+ const taroVersion = String(packageRequire('@tarojs/taro/package.json').version);
131
135
  return {
132
136
  'process.env.FRAMEWORK': JSON.stringify('react'),
133
137
  'process.env.SUPPORT_TARO_POLYFILL': JSON.stringify('disabled'),
@@ -3,5 +3,16 @@ 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
5
  import { createPageConfig } from './taro-runtime.js';
6
- const config = createPageConfig(PageComponent, __VPT_PAGE_PATH__, { root: { cn: [] } }, __VPT_PAGE_CONFIG__);
6
+ /*
7
+ * Generated Page WXML invokes Taro's unchanged recursive comp, whose input contract 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
9
+ * occur at any depth within them. vpt_fragment is therefore a WXML-only collection adapter. Its fixed nn selects a
10
+ * transparent template that iterates cn while one surrounding comp owns the Page's default slot. Runtime projection markers
11
+ * relay that slot only through the App branch containing the outlet. Without the fragment, Page WXML would need one comp—and
12
+ * one potential copy of the Page slot—for every App root, or comp 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__);
7
18
  export default config;
@@ -3,4 +3,4 @@
3
3
  import '@tarojs/plugin-platform-weapp/dist/runtime.js';
4
4
  export { createReactApp } from '@tarojs/plugin-framework-react/dist/runtime';
5
5
  export { default as ReactDOM } from '@tarojs/react';
6
- export { createPageConfig, createRecursiveComponentConfig, Current, document, injectPageInstance } from '@tarojs/runtime';
6
+ export { createPageConfig, createRecursiveComponentConfig } from '@tarojs/runtime';
@@ -3,4 +3,4 @@
3
3
  import '@tarojs/plugin-platform-weapp/dist/runtime.js';
4
4
  export { createReactApp } from '@tarojs/plugin-framework-react/dist/runtime';
5
5
  export { default as ReactDOM } from '@tarojs/react';
6
- export { createPageConfig, createRecursiveComponentConfig, Current, document, injectPageInstance } from '@tarojs/runtime';
6
+ export { createPageConfig, createRecursiveComponentConfig } from '@tarojs/runtime';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-taro",
3
- "version": "0.6.6",
3
+ "version": "0.6.8",
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",
@@ -63,7 +63,6 @@
63
63
  "@tarojs/plugin-platform-h5": "4.2.0",
64
64
  "@tarojs/plugin-platform-weapp": "4.2.0",
65
65
  "@tarojs/router": "4.2.0",
66
- "@tarojs/runtime": "4.2.0",
67
66
  "@tarojs/taro": "4.2.0",
68
67
  "@vitejs/plugin-react": "^6.0.5",
69
68
  "@weapp-tailwindcss/postcss": "3.2.8",
@@ -76,8 +75,9 @@
76
75
  "rxjs": "^7.8.2",
77
76
  "tailwindcss": "^4.3.3",
78
77
  "weapp-tailwindcss": "^5.2.11",
79
- "@tarojs/plugin-framework-react": "npm:vite-plugin-taro-plugin-framework-react@0.6.6",
80
- "@tarojs/react": "npm:vite-plugin-taro-react@0.6.6"
78
+ "@tarojs/plugin-framework-react": "npm:vite-plugin-taro-plugin-framework-react@0.6.8",
79
+ "@tarojs/react": "npm:vite-plugin-taro-react@0.6.8",
80
+ "@tarojs/runtime": "npm:vite-plugin-taro-runtime@0.6.8"
81
81
  },
82
82
  "peerDependencies": {
83
83
  "react": "^19.0.0",
@@ -31,12 +31,12 @@ export function createH5TargetPlugins(options: VptOptions): PluginOption[] {
31
31
  logLevel: 'warn'
32
32
  }),
33
33
  ...createH5SupportPlugins(),
34
- createH5TargetPlugin(options)
34
+ createH5Plugin(options)
35
35
  ]
36
36
  }
37
37
 
38
38
  /** Configures H5 resolution and supplies the specialized physical application entry. */
39
- function createH5TargetPlugin(options: VptOptions): Plugin {
39
+ function createH5Plugin(options: VptOptions): Plugin {
40
40
  const moduleResolver = createModuleResolver(options)
41
41
 
42
42
  return {
@@ -48,6 +48,10 @@ function createH5TargetPlugin(options: VptOptions): Plugin {
48
48
  resolve: {
49
49
  mainFields: ['main:h5', 'browser', 'module', 'jsnext:main', 'jsnext'],
50
50
  alias: [
51
+ {
52
+ find: /^@tarojs\/runtime$/,
53
+ replacement: packageRequire.resolve('@tarojs/runtime/dist/runtime.esm.js')
54
+ },
51
55
  {
52
56
  find: /^@tarojs\/components$/,
53
57
  replacement: packageRequire.resolve('@tarojs/components/lib/react')
@@ -59,10 +63,13 @@ function createH5TargetPlugin(options: VptOptions): Plugin {
59
63
  ]
60
64
  },
61
65
  optimizeDeps: {
62
- // The compiler-owned H5 app and Taro facade are injected after Vite's initial HTML scan. Prebundle
63
- // the facade's platform backend as one boundary so its CommonJS implementation details receive
64
- // interop without duplicating their package list. ReactDOM needs the same treatment for the H5 app.
65
- include: ['@tarojs/plugin-platform-h5/dist/runtime/apis', 'react-dom/client'],
66
+ /*
67
+ * The compiler-owned H5 app and Taro facade are injected after Vite's initial HTML scan, so declare their
68
+ * optimization entries explicitly. The platform backend needs CommonJS interop, ReactDOM is imported by
69
+ * the hidden app, and @tarojs/runtime must be a first-class entry so subsequently discovered Taro packages
70
+ * share its Current singleton instead of embedding private copies in their optimized chunks.
71
+ */
72
+ include: ['@tarojs/plugin-platform-h5/dist/runtime/apis', '@tarojs/runtime', 'react-dom/client'],
66
73
  // Dependency optimization is its own Rolldown build and does not run application transform plugins.
67
74
  // Register the same adapter there so optimized Taro components cannot embed Stencil's original client.
68
75
  rolldownOptions: {
@@ -15,12 +15,12 @@ export function createJsonAssets({
15
15
  nativeComponents: readonly { name: string; componentPath: string }[]
16
16
  }): Rolldown.EmittedAsset[] {
17
17
  return [
18
- createJsonAsset('app.json', {
19
- ...createAppConfig(options),
20
- ...(subpackages.length > 0 ? { subPackages: subpackages } : {})
21
- }),
18
+ createJsonAsset(
19
+ 'app.json',
20
+ createAppJson({ options: options, subpackages: subpackages, nativeComponents: nativeComponents })
21
+ ),
22
22
 
23
- ...options.pages.map((page) => createJsonAsset(`${page.path}.json`, createPageJson(page, nativeComponents))),
23
+ ...options.pages.map((page) => createJsonAsset(`${page.path}.json`, createPageJson(page))),
24
24
 
25
25
  createJsonAsset('project.config.json', options.projectConfigJson),
26
26
 
@@ -32,27 +32,58 @@ export function createJsonAssets({
32
32
  ]
33
33
  }
34
34
 
35
- /** Creates Page JSON with generated Taro and native component registrations. */
36
- function createPageJson(
37
- page: VptPageOption,
35
+ /** Creates App JSON with globally inherited native registrations and generated subpackages. */
36
+ function createAppJson({
37
+ options,
38
+ subpackages,
39
+ nativeComponents
40
+ }: {
41
+ options: VptOptions
42
+ subpackages: readonly GeneratedSubpackage[]
38
43
  nativeComponents: readonly { name: string; componentPath: string }[]
39
- ): VptJsonObject {
40
- const usingComponents = isJsonObject(page.config.usingComponents) ? page.config.usingComponents : {}
44
+ }): VptJsonObject {
45
+ const appConfig = createAppConfig(options)
46
+
47
+ const nativeUsingComponents = nativeComponents.map(({ name, componentPath }) => [name, componentPath])
41
48
 
42
49
  // Cross-package components require a placeholder while WeChat downloads their generated subpackage. Paths are
43
50
  // root-absolute, so remove the leading slash before testing the output-relative subpackage prefix.
44
51
  // https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/async.html
45
52
  // https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/placeholder.html
46
- const placeholderEntries = nativeComponents.flatMap(({ name, componentPath }) =>
53
+ const componentPlaceholders = nativeComponents.flatMap(({ name, componentPath }) =>
47
54
  isGeneratedSubpackageFile(componentPath.slice(1)) ? ([[name, 'view']] as const) : []
48
55
  )
49
56
 
57
+ return {
58
+ ...appConfig,
59
+ ...(nativeUsingComponents.length > 0
60
+ ? {
61
+ usingComponents: {
62
+ ...(isJsonObject(appConfig.usingComponents) ? appConfig.usingComponents : {}),
63
+ ...Object.fromEntries(nativeUsingComponents)
64
+ }
65
+ }
66
+ : {}),
67
+ ...(componentPlaceholders.length > 0
68
+ ? {
69
+ componentPlaceholder: {
70
+ ...(isJsonObject(appConfig.componentPlaceholder) ? appConfig.componentPlaceholder : {}),
71
+ ...Object.fromEntries(componentPlaceholders)
72
+ }
73
+ }
74
+ : {}),
75
+ ...(subpackages.length > 0 ? { subPackages: subpackages } : {})
76
+ }
77
+ }
78
+
79
+ /** Preserves configured Page JSON and registers only Taro's local recursive component entry. */
80
+ function createPageJson(page: VptPageOption): VptJsonObject {
81
+ const usingComponents = isJsonObject(page.config.usingComponents) ? page.config.usingComponents : {}
82
+
50
83
  return {
51
84
  ...page.config,
52
- ...(placeholderEntries.length > 0 ? { componentPlaceholder: Object.fromEntries(placeholderEntries) } : {}),
53
85
  usingComponents: {
54
86
  ...usingComponents,
55
- ...Object.fromEntries(nativeComponents.map(({ name, componentPath }) => [name, componentPath])),
56
87
  comp: toRootRelativePath(page.path, 'comp')
57
88
  }
58
89
  }
@@ -30,7 +30,7 @@ export function createTemplateAssets(
30
30
  return [
31
31
  createAsset(
32
32
  'base.wxml',
33
- templateBuilder.buildTemplate(collectTemplateComponentConfig(bundle, nativeComponents))
33
+ templateBuilder.buildBaseTemplate(collectTemplateComponentConfig(bundle, nativeComponents))
34
34
  ),
35
35
  createAsset('utils.wxs', templateBuilder.buildXScript()),
36
36
  createAsset('comp.wxml', templateBuilder.buildBaseComponentTemplate('.wxml')),
@@ -48,7 +48,106 @@ export function createTemplateAssets(
48
48
  ]
49
49
  }
50
50
 
51
- /** Creates the Taro WeChat template builder without invoking its Webpack integration. */
51
+ /**
52
+ * Adapts Taro's stock template builder to the WXML half of WX App wrapping without changing its recursive renderer.
53
+ *
54
+ * End-to-end contract
55
+ * -------------------
56
+ * React and Taro retain one in-memory ownership tree:
57
+ *
58
+ * App React root
59
+ * -> App host records
60
+ * -> vpt_page_outlet host at App {children}
61
+ * -> independently scheduled Taro Page roots
62
+ *
63
+ * The patched WX document makes the singleton App host a TaroRootElement. App host mutations therefore batch under app.*
64
+ * and fan out to every mounted native Page. Each Page root remains its own TaroRootElement and emits only page.*. The React
65
+ * host renderer marks the outlet and its App ancestors with an ordinary compact `vo` prop after each commit, while patched
66
+ * hydrate() stops at vpt_page_outlet. Page roots remain attached for React Context, lifecycle, events, removal, refs, effects,
67
+ * and HMR without entering app data.
68
+ *
69
+ * Build-time output
70
+ * -----------------
71
+ * createTemplateAssets still asks one builder for the normal five products: shared base.wxml, utils.wxs, comp.wxml,
72
+ * comp.json, and each Page WXML. This adapter specializes only the two products that own the native join:
73
+ *
74
+ * - shared base.wxml receives branch-local slot forwarding plus vpt_fragment and vpt_page_outlet template definitions;
75
+ * - each Page WXML replaces Taro's root:root entry with one generic comp bound to app and one caller-owned taro_tmpl bound
76
+ * to page as that component's default slot.
77
+ *
78
+ * utils.wxs remains Taro's normal compact-node dispatcher. comp.wxml and comp.json remain Taro's generic depth-reset
79
+ * component and still know only i, l, virtual-host behavior, and eh event dispatch. There is one shared template namespace,
80
+ * no Page-specific base file, no App/Page mode property, and no Page object threaded through App template data.
81
+ *
82
+ * First native Page
83
+ * -----------------
84
+ * createPageConfig starts the native Page with:
85
+ *
86
+ * app = { nn: 'vpt_fragment', cn: [] }
87
+ * page = { cn: [] }
88
+ *
89
+ * nn lets unchanged comp dispatch one input object even though App JSX may produce one or many root hosts. The record
90
+ * is WXML-only rather than a Taro host, so it needs no sid. After React commits the Page root below the outlet, the framework
91
+ * queues a lazy hydrate(AppRoot).cn value beside the Page root's already-pending page.* payloads. Taro drains both through the
92
+ * native Page's existing first setData, making App wrapping and Page content appear atomically.
93
+ *
94
+ * Native WXML execution
95
+ * ---------------------
96
+ * Data/slot ownership and named-template ownership intentionally travel through different scopes:
97
+ *
98
+ * Page WXML (owns app, page, Page eh, and Page-content light DOM)
99
+ * -> <comp i="{{app}}"> (crosses into a virtual custom-component scope)
100
+ * -> comp.wxml (owns App eh, imports unchanged utils.wxs and shared base.wxml)
101
+ * -> tmpl_0_vpt_fragment (iterates the real App compact roots in app.cn)
102
+ * -> stock Taro templates (render App hosts and recurse through their cn arrays)
103
+ * -> depth-reset <comp i="{{i}}" l="{{l}}">
104
+ * -> forwards <slot /> only when this compact subtree root has i.vo
105
+ * -> tmpl_0_vpt_page_outlet at React's exact {children} position
106
+ * -> <slot />
107
+ * -> caller-owned <template is="taro_tmpl" data="{{root:page}}" />
108
+ * -> stock Taro templates render this native Page's page.cn records
109
+ *
110
+ * The virtual comp and both private templates add no native layout node. `vo` is part of the existing compact node i, not a
111
+ * component property or template context. App events execute through comp.eh; slotted Page events retain the native Page's
112
+ * eh. Both resolve the original Taro sid through the same event source.
113
+ *
114
+ * Named-template scope
115
+ * --------------------
116
+ * Slots transfer caller-owned light DOM only. They do not transfer the caller's named-template table or WXS modules. App
117
+ * dispatch runs inside comp.wxml, so vpt_fragment and vpt_page_outlet must live in base.wxml imported by that component.
118
+ * Putting those definitions in buildPageTemplate produces Page WXML that compiles, but component runtime dispatch fails with
119
+ * `Template tmpl_0_vpt_fragment not found`. Shared base.wxml makes the names visible in the root and every depth-reset comp
120
+ * scope and emits them once rather than once per Page.
121
+ *
122
+ * Projection-spine ownership
123
+ * --------------------------
124
+ * React's host renderer runs after the final commit tree exists and caches the outlet-to-root Taro host ancestor array. It
125
+ * skips the unchanged root-side suffix, gives old leaf-side nodes the ordinary host prop vo=false, and gives new ones vo=true.
126
+ * Taro's existing lazy structural hydration runs later and therefore serializes those props without projection-specific
127
+ * scheduler or hydrate behavior. If React replaces the outlet host while moving it, the renderer finds the unique new marker
128
+ * once and caches its new ancestor array. At a depth reset, i.vo makes forwarding an O(1) local decision; WXML never searches
129
+ * descendants, and Page trees instantiate no unnamed slot.
130
+ *
131
+ * Steady-state updates and navigation
132
+ * -----------------------------------
133
+ * A page.* setData updates only the caller-owned Page template inside the slot. app is not passed through that template and
134
+ * comp.i does not change, so Page updates cannot invalidate App recursion. Ordinary app.* payloads and outlet-spine marker
135
+ * changes remain in Taro's granular batch before it fans out to every retained native Page. Adding or removing a React Page
136
+ * root mutates the outlet only in memory, and the runtime suppresses that marker's native child update. A newly pushed Page
137
+ * receives the latest complete App snapshot in its initial batch, while existing and hidden Pages require no navigation
138
+ * synchronization.
139
+ *
140
+ * Method responsibilities
141
+ * -----------------------
142
+ * 1. buildBaseTemplate wraps Taro's buildTemplate output, preserves every stock host template, guards the slot at Taro's
143
+ * existing depth-reset call site with i.vo, and adds the two private definitions to the shared namespace.
144
+ * 2. buildPageTemplate owns only the Page boundary: app binding, page binding, and the single Page-content slot.
145
+ * 3. buildXScript delegates unchanged because routing metadata already travels inside compact node i.
146
+ * 4. buildBaseComponentTemplate delegates unchanged so recursive comp remains generic and feature-independent.
147
+ *
148
+ * H5 never calls this WX output builder. Its App continues to receive ordinary Fragment children and none of these native
149
+ * data roots, templates, custom-component boundaries, or slot rules enter the browser build.
150
+ */
52
151
  function createTemplateBuilder() {
53
152
  const platform = new WxPlatform(
54
153
  {
@@ -62,7 +161,110 @@ function createTemplateBuilder() {
62
161
  {}
63
162
  )
64
163
  platform.modifyTemplate({})
65
- return platform.template
164
+ const taroTemplateBuilder = platform.template
165
+
166
+ /**
167
+ * Replaces one pinned Taro fragment so an upstream template change cannot silently break the coordinated
168
+ * framework/runtime/WXML boundary or partially apply the feature.
169
+ */
170
+ function replaceExactlyOnce(source: string, current: string, replacement: string, description: string): string {
171
+ const firstIndex = source.indexOf(current)
172
+ const duplicateIndex = firstIndex === -1 ? -1 : source.indexOf(current, firstIndex + current.length)
173
+ if (firstIndex === -1 || duplicateIndex !== -1) {
174
+ throw new Error(`Expected one ${description}, found ${firstIndex === -1 ? 0 : 'multiple'}`)
175
+ }
176
+ return `${source.slice(0, firstIndex)}${replacement}${source.slice(firstIndex + current.length)}`
177
+ }
178
+
179
+ return {
180
+ buildBaseTemplate: (componentConfig: TemplateComponentConfig) => {
181
+ const source = taroTemplateBuilder.buildTemplate(componentConfig)
182
+ /*
183
+ * Taro inserts recursive comp only when template depth resets. App {children} may be below any number of those
184
+ * boundaries, so each boundary reads the renderer-maintained vo marker on its compact subtree root before forwarding
185
+ * the caller's default slot. No App/Page mode or Page data is threaded through template scopes: Page recursion
186
+ * and unrelated App branches have no marker, while the outlet spine carries the one Page-owned slot.
187
+ */
188
+ const slotTransparentRecursion = replaceExactlyOnce(
189
+ source,
190
+ '<comp i="{{i}}" l="{{l}}" />',
191
+ `<comp i="{{i}}" l="{{l}}"><slot wx:if="{{i.vo}}" /></comp>`,
192
+ 'recursive comp call'
193
+ )
194
+
195
+ /*
196
+ * These definitions belong in shared base.wxml rather than buildPageTemplate. Although native data is Page-owned,
197
+ * the dynamic calls that render App records execute after crossing into comp.wxml's component scope. WXML slots
198
+ * transfer caller-owned light DOM, not the caller's named-template table or WXS modules. comp.wxml can therefore
199
+ * resolve only its own definitions and those imported from base.wxml.
200
+ *
201
+ * Defining the two names in Page WXML is not merely redundant: WeChat accepts that Page file at compile time, then
202
+ * comp.wxml's runtime dispatch fails with `Template tmpl_0_vpt_fragment not found` because component template
203
+ * resolution never searches the caller Page. Shared base.wxml is already imported by comp.wxml, makes both names
204
+ * visible at every depth-reset component scope, and emits them once instead of once per generated Page.
205
+ *
206
+ * Page WXML can give generic comp one i object, whereas App output is a root collection. vpt_fragment bridges
207
+ * those contracts without becoming a native or Taro host: its nn is only a template discriminator and its
208
+ * template emits each real cn record directly. Keeping the collection behind one comp is important because that
209
+ * component owns exactly one Page slot regardless of whether App rendered zero, one, or many top-level hosts.
210
+ * The synthetic record itself is not keyed or event-addressable, so it deliberately has no sid. Its cn items are
211
+ * different: they are real hydrated Taro elements, text nodes, or the outlet, and every one has Taro's stable sid.
212
+ * wx:key="sid" matches Taro's stock root.cn loop so insertion/reordering preserves native-component instances,
213
+ * sibling identity, and event-source routing instead of reusing children only by array position.
214
+ *
215
+ * vpt_page_outlet is the matching terminal. The patched runtime retains Page roots below that marker in memory
216
+ * and serializes no children into app data, while React's host renderer marks its compact ancestor spine with vo.
217
+ * The slot inserts the parent Page's separate page data at the same visual position and adds no native layout
218
+ * wrapper.
219
+ */
220
+ return `${slotTransparentRecursion}
221
+ <template name="tmpl_0_vpt_fragment">
222
+ <template
223
+ is="{{xs.a(0, item.nn, '')}}"
224
+ data="{{i:item,c:1,l:xs.f('',item.nn)}}"
225
+ wx:for="{{i.cn}}"
226
+ wx:key="sid"
227
+ />
228
+ </template>
229
+ <template name="tmpl_0_vpt_page_outlet"><slot /></template>
230
+ `
231
+ },
232
+ buildXScript: () => {
233
+ // Alias selection and compact paths are unchanged; both App and Page records use Taro's normal node vocabulary,
234
+ // and projection ownership already travels on the current i object as vo.
235
+ return taroTemplateBuilder.buildXScript()
236
+ },
237
+ buildBaseComponentTemplate: (ext: string) => {
238
+ /*
239
+ * Keep comp generic. i is the current compact node dispatched through i.nn. l is Taro's lineage of selected
240
+ * special/native aliases: xs.f records bounded/nestable ancestors and xs.a uses that history when choosing a
241
+ * generated template level on non-recursive WXML platforms. The current comp.wxml restarts its local lineage from
242
+ * i.nn, but l remains part of Taro's intentional depth-reset component contract and platform variants may consume
243
+ * it. Preserve that upstream binding; only the new Page-root comp starts with the property's empty default. Slot
244
+ * forwarding lives at this base.wxml call site, and vo already belongs to i, so comp still needs no App/Page mode,
245
+ * projection property, or Page data.
246
+ */
247
+ return taroTemplateBuilder.buildBaseComponentTemplate(ext)
248
+ },
249
+ buildPageTemplate: (baseTempPath: string, page: Record<string, unknown>) => {
250
+ const source = taroTemplateBuilder.buildPageTemplate(baseTempPath, page)
251
+
252
+ /*
253
+ * The native Page owns both data bindings. app is the transparent single-node adapter rendered by unchanged
254
+ * comp; the caller-owned taro_tmpl still reads only page and becomes comp's one default slot. A page.* update
255
+ * therefore cannot enter App template scopes or a component property, while App recursion can place the Page
256
+ * exactly at {children} by consuming the slot at vpt_page_outlet. The root call omits l because this native comp
257
+ * starts a fresh lineage scope and its generic property already defaults to the empty string. It also emits no id:
258
+ * no runtime lookup, event dispatch, ref, or selector addresses this virtual boundary.
259
+ */
260
+ return replaceExactlyOnce(
261
+ source,
262
+ '<template is="taro_tmpl" data="{{root:root}}" />',
263
+ `<comp i="{{app}}"><template is="taro_tmpl" data="{{root:page}}" /></comp>`,
264
+ 'Page template entry'
265
+ )
266
+ }
267
+ }
66
268
  }
67
269
 
68
270
  /** Creates template metadata from reachable Taro hosts and native component JSX fields. */
@@ -5,8 +5,9 @@ import { createPlacement, type GeneratedSubpackage, type PackageLocation, type P
5
5
 
6
6
  export type { GeneratedSubpackage, Placement } from './placement.ts'
7
7
 
8
- const pnpmFrameworkPackagePattern = /\/node_modules\/\.pnpm\/(?:@tarojs\+|react(?:-dom|-reconciler)?@|scheduler@)/
9
- const workspaceFrameworkPackagePattern = /\/packages\/(?:taro-react|taro-plugin-framework-react)\//
8
+ const pnpmFrameworkPackagePattern =
9
+ /\/node_modules\/\.pnpm\/(?:@tarojs\+|vite-plugin-taro-runtime@|react(?:-dom|-reconciler)?@|scheduler@)/
10
+ const workspaceFrameworkPackagePattern = /\/packages\/(?:taro-react|taro-plugin-framework-react|taro-runtime)\//
10
11
 
11
12
  /** Selects the explicit React/Taro roots whose complete dependency closure forms the framework vendor chunk. */
12
13
  export function isWxFrameworkVendorModule(moduleId: string): boolean {
@@ -43,6 +43,10 @@ function createWxPlugin(options: VptOptions, resolver: WxResolver, placement: Wx
43
43
 
44
44
  resolve: {
45
45
  alias: [
46
+ {
47
+ find: /^@tarojs\/runtime$/,
48
+ replacement: packageRequire.resolve('@tarojs/runtime/dist/index.js')
49
+ },
46
50
  {
47
51
  find: /^@tarojs\/components$/,
48
52
  replacement: packageRequire.resolve('@tarojs/plugin-platform-weapp/dist/components-react')
@@ -157,7 +161,7 @@ function createWxPlugin(options: VptOptions, resolver: WxResolver, placement: Wx
157
161
 
158
162
  /** Creates the build-time constants required by Taro's legacy feature gates. */
159
163
  function createTaroDefines(): Record<string, string> {
160
- const taroVersion = String((packageRequire('@tarojs/runtime/package.json') as { version: string }).version)
164
+ const taroVersion = String((packageRequire('@tarojs/taro/package.json') as { version: string }).version)
161
165
 
162
166
  return {
163
167
  'process.env.FRAMEWORK': JSON.stringify('react'),
@@ -8,6 +8,22 @@ import { createPageConfig } from './taro-runtime.ts'
8
8
  declare const __VPT_PAGE_PATH__: string
9
9
  declare const __VPT_PAGE_CONFIG__: Record<string, unknown>
10
10
 
11
- const config = createPageConfig(PageComponent, __VPT_PAGE_PATH__, { root: { cn: [] } }, __VPT_PAGE_CONFIG__)
11
+ /*
12
+ * Generated Page WXML invokes Taro's unchanged recursive comp, whose input contract is one compact node selected by i.nn.
13
+ * App JSX does not have that cardinality: it may return one or many top-level hosts, and the private Page outlet may
14
+ * occur at any depth within them. vpt_fragment is therefore a WXML-only collection adapter. Its fixed nn selects a
15
+ * transparent template that iterates cn while one surrounding comp owns the Page's default slot. Runtime projection markers
16
+ * relay that slot only through the App branch containing the outlet. Without the fragment, Page WXML would need one comp—and
17
+ * one potential copy of the Page slot—for every App root, or comp would need an App-specific collection mode.
18
+ *
19
+ * This record is not a Taro host: it has no Fiber, event source, ref, lifecycle, native element, or keyed parent collection.
20
+ * It consequently needs no sid. Only cn is seeded and updated; nn remains the stable generic-template discriminator.
21
+ */
22
+ const config = createPageConfig(
23
+ PageComponent,
24
+ __VPT_PAGE_PATH__,
25
+ { app: { nn: 'vpt_fragment', cn: [] }, page: { cn: [] } },
26
+ __VPT_PAGE_CONFIG__
27
+ )
12
28
 
13
29
  export default config
@@ -5,10 +5,4 @@ import '@tarojs/plugin-platform-weapp/dist/runtime.js'
5
5
 
6
6
  export { createReactApp } from '@tarojs/plugin-framework-react/dist/runtime'
7
7
  export { default as ReactDOM } from '@tarojs/react'
8
- export {
9
- createPageConfig,
10
- createRecursiveComponentConfig,
11
- Current,
12
- document,
13
- injectPageInstance
14
- } from '@tarojs/runtime'
8
+ export { createPageConfig, createRecursiveComponentConfig } from '@tarojs/runtime'
@@ -31,6 +31,14 @@ declare const wx: {
31
31
  request(options: WeChatRequestOptions): void
32
32
  }
33
33
 
34
+ /** Native Page surface used by the singleton App-data scheduler. */
35
+ type WeChatPage = {
36
+ setData(data: Readonly<Record<string, unknown>>, callback?: () => void): void
37
+ }
38
+
39
+ /** Returns every mounted native Page in stack order. */
40
+ declare function getCurrentPages(): WeChatPage[]
41
+
34
42
  /** Registers the native WeChat Mini Program application. */
35
43
  declare function App(options: object): void
36
44