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.
- package/dist/node/plugins/h5/plugins.js +13 -6
- package/dist/node/plugins/wx/output/json.js +31 -11
- package/dist/node/plugins/wx/output/templates.js +191 -3
- package/dist/node/plugins/wx/placer/placer.js +2 -2
- package/dist/node/plugins/wx/plugins.js +5 -1
- package/dist/runtime/wx/capsule/page.js +12 -1
- package/dist/runtime/wx/capsule/taro-runtime.d.ts +1 -1
- package/dist/runtime/wx/capsule/taro-runtime.js +1 -1
- package/package.json +4 -4
- package/src/node/plugins/h5/plugins.ts +13 -6
- package/src/node/plugins/wx/output/json.ts +44 -13
- package/src/node/plugins/wx/output/templates.ts +205 -3
- package/src/node/plugins/wx/placer/placer.ts +3 -2
- package/src/node/plugins/wx/plugins.ts +5 -1
- package/src/runtime/wx/capsule/page.ts +17 -1
- package/src/runtime/wx/capsule/taro-runtime.ts +1 -7
- package/src/runtime/wx/wechat.d.ts +8 -0
|
@@ -28,11 +28,11 @@ export function createH5TargetPlugins(options) {
|
|
|
28
28
|
logLevel: 'warn'
|
|
29
29
|
}),
|
|
30
30
|
...createH5SupportPlugins(),
|
|
31
|
-
|
|
31
|
+
createH5Plugin(options)
|
|
32
32
|
];
|
|
33
33
|
}
|
|
34
34
|
/** Configures H5 resolution and supplies the specialized physical application entry. */
|
|
35
|
-
function
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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
|
|
20
|
-
function
|
|
21
|
-
const
|
|
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
|
|
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.
|
|
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
|
-
/**
|
|
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
|
-
|
|
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/
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
80
|
-
"@tarojs/react": "npm:vite-plugin-taro-react@0.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
|
-
|
|
34
|
+
createH5Plugin(options)
|
|
35
35
|
]
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/** Configures H5 resolution and supplies the specialized physical application entry. */
|
|
39
|
-
function
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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(
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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
|
|
36
|
-
function
|
|
37
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
/**
|
|
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
|
-
|
|
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 =
|
|
9
|
-
|
|
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/
|
|
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
|
-
|
|
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
|
|