vite-plugin-taro 0.6.8 → 0.6.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.en.md +3 -0
  2. package/README.md +3 -0
  3. package/dist/node/plugins/wx/dev/dev-host.js +5 -0
  4. package/dist/node/plugins/wx/module/module.d.ts +6 -2
  5. package/dist/node/plugins/wx/module/module.js +12 -3
  6. package/dist/node/plugins/wx/output/files.d.ts +2 -1
  7. package/dist/node/plugins/wx/output/files.js +8 -4
  8. package/dist/node/plugins/wx/output/templates.d.ts +13 -3
  9. package/dist/node/plugins/wx/output/templates.js +107 -41
  10. package/dist/node/plugins/wx/plugins.js +1 -0
  11. package/dist/node/plugins/wx/resolve/resolver.js +2 -1
  12. package/dist/node/plugins/wx/styles/plugins.js +19 -12
  13. package/dist/runtime/wx/capsule/component.d.ts +2 -2
  14. package/dist/runtime/wx/capsule/component.js +3 -2
  15. package/dist/runtime/wx/capsule/taro-runtime.d.ts +1 -1
  16. package/dist/runtime/wx/capsule/taro-runtime.js +1 -1
  17. package/dist/runtime/wx/native/component.js +1 -1
  18. package/dist/runtime/wx/native/custom-wrapper.d.ts +1 -0
  19. package/dist/runtime/wx/native/custom-wrapper.js +3 -0
  20. package/dist/runtime/wx/systemjs/system-core.js +4 -4
  21. package/package.json +30 -5
  22. package/src/node/plugins/wx/dev/dev-host.ts +5 -0
  23. package/src/node/plugins/wx/module/module.ts +14 -3
  24. package/src/node/plugins/wx/output/files.ts +9 -3
  25. package/src/node/plugins/wx/output/templates.ts +151 -56
  26. package/src/node/plugins/wx/plugins.ts +1 -0
  27. package/src/node/plugins/wx/resolve/resolver.ts +3 -0
  28. package/src/node/plugins/wx/styles/plugins.ts +20 -13
  29. package/src/runtime/wx/capsule/component.ts +3 -2
  30. package/src/runtime/wx/capsule/taro-runtime.ts +1 -1
  31. package/src/runtime/wx/native/component.ts +1 -1
  32. package/src/runtime/wx/native/custom-wrapper.ts +4 -0
  33. package/src/runtime/wx/systemjs/system-core.js +4 -4
  34. package/dist/node/plugins/wx/output/json.d.ts +0 -14
  35. package/dist/node/plugins/wx/output/json.js +0 -72
  36. package/src/node/plugins/wx/output/json.ts +0 -109
package/README.en.md CHANGED
@@ -10,6 +10,8 @@ Build WeChat Mini Program and Web apps with Vite 8, React 19, Taro 4, and Tailwi
10
10
 
11
11
  Website: <https://vpt.js.org>
12
12
 
