vite-plugin-taro 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/README.en.md +44 -0
  2. package/README.md +22 -497
  3. package/client.d.ts +9 -0
  4. package/dist/node/plugins/client/client-taro.d.ts +4 -0
  5. package/dist/node/plugins/client/client-taro.js +19 -0
  6. package/dist/node/plugins/client/constant.d.ts +4 -0
  7. package/dist/node/plugins/client/constant.js +4 -0
  8. package/dist/node/plugins/conditional/conditional-directives.d.ts +4 -0
  9. package/dist/node/plugins/{conditional-directives.js → conditional/conditional-directives.js} +27 -11
  10. package/dist/node/plugins/css/plugins.d.ts +4 -0
  11. package/dist/node/plugins/css/plugins.js +84 -0
  12. package/dist/node/plugins/h5/constant.d.ts +2 -0
  13. package/dist/node/plugins/h5/constant.js +3 -0
  14. package/dist/node/plugins/h5/plugins.d.ts +4 -0
  15. package/dist/node/plugins/h5/plugins.js +161 -0
  16. package/dist/node/plugins/h5/resolver/module-resolver.d.ts +14 -0
  17. package/dist/node/plugins/h5/resolver/module-resolver.js +19 -0
  18. package/dist/node/plugins/h5/transform-app.d.ts +10 -0
  19. package/dist/node/plugins/h5/transform-app.js +48 -0
  20. package/dist/node/plugins/wx/dev/dev-host.d.ts +15 -0
  21. package/dist/node/plugins/wx/dev/dev-host.js +352 -0
  22. package/dist/node/plugins/wx/dev/hmr-files.d.ts +28 -0
  23. package/dist/node/plugins/wx/dev/hmr-files.js +34 -0
  24. package/dist/node/plugins/wx/dev/patch-publisher.d.ts +22 -0
  25. package/dist/node/plugins/wx/dev/patch-publisher.js +40 -0
  26. package/dist/node/plugins/wx/dev/plugin.d.ts +7 -0
  27. package/dist/node/plugins/wx/dev/plugin.js +73 -0
  28. package/dist/node/plugins/wx/dev/react-refresh.d.ts +20 -0
  29. package/dist/node/plugins/wx/dev/react-refresh.js +198 -0
  30. package/dist/node/plugins/wx/module.d.ts +45 -0
  31. package/dist/node/plugins/wx/module.js +66 -0
  32. package/dist/node/plugins/wx/native/compile-native-component-interface.d.ts +21 -0
  33. package/dist/node/plugins/wx/native/compile-native-component-interface.js +29 -0
  34. package/dist/node/plugins/wx/native/create-native-component-output.d.ts +15 -0
  35. package/dist/node/plugins/wx/native/create-native-component-output.js +39 -0
  36. package/dist/node/plugins/wx/native/native-component-assets.d.ts +26 -0
  37. package/dist/node/plugins/wx/native/native-component-assets.js +76 -0
  38. package/dist/node/plugins/wx/native/native-component-interface.d.ts +12 -0
  39. package/dist/node/plugins/wx/native/native-component-interface.js +201 -0
  40. package/dist/node/plugins/wx/output/files.d.ts +12 -0
  41. package/dist/node/plugins/wx/output/files.js +12 -0
  42. package/dist/node/plugins/wx/output/json.d.ts +14 -0
  43. package/dist/node/plugins/wx/output/json.js +52 -0
  44. package/dist/node/plugins/wx/output/relative-root.d.ts +2 -0
  45. package/dist/node/plugins/wx/output/relative-root.js +6 -0
  46. package/dist/node/plugins/wx/output/templates.d.ts +7 -0
  47. package/dist/node/plugins/wx/output/templates.js +100 -0
  48. package/dist/node/plugins/wx/placement/placer.d.ts +78 -0
  49. package/dist/node/plugins/wx/placement/placer.js +145 -0
  50. package/dist/node/plugins/wx/placement/plan.d.ts +46 -0
  51. package/dist/node/plugins/wx/placement/plan.js +210 -0
  52. package/dist/node/plugins/wx/plugins.d.ts +4 -0
  53. package/dist/node/plugins/wx/plugins.js +137 -0
  54. package/dist/node/plugins/wx/render/capsule-wrapper.d.ts +3 -0
  55. package/dist/node/plugins/wx/render/capsule-wrapper.js +64 -0
  56. package/dist/node/plugins/wx/render/capsule.d.ts +4 -0
  57. package/dist/node/plugins/wx/render/capsule.js +13 -0
  58. package/dist/node/plugins/wx/render/native.d.ts +9 -0
  59. package/dist/node/plugins/wx/render/native.js +45 -0
  60. package/dist/node/plugins/wx/render/transport.d.ts +17 -0
  61. package/dist/node/plugins/wx/render/transport.js +94 -0
  62. package/dist/node/plugins/wx/resolve/resolver.d.ts +7 -0
  63. package/dist/node/plugins/wx/resolve/resolver.js +86 -0
  64. package/dist/node/plugins/wx/resolve/specialize-bootstrap.d.ts +9 -0
  65. package/dist/node/plugins/wx/resolve/specialize-bootstrap.js +9 -0
  66. package/dist/node/plugins/wx/resolve/specialize-page-capsule.d.ts +9 -0
  67. package/dist/node/plugins/wx/resolve/specialize-page-capsule.js +11 -0
  68. package/dist/node/utils/constant.d.ts +1 -0
  69. package/dist/node/utils/constant.js +1 -0
  70. package/dist/node/utils/memoize.d.ts +122 -0
  71. package/dist/node/utils/memoize.js +86 -0
  72. package/dist/node/utils/modules.d.ts +20 -24
  73. package/dist/node/utils/modules.js +21 -34
  74. package/dist/node/utils/once.d.ts +10 -0
  75. package/dist/node/utils/once.js +20 -0
  76. package/dist/node/utils/packages.d.ts +1 -0
  77. package/dist/node/utils/packages.js +1 -1
  78. package/dist/node/utils/project-config.d.ts +3 -0
  79. package/dist/node/utils/project-config.js +8 -0
  80. package/dist/node/utils/serialized-task-queue.d.ts +9 -0
  81. package/dist/node/utils/serialized-task-queue.js +17 -0
  82. package/dist/node/utils/transform.d.ts +11 -0
  83. package/dist/node/utils/transform.js +58 -0
  84. package/dist/node/vite-plugin.d.ts +1 -1
  85. package/dist/node/vite-plugin.js +11 -27
  86. package/dist/options.d.ts +3 -4
  87. package/dist/runtime/{taro → client/taro}/api.d.ts +0 -1
  88. package/dist/runtime/client/taro/api.js +9 -0
  89. package/dist/runtime/client/taro/component.d.ts +1 -0
  90. package/dist/runtime/client/taro/component.js +1 -0
  91. package/dist/runtime/client/taro/define-native-component.d.ts +7 -0
  92. package/dist/runtime/client/taro/define-native-component.js +4 -0
  93. package/dist/runtime/h5/app.d.ts +2 -0
  94. package/dist/runtime/h5/app.js +15 -0
  95. package/dist/runtime/h5/taro-runtime.d.ts +1 -1
  96. package/dist/runtime/h5/taro-runtime.js +1 -4
  97. package/dist/runtime/wx/amphibious/bootstrap.d.ts +4 -0
  98. package/dist/runtime/wx/amphibious/bootstrap.js +16 -0
  99. package/dist/runtime/wx/amphibious/transport.d.ts +2 -0
  100. package/dist/runtime/wx/amphibious/transport.js +4 -0
  101. package/dist/runtime/wx/capsule/app.d.ts +2 -0
  102. package/dist/runtime/wx/capsule/app.js +7 -0
  103. package/dist/runtime/wx/capsule/component.d.ts +3 -0
  104. package/dist/runtime/wx/capsule/component.js +4 -0
  105. package/dist/runtime/wx/capsule/page.d.ts +3 -0
  106. package/dist/runtime/wx/capsule/page.js +68 -0
  107. package/dist/runtime/wx/capsule/taro-runtime.d.ts +6 -0
  108. package/dist/runtime/wx/capsule/taro-runtime.js +6 -0
  109. package/dist/runtime/wx/dev/dev-runtime.d.ts +1 -0
  110. package/dist/runtime/wx/dev/dev-runtime.js +246 -0
  111. package/dist/runtime/wx/native/app.d.ts +1 -0
  112. package/dist/runtime/wx/native/app.js +3 -0
  113. package/dist/runtime/wx/native/component.d.ts +1 -0
  114. package/dist/runtime/wx/native/component.js +3 -0
  115. package/dist/runtime/wx/native/page.d.ts +1 -0
  116. package/dist/runtime/wx/native/page.js +4 -0
  117. package/dist/runtime/wx/systemjs/system-core.d.ts +4 -0
  118. package/dist/runtime/wx/systemjs/system-core.js +331 -0
  119. package/package.json +21 -14
  120. package/src/node/plugins/client/client-taro.ts +23 -0
  121. package/src/node/plugins/client/constant.ts +5 -0
  122. package/src/node/plugins/{conditional-directives.ts → conditional/conditional-directives.ts} +33 -13
  123. package/src/node/plugins/css/plugins.ts +96 -0
  124. package/src/node/plugins/h5/constant.ts +4 -0
  125. package/src/node/plugins/h5/plugins.ts +197 -0
  126. package/src/node/plugins/h5/resolver/module-resolver.ts +32 -0
  127. package/src/node/plugins/h5/transform-app.ts +90 -0
  128. package/src/node/plugins/wx/dev/dev-host.ts +448 -0
  129. package/src/node/plugins/wx/dev/hmr-files.ts +55 -0
  130. package/src/node/plugins/wx/dev/patch-publisher.ts +49 -0
  131. package/src/node/plugins/wx/dev/plugin.ts +82 -0
  132. package/src/node/plugins/wx/dev/react-refresh.ts +227 -0
  133. package/src/node/plugins/wx/module.ts +97 -0
  134. package/src/node/plugins/wx/native/compile-native-component-interface.ts +47 -0
  135. package/src/node/plugins/wx/native/create-native-component-output.ts +57 -0
  136. package/src/node/plugins/wx/native/native-component-assets.ts +114 -0
  137. package/src/node/plugins/wx/native/native-component-interface.ts +265 -0
  138. package/src/node/plugins/wx/output/files.ts +27 -0
  139. package/src/node/plugins/wx/output/json.ts +79 -0
  140. package/src/node/plugins/wx/output/relative-root.ts +7 -0
  141. package/src/node/plugins/wx/output/templates.ts +141 -0
  142. package/src/node/plugins/wx/placement/placer.ts +172 -0
  143. package/src/node/plugins/wx/placement/plan.ts +306 -0
  144. package/src/node/plugins/wx/plugins.ts +165 -0
  145. package/src/node/plugins/wx/render/capsule-wrapper.ts +86 -0
  146. package/src/node/plugins/wx/render/capsule.ts +21 -0
  147. package/src/node/plugins/wx/render/native.ts +84 -0
  148. package/src/node/plugins/wx/render/transport.ts +157 -0
  149. package/src/node/plugins/wx/resolve/resolver.ts +125 -0
  150. package/src/node/plugins/wx/resolve/specialize-bootstrap.ts +27 -0
  151. package/src/node/plugins/wx/resolve/specialize-page-capsule.ts +29 -0
  152. package/src/node/utils/constant.ts +1 -0
  153. package/src/node/utils/memoize.ts +149 -0
  154. package/src/node/utils/modules.ts +36 -38
  155. package/src/node/utils/once.ts +21 -0
  156. package/src/node/utils/packages.ts +1 -1
  157. package/src/node/utils/project-config.ts +10 -0
  158. package/src/node/utils/serialized-task-queue.ts +20 -0
  159. package/src/node/utils/transform.ts +82 -0
  160. package/src/node/vite-plugin.ts +12 -32
  161. package/src/options.ts +3 -4
  162. package/src/runtime/client/taro/api.ts +11 -0
  163. package/src/runtime/client/taro/component.ts +1 -0
  164. package/src/runtime/client/taro/define-native-component.ts +15 -0
  165. package/src/runtime/h5/app.ts +25 -0
  166. package/src/runtime/h5/taro-runtime.ts +1 -4
  167. package/src/runtime/wx/amphibious/bootstrap.ts +22 -0
  168. package/src/runtime/wx/amphibious/transport.ts +10 -0
  169. package/src/runtime/wx/capsule/app.ts +9 -0
  170. package/src/runtime/wx/capsule/component.ts +6 -0
  171. package/src/runtime/wx/capsule/page.ts +96 -0
  172. package/src/runtime/wx/capsule/taro-runtime.ts +14 -0
  173. package/src/runtime/wx/dev/dev-runtime.ts +317 -0
  174. package/src/runtime/wx/native/app.ts +4 -0
  175. package/src/runtime/wx/native/component.ts +4 -0
  176. package/src/runtime/wx/native/page.ts +6 -0
  177. package/src/runtime/wx/systemjs/LICENSE +10 -0
  178. package/src/runtime/wx/systemjs/system-core.js +370 -0
  179. package/src/runtime/wx/systemjs/system.d.ts +55 -0
  180. package/src/runtime/wx/wechat.d.ts +52 -0
  181. package/README.zh.md +0 -521
  182. package/dist/node/build-context.d.ts +0 -25
  183. package/dist/node/build-context.js +0 -47
  184. package/dist/node/css/css-pipeline.d.ts +0 -35
  185. package/dist/node/css/css-pipeline.js +0 -154
  186. package/dist/node/plugins/conditional-directives.d.ts +0 -3
  187. package/dist/node/plugins/taro-runtime.d.ts +0 -5
  188. package/dist/node/plugins/taro-runtime.js +0 -18
  189. package/dist/node/targets/h5/plugin.d.ts +0 -6
  190. package/dist/node/targets/h5/plugin.js +0 -117
  191. package/dist/node/targets/h5/virtual-modules.d.ts +0 -5
  192. package/dist/node/targets/h5/virtual-modules.js +0 -60
  193. package/dist/node/targets/wx/companion-assets.d.ts +0 -19
  194. package/dist/node/targets/wx/companion-assets.js +0 -129
  195. package/dist/node/targets/wx/dev-server/development-session.d.ts +0 -32
  196. package/dist/node/targets/wx/dev-server/development-session.js +0 -215
  197. package/dist/node/targets/wx/dev-server/full-build-scheduler.d.ts +0 -14
  198. package/dist/node/targets/wx/dev-server/full-build-scheduler.js +0 -60
  199. package/dist/node/targets/wx/dev-server/js-utils.d.ts +0 -5
  200. package/dist/node/targets/wx/dev-server/js-utils.js +0 -28
  201. package/dist/node/targets/wx/dev-server/output.d.ts +0 -19
  202. package/dist/node/targets/wx/dev-server/output.js +0 -44
  203. package/dist/node/targets/wx/dev-server/rolldown-runtime-source.d.ts +0 -8
  204. package/dist/node/targets/wx/dev-server/rolldown-runtime-source.js +0 -95
  205. package/dist/node/targets/wx/dev-server/update-server-state.d.ts +0 -65
  206. package/dist/node/targets/wx/dev-server/update-server-state.js +0 -116
  207. package/dist/node/targets/wx/dev-server/update-transport.d.ts +0 -28
  208. package/dist/node/targets/wx/dev-server/update-transport.js +0 -212
  209. package/dist/node/targets/wx/dev-server/vite-bundled-dev-adapter.d.ts +0 -44
  210. package/dist/node/targets/wx/dev-server/vite-bundled-dev-adapter.js +0 -145
  211. package/dist/node/targets/wx/development-files.d.ts +0 -8
  212. package/dist/node/targets/wx/development-files.js +0 -8
  213. package/dist/node/targets/wx/plugin.d.ts +0 -6
  214. package/dist/node/targets/wx/plugin.js +0 -144
  215. package/dist/node/targets/wx/react-refresh.d.ts +0 -4
  216. package/dist/node/targets/wx/react-refresh.js +0 -38
  217. package/dist/node/targets/wx/virtual-modules.d.ts +0 -16
  218. package/dist/node/targets/wx/virtual-modules.js +0 -120
  219. package/dist/node/utils/async.d.ts +0 -8
  220. package/dist/node/utils/async.js +0 -16
  221. package/dist/node/utils/filesystem.d.ts +0 -13
  222. package/dist/node/utils/filesystem.js +0 -43
  223. package/dist/runtime/taro/api.js +0 -11
  224. package/dist/runtime/taro/components.d.ts +0 -2
  225. package/dist/runtime/taro/components.js +0 -2
  226. package/dist/runtime/wx/page-update.d.ts +0 -6
  227. package/dist/runtime/wx/page-update.js +0 -157
  228. package/dist/runtime/wx/taro-runtime.d.ts +0 -5
  229. package/dist/runtime/wx/taro-runtime.js +0 -6
  230. package/dist/runtime/wx/update-client-state.d.ts +0 -68
  231. package/dist/runtime/wx/update-client-state.js +0 -84
  232. package/dist/runtime/wx/update-client.d.ts +0 -1
  233. package/dist/runtime/wx/update-client.js +0 -171
  234. package/src/node/build-context.ts +0 -62
  235. package/src/node/css/css-pipeline.ts +0 -171
  236. package/src/node/plugins/taro-runtime.ts +0 -22
  237. package/src/node/targets/h5/plugin.ts +0 -135
  238. package/src/node/targets/h5/virtual-modules.ts +0 -71
  239. package/src/node/targets/wx/companion-assets.ts +0 -174
  240. package/src/node/targets/wx/dev-server/development-session.ts +0 -271
  241. package/src/node/targets/wx/dev-server/full-build-scheduler.ts +0 -62
  242. package/src/node/targets/wx/dev-server/js-utils.ts +0 -33
  243. package/src/node/targets/wx/dev-server/output.ts +0 -56
  244. package/src/node/targets/wx/dev-server/rolldown-runtime-source.ts +0 -95
  245. package/src/node/targets/wx/dev-server/update-server-state.ts +0 -183
  246. package/src/node/targets/wx/dev-server/update-transport.ts +0 -261
  247. package/src/node/targets/wx/dev-server/vite-bundled-dev-adapter.ts +0 -203
  248. package/src/node/targets/wx/development-files.ts +0 -11
  249. package/src/node/targets/wx/plugin.ts +0 -164
  250. package/src/node/targets/wx/react-refresh.ts +0 -51
  251. package/src/node/targets/wx/virtual-modules.ts +0 -152
  252. package/src/node/utils/async.ts +0 -19
  253. package/src/node/utils/filesystem.ts +0 -45
  254. package/src/runtime/taro/api.ts +0 -13
  255. package/src/runtime/taro/components.ts +0 -2
  256. package/src/runtime/wx/page-update.ts +0 -230
  257. package/src/runtime/wx/react-refresh.d.ts +0 -20
  258. package/src/runtime/wx/taro-runtime.ts +0 -7
  259. package/src/runtime/wx/update-client-state.ts +0 -138
  260. package/src/runtime/wx/update-client.ts +0 -223
