vite-plugin-taro 0.6.0 → 0.6.1

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 (41) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/node/plugins/wx/chunk-path.d.ts +8 -0
  4. package/dist/node/plugins/wx/chunk-path.js +18 -0
  5. package/dist/node/plugins/wx/dev/wx-dev-options.js +21 -0
  6. package/dist/node/plugins/wx/native/create-native-component-output.d.ts +3 -1
  7. package/dist/node/plugins/wx/native/create-native-component-output.js +3 -5
  8. package/dist/node/plugins/wx/output/files.d.ts +3 -2
  9. package/dist/node/plugins/wx/output/files.js +2 -2
  10. package/dist/node/plugins/wx/output/json.d.ts +1 -1
  11. package/dist/node/plugins/wx/output/json.js +1 -1
  12. package/dist/node/plugins/wx/placer/placement.d.ts +50 -0
  13. package/dist/node/plugins/wx/placer/placement.js +245 -0
  14. package/dist/node/plugins/wx/placer/placer.d.ts +60 -0
  15. package/dist/node/plugins/wx/placer/placer.js +123 -0
  16. package/dist/node/plugins/wx/plugins.js +16 -16
  17. package/dist/node/plugins/wx/render/capsule-wrapper.js +3 -3
  18. package/dist/node/plugins/wx/render/native.js +5 -5
  19. package/dist/node/plugins/wx/render/transport.d.ts +7 -3
  20. package/dist/node/plugins/wx/render/transport.js +17 -8
  21. package/dist/node/utils/modules.d.ts +0 -2
  22. package/dist/node/utils/modules.js +0 -7
  23. package/package.json +3 -3
  24. package/src/node/plugins/wx/chunk-path.ts +22 -0
  25. package/src/node/plugins/wx/dev/wx-dev-options.ts +21 -0
  26. package/src/node/plugins/wx/native/create-native-component-output.ts +6 -5
  27. package/src/node/plugins/wx/output/files.ts +5 -3
  28. package/src/node/plugins/wx/output/json.ts +1 -2
  29. package/src/node/plugins/wx/placer/placement.ts +364 -0
  30. package/src/node/plugins/wx/placer/placer.ts +154 -0
  31. package/src/node/plugins/wx/plugins.ts +18 -18
  32. package/src/node/plugins/wx/render/capsule-wrapper.ts +3 -3
  33. package/src/node/plugins/wx/render/native.ts +5 -5
  34. package/src/node/plugins/wx/render/transport.ts +23 -7
  35. package/src/node/utils/modules.ts +0 -8
  36. package/dist/node/plugins/wx/placement/placer.d.ts +0 -78
  37. package/dist/node/plugins/wx/placement/placer.js +0 -158
  38. package/dist/node/plugins/wx/placement/plan.d.ts +0 -46
  39. package/dist/node/plugins/wx/placement/plan.js +0 -210
  40. package/src/node/plugins/wx/placement/placer.ts +0 -187
  41. package/src/node/plugins/wx/placement/plan.ts +0 -306
