vite-plugin-taro 0.2.2 → 0.3.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 (133) hide show
  1. package/README.md +83 -106
  2. package/README.zh.md +83 -104
  3. package/dist/node/build-context.d.ts +25 -0
  4. package/dist/node/build-context.js +48 -0
  5. package/dist/node/css/css-pipeline.d.ts +21 -0
  6. package/dist/node/css/css-pipeline.js +129 -0
  7. package/dist/node/plugins/conditional-directives.d.ts +3 -0
  8. package/dist/node/plugins/conditional-directives.js +62 -0
  9. package/dist/node/plugins/taro-runtime.d.ts +5 -0
  10. package/dist/node/plugins/taro-runtime.js +18 -0
  11. package/dist/node/targets/h5/plugin.d.ts +6 -0
  12. package/dist/node/targets/h5/plugin.js +117 -0
  13. package/dist/node/targets/h5/virtual-modules.d.ts +5 -0
  14. package/dist/node/targets/h5/virtual-modules.js +60 -0
  15. package/dist/node/targets/wx/companion-assets.d.ts +18 -0
  16. package/dist/node/targets/wx/companion-assets.js +121 -0
  17. package/dist/node/targets/wx/dev-server/development-session.d.ts +31 -0
  18. package/dist/node/targets/wx/dev-server/development-session.js +202 -0
  19. package/dist/node/targets/wx/dev-server/full-build-scheduler.d.ts +14 -0
  20. package/dist/node/targets/wx/dev-server/full-build-scheduler.js +60 -0
  21. package/dist/node/targets/wx/dev-server/js-utils.d.ts +5 -0
  22. package/dist/node/targets/wx/dev-server/js-utils.js +28 -0
  23. package/dist/node/targets/wx/dev-server/output.d.ts +19 -0
  24. package/dist/node/targets/wx/dev-server/output.js +44 -0
  25. package/dist/node/targets/wx/dev-server/rolldown-runtime-source.d.ts +8 -0
  26. package/dist/node/targets/wx/dev-server/rolldown-runtime-source.js +95 -0
  27. package/dist/node/targets/wx/dev-server/update-server-state.d.ts +65 -0
  28. package/dist/node/targets/wx/dev-server/update-server-state.js +116 -0
  29. package/dist/node/targets/wx/dev-server/update-transport.d.ts +28 -0
  30. package/dist/node/targets/wx/dev-server/update-transport.js +212 -0
  31. package/dist/node/targets/wx/dev-server/vite-bundled-dev-adapter.d.ts +44 -0
  32. package/dist/node/targets/wx/dev-server/vite-bundled-dev-adapter.js +145 -0
  33. package/dist/node/targets/wx/development-files.d.ts +8 -0
  34. package/dist/node/targets/wx/development-files.js +8 -0
  35. package/dist/node/targets/wx/plugin.d.ts +6 -0
  36. package/dist/node/targets/wx/plugin.js +143 -0
  37. package/dist/node/targets/wx/react-refresh.d.ts +4 -0
  38. package/dist/node/targets/wx/react-refresh.js +38 -0
  39. package/dist/node/targets/wx/virtual-modules.d.ts +16 -0
  40. package/dist/node/targets/wx/virtual-modules.js +120 -0
  41. package/dist/node/utils/async.d.ts +8 -0
  42. package/dist/node/utils/async.js +16 -0
  43. package/dist/node/utils/filesystem.d.ts +13 -0
  44. package/dist/node/utils/filesystem.js +43 -0
  45. package/dist/{vite/utils.d.ts → node/utils/modules.d.ts} +2 -2
  46. package/dist/{vite/utils.js → node/utils/modules.js} +10 -3
  47. package/dist/node/utils/packages.d.ts +2 -0
  48. package/dist/node/utils/packages.js +8 -0
  49. package/dist/node/vite-plugin.d.ts +4 -0
  50. package/dist/node/vite-plugin.js +33 -0
  51. package/dist/options.d.ts +21 -0
  52. package/dist/{shim/h5.d.ts → runtime/h5/taro-runtime.d.ts} +3 -1
  53. package/dist/runtime/h5/taro-runtime.js +9 -0
  54. package/dist/runtime/taro/api.d.ts +5 -0
  55. package/dist/{virtual → runtime/taro}/api.js +3 -0
  56. package/dist/runtime/taro/components.d.ts +2 -0
  57. package/dist/runtime/taro/components.js +2 -0
  58. package/dist/runtime/wx/page-update.d.ts +6 -0
  59. package/dist/runtime/wx/page-update.js +157 -0
  60. package/dist/{shim/wx.d.ts → runtime/wx/taro-runtime.d.ts} +1 -0
  61. package/dist/{shim/wx.js → runtime/wx/taro-runtime.js} +1 -0
  62. package/dist/runtime/wx/update-client-state.d.ts +68 -0
  63. package/dist/runtime/wx/update-client-state.js +84 -0
  64. package/dist/runtime/wx/update-client.d.ts +1 -0
  65. package/dist/runtime/wx/update-client.js +170 -0
  66. package/dist/vite.d.ts +2 -2
  67. package/dist/vite.js +1 -1
  68. package/package.json +15 -11
  69. package/src/node/build-context.ts +63 -0
  70. package/src/node/css/css-pipeline.ts +147 -0
  71. package/src/node/plugins/conditional-directives.ts +76 -0
  72. package/src/node/plugins/taro-runtime.ts +22 -0
  73. package/src/node/targets/h5/plugin.ts +135 -0
  74. package/src/node/targets/h5/virtual-modules.ts +71 -0
  75. package/src/node/targets/wx/companion-assets.ts +160 -0
  76. package/src/node/targets/wx/dev-server/development-session.ts +253 -0
  77. package/src/node/targets/wx/dev-server/full-build-scheduler.ts +62 -0
  78. package/src/node/targets/wx/dev-server/js-utils.ts +33 -0
  79. package/src/node/targets/wx/dev-server/output.ts +56 -0
  80. package/src/node/targets/wx/dev-server/rolldown-runtime-source.ts +95 -0
  81. package/src/node/targets/wx/dev-server/update-server-state.ts +183 -0
  82. package/src/node/targets/wx/dev-server/update-transport.ts +261 -0
  83. package/src/node/targets/wx/dev-server/vite-bundled-dev-adapter.ts +203 -0
  84. package/src/node/targets/wx/development-files.ts +11 -0
  85. package/src/node/targets/wx/plugin.ts +163 -0
  86. package/src/node/targets/wx/react-refresh.ts +51 -0
  87. package/src/node/targets/wx/virtual-modules.ts +152 -0
  88. package/src/node/utils/async.ts +19 -0
  89. package/src/node/utils/filesystem.ts +45 -0
  90. package/src/{vite/utils.ts → node/utils/modules.ts} +11 -3
  91. package/src/node/utils/packages.ts +10 -0
  92. package/src/node/vite-plugin.ts +41 -0
  93. package/src/options.ts +25 -0
  94. package/src/runtime/h5/taro-css.d.ts +3 -0
  95. package/src/runtime/h5/taro-runtime.ts +10 -0
  96. package/src/{virtual → runtime/taro}/api.ts +3 -0
  97. package/src/runtime/taro/components.ts +2 -0
  98. package/src/runtime/wx/page-update.ts +230 -0
  99. package/src/runtime/wx/react-refresh.d.ts +20 -0
  100. package/src/{shim/wx.ts → runtime/wx/taro-runtime.ts} +1 -0
  101. package/src/runtime/wx/update-client-state.ts +138 -0
  102. package/src/runtime/wx/update-client.ts +222 -0
  103. package/src/vite.ts +2 -2
  104. package/dist/shim/h5.js +0 -5
  105. package/dist/virtual/api.d.ts +0 -3
  106. package/dist/virtual/components.d.ts +0 -1
  107. package/dist/virtual/components.js +0 -1
  108. package/dist/vite/constants.d.ts +0 -4
  109. package/dist/vite/constants.js +0 -8
  110. package/dist/vite/plugins.d.ts +0 -8
  111. package/dist/vite/plugins.js +0 -186
  112. package/dist/vite/tailwindcss.d.ts +0 -3
  113. package/dist/vite/tailwindcss.js +0 -35
  114. package/dist/vite/targets/h5.d.ts +0 -31
  115. package/dist/vite/targets/h5.js +0 -201
  116. package/dist/vite/targets/wx.d.ts +0 -74
  117. package/dist/vite/targets/wx.js +0 -362
  118. package/dist/vite/types.d.ts +0 -38
  119. package/dist/vite/virtual-modules.d.ts +0 -3
  120. package/dist/vite/virtual-modules.js +0 -13
  121. package/dist/vite/vite-plugin-taro.d.ts +0 -7
  122. package/dist/vite/vite-plugin-taro.js +0 -80
  123. package/src/shim/h5.ts +0 -6
  124. package/src/virtual/components.ts +0 -1
  125. package/src/vite/constants.ts +0 -12
  126. package/src/vite/plugins.ts +0 -218
  127. package/src/vite/tailwindcss.ts +0 -41
  128. package/src/vite/targets/h5.ts +0 -229
  129. package/src/vite/targets/wx.ts +0 -436
  130. package/src/vite/types.ts +0 -48
  131. package/src/vite/virtual-modules.ts +0 -14
  132. package/src/vite/vite-plugin-taro.ts +0 -106
  133. /package/dist/{vite/types.js → options.js} +0 -0