@@ -0,0 +1,201 @@
1
+ import path from 'node:path';
2
+ import { RolldownMagicString } from 'rolldown';
3
+ import { parseAst } from 'rolldown/parseAst';
4
+ import { normalizeModuleId } from '../../../utils/modules.js';
5
+ import { clientTaroNativeId } from '../../client/constant.js';
6
+ /** Replaces native component interface calls with source-folder names and returns their static metadata. */
7
+ export function transformNativeComponentInterfaces(code, id, sourcemap) {
8
+ const moduleId = normalizeModuleId(id);
9
+ const program = parseAst(code, {
10
+ astType: 'ts',
11
+ lang: 'tsx',
12
+ preserveParens: false,
13
+ sourceType: 'module'
14
+ }, moduleId);
15
+ const declaredInterfaces = collectInterfaceDeclarations(program);
16
+ const macroImports = collectMacroImports(program);
17
+ const importedMacroNames = new Set(macroImports.flatMap((item) => item.names));
18
+ const calls = collectTopLevelMacroCalls(program, importedMacroNames);
19
+ const replacements = calls.map((call) => {
20
+ const definition = parseDefinition(call, moduleId, declaredInterfaces, (message) => createTransformError(message, moduleId, code, call.start));
21
+ return { call, definition };
22
+ });
23
+ // This transform-local mutable buffer applies non-overlapping AST edits without regenerating untouched source.
24
+ const transformed = new RolldownMagicString(code, { filename: moduleId });
25
+ replacements.forEach(({ call, definition }) => {
26
+ transformed.overwrite(call.start, call.end, JSON.stringify(path.posix.basename(definition.folder)));
27
+ });
28
+ macroImports.forEach(({ declaration }) => {
29
+ transformed.remove(declaration.start, declaration.end);
30
+ });
31
+ return {
32
+ code: transformed.toString(),
33
+ map: sourcemap ? createSourceMap(transformed, moduleId) : null,
34
+ definitions: replacements.map(({ definition }) => definition)
35
+ };
36
+ }
37
+ /** Normalizes Rolldown's native source-map object to the public plugin source-map shape. */
38
+ function createSourceMap(transformed, moduleId) {
39
+ const sourceMap = transformed.generateMap({
40
+ hires: 'boundary',
41
+ includeContent: true,
42
+ source: moduleId
43
+ });
44
+ return {
45
+ file: sourceMap.file,
46
+ mappings: sourceMap.mappings,
47
+ names: sourceMap.names,
48
+ sources: sourceMap.sources,
49
+ sourcesContent: sourceMap.sourcesContent,
50
+ version: sourceMap.version
51
+ };
52
+ }
53
+ /** Finds named imports of the compile-time macro and records their local aliases. */
54
+ function collectMacroImports(program) {
55
+ return program.body.flatMap((statement) => {
56
+ if (statement.type !== 'ImportDeclaration' || statement.source.value !== clientTaroNativeId) {
57
+ return [];
58
+ }
59
+ const names = statement.specifiers.flatMap((specifier) => {
60
+ if (specifier.type !== 'ImportSpecifier' || getStaticName(specifier.imported) !== 'defineNativeComponent') {
61
+ return [];
62
+ }
63
+ return [specifier.local.name];
64
+ });
65
+ return names.length === 0 ? [] : [{ declaration: statement, names }];
66
+ });
67
+ }
68
+ /** Collects direct module-level macro calls, excluding calls inside nested scopes. */
69
+ function collectTopLevelMacroCalls(program, importedMacroNames) {
70
+ return program.body.flatMap((statement) => {
71
+ if (statement.type === 'ExpressionStatement') {
72
+ return isImportedMacroCall(statement.expression, importedMacroNames) ? [statement.expression] : [];
73
+ }
74
+ if (statement.type === 'ExportDefaultDeclaration') {
75
+ return isImportedMacroCall(statement.declaration, importedMacroNames) ? [statement.declaration] : [];
76
+ }
77
+ const declaration = statement.type === 'ExportNamedDeclaration' ? statement.declaration : statement;
78
+ if (declaration?.type !== 'VariableDeclaration') {
79
+ return [];
80
+ }
81
+ return declaration.declarations.flatMap(({ init }) => {
82
+ return isImportedMacroCall(init, importedMacroNames) ? [init] : [];
83
+ });
84
+ });
85
+ }
86
+ /** Matches a direct call to one of the macro's module-level import bindings. */
87
+ function isImportedMacroCall(node, importedMacroNames) {
88
+ return (node?.type === 'CallExpression' && node.callee.type === 'Identifier' && importedMacroNames.has(node.callee.name));
89
+ }
90
+ /** Parses one native entry loader and its optional TypeScript interface. */
91
+ function parseDefinition(call, moduleId, declaredInterfaces, buildError) {
92
+ if (call.arguments.length !== 1) {
93
+ throw buildError('defineNativeComponent() requires exactly one entry loader');
94
+ }
95
+ const entryReference = readStaticEntryImport(call.arguments[0]);
96
+ if (!entryReference) {
97
+ throw buildError('Native component entry must use () => import(...) with a static relative .js path');
98
+ }
99
+ const entryPath = normalizeModuleId(path.resolve(path.dirname(moduleId), entryReference));
100
+ return {
101
+ folder: path.posix.dirname(entryPath),
102
+ entry: path.posix.basename(entryPath, '.js'),
103
+ fields: readInterfaceFields(call.typeArguments, declaredInterfaces, buildError)
104
+ };
105
+ }
106
+ /** Collects non-generic local object aliases and interfaces as native template field declarations. */
107
+ function collectInterfaceDeclarations(program) {
108
+ // This module-local map supports nearby declarations without constructing a TypeScript type-checker program.
109
+ const declarations = new Map();
110
+ for (const statement of program.body) {
111
+ const declaration = statement.type === 'ExportNamedDeclaration' ? statement.declaration : statement;
112
+ if (declaration?.type === 'TSTypeAliasDeclaration' &&
113
+ !declaration.typeParameters &&
114
+ declaration.typeAnnotation.type === 'TSTypeLiteral') {
115
+ declarations.set(declaration.id.name, declaration.typeAnnotation.members);
116
+ continue;
117
+ }
118
+ if (declaration?.type === 'TSInterfaceDeclaration' &&
119
+ !declaration.typeParameters &&
120
+ declaration.extends.length === 0) {
121
+ declarations.set(declaration.id.name, declaration.body.body);
122
+ }
123
+ }
124
+ return declarations;
125
+ }
126
+ /** Resolves an inline type or a non-generic object declaration from the current module. */
127
+ function resolveInterfaceMembers(typeNode, declarations) {
128
+ if (typeNode.type === 'TSTypeLiteral') {
129
+ return typeNode.members;
130
+ }
131
+ if (typeNode.type === 'TSTypeReference' && typeNode.typeName.type === 'Identifier' && !typeNode.typeArguments) {
132
+ return declarations.get(typeNode.typeName.name);
133
+ }
134
+ }
135
+ /** Reads native template field names without resolving or validating their TypeScript value types. */
136
+ function readInterfaceFields(typeArguments, declaredInterfaces, buildError) {
137
+ if (!typeArguments) {
138
+ return [];
139
+ }
140
+ if (typeArguments.params.length !== 1) {
141
+ throw buildError('Native component interface must be one TypeScript object type');
142
+ }
143
+ const firstType = typeArguments.params[0];
144
+ const members = firstType ? resolveInterfaceMembers(firstType, declaredInterfaces) : undefined;
145
+ if (!members) {
146
+ throw buildError('Native component interface must be inline or declared in the same module');
147
+ }
148
+ // This local set preserves declaration order while rejecting ambiguous duplicate template fields.
149
+ const names = new Set();
150
+ for (const member of members) {
151
+ if (member.type !== 'TSPropertySignature' && member.type !== 'TSMethodSignature') {
152
+ throw buildError('Native component interface must contain only static fields');
153
+ }
154
+ if (member.computed) {
155
+ throw buildError('Native component interface contains a computed field');
156
+ }
157
+ const name = getStaticName(member.key);
158
+ if (!name) {
159
+ throw buildError('Native component interface contains a computed field');
160
+ }
161
+ if (names.has(name)) {
162
+ throw buildError(`Duplicate native component interface field: ${name}`);
163
+ }
164
+ names.add(name);
165
+ }
166
+ return [...names].filter((name) => name !== 'children');
167
+ }
168
+ /** Reads an identifier or string-literal key. */
169
+ function getStaticName(node) {
170
+ if (node.type === 'Identifier') {
171
+ return node.name;
172
+ }
173
+ if (node.type === 'Literal' && typeof node.value === 'string') {
174
+ return node.value;
175
+ }
176
+ }
177
+ /** Reads a zero-argument entry loader containing one statically resolvable native `.js` import. */
178
+ function readStaticEntryImport(node) {
179
+ if (node?.type !== 'ArrowFunctionExpression' || node.params.length !== 0 || node.body.type !== 'ImportExpression') {
180
+ return undefined;
181
+ }
182
+ const importExpression = node.body;
183
+ if (importExpression.source.type !== 'Literal' ||
184
+ typeof importExpression.source.value !== 'string' ||
185
+ importExpression.options !== null ||
186
+ !isLocalJavaScriptEntry(importExpression.source.value)) {
187
+ return undefined;
188
+ }
189
+ return importExpression.source.value;
190
+ }
191
+ /** Limits component entries to JavaScript files reachable from their interface module. */
192
+ function isLocalJavaScriptEntry(entry) {
193
+ return (entry.startsWith('./') || entry.startsWith('../')) && path.posix.extname(entry) === '.js';
194
+ }
195
+ /** Adds a stable source location to semantic errors reported after parsing. */
196
+ function createTransformError(message, moduleId, code, offset) {
197
+ const prefix = code.slice(0, offset);
198
+ const line = prefix.split('\n').length;
199
+ const column = offset - prefix.lastIndexOf('\n');
200
+ return new Error(`${message}\n${moduleId}:${line}:${column}`);
201
+ }
@@ -0,0 +1,12 @@
1
+ import type { Rolldown } from 'vite';
2
+ import type { VitePluginTaroOptions } from '../../../../options.ts';
3
+ import type { GeneratedSubpackage } from '../placement/placer.ts';
4
+ /** Creates every native companion file derived from the final Rolldown bundle. */
5
+ export declare function createOutputFiles({ bundle, options, subpackages, getModuleInfo }: {
6
+ bundle: Rolldown.OutputBundle;
7
+ options: VitePluginTaroOptions;
8
+ subpackages: readonly GeneratedSubpackage[];
9
+ getModuleInfo: (moduleId: string) => {
10
+ meta: Rolldown.CustomPluginOptions;
11
+ } | null;
12
+ }): Promise<Rolldown.EmittedFile[]>;
@@ -0,0 +1,12 @@
1
+ import { createNativeComponentOutput } from '../native/create-native-component-output.js';
2
+ import { createJsonAssets } from './json.js';
3
+ import { createTemplateAssets } from './templates.js';
4
+ /** Creates every native companion file derived from the final Rolldown bundle. */
5
+ export async function createOutputFiles({ bundle, options, subpackages, getModuleInfo }) {
6
+ const nativeOutput = await createNativeComponentOutput({ bundle, getModuleInfo });
7
+ return [
8
+ ...createJsonAssets({ options, subpackages, nativeComponents: nativeOutput.registrations }),
9
+ ...createTemplateAssets(bundle, options, nativeOutput.registrations),
10
+ ...nativeOutput.files
11
+ ];
12
+ }
@@ -0,0 +1,14 @@
1
+ import type { Rolldown } from 'vite';
2
+ import type { JsonObject, VitePluginTaroOptions } from '../../../../options.ts';
3
+ import type { GeneratedSubpackage } from '../placement/placer.ts';
4
+ /** Creates every configured native JSON asset. */
5
+ export declare function createJsonAssets({ options, subpackages, nativeComponents }: {
6
+ options: VitePluginTaroOptions;
7
+ subpackages: readonly GeneratedSubpackage[];
8
+ nativeComponents: readonly {
9
+ name: string;
10
+ componentPath: string;
11
+ }[];
12
+ }): Rolldown.EmittedAsset[];
13
+ /** Serializes one native JSON object with stable formatting. */
14
+ export declare function renderJson(value: JsonObject): string;
@@ -0,0 +1,52 @@
1
+ import { createAppConfig } from '../../../utils/project-config.js';
2
+ import { isGeneratedSubpackageFile } from '../placement/plan.js';
3
+ import { toRootRelativePath } from './relative-root.js';
4
+ /** Creates every configured native JSON asset. */
5
+ export function createJsonAssets({ options, subpackages, nativeComponents }) {
6
+ return [
7
+ createJsonAsset('app.json', {
8
+ ...createAppConfig(options),
9
+ ...(subpackages.length > 0 ? { subPackages: subpackages } : {})
10
+ }),
11
+ ...options.pages.map((page) => createJsonAsset(`${page.path}.json`, createPageJson(page, nativeComponents))),
12
+ createJsonAsset('project.config.json', options.projectConfigJson),
13
+ ...(options.projectPrivateConfigJson
14
+ ? [createJsonAsset('project.private.config.json', options.projectPrivateConfigJson)]
15
+ : []),
16
+ createJsonAsset('sitemap.json', options.sitemapJson)
17
+ ];
18
+ }
19
+ /** Creates Page JSON with generated Taro and native component registrations. */
20
+ function createPageJson(page, nativeComponents) {
21
+ const usingComponents = isJsonObject(page.config.usingComponents) ? page.config.usingComponents : {};
22
+ // Cross-package components require a placeholder while WeChat downloads their generated subpackage. Paths are
23
+ // root-absolute, so remove the leading slash before testing the output-relative subpackage prefix.
24
+ // https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/async.html
25
+ // https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/placeholder.html
26
+ const placeholderEntries = nativeComponents.flatMap(({ name, componentPath }) => isGeneratedSubpackageFile(componentPath.slice(1)) ? [[name, 'view']] : []);
27
+ return {
28
+ ...page.config,
29
+ ...(placeholderEntries.length > 0 ? { componentPlaceholder: Object.fromEntries(placeholderEntries) } : {}),
30
+ usingComponents: {
31
+ ...usingComponents,
32
+ ...Object.fromEntries(nativeComponents.map(({ name, componentPath }) => [name, componentPath])),
33
+ comp: toRootRelativePath(page.path, 'comp')
34
+ }
35
+ };
36
+ }
37
+ /** Tests whether a configured JSON value can be merged as an object. */
38
+ function isJsonObject(value) {
39
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
40
+ }
41
+ /** Serializes one native JSON object with stable formatting. */
42
+ export function renderJson(value) {
43
+ return `${JSON.stringify(value, null, 4)}\n`;
44
+ }
45
+ /** Creates one native JSON asset. */
46
+ function createJsonAsset(fileName, value) {
47
+ return {
48
+ type: 'asset',
49
+ fileName,
50
+ source: renderJson(value)
51
+ };
52
+ }
@@ -0,0 +1,2 @@
1
+ /** Creates a Page-relative path to a file emitted at the wx output root. */
2
+ export declare function toRootRelativePath(pagePath: string, rootFileName: string): string;
@@ -0,0 +1,6 @@
1
+ import path from 'node:path';
2
+ /** Creates a Page-relative path to a file emitted at the wx output root. */
3
+ export function toRootRelativePath(pagePath, rootFileName) {
4
+ const relativePath = path.posix.relative(path.posix.dirname(pagePath), rootFileName);
5
+ return relativePath.startsWith('.') ? relativePath : `./${relativePath}`;
6
+ }
@@ -0,0 +1,7 @@
1
+ import type { Rolldown } from 'vite';
2
+ import type { VitePluginTaroOptions } 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: VitePluginTaroOptions, nativeComponents: readonly {
5
+ name: string;
6
+ fields: readonly string[];
7
+ }[]): Rolldown.EmittedAsset[];
@@ -0,0 +1,100 @@
1
+ import { recursiveMerge } from '@tarojs/helper';
2
+ import { Weapp as WxPlatform } from '@tarojs/plugin-platform-weapp';
3
+ import { normalizeModuleId } from '../../../utils/modules.js';
4
+ import { packageRequire } from '../../../utils/packages.js';
5
+ import { renderJson } from './json.js';
6
+ import { toRootRelativePath } from './relative-root.js';
7
+ 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
+ const templateBuilder = createTemplateBuilder();
11
+ return [
12
+ createAsset('base.wxml', templateBuilder.buildTemplate(collectTemplateComponentConfig(bundle, nativeComponents))),
13
+ createAsset('utils.wxs', templateBuilder.buildXScript()),
14
+ createAsset('comp.wxml', templateBuilder.buildBaseComponentTemplate('.wxml')),
15
+ createAsset('comp.json', renderJson(createComponentJson())),
16
+ ...options.pages.flatMap((page) => [
17
+ createAsset(`${page.path}.wxml`, templateBuilder.buildPageTemplate(toRootRelativePath(page.path, 'base.wxml'), {
18
+ content: page.config,
19
+ path: page.path
20
+ })),
21
+ createAsset(`${page.path}.wxss`, '')
22
+ ])
23
+ ];
24
+ }
25
+ /** Creates the Taro WeChat template builder without invoking its Webpack integration. */
26
+ function createTemplateBuilder() {
27
+ const platform = new WxPlatform({
28
+ helper: {
29
+ recursiveMerge
30
+ },
31
+ modifyWebpackChain() { },
32
+ registerPlatform() { }
33
+ }, {}, {});
34
+ platform.modifyTemplate({});
35
+ return platform.template;
36
+ }
37
+ /** Creates template metadata from reachable Taro hosts and native component JSX fields. */
38
+ function collectTemplateComponentConfig(bundle, nativeComponents) {
39
+ // This local config accumulates names in output order before the template builder consumes it.
40
+ const config = {
41
+ includes: new Set([
42
+ 'view',
43
+ 'catch-view',
44
+ 'static-view',
45
+ 'pure-view',
46
+ 'click-view',
47
+ 'scroll-view',
48
+ 'image',
49
+ 'static-image',
50
+ 'text',
51
+ 'static-text'
52
+ ]),
53
+ exclude: new Set(),
54
+ thirdPartyComponents: new Map(),
55
+ includeAll: false
56
+ };
57
+ const components = findBundleModule(bundle, taroComponentsModulePath);
58
+ for (const name of components?.renderedExports ?? []) {
59
+ config.includes.add(toDashed(name));
60
+ }
61
+ for (const component of nativeComponents) {
62
+ config.thirdPartyComponents.set(component.name, new Set(component.fields));
63
+ }
64
+ return config;
65
+ }
66
+ /** Finds one module in the final chunk metadata. */
67
+ function findBundleModule(bundle, resolvedId) {
68
+ const normalizedResolvedId = normalizeModuleId(resolvedId);
69
+ for (const output of Object.values(bundle)) {
70
+ if (output.type !== 'chunk') {
71
+ continue;
72
+ }
73
+ const found = Object.entries(output.modules).find(([id]) => normalizeModuleId(id) === normalizedResolvedId);
74
+ if (found) {
75
+ return found[1];
76
+ }
77
+ }
78
+ }
79
+ /** Converts a React component export to Taro's dashed host-component name. */
80
+ function toDashed(value) {
81
+ return value.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
82
+ }
83
+ /** Creates the recursive component configuration expected by Taro's templates. */
84
+ function createComponentJson() {
85
+ return {
86
+ component: true,
87
+ styleIsolation: 'apply-shared',
88
+ usingComponents: {
89
+ comp: './comp'
90
+ }
91
+ };
92
+ }
93
+ /** Creates one emitted template or style asset. */
94
+ function createAsset(fileName, source) {
95
+ return {
96
+ type: 'asset',
97
+ fileName,
98
+ source
99
+ };
100
+ }
@@ -0,0 +1,78 @@
1
+ import type { Rolldown } from 'vite';
2
+ import { type ModuleGraph } from './plan.ts';
3
+ /** Native app.json declaration for one generated code-only subpackage. */
4
+ export type GeneratedSubpackage = {
5
+ /** Stable native alias derived from the generated root hash. */
6
+ name: string;
7
+ /** Physical directory containing this subpackage's emitted capsules. */
8
+ root: string;
9
+ /** Marks this as a code-only subpackage with no native Page routes. */
10
+ pages: readonly [];
11
+ };
12
+ /**
13
+ * Creates the stateful adapter between graph planning and Rolldown's output lifecycle:
14
+ *
15
+ * - renderStart analyzes transformed modules before chunking.
16
+ * - codeSplitting prevents modules assigned to different packages from being merged.
17
+ * - filename callbacks materialize planned subpackage roots.
18
+ * - renderChunk derives native loading mode from those physical paths.
19
+ * - generateBundle reconciles the plan with chunks that survived tree shaking.
20
+ */
21
+ export declare function createPlacer(): {
22
+ /** Assigns every transformed module to main or one generated, size-bounded subpackage. */
23
+ analyze(graph: ModuleGraph): void;
24
+ /**
25
+ * Complete Rolldown fragment required to preserve package placement and native entry semantics.
26
+ *
27
+ * ```text
28
+ * native App/Page/Component shell entry
29
+ * └─ static import / runtime importSync() ─▶ explicit capsule entry + static closure [main, no TLA]
30
+ * └─ System.import() ─▶ lazy-a [package A, TLA allowed]
31
+ * └─ static import ─▶ lazy-b [package B]
32
+ *
33
+ * name() assigns lazy-a and lazy-b to their planned package groups
34
+ * includeDependenciesRecursively false: does not pull lazy-b back into package A
35
+ * preserveEntrySignatures allows cross-chunk bindings without weakening native entry exports
36
+ *
37
+ * package A capsule
38
+ * └─ SystemJS dependency ─▶ package B capsule
39
+ * ▲
40
+ * └─ main transport obtains registration with require.async()
41
+ * ```
42
+ *
43
+ * Explicit entries preserve the shell/capsule rendering boundary while their static edge executes synchronously
44
+ * from main. Once a dynamic boundary is crossed, physical fetches may be asynchronous and modules may use
45
+ * top-level await. SystemJS still links that complete static lazy graph before execution, even across packages.
46
+ */
47
+ rolldownOptions: {
48
+ output: {
49
+ /**
50
+ * Gives every generated subpackage a distinct Rolldown chunk group. Recursive dependency capture must
51
+ * stay disabled: lazy static dependencies may belong to other subpackages and SystemJS links them
52
+ * asynchronously.
53
+ */
54
+ codeSplitting: {
55
+ groups: {
56
+ name(moduleId: string): string | null;
57
+ includeDependenciesRecursively: boolean;
58
+ }[];
59
+ };
60
+ /** Preserves exact native shell paths while hashing transport and explicit capsule entries. */
61
+ entryFileNames(chunk: Rolldown.PreRenderedChunk): string;
62
+ /** Converts the planned owner into its physical main or generated subpackage filename template. */
63
+ chunkFileNames(chunk: Rolldown.PreRenderedChunk): string;
64
+ assetFileNames: string;
65
+ };
66
+ preserveEntrySignatures: 'allow-extension';
67
+ };
68
+ /**
69
+ * Selects loading mode from physical output rather than graph intent. Transport executes in main, so main files
70
+ * use require() and every generated subpackage file uses require.async().
71
+ */
72
+ getLoadMode(chunk: Rolldown.RenderedChunk): 'sync' | 'async';
73
+ /**
74
+ * Reconciles planned ownership with final chunks through module IDs. Tree-shaken subpackages disappear naturally,
75
+ * while roots are deduplicated and sorted before becoming deterministic app.json declarations.
76
+ */
77
+ getSubpackages(bundle: Rolldown.OutputBundle): GeneratedSubpackage[];
78
+ };
@@ -0,0 +1,145 @@
1
+ import { getWxExecutionKind, isTransportModule } from '../module.js';
2
+ import { createPlacementPlan, getSubpackageName, isGeneratedSubpackageFile, mainPackage } from './plan.js';
3
+ /**
4
+ * Creates the stateful adapter between graph planning and Rolldown's output lifecycle:
5
+ *
6
+ * - renderStart analyzes transformed modules before chunking.
7
+ * - codeSplitting prevents modules assigned to different packages from being merged.
8
+ * - filename callbacks materialize planned subpackage roots.
9
+ * - renderChunk derives native loading mode from those physical paths.
10
+ * - generateBundle reconciles the plan with chunks that survived tree shaking.
11
+ */
12
+ export function createPlacer() {
13
+ // Filename and output callbacks run after analyze and read this immutable plan through their shared closure.
14
+ let plan = new Map();
15
+ /**
16
+ * Reduces module ownership to one package for filename generation. Unknown Rolldown-generated modules default to
17
+ * main, empty chunks default to main, and a chunk containing multiple known owners is rejected before paths diverge.
18
+ */
19
+ function getChunkLocation(chunk) {
20
+ let location;
21
+ for (const moduleId of chunk.moduleIds) {
22
+ const moduleLocation = plan.get(moduleId) ?? mainPackage;
23
+ if (!location) {
24
+ location = moduleLocation;
25
+ continue;
26
+ }
27
+ if (!hasSameLocation(location, moduleLocation)) {
28
+ throw new Error(`wx chunk mixes package owners: ${chunk.moduleIds.join(', ')}`);
29
+ }
30
+ }
31
+ return location ?? mainPackage;
32
+ }
33
+ return {
34
+ /** Assigns every transformed module to main or one generated, size-bounded subpackage. */
35
+ analyze(graph) {
36
+ plan = createPlacementPlan(graph);
37
+ },
38
+ /**
39
+ * Complete Rolldown fragment required to preserve package placement and native entry semantics.
40
+ *
41
+ * ```text
42
+ * native App/Page/Component shell entry
43
+ * └─ static import / runtime importSync() ─▶ explicit capsule entry + static closure [main, no TLA]
44
+ * └─ System.import() ─▶ lazy-a [package A, TLA allowed]
45
+ * └─ static import ─▶ lazy-b [package B]
46
+ *
47
+ * name() assigns lazy-a and lazy-b to their planned package groups
48
+ * includeDependenciesRecursively false: does not pull lazy-b back into package A
49
+ * preserveEntrySignatures allows cross-chunk bindings without weakening native entry exports
50
+ *
51
+ * package A capsule
52
+ * └─ SystemJS dependency ─▶ package B capsule
53
+ * ▲
54
+ * └─ main transport obtains registration with require.async()
55
+ * ```
56
+ *
57
+ * Explicit entries preserve the shell/capsule rendering boundary while their static edge executes synchronously
58
+ * from main. Once a dynamic boundary is crossed, physical fetches may be asynchronous and modules may use
59
+ * top-level await. SystemJS still links that complete static lazy graph before execution, even across packages.
60
+ */
61
+ rolldownOptions: {
62
+ output: {
63
+ /**
64
+ * Gives every generated subpackage a distinct Rolldown chunk group. Recursive dependency capture must
65
+ * stay disabled: lazy static dependencies may belong to other subpackages and SystemJS links them
66
+ * asynchronously.
67
+ */
68
+ codeSplitting: {
69
+ groups: [
70
+ {
71
+ name(moduleId) {
72
+ const location = plan.get(moduleId);
73
+ return location?.kind === 'subpackage' ? getSubpackageName(location.root) : null;
74
+ },
75
+ // Do not let Rolldown pull a lazy group's static closure into one chunk. Past the nested dynamic
76
+ // boundary, transport may obtain registrations from several packages asynchronously before
77
+ // SystemJS links and executes the original graph, including cycles and top-level await.
78
+ includeDependenciesRecursively: false
79
+ }
80
+ ]
81
+ },
82
+ // strictExecutionOrder deliberately has no plugin default. When an application enables it through normal
83
+ // Rolldown output options, the generated helper runtime becomes amphibious: CommonJS evaluates it once and
84
+ // transport publishes that cached namespace to SystemJS.
85
+ /** Preserves exact native shell paths while hashing transport and explicit capsule entries. */
86
+ entryFileNames(chunk) {
87
+ return getWxExecutionKind(chunk) === 'native' && !isTransportModule(chunk)
88
+ ? '[name]'
89
+ : 'assets/[name]-[hash].js';
90
+ },
91
+ /** Converts the planned owner into its physical main or generated subpackage filename template. */
92
+ chunkFileNames(chunk) {
93
+ const location = getChunkLocation(chunk);
94
+ if (location.kind === 'main') {
95
+ return 'assets/[name]-[hash].js';
96
+ }
97
+ // The containing subpackage already supplies a stable identity. Do not leak the Rolldown group name
98
+ // into every physical chunk filename; content identity alone is sufficient beneath that root.
99
+ return `${location.root}/assets/[hash].js`;
100
+ },
101
+ // Keep generic assets independent of stylesheet conventions. The CSS adapter sees Tailwind's final
102
+ // mutation and owns the exact global `app.wxss` identity without confusing native Page companions.
103
+ assetFileNames: 'assets/[name]-[hash][extname]'
104
+ },
105
+ // Rolldown rejects strict entry signatures when code-splitting groups disable recursive dependency capture.
106
+ // allow-extension retains required native-entry exports while permitting the extra cross-chunk bindings used
107
+ // to split lazy static closures across physical packages.
108
+ preserveEntrySignatures: 'allow-extension'
109
+ },
110
+ /**
111
+ * Selects loading mode from physical output rather than graph intent. Transport executes in main, so main files
112
+ * use require() and every generated subpackage file uses require.async().
113
+ */
114
+ getLoadMode(chunk) {
115
+ return isGeneratedSubpackageFile(chunk.fileName) ? 'async' : 'sync';
116
+ },
117
+ /**
118
+ * Reconciles planned ownership with final chunks through module IDs. Tree-shaken subpackages disappear naturally,
119
+ * while roots are deduplicated and sorted before becoming deterministic app.json declarations.
120
+ */
121
+ getSubpackages(bundle) {
122
+ const emittedSubpackageRoots = new Set();
123
+ for (const output of Object.values(bundle)) {
124
+ if (output.type !== 'chunk') {
125
+ continue;
126
+ }
127
+ for (const moduleId of output.moduleIds) {
128
+ const location = plan.get(moduleId);
129
+ if (location?.kind === 'subpackage') {
130
+ emittedSubpackageRoots.add(location.root);
131
+ }
132
+ }
133
+ }
134
+ return [...emittedSubpackageRoots].sort().map((root) => ({
135
+ name: getSubpackageName(root),
136
+ root,
137
+ pages: []
138
+ }));
139
+ }
140
+ };
141
+ }
142
+ /** Compares main by discriminant and generated subpackages by their unique physical root. */
143
+ function hasSameLocation(left, right) {
144
+ return left.kind === 'main' ? right.kind === 'main' : right.kind === 'subpackage' && left.root === right.root;
145
+ }