@@ -0,0 +1,123 @@
1
+ import { getWxExecutionKind, isTransportModule } from '../module.js';
2
+ import { getNativeComponentAssetBytes } from '../native/native-component-assets.js';
3
+ import { createPlacement } from './placement.js';
4
+ /**
5
+ * Rolldown options owned by WX placement. Every field enforces a distinct output invariant. The plugin returns this object
6
+ * from its config hook, while direct Rolldown integration tests reuse the same value to exercise the identical lifecycle.
7
+ */
8
+ export const placementRolldownOptions = {
9
+ /**
10
+ * Output-stage naming remains under Rolldown's ownership. These options establish physical candidates and hash
11
+ * participation only; LTHP mutates the resulting OutputChunk filenames later without replacing the chunks.
12
+ */
13
+ output: {
14
+ /**
15
+ * Native App/Page/Component shells are files addressed directly by WeChat and must retain the exact names configured
16
+ * in `input`, such as `app.js` and `pages/home/index.js`. Transport is excluded even though it is CommonJS:
17
+ * application chunks import its content-hashed path, so it belongs with hashed runtime/capsule entries. `[hash]`
18
+ * remains a Rolldown placeholder here and is resolved only after renderChunk transforms finish.
19
+ */
20
+ entryFileNames(chunk) {
21
+ return getWxExecutionKind(chunk) === 'native' && !isTransportModule(chunk)
22
+ ? '[name]'
23
+ : 'assets/[name]-[hash].js';
24
+ },
25
+ /**
26
+ * Leaves chunk identity and collision handling entirely to Rolldown. This package-neutral physical pattern deliberately
27
+ * contains no LTHP owner; generateBundle adds only the selected package root to the existing Rolldown filename.
28
+ */
29
+ chunkFileNames: 'assets/[name]-[hash].js',
30
+ /**
31
+ * Emits generic Rolldown assets under one collision-resistant hashed namespace. Native-component folders are not
32
+ * governed by this option: createNativeComponentOutput preserves their required relative filenames and relocates the
33
+ * complete folder beside its owning JavaScript chunk after LTHP finalization.
34
+ */
35
+ assetFileNames: 'assets/[name]-[hash][extname]'
36
+ },
37
+ /**
38
+ * Keeps every native entry's required exports while allowing Rolldown to add cross-chunk bindings created by natural code
39
+ * splitting. `strict` can reject those extensions; `exports-only` can merge away native boundaries; `allow-extension`
40
+ * preserves the shell/capsule contract without forcing source-module placement groups.
41
+ */
42
+ preserveEntrySignatures: 'allow-extension'
43
+ };
44
+ /**
45
+ * Creates the `vpt:wx-placer` lifecycle owner:
46
+ *
47
+ * 1. Its config hook installs package-neutral Rolldown names and entry-signature semantics.
48
+ * 2. `renderStart` atomically starts a generation in `awaiting-chunks`; no stale placement remains reachable.
49
+ * 3. Its first pre-order `renderChunk` creates one immutable LTHP placement from the complete tree-shaken graph.
50
+ * 4. `vpt:wx` asks this plugin only for package ownership, physical relocation, and native loading mode.
51
+ * 5. Its pre-order `generateBundle` assigns each OutputChunk its package-qualified filename and publishes app.json declarations.
52
+ *
53
+ * The discriminated state is the only generation-local mutation: `idle → awaiting-chunks → planned → finalized`. Each hook
54
+ * performs one whole-state transition, so stale graph state, duplicate planning, and partially reset generations are
55
+ * unrepresentable.
56
+ */
57
+ export function createWxPlacementPlugin() {
58
+ // This one mutable cell is the output-generation state machine described above; hooks replace it atomically by phase.
59
+ let state = { phase: 'idle' };
60
+ function requirePlacement() {
61
+ if (state.phase === 'idle' || state.phase === 'awaiting-chunks') {
62
+ throw new Error('wx placement is unavailable before Rolldown exposes the final chunk graph');
63
+ }
64
+ return state.placement;
65
+ }
66
+ return {
67
+ name: 'vpt:wx-placer',
68
+ config() {
69
+ return {
70
+ build: {
71
+ rolldownOptions: placementRolldownOptions
72
+ }
73
+ };
74
+ },
75
+ renderStart() {
76
+ state = { phase: 'awaiting-chunks' };
77
+ },
78
+ renderChunk: {
79
+ order: 'pre',
80
+ handler(_code, _chunk, _outputOptions, meta) {
81
+ if (state.phase === 'planned') {
82
+ return;
83
+ }
84
+ if (state.phase !== 'awaiting-chunks') {
85
+ throw new Error(`wx placement received final chunks during the ${state.phase} phase`);
86
+ }
87
+ state = {
88
+ phase: 'planned',
89
+ placement: createPlacement({
90
+ chunks: meta.chunks,
91
+ getAdditionalModuleBytes: (moduleId) => getNativeComponentAssetBytes(this.getModuleInfo(moduleId)?.meta)
92
+ })
93
+ };
94
+ }
95
+ },
96
+ generateBundle: {
97
+ order: 'pre',
98
+ handler(_outputOptions, bundle) {
99
+ const placement = requirePlacement();
100
+ state = {
101
+ phase: 'finalized',
102
+ placement: placement,
103
+ subpackages: placement.finalize(bundle)
104
+ };
105
+ }
106
+ },
107
+ getPackageLocation(chunk) {
108
+ return requirePlacement().getPackageLocation(chunk);
109
+ },
110
+ getPhysicalChunkId(chunk) {
111
+ return requirePlacement().getPhysicalChunkId(chunk);
112
+ },
113
+ getLoadMode(chunk) {
114
+ return requirePlacement().getLoadMode(chunk);
115
+ },
116
+ getSubpackages() {
117
+ if (state.phase !== 'finalized') {
118
+ throw new Error('wx subpackages are unavailable before output finalization');
119
+ }
120
+ return state.subpackages;
121
+ }
122
+ };
123
+ }
@@ -4,9 +4,8 @@ import { clientTaroNativeId } from '../client/constant.js';
4
4
  import { createWxDevelopmentPlugin } from './dev/plugins.js';