13
+ > **AI-assisted development is recommended:** Follow the [VPT AI development guide](https://vpt.js.org/guides/ai/) and let a coding assistant create, develop, test, and validate your app.
14
+
13
15
  ## Quick Start
14
16
 
15
17
  ```sh
@@ -32,6 +34,7 @@ Continue with the [Quick Start guide](https://vpt.js.org/guides/quick-start/).
32
34
  ## Documentation
33
35
 
34
36
  - [Quick Start](https://vpt.js.org/guides/quick-start/)
37
+ - [AI-assisted development](https://vpt.js.org/guides/ai/)
35
38
  - [Styles](https://vpt.js.org/guides/styles/)
36
39
  - [Automatic subpackages](https://vpt.js.org/guides/automatic-subpackages/)
37
40
  - [Native WeChat components](https://vpt.js.org/guides/native-components/)
package/README.md CHANGED
@@ -10,6 +10,8 @@
10
10
 
11
11
  官网:<https://vpt.js.org>
12
12
 
13
+ > **推荐使用 AI 开发:** 阅读 [VPT AI 开发指南](https://vpt.js.org/guides/ai/),让编程助手创建项目并完成开发、测试和验证。
14
+
13
15
  ## 快速开始
14
16
 
15
17
  ```sh
@@ -33,6 +35,7 @@ npm create vite-taro@latest my-app
33
35
  ## 文档
34
36
 
35
37
  - [快速开始](https://vpt.js.org/guides/quick-start/)
38
+ - [AI 开发指南](https://vpt.js.org/guides/ai/)
36
39
  - [样式](https://vpt.js.org/guides/styles/)
37
40
  - [全自动分包](https://vpt.js.org/guides/automatic-subpackages/)
38
41
  - [微信原生组件](https://vpt.js.org/guides/native-components/)
@@ -67,6 +67,11 @@ export async function createWxDevHost({ server, options, styles }) {
67
67
  throw result;
68
68
  }
69
69
  await engine.ensureCurrentBuildFinish();
70
+ // onOutput admission happens on Rolldown's callback stack, but its style publication runs asynchronously through
71
+ // hostActions. Drain that transaction before returning control to Vite: otherwise the HTTP server can bind while the
72
+ // initial output action is still finalizing styles, causing both that action and the later listening action to observe
73
+ // the port and rotate the build identity twice.
74
+ await hostActions.waitForIdle();
70
75
  };
71
76
  // Vite binds the port only after initServer (and therefore the initial build) completes, so
72
77
  // the actual port is not observable while onOutput runs for the first build. The App metadata
@@ -19,15 +19,19 @@ export declare const appCapsulePath: string;
19
19
  export declare const componentShellFileName = "comp.js";
20
20
  /** Identifies the synchronous recursive Component shell source. */
21
21
  export declare const componentShellPath: string;
22
- /** Identifies the recursive Component capsule kept behind the native shell boundary. */
22
+ /** Identifies the recursive Component capsule shared by generated Taro component shells. */
23
23
  export declare const componentCapsulePath: string;
24
+ /** Forces Taro's CustomWrapper native shell to emit at its configured root path. */
25
+ export declare const customWrapperShellFileName = "custom-wrapper.js";
26
+ /** Identifies the synchronous CustomWrapper shell source. */
27
+ export declare const customWrapperShellPath: string;
24
28
  /** Resolves the configured Page component from its route-qualified capsule importer. */
25
29
  export declare const pageComponentId = "\0vpt:page-component";
26
30
  /** Gives every Page shell one private capsule target that can be resolved using its route. */
27
31
  export declare const pageCapsuleId = "\0vpt:page-capsule";
28
32
  /** Provides the Page capsule source specialized through a stable route query. */
29
33
  export declare const pageCapsulePath: string;
30
- /** Identifies the Taro facade shared by the App, Page, and recursive Component capsules. */
34
+ /** Identifies the Taro facade shared by the App, Page, and generated component capsules. */
31
35
  export declare const taroRuntimePath: string;
32
36
  /** Identifies the reusable synchronous native Page shell source. */
33
37
  export declare const pageShellPath: string;
@@ -22,21 +22,30 @@ export const appCapsulePath = resolvePackageFile('dist/runtime/wx/capsule/app.js
22
22
  export const componentShellFileName = 'comp.js';
23
23
  /** Identifies the synchronous recursive Component shell source. */
24
24
  export const componentShellPath = resolvePackageFile('dist/runtime/wx/native/component.js');
25
- /** Identifies the recursive Component capsule kept behind the native shell boundary. */
25
+ /** Identifies the recursive Component capsule shared by generated Taro component shells. */
26
26
  export const componentCapsulePath = resolvePackageFile('dist/runtime/wx/capsule/component.js');
27
+ /** Forces Taro's CustomWrapper native shell to emit at its configured root path. */
28
+ export const customWrapperShellFileName = 'custom-wrapper.js';
29
+ /** Identifies the synchronous CustomWrapper shell source. */
30
+ export const customWrapperShellPath = resolvePackageFile('dist/runtime/wx/native/custom-wrapper.js');
27
31
  /** Resolves the configured Page component from its route-qualified capsule importer. */
28
32
  export const pageComponentId = '\0vpt:page-component';
29
33
  /** Gives every Page shell one private capsule target that can be resolved using its route. */
30
34
  export const pageCapsuleId = '\0vpt:page-capsule';
31
35
  /** Provides the Page capsule source specialized through a stable route query. */
32
36
  export const pageCapsulePath = resolvePackageFile('dist/runtime/wx/capsule/page.js');
33
- /** Identifies the Taro facade shared by the App, Page, and recursive Component capsules. */
37
+ /** Identifies the Taro facade shared by the App, Page, and generated component capsules. */
34
38
  export const taroRuntimePath = resolvePackageFile('dist/runtime/wx/capsule/taro-runtime.js');
35
39
  /** Identifies the reusable synchronous native Page shell source. */
36
40
  export const pageShellPath = resolvePackageFile('dist/runtime/wx/native/page.js');
37
41
  // These fixed source identities describe entry roles independently from the final execution kind. A capsule entry may,
38
42
  // for example, become amphibious when Rolldown coalesces its generated runtime into the same output chunk.
39
- const shellModuleIds = new Set([appShellPath, componentShellPath, pageShellPath]);
43
+ const shellModuleIds = new Set([
44
+ appShellPath,
45
+ componentShellPath,
46
+ customWrapperShellPath,
47
+ pageShellPath
48
+ ]);
40
49
  const capsuleModuleIds = new Set([appCapsulePath, componentCapsulePath, pageCapsulePath]);
41
50
  const amphibiousModuleIds = new Set([bootstrapPath, rolldownRuntimeId]);
42
51
  const transportModuleIds = new Set([transportPath]);
@@ -2,10 +2,11 @@ import type { Rolldown } from 'vite';
2
2
  import type { VptOptions } from '../../../../options.ts';
3
3
  import type { GeneratedSubpackage, PackageLocation } from '../placer/placement.ts';
4
4
  /** Creates every compiler-owned WX file derived from the final Rolldown bundle. */
5
- export declare function createOutputFiles({ bundle, options, subpackages, getModuleInfo, getPackageLocation }: {
5
+ export declare function createOutputFiles({ bundle, options, subpackages, isProduction, getModuleInfo, getPackageLocation }: {
6
6
  bundle: Rolldown.OutputBundle;
7
7
  options: VptOptions;
8
8
  subpackages: readonly GeneratedSubpackage[];
9
+ isProduction: boolean;
9
10
  getModuleInfo: (moduleId: string) => {
10
11
  meta: Rolldown.CustomPluginOptions;
11
12
  } | null;
@@ -1,8 +1,7 @@
1
1
  import { createNativeComponentOutput } from '../native/create-native-component-output.js';
2
- import { createJsonAssets } from './json.js';
3
2
  import { createTemplateAssets } from './templates.js';
4
3
  /** Creates every compiler-owned WX file derived from the final Rolldown bundle. */
5
- export async function createOutputFiles({ bundle, options, subpackages, getModuleInfo, getPackageLocation }) {
4
+ export async function createOutputFiles({ bundle, options, subpackages, isProduction, getModuleInfo, getPackageLocation }) {
6
5
  const nativeOutput = await createNativeComponentOutput({ bundle, getModuleInfo, getPackageLocation });
7
6
  return [
8
7
  {
@@ -10,8 +9,13 @@ export async function createOutputFiles({ bundle, options, subpackages, getModul
10
9
  fileName: 'app.wxss',
11
10
  source: '@import "./assets/global.wxss";\n'
12
11
  },
13
- ...createJsonAssets({ options, subpackages, nativeComponents: nativeOutput.registrations }),
14
- ...createTemplateAssets(bundle, options, nativeOutput.registrations),
12
+ ...createTemplateAssets({
13
+ bundle: bundle,
14
+ options: options,
15
+ subpackages: subpackages,
16
+ nativeComponents: nativeOutput.registrations,
17
+ isProduction: isProduction
18
+ }),
15
19
  ...nativeOutput.files
16
20
  ];
17
21
  }
@@ -1,7 +1,17 @@
1
1
  import type { Rolldown } from 'vite';
2
2
  import type { VptOptions } from '../../../../options.ts';
3
- /** Creates Taro's shared WeChat templates and native WXML/WXSS companions for every Page. */
4
- export declare function createTemplateAssets(bundle: Rolldown.OutputBundle, options: VptOptions, nativeComponents: readonly {
3
+ import { type GeneratedSubpackage } from '../placer/placement.ts';
4
+ type NativeComponentRegistration = {
5
5
  name: string;
6
+ componentPath: string;
6
7
  fields: readonly string[];
7
- }[]): Rolldown.EmittedAsset[];
8
+ };
9
+ /** Creates every JSON, WXML, WXS, and Page WXSS asset owned by Taro's native rendering boundary. */
10
+ export declare function createTemplateAssets({ bundle, options, subpackages, nativeComponents, isProduction }: {
11
+ bundle: Rolldown.OutputBundle;
12
+ options: VptOptions;
13
+ subpackages: readonly GeneratedSubpackage[];
14
+ nativeComponents: readonly NativeComponentRegistration[];
15
+ isProduction: boolean;
16
+ }): Rolldown.EmittedAsset[];
17
+ export {};
@@ -2,24 +2,38 @@ import { recursiveMerge } from '@tarojs/helper';
2
2
  import { Weapp as WxPlatform } from '@tarojs/plugin-platform-weapp';
3
3
  import { normalizeModuleId } from '../../../utils/modules.js';
4
4
  import { packageRequire } from '../../../utils/packages.js';
5
- import { renderJson } from './json.js';
5
+ import { createAppConfig } from '../../../utils/project-config.js';
6
+ import { isGeneratedSubpackageFile } from '../placer/placement.js';
6
7
  import { toRootRelativePath } from './relative-root.js';
8
+ const customWrapperName = 'custom-wrapper';
7
9
  const taroComponentsModulePath = packageRequire.resolve('@tarojs/plugin-platform-weapp/dist/components-react');
8
- /** Creates Taro's shared WeChat templates and native WXML/WXSS companions for every Page. */
9
- export function createTemplateAssets(bundle, options, nativeComponents) {
10
+ /** Creates every JSON, WXML, WXS, and Page WXSS asset owned by Taro's native rendering boundary. */
11
+ export function createTemplateAssets({ bundle, options, subpackages, nativeComponents, isProduction }) {
10
12
  const templateBuilder = createTemplateBuilder();
13
+ const componentConfig = collectTemplateComponentConfig(bundle, nativeComponents);
14
+ const recursiveComponentJson = createRecursiveComponentJson();
15
+ const jsonAsset = (fileName, value) => createJsonAsset(fileName, value, isProduction);
11
16
  return [
12
- createAsset('base.wxml', templateBuilder.buildBaseTemplate(collectTemplateComponentConfig(bundle, nativeComponents))),
17
+ jsonAsset('app.json', createAppJson({ options, subpackages, nativeComponents })),
18
+ createAsset('base.wxml', templateBuilder.buildBaseTemplate(componentConfig)),
13
19
  createAsset('utils.wxs', templateBuilder.buildXScript()),
14
20
  createAsset('comp.wxml', templateBuilder.buildBaseComponentTemplate('.wxml')),
15
- createAsset('comp.json', renderJson(createComponentJson())),
21
+ jsonAsset('comp.json', recursiveComponentJson),
22
+ createAsset('custom-wrapper.wxml', templateBuilder.buildCustomComponentTemplate('.wxml')),
23
+ jsonAsset('custom-wrapper.json', recursiveComponentJson),
16
24
  ...options.pages.flatMap((page) => [
25
+ jsonAsset(`${page.path}.json`, createPageJson(page)),
17
26
  createAsset(`${page.path}.wxml`, templateBuilder.buildPageTemplate(toRootRelativePath(page.path, 'base.wxml'), {
18
27
  content: page.config,
19
28
  path: page.path
20
29
  })),
21
30
  createAsset(`${page.path}.wxss`, '')
22
- ])
31
+ ]),
32
+ jsonAsset('project.config.json', options.projectConfigJson),
33
+ ...(options.projectPrivateConfigJson
34
+ ? [jsonAsset('project.private.config.json', options.projectPrivateConfigJson)]
35
+ : []),
36
+ ...(options.sitemapJson ? [jsonAsset('sitemap.json', options.sitemapJson)] : [])
23
37
  ];
24
38
  }
25
39
  /**
@@ -42,16 +56,19 @@ export function createTemplateAssets(bundle, options, nativeComponents) {
42
56
  *
43
57
  * Build-time output
44
58
  * -----------------
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:
59
+ * createTemplateAssets asks one builder for shared base.wxml, utils.wxs, comp.wxml, custom-wrapper.wxml, and each Page WXML.
60
+ * This adapter specializes the products that own the native join:
47
61
  *
48
- * - shared base.wxml receives branch-local slot forwarding plus vpt_fragment and vpt_page_outlet template definitions;
62
+ * - shared base.wxml receives branch-local slot forwarding at comp boundaries plus vpt_fragment and vpt_page_outlet
63
+ * template definitions;
49
64
  * - each Page WXML replaces Taro's root:root entry with one generic comp bound to app and one caller-owned taro_tmpl bound
50
65
  * to page as that component's default slot.
51
66
  *
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.
67
+ * utils.wxs remains Taro's normal compact-node dispatcher. comp.wxml and custom-wrapper.wxml retain Taro's generic compact
68
+ * rendering contracts; their JSON companions register only recursive boundaries, while runtime configs retain standard
69
+ * event dispatch. CustomWrapper keeps Taro's Page-local ownership model and is not made slot-transparent for App wrapping.
70
+ * There is one shared template namespace, no Page-specific base file, no App/Page mode property, and no Page object threaded
71
+ * through App template data.
55
72
  *
56
73
  * First native Page
57
74
  * -----------------
@@ -74,7 +91,7 @@ export function createTemplateAssets(bundle, options, nativeComponents) {
74
91
  * -> comp.wxml (owns App eh, imports unchanged utils.wxs and shared base.wxml)
75
92
  * -> tmpl_0_vpt_fragment (iterates the real App compact roots in app.cn)
76
93
  * -> stock Taro templates (render App hosts and recurse through their cn arrays)
77
- * -> depth-reset <comp i="{{i}}" l="{{l}}">
94
+ * -> native recursion boundary <comp ...>
78
95
  * -> forwards <slot /> only when this compact subtree root has i.vo
79
96
  * -> tmpl_0_vpt_page_outlet at React's exact {children} position
80
97
  * -> <slot />
@@ -90,8 +107,8 @@ export function createTemplateAssets(bundle, options, nativeComponents) {
90
107
  * Slots transfer caller-owned light DOM only. They do not transfer the caller's named-template table or WXS modules. App
91
108
  * dispatch runs inside comp.wxml, so vpt_fragment and vpt_page_outlet must live in base.wxml imported by that component.
92
109
  * 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.
110
+ * `Template tmpl_0_vpt_fragment not found`. Shared base.wxml makes the names visible in the root and every recursive
111
+ * component scope and emits them once rather than once per Page.
95
112
  *
96
113
  * Projection-spine ownership
97
114
  * --------------------------
@@ -99,8 +116,8 @@ export function createTemplateAssets(bundle, options, nativeComponents) {
99
116
  * skips the unchanged root-side suffix, gives old leaf-side nodes the ordinary host prop vo=false, and gives new ones vo=true.
100
117
  * Taro's existing lazy structural hydration runs later and therefore serializes those props without projection-specific
101
118
  * 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.
119
+ * once and caches its new ancestor array. At each depth-reset comp boundary, i.vo makes forwarding an O(1) local decision;
120
+ * WXML never searches descendants, and Page trees instantiate no unnamed slot.
104
121
  *
105
122
  * Steady-state updates and navigation
106
123
  * -----------------------------------
@@ -113,11 +130,12 @@ export function createTemplateAssets(bundle, options, nativeComponents) {
113
130
  *
114
131
  * Method responsibilities
115
132
  * -----------------------
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.
133
+ * 1. buildBaseTemplate preserves stock host templates, makes each depth-reset comp boundary slot-transparent through i.vo,
134
+ * and adds the two private definitions to the shared namespace.
118
135
  * 2. buildPageTemplate owns only the Page boundary: app binding, page binding, and the single Page-content slot.
119
136
  * 3. buildXScript delegates unchanged because routing metadata already travels inside compact node i.
120
137
  * 4. buildBaseComponentTemplate delegates unchanged so recursive comp remains generic and feature-independent.
138
+ * 5. buildCustomComponentTemplate delegates unchanged so CustomWrapper renders compact children in its own native scope.
121
139
  *
122
140
  * H5 never calls this WX output builder. Its App continues to receive ordinary Fragment children and none of these native
123
141
  * data roots, templates, custom-component boundaries, or slot rules enter the browser build.
@@ -208,6 +226,10 @@ function createTemplateBuilder() {
208
226
  */
209
227
  return taroTemplateBuilder.buildBaseComponentTemplate(ext);
210
228
  },
229
+ buildCustomComponentTemplate: (ext) => {
230
+ // CustomWrapper renders its compact children in its own native scope; imported base templates perform dispatch.
231
+ return taroTemplateBuilder.buildCustomComponentTemplate(ext);
232
+ },
211
233
  buildPageTemplate: (baseTempPath, page) => {
212
234
  const source = taroTemplateBuilder.buildPageTemplate(baseTempPath, page);
213
235
  /*
@@ -222,10 +244,11 @@ function createTemplateBuilder() {
222
244
  }
223
245
  };
224
246
  }
225
- /** Creates template metadata from reachable Taro hosts and native component JSX fields. */
247
+ /** Creates template metadata from reachable Taro hosts, the standard CustomWrapper boundary, and native JSX fields. */
226
248
  function collectTemplateComponentConfig(bundle, nativeComponents) {
227
- // This local config accumulates names in output order before the template builder consumes it.
228
- const config = {
249
+ const components = findBundleModule(bundle, taroComponentsModulePath);
250
+ const renderedComponentNames = (components?.renderedExports ?? []).map(toDashed);
251
+ return {
229
252
  includes: new Set([
230
253
  'view',
231
254
  'catch-view',
@@ -236,20 +259,16 @@ function collectTemplateComponentConfig(bundle, nativeComponents) {
236
259
  'image',
237
260
  'static-image',
238
261
  'text',
239
- 'static-text'
262
+ 'static-text',
263
+ ...renderedComponentNames.filter((name) => name !== customWrapperName)
240
264
  ]),
241
265
  exclude: new Set(),
242
- thirdPartyComponents: new Map(),
266
+ thirdPartyComponents: new Map([
267
+ [customWrapperName, new Set()],
268
+ ...nativeComponents.map((component) => [component.name, new Set(component.fields)])
269
+ ]),
243
270
  includeAll: false
244
271
  };
245
- const components = findBundleModule(bundle, taroComponentsModulePath);
246
- for (const name of components?.renderedExports ?? []) {
247
- config.includes.add(toDashed(name));
248
- }
249
- for (const component of nativeComponents) {
250
- config.thirdPartyComponents.set(component.name, new Set(component.fields));
251
- }
252
- return config;
253
272
  }
254
273
  /** Finds one module in the final chunk metadata. */
255
274
  function findBundleModule(bundle, resolvedId) {
@@ -268,21 +287,68 @@ function findBundleModule(bundle, resolvedId) {
268
287
  function toDashed(value) {
269
288
  return value.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
270
289
  }
271
- /** Creates the recursive component configuration expected by Taro's templates. */
272
- function createComponentJson() {
290
+ /** Creates App JSON with globally inherited native registrations and generated subpackages. */
291
+ function createAppJson({ options, subpackages, nativeComponents }) {
292
+ const appConfig = createAppConfig(options);
293
+ const nativeUsingComponents = nativeComponents.map(({ name, componentPath }) => [name, componentPath]);
294
+ // Cross-package components require a placeholder while WeChat downloads their generated subpackage. Paths are
295
+ // root-absolute, so remove the leading slash before testing the output-relative subpackage prefix.
296
+ // https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/async.html
297
+ // https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/placeholder.html
298
+ const componentPlaceholders = nativeComponents.flatMap(({ name, componentPath }) => isGeneratedSubpackageFile(componentPath.slice(1)) ? [[name, 'view']] : []);
299
+ return {
300
+ ...appConfig,
301
+ ...(nativeUsingComponents.length > 0
302
+ ? {
303
+ usingComponents: {
304
+ ...(isJsonObject(appConfig.usingComponents) ? appConfig.usingComponents : {}),
305
+ ...Object.fromEntries(nativeUsingComponents)
306
+ }
307
+ }
308
+ : {}),
309
+ ...(componentPlaceholders.length > 0
310
+ ? {
311
+ componentPlaceholder: {
312
+ ...(isJsonObject(appConfig.componentPlaceholder) ? appConfig.componentPlaceholder : {}),
313
+ ...Object.fromEntries(componentPlaceholders)
314
+ }
315
+ }
316
+ : {}),
317
+ ...(subpackages.length > 0 ? { subPackages: subpackages } : {})
318
+ };
319
+ }
320
+ /** Preserves configured Page JSON and registers the generated recursive component entries. */
321
+ function createPageJson(page) {
322
+ const usingComponents = isJsonObject(page.config.usingComponents) ? page.config.usingComponents : {};
323
+ return {
324
+ ...page.config,
325
+ usingComponents: {
326
+ ...usingComponents,
327
+ comp: toRootRelativePath(page.path, 'comp'),
328
+ [customWrapperName]: toRootRelativePath(page.path, customWrapperName)
329
+ }
330
+ };
331
+ }
332
+ /** Creates the shared recursive component configuration used by comp and CustomWrapper. */
333
+ function createRecursiveComponentJson() {
273
334
  return {
274
335
  component: true,
275
336
  styleIsolation: 'apply-shared',
276
337
  usingComponents: {
277
- comp: './comp'
338
+ comp: './comp',
339
+ [customWrapperName]: `./${customWrapperName}`
278
340
  }
279
341
  };
280
342
  }
281
- /** Creates one emitted template or style asset. */
343
+ /** Tests whether a configured JSON value can be merged as an object. */
344
+ function isJsonObject(value) {
345
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
346
+ }
347
+ /** Creates compact production JSON and readable development JSON. */
348
+ function createJsonAsset(fileName, value, isProduction) {
349
+ return createAsset(fileName, isProduction ? JSON.stringify(value) : `${JSON.stringify(value, null, 4)}\n`);
350
+ }
351
+ /** Creates one emitted text asset. */
282
352
  function createAsset(fileName, source) {
283
- return {
284
- type: 'asset',
285
- fileName,
286
- source
287
- };
353
+ return { type: 'asset', fileName: fileName, source: source };
288
354
  }
@@ -119,6 +119,7 @@ function createWxPlugin(options, resolver, placement) {
119
119
  bundle,
120
120
  options,
121
121
  subpackages,
122
+ isProduction: this.environment.config.isProduction,
122
123
  getModuleInfo: (moduleId) => this.getModuleInfo(moduleId),
123
124
  getPackageLocation: placement.getPackageLocation
124
125
  });
@@ -2,7 +2,7 @@ import { normalizePath } from 'vite';
2
2
  import { normalizeModuleId, resolveAppComponentPath, resolvePageComponentPath } from '../../../utils/modules.js';
3
3
  import { createAppConfig } from '../../../utils/project-config.js';
4
4
  import { appComponentId } from '../../client/constant.js';
5
- import { appCapsulePath, appShellFileName, appShellPath, bootstrapPath, componentCapsulePath, componentShellFileName, componentShellPath, pageCapsuleId, pageCapsulePath, pageComponentId, pageShellPath, transportPath, vitePreloadId } from '../module/module.js';
5
+ import { appCapsulePath, appShellFileName, appShellPath, bootstrapPath, componentCapsulePath, componentShellFileName, componentShellPath, customWrapperShellFileName, customWrapperShellPath, pageCapsuleId, pageCapsulePath, pageComponentId, pageShellPath, transportPath, vitePreloadId } from '../module/module.js';
6
6
  import { specializeAppCapsule } from './specialize-app-capsule.js';
7
7
  import { specializePageCapsule } from './specialize-page-capsule.js';
8
8
  /** Creates the resolver and source specializer for the wx module graph. */
@@ -76,6 +76,7 @@ function createEntryGraph(pages) {
76
76
  ['app-capsule', appCapsulePath],
77
77
  [componentShellFileName, componentShellPath],
78
78
  ['component-capsule', componentCapsulePath],
79
+ [customWrapperShellFileName, customWrapperShellPath],
79
80
  ...pageEntries.flatMap((entry) => {
80
81
  return [
81
82
  [entry.shellName, entry.shellId],
@@ -140,6 +140,18 @@ export function createWxStylePlugin(applicationEntryIds) {
140
140
  const finalizeCurrentOutput = (context, javaScript) => {
141
141
  return finalizeOutput(entryIds, styleByModuleId, context.getModuleInfo.bind(context), weappContext, javaScript);
142
142
  };
143
+ /** Invalidates every Tailwind root fed by one changed compiler dependency. */
144
+ const invalidateTailwindDependencies = (dependencyId) => {
145
+ styleByModuleId.forEach((style, styleId) => {
146
+ if (style.tailwind?.dependencies.has(dependencyId)) {
147
+ styleByModuleId.set(styleId, {
148
+ css: style.css,
149
+ // The owning root transform replaces the generator using current source and plugin context.
150
+ tailwind: { ...style.tailwind, invalidated: true }
151
+ });
152
+ }
153
+ });
154
+ };
143
155
  /** Releases native compiler resources while retaining captured CSS needed by subsequent output callbacks. */
144
156
  const disposeTailwindRoots = () => {
145
157
  styleByModuleId.forEach((style, styleId) => {
@@ -219,19 +231,14 @@ export function createWxStylePlugin(applicationEntryIds) {
219
231
  return { code: compiled.css, map: null };
220
232
  }
221
233
  },
222
- /** Marks roots whose compiler inputs changed; Rolldown still decides when those roots are transformed. */
234
+ /** Invalidates compiler state before Rolldown transforms roots selected through their watched dependencies. */
235
+ hotUpdate(update) {
236
+ invalidateTailwindDependencies(normalizeModuleId(update.file));
237
+ return update.modules;
238
+ },
239
+ /** Marks roots whose compiler inputs changed for non-HMR Rolldown watch lifecycles. */
223
240
  watchChange(id) {
224
- const dependencyId = normalizeModuleId(id);
225
- // A dependency may feed multiple roots, so every retained root must be checked before the next transform wave.
226
- styleByModuleId.forEach((style, styleId) => {
227
- if (style.tailwind?.dependencies.has(dependencyId)) {
228
- styleByModuleId.set(styleId, {
229
- css: style.css,
230
- // Delay generator replacement until the owning root transform has current root source and graph context.
231
- tailwind: { ...style.tailwind, invalidated: true }
232
- });
233
- }
234
- });
241
+ invalidateTailwindDependencies(normalizeModuleId(id));
235
242
  },
236
243
  generateBundle: {
237
244
  // Vite must finish chunking and CSS extraction before VPT can finalize the complete WX output transaction.
@@ -1,3 +1,3 @@
1
1
  import './app.ts';
2
- declare const _default: any;
3
- export default _default;
2
+ export declare const componentConfig: any;
3
+ export declare const customWrapperConfig: any;
@@ -1,4 +1,5 @@
1
- // Keep recursive Component activation behind the same App initialization barrier as Page activation.
1
+ // Keep generated recursive component activation behind the same App initialization barrier as Page activation.
2
2
  import './app.js';
3
3
  import { createRecursiveComponentConfig } from './taro-runtime.js';
4
- export default createRecursiveComponentConfig('comp');
4
+ export const componentConfig = createRecursiveComponentConfig('comp');
5
+ export const customWrapperConfig = createRecursiveComponentConfig('custom-wrapper');
@@ -1,5 +1,5 @@
1
1
  /** biome-ignore-all assist/source/organizeImports: keep side effect orders */
2
- /** Taro runtime exports shared by the App, Page, and recursive Component capsules. */
2
+ /** Taro runtime exports shared by the App, Page, and generated recursive component capsules. */
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';
@@ -1,5 +1,5 @@
1
1
  /** biome-ignore-all assist/source/organizeImports: keep side effect orders */
2
- /** Taro runtime exports shared by the App, Page, and recursive Component capsules. */
2
+ /** Taro runtime exports shared by the App, Page, and generated recursive component capsules. */
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';
@@ -1,3 +1,3 @@
1
1
  import '../amphibious/bootstrap.js';
2
- import componentConfig from '../capsule/component.js';
2
+ import { componentConfig } from '../capsule/component.js';
3
3
  Component(componentConfig);
@@ -0,0 +1 @@
1
+ import '../amphibious/bootstrap.ts';
@@ -0,0 +1,3 @@
1
+ import '../amphibious/bootstrap.js';
2
+ import { customWrapperConfig } from '../capsule/component.js';
3
+ Component(customWrapperConfig);
@@ -16,8 +16,8 @@
16
16
  */
17
17
  var envGlobal = global;
18
18
  var hasSymbol = typeof Symbol !== 'undefined';
19
- function errMsg(errCode, msg) {
20
- return (msg || '') + ' (SystemJS https://github.com/systemjs/systemjs/blob/main/docs/errors.md#' + errCode + ')';
19
+ function createMissingRegistrationError(id) {
20
+ return Error('Module did not instantiate: ' + id);
21
21
  }
22
22
  export { REGISTRY, systemJSPrototype };
23
23
  var toStringTag = hasSymbol && Symbol.toStringTag;
@@ -63,7 +63,7 @@ function linkLoadSync(loader, id) {
63
63
  if (isThenable(registration))
64
64
  throw createAsyncGraphError(id);
65
65
  if (!registration)
66
- throw Error(errMsg(2, id));
66
+ throw createMissingRegistrationError(id);
67
67
  // This row is intentionally mutable SystemJS-owned linking and execution state.
68
68
  var load = {
69
69
  id: id,
@@ -170,7 +170,7 @@ export function getOrCreateLoad(loader, id, firstParentUrl, meta) {
170
170
  })
171
171
  .then(function (registration) {
172
172
  if (!registration)
173
- throw Error(errMsg(2, id));
173
+ throw createMissingRegistrationError(id);
174
174
  var declared = registration[1](createExport(load), registration[1].length === 2 ? createDeclarationContext(loader, id) : undefined);
175
175
  load.e = declared.execute || function () { };
176
176
  return [registration[0], declared.setters || [], registration[2] || []];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-taro",
3
- "version": "0.6.8",
3
+ "version": "0.6.12",
4
4
  "author": "sep2",
5
5
  "description": "Vite 8 plugin for building one React/Taro codebase for WeChat Mini Program and H5 targets.",
6
6
  "type": "module",
@@ -75,15 +75,38 @@
75
75
  "rxjs": "^7.8.2",
76
76
  "tailwindcss": "^4.3.3",
77
77
  "weapp-tailwindcss": "^5.2.11",
78
- "@tarojs/plugin-framework-react": "npm:vite-plugin-taro-plugin-framework-react@0.6.8",
79
- "@tarojs/react": "npm:vite-plugin-taro-react@0.6.8",
80
- "@tarojs/runtime": "npm:vite-plugin-taro-runtime@0.6.8"
78
+ "@tarojs/react": "npm:vite-plugin-taro-react@0.6.12",
79
+ "@tarojs/runtime": "npm:vite-plugin-taro-runtime@0.6.12",
80
+ "@tarojs/plugin-framework-react": "npm:vite-plugin-taro-plugin-framework-react@0.6.12"
81
81
  },
82
82
  "peerDependencies": {
83
83
  "react": "^19.0.0",
84
84
  "react-dom": "^19.0.0",
85
85
  "vite": "8.2.1"
86
86
  },
87
+ "c8": {
88
+ "all": true,
89
+ "include": [
90
+ "src/**/*.{ts,js}"
91
+ ],
92
+ "exclude": [
93
+ "src/**/*.test.ts",
94
+ "src/**/*.d.ts",
95
+ "src/options.ts"
96
+ ],
97
+ "reporter": [
98
+ "text",
99
+ "html",
100
+ "lcov",
101
+ "json-summary"
102
+ ],
103
+ "reports-dir": "coverage",
104
+ "check-coverage": true,
105
+ "statements": 90,
106
+ "branches": 85,
107
+ "functions": 90,
108
+ "lines": 90
109
+ },
87
110
  "devDependencies": {
88
111
  "@babel/plugin-transform-dynamic-import": "^7.29.7",
89
112
  "@babel/plugin-transform-modules-commonjs": "^7.29.7",
@@ -94,12 +117,14 @@
94
117
  "@types/react": "^19.2.17",
95
118
  "@types/react-dom": "^19.2.3",
96
119
  "@types/react-refresh": "^0.14.7",
120
+ "c8": "^10.1.3",
97
121
  "typescript": "^7.0.2",
98
122
  "vite": "8.2.1"
99
123
  },
100
124
  "scripts": {
101
125
  "build": "node ../../scripts/sync-plugin-readme.ts && node ../../scripts/clean-directory.ts dist && tsc --project tsconfig.build.json",
102
- "test": "node --test",
126
+ "test": "node --test --test-concurrency=2",
127
+ "test:coverage": "c8 node --test --test-concurrency=2",
103
128
  "typecheck": "tsc --project tsconfig.json --noEmit"
104
129
  }
105
130
  }