@@ -0,0 +1,145 @@
1
+ import path from 'node:path';
2
+ import { wxPagePreloadFile, wxUpdateControlFile, wxUpdateFile } from '../development-files.js';
3
+ import { wxRolldownRuntimeSource } from './rolldown-runtime-source.js';
4
+ const clientId = 'vite-plugin-taro-wx';
5
+ /** Isolates the private Vite bundled-development API used by the WX session. */
6
+ export class ViteBundledDevAdapter {
7
+ config;
8
+ server;
9
+ callbacks;
10
+ bundledDev;
11
+ constructor(config, server, callbacks) {
12
+ this.config = config;
13
+ this.server = server;
14
+ this.callbacks = callbacks;
15
+ }
16
+ install() {
17
+ const bundledDev = this.server.environments.client.bundledDev;
18
+ if (!bundledDev)
19
+ throw new Error('vite-plugin-taro requires Vite bundled development for WX development.');
20
+ if (typeof bundledDev.getRolldownOptions !== 'function' ||
21
+ typeof bundledDev.storeOutputFiles !== 'function' ||
22
+ typeof bundledDev.handleHmrOutput !== 'function') {
23
+ throw new Error('vite-plugin-taro does not support this Vite bundled-development API shape.');
24
+ }
25
+ this.bundledDev = bundledDev;
26
+ this.installOptions(bundledDev);
27
+ this.installOutput(bundledDev);
28
+ this.installPatches(bundledDev);
29
+ this.installListener(bundledDev);
30
+ }
31
+ registerBundleModules(output) {
32
+ const moduleIds = new Set();
33
+ for (const item of output) {
34
+ if (item.type !== 'chunk')
35
+ continue;
36
+ for (const id of Object.keys(item.modules ?? {}))
37
+ moduleIds.add(toStableModuleId(id, this.config.root));
38
+ }
39
+ this.registerModules([...moduleIds]);
40
+ return moduleIds.size;
41
+ }
42
+ registerPatchModules(code) {
43
+ const moduleIds = new Set();
44
+ for (const match of code.matchAll(/create(?:Esm|Cjs)Initializer\("([^"]+)"/g))
45
+ moduleIds.add(match[1]);
46
+ this.registerModules([...moduleIds]);
47
+ }
48
+ registerModules(moduleIds) {
49
+ if (moduleIds.length)
50
+ this.bundledDev?._devEngine?.registerModules(clientId, moduleIds);
51
+ }
52
+ async rebuild() {
53
+ const engine = this.bundledDev?._devEngine;
54
+ if (!engine)
55
+ return;
56
+ engine.triggerFullBuild();
57
+ await engine.ensureLatestBuildOutput();
58
+ }
59
+ installOptions(bundledDev) {
60
+ const getRolldownOptions = bundledDev.getRolldownOptions.bind(bundledDev);
61
+ bundledDev.getRolldownOptions = async () => {
62
+ const options = await getRolldownOptions();
63
+ if (!options.output)
64
+ options.output = {};
65
+ const output = getFirstOutput(options);
66
+ const configuredOutput = this.config.build.rolldownOptions.output;
67
+ const desiredOutput = Array.isArray(configuredOutput) ? configuredOutput[0] : configuredOutput;
68
+ Object.assign(output, desiredOutput, {
69
+ format: 'cjs',
70
+ sourcemap: false,
71
+ minify: false,
72
+ banner: createWxDevelopmentBanner
73
+ });
74
+ options.experimental ??= {};
75
+ options.experimental.devMode = {
76
+ ...(typeof options.experimental.devMode === 'object' ? options.experimental.devMode : {}),
77
+ lazy: false,
78
+ // Rolldown compiles this source into runtime.js before any Taro/React module executes.
79
+ implement: wxRolldownRuntimeSource
80
+ };
81
+ return options;
82
+ };
83
+ }
84
+ installOutput(bundledDev) {
85
+ const storeOutputFiles = bundledDev.storeOutputFiles.bind(bundledDev);
86
+ bundledDev.storeOutputFiles = (output) => {
87
+ storeOutputFiles(output);
88
+ this.callbacks.onOutput(output);
89
+ };
90
+ }
91
+ installPatches(bundledDev) {
92
+ const handleHmrOutput = bundledDev.handleHmrOutput.bind(bundledDev);
93
+ bundledDev.handleHmrOutput = (client, files, output, info) => {
94
+ if (output.type === 'Noop')
95
+ return;
96
+ if (this.callbacks.onPatch(files, output))
97
+ handleHmrOutput(client, files, output, info);
98
+ };
99
+ }
100
+ installListener(bundledDev) {
101
+ const listen = bundledDev.listen.bind(bundledDev);
102
+ bundledDev.listen = async () => {
103
+ await listen();
104
+ bundledDev.clients.setupIfNeeded({ send: (payload) => this.handlePayload(payload) }, clientId);
105
+ const engine = bundledDev._devEngine;
106
+ if (!engine)
107
+ throw new Error('vite-plugin-taro expected Vite to initialize the WX DevEngine.');
108
+ await engine.ensureCurrentBuildFinish();
109
+ if ((await engine.getBundleState()).lastBuildErrored) {
110
+ throw new Error('The initial WX bundled-development build failed.');
111
+ }
112
+ await this.callbacks.waitForInitialBundle();
113
+ };
114
+ }
115
+ handlePayload(payload) {
116
+ if (typeof payload !== 'object' || payload === null)
117
+ return;
118
+ const candidate = payload;
119
+ if (candidate.type === 'error' && typeof candidate.err?.message === 'string') {
120
+ this.callbacks.onError(candidate.err.message);
121
+ }
122
+ }
123
+ }
124
+ function toStableModuleId(id, root) {
125
+ const normalizedId = id.replace(/\\/g, '/');
126
+ if (normalizedId.startsWith('\0') || !path.posix.isAbsolute(normalizedId))
127
+ return normalizedId;
128
+ return path.posix.relative(root.replace(/\\/g, '/'), normalizedId);
129
+ }
130
+ function getFirstOutput(options) {
131
+ if (!Array.isArray(options.output))
132
+ return options.output;
133
+ const output = options.output[0] ?? {};
134
+ options.output[0] = output;
135
+ return output;
136
+ }
137
+ /** Adds native direct dependencies whose execution order cannot be represented through the Rolldown graph. */
138
+ function createWxDevelopmentBanner(chunk) {
139
+ if (chunk.fileName === 'app.js')
140
+ return `require(${JSON.stringify(`./${wxUpdateControlFile}`)});`;
141
+ if (!chunk.fileName.startsWith('pages/') || !chunk.fileName.endsWith('.js'))
142
+ return '';
143
+ const prefix = '../'.repeat(chunk.fileName.split('/').length - 1);
144
+ return `require(${JSON.stringify(`${prefix}runtime.js`)});require(${JSON.stringify(`${prefix}${wxPagePreloadFile}`)});require(${JSON.stringify(`${prefix}${wxUpdateFile}`)});`;
145
+ }
@@ -0,0 +1,8 @@
1
+ /** Plugin-owned files added only to a WX development output. */
2
+ export declare const wxDevelopmentDirectory = "vpt-hmr";
3
+ /** App-only metadata containing the local transport URL, token, and build ID. */
4
+ export declare const wxUpdateControlFile = "vpt-hmr/control.js";
5
+ /** Stable page-component initialization required before replaying retained updates. */
6
+ export declare const wxPagePreloadFile = "vpt-hmr/preload.js";
7
+ /** The one mutable, literal JavaScript update dependency required by every page. */
8
+ export declare const wxUpdateFile = "vpt-hmr/update.js";
@@ -0,0 +1,8 @@
1
+ /** Plugin-owned files added only to a WX development output. */
2
+ export const wxDevelopmentDirectory = 'vpt-hmr';
3
+ /** App-only metadata containing the local transport URL, token, and build ID. */
4
+ export const wxUpdateControlFile = `${wxDevelopmentDirectory}/control.js`;
5
+ /** Stable page-component initialization required before replaying retained updates. */
6
+ export const wxPagePreloadFile = `${wxDevelopmentDirectory}/preload.js`;
7
+ /** The one mutable, literal JavaScript update dependency required by every page. */
8
+ export const wxUpdateFile = `${wxDevelopmentDirectory}/update.js`;
@@ -0,0 +1,6 @@
1
+ import type { PluginOption, UserConfig } from 'vite';
2
+ import type { BuildContext } from '../../build-context.ts';
3
+ /** Creates the plugins that own the complete WX build and development lifecycle. */
4
+ export declare function createWxTargetPlugins(context: BuildContext): PluginOption[];
5
+ /** Supplies the complete WX target configuration from the same target owner as its plugin. */
6
+ export declare function createWxViteConfig(context: BuildContext): UserConfig;
@@ -0,0 +1,143 @@
1
+ import path from 'node:path';
2
+ import { normalizeModuleId, stripVirtualPrefix } from '../../utils/modules.js';
3
+ import { packageRequire } from '../../utils/packages.js';
4
+ import { emitWxCompanionAssets } from './companion-assets.js';
5
+ import { WxDevelopmentSession } from './dev-server/development-session.js';
6
+ import { transformWxReactRefreshModule } from './react-refresh.js';
7
+ import { emitWxEntryChunks, isWxVirtualModuleId, loadWxVirtualModule, virtualWxAppId } from './virtual-modules.js';
8
+ const taroWxComponentsPath = packageRequire.resolve('@tarojs/plugin-platform-weapp/dist/components-react');
9
+ const vitePluginTaroSourcePath = normalizeModuleId(path.dirname(packageRequire.resolve('vite-plugin-taro')));
10
+ const taroVersion = String(packageRequire('@tarojs/runtime/package.json').version);
11
+ /** Creates the plugins that own the complete WX build and development lifecycle. */
12
+ export function createWxTargetPlugins(context) {
13
+ return [createWxTargetPlugin(context)];
14
+ }
15
+ function createWxTargetPlugin(context) {
16
+ let developmentSession;
17
+ return {
18
+ name: 'vite-plugin-taro:wx',
19
+ resolveId: {
20
+ order: 'pre',
21
+ handler(id) {
22
+ return isWxVirtualModuleId(id) ? `\0${id}` : undefined;
23
+ }
24
+ },
25
+ load: {
26
+ order: 'post',
27
+ handler(id) {
28
+ const cleanId = stripVirtualPrefix(id);
29
+ emitWxEntryChunks(this, context, cleanId);
30
+ return loadWxVirtualModule(cleanId, context);
31
+ }
32
+ },
33
+ transform: {
34
+ order: 'post',
35
+ async handler(code, id) {
36
+ if (!context.development)
37
+ return;
38
+ const transformed = await transformWxReactRefreshModule(code, id, context.project.appComponentFile);
39
+ return transformed === code ? undefined : transformed;
40
+ }
41
+ },
42
+ generateBundle: {
43
+ order: 'post',
44
+ handler(_, bundle) {
45
+ emitWxCompanionAssets(this, bundle, context);
46
+ }
47
+ },
48
+ configureServer: {
49
+ order: 'post',
50
+ handler(server) {
51
+ developmentSession = new WxDevelopmentSession(context, server);
52
+ developmentSession.install();
53
+ }
54
+ },
55
+ closeBundle() {
56
+ return developmentSession?.close();
57
+ }
58
+ };
59
+ }
60
+ /** Supplies the complete WX target configuration from the same target owner as its plugin. */
61
+ export function createWxViteConfig(context) {
62
+ return {
63
+ define: createWxTaroDefines(),
64
+ experimental: context.development ? { bundledDev: true } : undefined,
65
+ css: {
66
+ lightningcss: {
67
+ visitor: {
68
+ // keep CSS3 style single colon :before, :after
69
+ // while WeChat Mini Program does not support the double colons versions
70
+ Selector(selector) {
71
+ return selector.map((component) => {
72
+ if (component.type === 'pseudo-element' &&
73
+ (component.kind === 'before' || component.kind === 'after')) {
74
+ return {
75
+ type: 'pseudo-element',
76
+ kind: 'custom',
77
+ name: component.kind
78
+ };
79
+ }
80
+ return component;
81
+ });
82
+ }
83
+ }
84
+ }
85
+ },
86
+ resolve: {
87
+ alias: [{ find: /^@tarojs\/components$/, replacement: taroWxComponentsPath }]
88
+ },
89
+ build: {
90
+ target: 'es2018',
91
+ assetsInlineLimit: 1024,
92
+ cssCodeSplit: false,
93
+ cssMinify: context.development ? false : 'lightningcss',
94
+ minify: !context.development,
95
+ rolldownOptions: {
96
+ input: { app: virtualWxAppId },
97
+ experimental: {
98
+ // remove vite virtual module \0 in comments, which causes WeChat DevTools failed to compile
99
+ attachDebugInfo: 'none'
100
+ },
101
+ output: {
102
+ format: 'cjs',
103
+ entryFileNames: '[name].js',
104
+ assetFileNames: 'assets/[name][extname]',
105
+ chunkFileNames: ({ name }) => `${name === 'rolldown-runtime' ? 'runtime' : name}.js`,
106
+ strictExecutionOrder: true,
107
+ codeSplitting: {
108
+ includeDependenciesRecursively: false,
109
+ minSize: 0,
110
+ groups: [
111
+ { name: 'taro', test: isWxTaroChunkModule, priority: 100 },
112
+ { name: 'vendors', test: isNodeModule, priority: 10 },
113
+ { name: 'common', minShareCount: 2, minModuleSize: 1, priority: 1 }
114
+ ]
115
+ }
116
+ }
117
+ }
118
+ }
119
+ };
120
+ }
121
+ function createWxTaroDefines() {
122
+ return {
123
+ 'process.env.FRAMEWORK': JSON.stringify('react'),
124
+ 'process.env.SUPPORT_TARO_POLYFILL': JSON.stringify('disabled'),
125
+ 'process.env.TARO_ENV': JSON.stringify('weapp'),
126
+ 'process.env.TARO_PLATFORM': JSON.stringify('mini'),
127
+ 'process.env.TARO_VERSION': JSON.stringify(taroVersion),
128
+ ENABLE_ADJACENT_HTML: 'false',
129
+ ENABLE_CLONE_NODE: 'false',
130
+ ENABLE_CONTAINS: 'false',
131
+ ENABLE_INNER_HTML: 'false',
132
+ ENABLE_MUTATION_OBSERVER: 'false',
133
+ ENABLE_SIZE_APIS: 'false',
134
+ ENABLE_TEMPLATE_CONTENT: 'false'
135
+ };
136
+ }
137
+ function isWxTaroChunkModule(id) {
138
+ const normalizedId = normalizeModuleId(id);
139
+ return normalizedId.includes('/node_modules/@tarojs/') || normalizedId.startsWith(`${vitePluginTaroSourcePath}/`);
140
+ }
141
+ function isNodeModule(id) {
142
+ return normalizeModuleId(id).includes('/node_modules/');
143
+ }
@@ -0,0 +1,4 @@
1
+ /** Rewrites Vite's browser-oriented Refresh output for the WX App Service global environment. */
2
+ export declare function transformWxReactRefreshModule(code: string, id: string, appComponentFile: string): Promise<string>;
3
+ /** Creates the virtual preamble that installs React Refresh before the generated WX App entry executes. */
4
+ export declare function createWxReactRefreshPreambleSource(): string;
@@ -0,0 +1,38 @@
1
+ import { transformWithOxc } from 'vite';
2
+ import { normalizeModuleId } from '../../utils/modules.js';
3
+ /** Rewrites Vite's browser-oriented Refresh output for the WX App Service global environment. */
4
+ export async function transformWxReactRefreshModule(code, id, appComponentFile) {
5
+ const appFile = normalizeModuleId(appComponentFile);
6
+ const instrumented = normalizeModuleId(id) === appFile ? await instrumentWxAppComponent(code, appFile) : code;
7
+ return instrumented
8
+ .replaceAll('window.$Refresh', 'globalThis.$Refresh')
9
+ .replaceAll('window.__registerBeforePerformReactRefresh', 'globalThis.__registerBeforePerformReactRefresh')
10
+ .replaceAll('window.__getReactRefreshIgnoredExports', 'globalThis.__getReactRefreshIgnoredExports')
11
+ .replace('export function register(type, id) {', 'export function register(type, id) {\n if (globalThis.__VITE_PLUGIN_TARO_WX_PAGE_UPDATE__?.blockRefreshRegistration) return')
12
+ .replace('\n performReactRefresh()\n', '\n globalThis.__VITE_PLUGIN_TARO_WX_PAGE_UPDATE__?.afterRefresh?.(performReactRefresh())\n');
13
+ }
14
+ /** Creates the virtual preamble that installs React Refresh before the generated WX App entry executes. */
15
+ export function createWxReactRefreshPreambleSource() {
16
+ return `import RefreshRuntime from '/@react-refresh'
17
+ RefreshRuntime.injectIntoGlobalHook(globalThis)
18
+ globalThis.$RefreshReg$ = () => {}
19
+ globalThis.$RefreshSig$ = () => (type) => type
20
+ `;
21
+ }
22
+ /** Adds Refresh registration to JSX-free App modules that Vite's normal JSX transform would otherwise skip. */
23
+ async function instrumentWxAppComponent(code, appFile) {
24
+ const transformed = await transformWithOxc(code, appFile, {
25
+ lang: 'js',
26
+ sourcemap: false,
27
+ jsx: { refresh: true }
28
+ });
29
+ return `import {
30
+ createSignatureFunctionForTransform as __wxCreateRefreshSignature,
31
+ register as __wxRegisterRefreshType
32
+ } from '/@react-refresh'
33
+ const $RefreshReg$ = (type, id) => __wxRegisterRefreshType(type, ${JSON.stringify(`${appFile} `)} + id)
34
+ const $RefreshSig$ = __wxCreateRefreshSignature
35
+ ${transformed.code}
36
+ if (import.meta.hot) import.meta.hot.accept()
37
+ `;
38
+ }
@@ -0,0 +1,16 @@
1
+ import type { BuildContext } from '../../build-context.ts';
2
+ /** Generated native App entry. */
3
+ export declare const virtualWxAppId = "virtual:vite-plugin-taro/wx/app";
4
+ type WxChunkEmitter = {
5
+ emitFile(chunk: {
6
+ type: 'chunk';
7
+ id: string;
8
+ fileName: string;
9
+ implicitlyLoadedAfterOneOf: string[];
10
+ }): string;
11
+ };
12
+ export declare function isWxVirtualModuleId(id: string): boolean;
13
+ export declare function loadWxVirtualModule(id: string, context: BuildContext): string | undefined;
14
+ /** Registers every configured page and the shared recursive component as eager WX entry chunks. */
15
+ export declare function emitWxEntryChunks(emitter: WxChunkEmitter, context: BuildContext, id: string): void;
16
+ export {};
@@ -0,0 +1,120 @@
1
+ import { createPageComponentImportPath, toViteFileImportPath } from '../../utils/modules.js';
2
+ import { resolvePackageFile } from '../../utils/packages.js';
3
+ import { wxPagePreloadFile } from './development-files.js';
4
+ import { createWxReactRefreshPreambleSource } from './react-refresh.js';
5
+ const wxTaroRuntimeImportPath = runtimeImportPath('taro-runtime.js');
6
+ const wxPageUpdateImportPath = runtimeImportPath('page-update.js');
7
+ const wxUpdateClientImportPath = runtimeImportPath('update-client.js');
8
+ /** Generated native App entry. */
9
+ export const virtualWxAppId = 'virtual:vite-plugin-taro/wx/app';
10
+ /** Generated recursive Taro component entry. */
11
+ const virtualWxComponentId = 'virtual:vite-plugin-taro/wx/component';
12
+ /** Prefix for one generated native entry per configured page route. */
13
+ const virtualWxPageIdPrefix = 'virtual:vite-plugin-taro/wx/page/';
14
+ /** App bootstrap module that installs React Refresh globals before application modules execute. */
15
+ const virtualWxRefreshPreambleId = 'virtual:vite-plugin-taro/wx/refresh-preamble';
16
+ /** Development-only entry that initializes all configured page components before update replay. */
17
+ const virtualWxPagePreloadId = 'virtual:vite-plugin-taro/wx/page-preload';
18
+ export function isWxVirtualModuleId(id) {
19
+ return (id === virtualWxAppId ||
20
+ id === virtualWxComponentId ||
21
+ id === virtualWxRefreshPreambleId ||
22
+ id === virtualWxPagePreloadId ||
23
+ id.startsWith(virtualWxPageIdPrefix));
24
+ }
25
+ export function loadWxVirtualModule(id, context) {
26
+ if (id === virtualWxRefreshPreambleId) {
27
+ return createWxReactRefreshPreambleSource();
28
+ }
29
+ if (id === virtualWxAppId) {
30
+ return createWxAppEntrySource(context);
31
+ }
32
+ if (id === virtualWxComponentId) {
33
+ return createWxComponentEntrySource();
34
+ }
35
+ if (id === virtualWxPagePreloadId) {
36
+ return createWxPagePreloadSource(context);
37
+ }
38
+ if (id.startsWith(virtualWxPageIdPrefix)) {
39
+ const pagePath = id.slice(virtualWxPageIdPrefix.length);
40
+ const page = context.project.pages.find((candidate) => candidate.path === pagePath);
41
+ if (page) {
42
+ return createWxPageEntrySource(page, context);
43
+ }
44
+ }
45
+ }
46
+ /** Registers every configured page and the shared recursive component as eager WX entry chunks. */
47
+ export function emitWxEntryChunks(emitter, context, id) {
48
+ if (id !== virtualWxAppId)
49
+ return;
50
+ for (const page of context.project.pages) {
51
+ emitter.emitFile({
52
+ type: 'chunk',
53
+ id: `${virtualWxPageIdPrefix}${page.path}`,
54
+ fileName: `${page.path}.js`,
55
+ implicitlyLoadedAfterOneOf: [id]
56
+ });
57
+ }
58
+ emitter.emitFile({
59
+ type: 'chunk',
60
+ id: virtualWxComponentId,
61
+ fileName: 'comp.js',
62
+ implicitlyLoadedAfterOneOf: [id]
63
+ });
64
+ if (context.development) {
65
+ emitter.emitFile({
66
+ type: 'chunk',
67
+ id: virtualWxPagePreloadId,
68
+ fileName: wxPagePreloadFile,
69
+ implicitlyLoadedAfterOneOf: [id]
70
+ });
71
+ }
72
+ }
73
+ function createWxAppEntrySource(context) {
74
+ const refreshPreamble = context.development ? `import ${JSON.stringify(virtualWxRefreshPreambleId)}\n` : '';
75
+ const updateClient = context.development
76
+ ? `import { startWxUpdateClient } from ${JSON.stringify(wxUpdateClientImportPath)}\n`
77
+ : '';
78
+ return `${refreshPreamble}${updateClient}import { createReactApp, ReactDOM } from ${JSON.stringify(wxTaroRuntimeImportPath)}
79
+ import React from 'react'
80
+ import AppComponent from ${JSON.stringify(toViteFileImportPath(context.project.appComponentFile))}
81
+
82
+ const appConfig = ${JSON.stringify(context.project.appConfig)}
83
+ App(createReactApp(AppComponent, React, ReactDOM, appConfig))
84
+ ${context.development ? 'startWxUpdateClient()' : ''}
85
+ `;
86
+ }
87
+ function createWxPageEntrySource(page, context) {
88
+ const hotUpdateImport = context.development
89
+ ? `import { decorateWxPageConfig, registerWxPage } from ${JSON.stringify(wxPageUpdateImportPath)}\n`
90
+ : '';
91
+ const createConfig = `createPageConfig(PageComponent, '${page.path}', { root: { cn: [] } }, pageConfig)`;
92
+ const pageRegistration = context.development
93
+ ? `registerWxPage(${JSON.stringify(page.path)}, () => Page(taroPageConfig))`
94
+ : 'Page(taroPageConfig)';
95
+ return `${hotUpdateImport}import { createPageConfig } from ${JSON.stringify(wxTaroRuntimeImportPath)}
96
+ import PageComponent from ${JSON.stringify(createPageComponentImportPath(page.path))}
97
+
98
+ const pageConfig = ${JSON.stringify(page.config)}
99
+ const taroPageConfig = ${context.development ? `decorateWxPageConfig(${createConfig})` : createConfig}
100
+ if (PageComponent && PageComponent.behaviors) {
101
+ taroPageConfig.behaviors = (taroPageConfig.behaviors || []).concat(PageComponent.behaviors)
102
+ }
103
+ ${pageRegistration}
104
+ `;
105
+ }
106
+ function createWxPagePreloadSource(context) {
107
+ const imports = context.project.pages.map((page, index) => {
108
+ return `import Page${index} from ${JSON.stringify(createPageComponentImportPath(page.path))}`;
109
+ });
110
+ return `${imports.join('\n')}\nvoid [${context.project.pages.map((_, index) => `Page${index}`).join(', ')}]\n`;
111
+ }
112
+ function runtimeImportPath(fileName) {
113
+ return toViteFileImportPath(resolvePackageFile('dist/runtime/wx', fileName));
114
+ }
115
+ function createWxComponentEntrySource() {
116
+ return `import { createRecursiveComponentConfig } from ${JSON.stringify(wxTaroRuntimeImportPath)}
117
+
118
+ Component(createRecursiveComponentConfig())
119
+ `;
120
+ }
@@ -0,0 +1,8 @@
1
+ /** Generic asynchronous coordination helpers for Node-side plugin services. */
2
+ export declare class SerializedTaskQueue {
3
+ private readonly reportError;
4
+ private tail;
5
+ constructor(reportError: (error: unknown) => void);
6
+ enqueue(task: () => Promise<void>): Promise<void>;
7
+ waitForIdle(): Promise<void>;
8
+ }
@@ -0,0 +1,16 @@
1
+ /** Generic asynchronous coordination helpers for Node-side plugin services. */
2
+ export class SerializedTaskQueue {
3
+ reportError;
4
+ tail = Promise.resolve();
5
+ constructor(reportError) {
6
+ this.reportError = reportError;
7
+ }
8
+ enqueue(task) {
9
+ const work = this.tail.then(task);
10
+ this.tail = work.catch(this.reportError);
11
+ return work;
12
+ }
13
+ async waitForIdle() {
14
+ await this.tail;
15
+ }
16
+ }
@@ -0,0 +1,13 @@
1
+ type FileSource = string | Uint8Array;
2
+ type FileWrite = {
3
+ file: string;
4
+ source: FileSource;
5
+ };
6
+ /** Writes through a sibling temporary file so observers never read partial contents. */
7
+ export declare function writeFileAtomically(file: string, source: FileSource): Promise<void>;
8
+ export declare function writeFilesAtomically(files: FileWrite[]): Promise<void>;
9
+ /** Copies a directory when present and treats a missing optional source as empty. */
10
+ export declare function copyDirectoryIfExists(source: string, destination: string): Promise<void>;
11
+ /** Mirrors one optional file: copy existing files, ignore directories, and remove a missing source's destination. */
12
+ export declare function copyFileOrRemove(source: string, destination: string): Promise<void>;
13
+ export {};
@@ -0,0 +1,43 @@
1
+ /** Node-only filesystem helpers shared by build targets and development services. */
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ /** Writes through a sibling temporary file so observers never read partial contents. */
5
+ export async function writeFileAtomically(file, source) {
6
+ await fs.mkdir(path.dirname(file), { recursive: true });
7
+ const temporaryFile = `${file}.tmp`;
8
+ await fs.writeFile(temporaryFile, source);
9
+ await fs.rename(temporaryFile, file);
10
+ }
11
+ export async function writeFilesAtomically(files) {
12
+ await Promise.all(files.map(({ file, source }) => writeFileAtomically(file, source)));
13
+ }
14
+ /** Copies a directory when present and treats a missing optional source as empty. */
15
+ export async function copyDirectoryIfExists(source, destination) {
16
+ if (!source)
17
+ return;
18
+ try {
19
+ await fs.cp(source, destination, { recursive: true, force: true });
20
+ }
21
+ catch (error) {
22
+ if (!isMissingFileError(error))
23
+ throw error;
24
+ }
25
+ }
26
+ /** Mirrors one optional file: copy existing files, ignore directories, and remove a missing source's destination. */
27
+ export async function copyFileOrRemove(source, destination) {
28
+ try {
29
+ const stat = await fs.stat(source);
30
+ if (stat.isDirectory())
31
+ return;
32
+ await fs.mkdir(path.dirname(destination), { recursive: true });
33
+ await fs.copyFile(source, destination);
34
+ }
35
+ catch (error) {
36
+ if (!isMissingFileError(error))
37
+ throw error;
38
+ await fs.rm(destination, { recursive: true, force: true });
39
+ }
40
+ }
41
+ function isMissingFileError(error) {
42
+ return error instanceof Error && 'code' in error && error.code === 'ENOENT';
43
+ }
@@ -5,14 +5,14 @@
5
5
  * https://github.com/NervJS/taro/blob/f0e5c39d5f04290db975670411e23c3a396e15f8/packages/taro-webpack5-runner/src/utils/app.ts#L74-L90
6
6
  * https://github.com/NervJS/taro/blob/f0e5c39d5f04290db975670411e23c3a396e15f8/packages/taro-loader/src/h5.ts#L12-L21
7
7
  */
8
- export declare function createPageComponentImport(pagePath: string): string;
8
+ export declare function createPageComponentImportPath(pagePath: string): string;
9
9
  /**
10
10
  * Converts a local file path into a Vite file-system import specifier.
11
11
  *
12
12
  * Rolldown does not reliably resolve raw Windows absolute paths from virtual
13
13
  * module source, so generated imports use Vite's /@fs/ prefix instead.
14
14
  */
15
- export declare function toImportPath(filePath: string): string;
15
+ export declare function toViteFileImportPath(filePath: string): string;
16
16
  /**
17
17
  * Removes Rollup/Vite's internal virtual-module prefix before ID comparisons.
18
18
  */
@@ -1,3 +1,4 @@
1
+ /** Node-side helpers for application module files, Vite import specifiers, and normalized module IDs. */
1
2
  import path from 'node:path';
2
3
  /**
3
4
  * Derives a page component import from a Taro-style page path.
@@ -6,8 +7,14 @@ import path from 'node:path';
6
7
  * https://github.com/NervJS/taro/blob/f0e5c39d5f04290db975670411e23c3a396e15f8/packages/taro-webpack5-runner/src/utils/app.ts#L74-L90
7
8
  * https://github.com/NervJS/taro/blob/f0e5c39d5f04290db975670411e23c3a396e15f8/packages/taro-loader/src/h5.ts#L12-L21
8
9
  */
9
- export function createPageComponentImport(pagePath) {
10
- return toImportPath(`src/${pagePath}.tsx`);
10
+ export function createPageComponentImportPath(pagePath) {
11
+ return toViteFileImportPath(createPageComponentFile(pagePath));
12
+ }
13
+ /**
14
+ * Resolves the source file backing a Taro-style page path.
15
+ */
16
+ function createPageComponentFile(pagePath) {
17
+ return path.resolve(`src/${pagePath}.tsx`);
11
18
  }
12
19
  /**
13
20
  * Converts a local file path into a Vite file-system import specifier.
@@ -15,7 +22,7 @@ export function createPageComponentImport(pagePath) {
15
22
  * Rolldown does not reliably resolve raw Windows absolute paths from virtual
16
23
  * module source, so generated imports use Vite's /@fs/ prefix instead.
17
24
  */
18
- export function toImportPath(filePath) {
25
+ export function toViteFileImportPath(filePath) {
19
26
  return `/@fs/${normalizeModuleId(path.resolve(filePath))}`;
20
27
  }
21
28
  /**
@@ -0,0 +1,2 @@
1
+ export declare const packageRequire: NodeJS.Require;
2
+ export declare function resolvePackageFile(...segments: string[]): string;
@@ -0,0 +1,8 @@
1
+ /** Resolves dependencies and package-owned files relative to the installed plugin rather than the user project. */
2
+ import { createRequire } from 'node:module';
3
+ import path from 'node:path';
4
+ export const packageRequire = createRequire(import.meta.url);
5
+ const packageRoot = path.dirname(packageRequire.resolve('vite-plugin-taro/package.json'));
6
+ export function resolvePackageFile(...segments) {
7
+ return path.join(packageRoot, ...segments);
8
+ }
@@ -0,0 +1,4 @@
1
+ import type { PluginOption } from 'vite';
2
+ import type { VitePluginTaroOptions } from '../options.ts';
3
+ /** Creates the Vite plugins for the selected Taro target. */
4
+ export default function vitePluginTaro(options: VitePluginTaroOptions): PluginOption[];