5
5
  import { getWxExecutionKind, isTransportModule } from './module.js';
6
6
  import { compileNativeComponentInterface } from './native/compile-native-component-interface.js';
7
- import { getNativeComponentAssetBytes } from './native/native-component-assets.js';
8
7
  import { createOutputFiles } from './output/files.js';
9
- import { createPlacer } from './placement/placer.js';
8
+ import { createWxPlacementPlugin } from './placer/placer.js';
10
9
  import { renderCapsule } from './render/capsule.js';
11
10
  import { renderNative } from './render/native.js';
12
11
  import { materializeTransport } from './render/transport.js';
@@ -17,15 +16,16 @@ export function createWxTargetPlugins(options) {
17
16
  const resolver = createResolver(options);
18
17
  // Reuse the resolver instance's ordered application subset. Rolldown's complete input also contains bootstrap, transport,
19
18
  // shell, and component entries; entry membership alone cannot recover which roots define the App/Page CSS cascade.
19
+ const placement = createWxPlacementPlugin();
20
20
  return [
21
+ placement,
21
22
  createWxStylePlugins(),
22
- createWxPlugin(options, resolver),
23
+ createWxPlugin(options, resolver, placement),
23
24
  createWxDevelopmentPlugin(options, resolver.applicationEntryIds)
24
25
  ];
25
26
  }
26
27
  /** Configures the complete wx target build pipeline. */
27
- function createWxPlugin(options, resolver) {
28
- const placer = createPlacer();
28
+ function createWxPlugin(options, resolver, placement) {
29
29
  return {
30
30
  name: 'vpt:wx',
31
31
  config(_config, _env) {
@@ -54,7 +54,8 @@ function createWxPlugin(options, resolver) {
54
54
  assetsInlineLimit: 0,
55
55
  target: esTarget,
56
56
  rolldownOptions: {
57
- ...placer.rolldownOptions,
57
+ // The dedicated vpt:wx-placer plugin owns output naming and entry-signature semantics. This plugin owns
58
+ // only the closed named input set of native shells, lifecycle capsules, bootstrap, and transport entries.
58
59
  input: resolver.input
59
60
  }
60
61
  }
@@ -78,16 +79,10 @@ function createWxPlugin(options, resolver) {
78
79
  return resolver.specialize(code, id, sourcemap);
79
80
  }
80
81
  },
81
- renderStart() {
82
- placer.analyze({
83
- moduleIds: this.getModuleIds(),
84
- getModuleInfo: (moduleId) => this.getModuleInfo(moduleId),
85
- getAdditionalModuleBytes: (info) => getNativeComponentAssetBytes(info.meta)
86
- });
87
- },
88
82
  renderChunk: {
89
83
  order: 'post',
90
84
  async handler(code, chunk, outputOptions, meta) {
85
+ // vpt:wx-placer runs first and has already created immutable placement from this complete chunk graph.
91
86
  const executionKind = getWxExecutionKind(chunk);
92
87
  const sourcemap = Boolean(outputOptions.sourcemap);
93
88
  if (executionKind === 'capsule') {
@@ -101,7 +96,8 @@ function createWxPlugin(options, resolver) {
101
96
  code: native.code,
102
97
  transportChunk: chunk,
103
98
  chunks: meta.chunks,
104
- getLoadMode: placer.getLoadMode,
99
+ getLoadMode: placement.getLoadMode,
100
+ getPhysicalChunkId: placement.getPhysicalChunkId,
105
101
  sourcemap
106
102
  });
107
103
  }
@@ -117,12 +113,16 @@ function createWxPlugin(options, resolver) {
117
113
  */
118
114
  order: 'post',
119
115
  async handler(_, bundle) {
120
- const subpackages = placer.getSubpackages(bundle);
116
+ // LTHP joins OutputChunks to their preliminary logical IDs and assigns Rolldown-owned physical filenames.
117
+ // createOutputFiles then observes those paths to relocate native component folders, emit placeholders, and
118
+ // declare only surviving package roots in app.json. No JavaScript chunk is manually emitted or copied.
119
+ const subpackages = placement.getSubpackages();
121
120
  const outputFiles = await createOutputFiles({
122
121
  bundle,
123
122
  options,
124
123
  subpackages,
125
- getModuleInfo: (moduleId) => this.getModuleInfo(moduleId)
124
+ getModuleInfo: (moduleId) => this.getModuleInfo(moduleId),
125
+ getPackageLocation: placement.getPackageLocation
126
126
  });
127
127
  outputFiles.forEach((file) => {
128
128
  this.emitFile(file);
@@ -1,5 +1,5 @@
1
1
  import { types } from '@babel/core';
2
- import { resolveChunkReference } from '../../../utils/modules.js';
2
+ import { resolveLogicalChunkReference } from '../chunk-path.js';
3
3
  /** Wraps System.register as an inert CommonJS capsule tuple with canonical final dependency IDs. */
4
4
  export function wrapCapsulePlugin(fileName) {
5
5
  return {
@@ -54,11 +54,11 @@ function canonicalizeStaticReference(reference, fileName) {
54
54
  if (!types.isStringLiteral(reference)) {
55
55
  throw new Error(`Expected a literal System.register dependency in ${fileName}`);
56
56
  }
57
- reference.value = resolveChunkReference(fileName, reference.value);
57
+ reference.value = resolveLogicalChunkReference(fileName, reference.value);
58
58
  }
59
59
  /** Resolves application literals while preserving runtime-computed IDs injected by the development runtime. */
60
60
  function canonicalizeDynamicReference(reference, fileName) {
61
61
  if (types.isStringLiteral(reference) && (reference.value.startsWith('./') || reference.value.startsWith('../'))) {
62
- reference.value = resolveChunkReference(fileName, reference.value);
62
+ reference.value = resolveLogicalChunkReference(fileName, reference.value);
63
63
  }
64
64
  }
@@ -1,7 +1,7 @@
1
1
  import { types } from '@babel/core';
2
2
  import transformModulesCommonjs from '@babel/plugin-transform-modules-commonjs';
3
- import { resolveChunkReference } from '../../../utils/modules.js';
4
3
  import { transformWithBabel } from '../../../utils/transform.js';
4
+ import { resolveLogicalChunkReference, resolvePhysicalChunkReference } from '../chunk-path.js';
5
5
  import { getWxEntryRole } from '../module.js';
6
6
  /** Renders a native module while activating its statically imported capsules through SystemJS. */
7
7
  export function renderNative({ code, chunk, chunks, sourcemap }) {
@@ -17,8 +17,8 @@ function connectNativeCapsulesPlugin(fileName, chunks) {
17
17
  if (!reference.startsWith('./') && !reference.startsWith('../')) {
18
18
  return;
19
19
  }
20
- const chunkId = resolveChunkReference(fileName, reference);
21
- const importedChunk = chunks[chunkId];
20
+ const physicalChunkId = resolvePhysicalChunkReference(fileName, reference);
21
+ const importedChunk = chunks[physicalChunkId];
22
22
  if (!importedChunk || getWxEntryRole(importedChunk) !== 'capsule') {
23
23
  return;
24
24
  }
@@ -26,12 +26,12 @@ function connectNativeCapsulesPlugin(fileName, chunks) {
26
26
  if (importPath.node.specifiers.length !== 1 ||
27
27
  !specifier ||
28
28
  types.isImportNamespaceSpecifier(specifier)) {
29
- throw new Error(`Expected one capsule value import from ${chunkId} in ${fileName}`);
29
+ throw new Error(`Expected one capsule value import from ${physicalChunkId} in ${fileName}`);
30
30
  }
31
31
  const imported = types.isImportDefaultSpecifier(specifier)
32
32
  ? types.identifier('default')
33
33
  : specifier.imported;
34
- const importedConfig = types.memberExpression(createSyncImport(chunkId), types.cloneNode(imported), types.isStringLiteral(imported));
34
+ const importedConfig = types.memberExpression(createSyncImport(resolveLogicalChunkReference(fileName, reference)), types.cloneNode(imported), types.isStringLiteral(imported));
35
35
  importPath.replaceWith(types.variableDeclaration('const', [
36
36
  types.variableDeclarator(types.cloneNode(specifier.local), importedConfig)
37
37
  ]));
@@ -1,17 +1,21 @@
1
1
  import type { Rolldown } from 'vite';
2
2
  import { type AstTransformResult } from '../../../utils/transform.ts';
3
3
  /**
4
- * Materializes transport while Rolldown's preliminary hash placeholders are still active, so every injected physical
5
- * reference participates in final hash calculation instead of changing code after its filename has been fixed.
4
+ * Materializes transport while Rolldown's preliminary hash placeholders are still active. Each switch case deliberately has
5
+ * two IDs: the package-neutral preliminary filename without its `assets/` directory becomes the SystemJS registration
6
+ * identity, while the LTHP-selected assets/package-qualified filename becomes the literal native require path. Rolldown
7
+ * substitutes both hashes after this transform, so the
8
+ * generated transport code and its own content hash describe the exact files that `generateBundle` later materializes.
6
9
  *
7
10
  * This intentionally creates broad hash invalidation: changing one capsule can rename transport, then bootstrap, then
8
11
  * chunks that import bootstrap. A Mini Program ships one application package rather than independently cached HTTP
9
12
  * chunks, so honest content hashes and automatic graph linking are more valuable than minimizing that hash fan-out.
10
13
  */
11
- export declare function materializeTransport({ code, transportChunk, chunks, getLoadMode, sourcemap }: {
14
+ export declare function materializeTransport({ code, transportChunk, chunks, getLoadMode, getPhysicalChunkId, sourcemap }: {
12
15
  code: string;
13
16
  transportChunk: Rolldown.RenderedChunk;
14
17
  chunks: Readonly<Record<string, Rolldown.RenderedChunk>>;
15
18
  getLoadMode(chunk: Rolldown.RenderedChunk): 'sync' | 'async';
19
+ getPhysicalChunkId?: (chunk: Rolldown.RenderedChunk) => string;
16
20
  sourcemap?: boolean;
17
21
  }): Promise<AstTransformResult>;
@@ -1,19 +1,24 @@
1
1
  import path from 'node:path';
2
2
  import { types } from '@babel/core';
3
3
  import { replaceWithAst } from '../../../utils/transform.js';
4
+ import { toLogicalChunkId } from '../chunk-path.js';
4
5
  import { getWxExecutionKind } from '../module.js';
5
6
  const transportPlaceholder = '__VPT_TRANSPORT__';
6
7
  const moduleIdParameter = 'moduleId';
7
8
  const exportBindingParameter = 'exportBinding';
8
9
  /**
9
- * Materializes transport while Rolldown's preliminary hash placeholders are still active, so every injected physical
10
- * reference participates in final hash calculation instead of changing code after its filename has been fixed.
10
+ * Materializes transport while Rolldown's preliminary hash placeholders are still active. Each switch case deliberately has
11
+ * two IDs: the package-neutral preliminary filename without its `assets/` directory becomes the SystemJS registration
12
+ * identity, while the LTHP-selected assets/package-qualified filename becomes the literal native require path. Rolldown
13
+ * substitutes both hashes after this transform, so the
14
+ * generated transport code and its own content hash describe the exact files that `generateBundle` later materializes.
11
15
  *
12
16
  * This intentionally creates broad hash invalidation: changing one capsule can rename transport, then bootstrap, then
13
17
  * chunks that import bootstrap. A Mini Program ships one application package rather than independently cached HTTP
14
18
  * chunks, so honest content hashes and automatic graph linking are more valuable than minimizing that hash fan-out.
15
19
  */
16
- export async function materializeTransport({ code, transportChunk, chunks, getLoadMode, sourcemap = true }) {
20
+ export async function materializeTransport({ code, transportChunk, chunks, getLoadMode, getPhysicalChunkId = (chunk) => chunk.fileName, sourcemap = true }) {
21
+ const physicalTransportId = getPhysicalChunkId(transportChunk);
17
22
  // Babel constructs and safely serializes an expression shaped like:
18
23
  // (moduleId) => {
19
24
  // switch (moduleId) {
@@ -28,12 +33,16 @@ export async function materializeTransport({ code, transportChunk, chunks, getLo
28
33
  .sort((left, right) => left.chunk.fileName.localeCompare(right.chunk.fileName))
29
34
  .map(({ chunk, kind }) => {
30
35
  const loadMode = getLoadMode(chunk);
36
+ const logicalChunkId = toLogicalChunkId(chunk.fileName);
37
+ // Only native loading crosses the logical/physical boundary and receives the assets/package-qualified path.
38
+ const physicalChunkId = getPhysicalChunkId(chunk);
31
39
  if (kind === 'amphibious' && loadMode !== 'sync') {
32
40
  throw new Error(`Amphibious wx module must be in the main package: ${chunk.fileName}`);
33
41
  }
34
42
  return createTransportCase({
35
- chunkId: chunk.fileName,
36
- transportFileName: transportChunk.fileName,
43
+ chunkId: logicalChunkId,
44
+ transportFileName: physicalTransportId,
45
+ physicalChunkId: physicalChunkId,
37
46
  loadMode,
38
47
  kind
39
48
  });
@@ -58,9 +67,9 @@ function getTransportedChunks(chunks) {
58
67
  }
59
68
  return transportedChunks;
60
69
  }
61
- /** Creates one canonical-ID switch case while keeping its native require argument literal. */
62
- function createTransportCase({ chunkId, transportFileName, loadMode, kind }) {
63
- const requirePath = toNativeRequirePath(transportFileName, chunkId);
70
+ /** Creates one logical-ID switch case while keeping its physical native require argument literal. */
71
+ function createTransportCase({ chunkId, transportFileName, loadMode, kind, physicalChunkId }) {
72
+ const requirePath = toNativeRequirePath(transportFileName, physicalChunkId);
64
73
  const requireCallee = loadMode === 'sync'
65
74
  ? types.identifier('require')
66
75
  : types.memberExpression(types.identifier('require'), types.identifier('async'));
@@ -6,8 +6,6 @@ type PageComponentPathOptions = {
6
6
  pagePath: string;
7
7
  projectRoot: string;
8
8
  };
9
- /** Resolves a final relative reference to the canonical output chunk ID used by the WX module registry. */
10
- export declare function resolveChunkReference(importerChunkId: string, reference: string): string;
11
9
  /** Resolves the source file for the configured App component. */
12
10
  export declare function resolveAppComponentPath({ appPath, projectRoot }: AppComponentPathOptions): string;
13
11
  /** Resolves the source file for one configured Page component. */
@@ -1,12 +1,5 @@
1
1
  import path from 'node:path';
2
2
  import { normalizePath } from 'vite';
3
- /** Resolves a final relative reference to the canonical output chunk ID used by the WX module registry. */
4
- export function resolveChunkReference(importerChunkId, reference) {
5
- if (!reference.startsWith('./') && !reference.startsWith('../')) {
6
- throw new Error(`Expected a relative chunk reference in ${importerChunkId}: ${reference}`);
7
- }
8
- return path.posix.join(path.posix.dirname(importerChunkId), reference);
9
- }
10
3
  /** Resolves the source file for the configured App component. */
11
4
  export function resolveAppComponentPath({ appPath, projectRoot }) {
12
5
  return path.resolve(projectRoot, appPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-taro",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
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",
@@ -76,8 +76,8 @@
76
76
  "rxjs": "^7.8.2",
77
77
  "tailwindcss": "^4.3.3",
78
78
  "weapp-tailwindcss": "^5.2.11",
79
- "@tarojs/react": "npm:vite-plugin-taro-react@0.6.0",
80
- "@tarojs/plugin-framework-react": "npm:vite-plugin-taro-plugin-framework-react@0.6.0"
79
+ "@tarojs/plugin-framework-react": "npm:vite-plugin-taro-plugin-framework-react@0.6.1",
80
+ "@tarojs/react": "npm:vite-plugin-taro-react@0.6.1"
81
81
  },
82
82
  "peerDependencies": {
83
83
  "react": "^19.0.0",
@@ -0,0 +1,22 @@
1
+ import path from 'node:path'
2
+
3
+ /** Physical directory in which Rolldown writes generated JavaScript chunks inside each native package. */
4
+ export const generatedChunkDirectory = 'assets'
5
+
6
+ /** Projects one Rolldown-owned physical candidate path into the package-neutral SystemJS identity. */
7
+ export function toLogicalChunkId(physicalChunkId: string): string {
8
+ return path.posix.relative(generatedChunkDirectory, physicalChunkId)
9
+ }
10
+
11
+ /** Resolves one relative Rolldown-generated import to its preliminary physical chunk path. */
12
+ export function resolvePhysicalChunkReference(importerChunkId: string, reference: string): string {
13
+ if (!reference.startsWith('./') && !reference.startsWith('../')) {
14
+ throw new Error(`Expected a relative chunk reference in ${importerChunkId}: ${reference}`)
15
+ }
16
+ return path.posix.join(path.posix.dirname(importerChunkId), reference)
17
+ }
18
+
19
+ /** Projects one relative Rolldown-generated import into its package-neutral SystemJS identity. */
20
+ export function resolveLogicalChunkReference(importerChunkId: string, reference: string): string {
21
+ return toLogicalChunkId(resolvePhysicalChunkReference(importerChunkId, reference))
22
+ }
@@ -68,12 +68,26 @@ export function installWxDevOptions({
68
68
  * mutating configuredOutput itself would leak development normalization back into the user's resolved Vite config.
69
69
  */
70
70
  Object.assign(output, configured, {
71
+ // Development output is overwritten in place after every complete build. Strip hash placeholders from the
72
+ // configured asset pattern so old files cannot accumulate and native JSON/WXML references remain stable.
71
73
  assetFileNames: createStableFileNames(configured.assetFileNames, 'assets/[name][extname]'),
74
+ // Banners create physical CommonJS edges after graph analysis: App initializes the dev runtime and each Page
75
+ // consumes the stable patch journal without allowing those host-only files into the application chunk graph.
72
76
  banner: createEntryBanner(pageFiles),
77
+ // Preserve the configured directory/name shape while removing content hashes. Stable chunk paths let DevTools
78
+ // overwrite executable files and let cumulative HMR patches address one persistent physical module identity.
73
79
  chunkFileNames: createStableFileNames(configured.chunkFileNames, 'assets/[name].js'),
80
+ // Native entry paths are public Mini Program routes (`app.js`, `pages/.../index.js`); development must never hash
81
+ // or relocate them because DevTools determines App/Page reload behavior from those exact filenames.
74
82
  entryFileNames: createStableFileNames(configured.entryFileNames, '[name]'),
83
+ // Keep ESM until the existing WX renderChunk pipeline classifies each final chunk and converts capsules to
84
+ // System.register data or native/amphibious entries to CommonJS. Choosing CommonJS here would erase that boundary.
75
85
  format: 'es',
86
+ // Bundled development emits complete physical output repeatedly. Minifying bounds disk transfer and DevTools
87
+ // compile work; source-level HMR diagnostics still come from Vite/Rolldown before this final output pass.
76
88
  minify: true,
89
+ // DevTools executes physical WX files and HMR applies module factories rather than browser source maps. Disabling
90
+ // maps avoids extra output files and prevents Vite's Oxc sourcemap transform from touching generated host code.
77
91
  sourcemap: false
78
92
  })
79
93
 
@@ -84,9 +98,16 @@ export function installWxDevOptions({
84
98
  rolldownOptions.experimental ??= {}
85
99
  const existingDevMode = rolldownOptions.experimental.devMode
86
100
  rolldownOptions.experimental.devMode = {
101
+ // Retain unknown user/forward-compatible devMode fields while the three explicit WX invariants below win.
87
102
  ...(typeof existingDevMode === 'object' ? existingDevMode : {}),
103
+ // Install the WX-adapted self-contained Rolldown runtime. It consumes physical patch journals and reports
104
+ // acknowledgements/rebuild requests through the host bridge instead of relying on browser globals or sockets.
88
105
  implement: await bundleRuntimeSource(),
106
+ // Produce a complete output graph on the initial build. Lazy per-request compilation cannot establish the closed
107
+ // App/Page graph, native companions, style sidecars, and build identity required before any patch is admitted.
89
108
  lazy: false,
109
+ // Keep Rolldown's common runtime injection because generated application factories call its module registry and
110
+ // HMR primitives. Skipping it would leave the custom implementation without the runtime surface it extends.
90
111
  skipCommonRuntimeInjection: false
91
112
  }
92
113
 
@@ -1,16 +1,18 @@
1
1
  import { readFile } from 'node:fs/promises'
2
2
  import path from 'node:path'
3
3
  import type { Rolldown } from 'vite'
4
- import { isGeneratedSubpackageFile } from '../placement/plan.ts'
4
+ import type { PackageLocation } from '../placer/placement.ts'
5
5
  import { getNativeComponentSources } from './native-component-assets.ts'
6
6
 
7
7
  /** Creates opaque native files and their registrations from each surviving JSX interface module. */
8
8
  export async function createNativeComponentOutput({
9
9
  bundle,
10
- getModuleInfo
10
+ getModuleInfo,
11
+ getPackageLocation
11
12
  }: {
12
13
  bundle: Rolldown.OutputBundle
13
14
  getModuleInfo: (moduleId: string) => { meta: Rolldown.CustomPluginOptions } | null
15
+ getPackageLocation(chunk: Rolldown.OutputChunk): PackageLocation
14
16
  }) {
15
17
  // Files and registrations accumulate in final chunk order for deterministic output.
16
18
  const files: Rolldown.EmittedAsset[] = []
@@ -25,9 +27,8 @@ export async function createNativeComponentOutput({
25
27
  if (output.type !== 'chunk') {
26
28
  continue
27
29
  }
28
- const packageRoot = isGeneratedSubpackageFile(output.fileName)
29
- ? path.posix.dirname(path.posix.dirname(output.fileName))
30
- : undefined
30
+ const location = getPackageLocation(output)
31
+ const packageRoot = location.kind === 'subpackage' ? location.root : undefined
31
32
 
32
33
  for (const moduleId of output.moduleIds) {
33
34
  const sources = getNativeComponentSources(getModuleInfo(moduleId)?.meta)
@@ -1,7 +1,7 @@
1
1
  import type { Rolldown } from 'vite'
2
2
  import type { VptOptions } from '../../../../options.ts'
3
3
  import { createNativeComponentOutput } from '../native/create-native-component-output.ts'
4
- import type { GeneratedSubpackage } from '../placement/placer.ts'
4
+ import type { GeneratedSubpackage, PackageLocation } from '../placer/placement.ts'
5
5
  import { createJsonAssets } from './json.ts'
6
6
  import { createTemplateAssets } from './templates.ts'
7
7
 
@@ -10,14 +10,16 @@ export async function createOutputFiles({
10
10
  bundle,
11
11
  options,
12
12
  subpackages,
13
- getModuleInfo
13
+ getModuleInfo,
14
+ getPackageLocation
14
15
  }: {
15
16
  bundle: Rolldown.OutputBundle
16
17
  options: VptOptions
17
18
  subpackages: readonly GeneratedSubpackage[]
18
19
  getModuleInfo: (moduleId: string) => { meta: Rolldown.CustomPluginOptions } | null
20
+ getPackageLocation(chunk: Rolldown.OutputChunk): PackageLocation
19
21
  }): Promise<Rolldown.EmittedFile[]> {
20
- const nativeOutput = await createNativeComponentOutput({ bundle, getModuleInfo })
22
+ const nativeOutput = await createNativeComponentOutput({ bundle, getModuleInfo, getPackageLocation })
21
23
 
22
24
  return [
23
25
  {
@@ -1,8 +1,7 @@
1
1
  import type { Rolldown } from 'vite'
2
2
  import type { VptJsonObject, VptOptions, VptPageOption } from '../../../../options.ts'
3
3
  import { createAppConfig } from '../../../utils/project-config.ts'
4
- import type { GeneratedSubpackage } from '../placement/placer.ts'
5
- import { isGeneratedSubpackageFile } from '../placement/plan.ts'
4
+ import { type GeneratedSubpackage, isGeneratedSubpackageFile } from '../placer/placement.ts'
6
5
  import { toRootRelativePath } from './relative-root.ts'
7
6
 
8
7
  /** Creates every configured native JSON asset. */