@lynx-js/react-webpack-plugin-canary 0.9.2 → 0.9.3-canary-20260522-11ef105e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @lynx-js/react-webpack-plugin
2
2
 
3
+ ## 0.9.3-canary-20260522094605-11ef105e3dcc3c08f098360d5a3e0367efe4a9d4
4
+
5
+ ### Patch Changes
6
+
7
+ - Inject the `lynxProcessEvalResult` runtime module only into main-thread chunks. The previous guard checked the chunk name for `:background`, which never matched the actual chunk names (`main__background`, `foo.js-react__background`), so the runtime was duplicated into background and async background chunks. ([#2692](https://github.com/lynx-family/lynx-stack/pull/2692))
8
+
9
+ - Updated dependencies []:
10
+ - @lynx-js/template-webpack-plugin@0.11.2-canary-20260522094605-11ef105e3dcc3c08f098360d5a3e0367efe4a9d4
11
+
3
12
  ## 0.9.2
4
13
 
5
14
  ### Patch Changes
@@ -31,7 +40,6 @@
31
40
  ### Minor Changes
32
41
 
33
42
  - feat: add `globalPropsMode` option to `PluginReactLynxOptions` ([#2346](https://github.com/lynx-family/lynx-stack/pull/2346))
34
-
35
43
  - When configured to `"event"`, `updateGlobalProps` will only trigger a global event and skip the `runWithForce` flow.
36
44
  - Defaults to `"reactive"`, which means `updateGlobalProps` will trigger re-render automatically.
37
45
 
@@ -40,7 +48,6 @@
40
48
  - Fix sourcemap misalignment when wrapping lazy bundle main-thread chunks. ([#2361](https://github.com/lynx-family/lynx-stack/pull/2361))
41
49
 
42
50
  The lazy bundle IIFE wrapper is now injected in `processAssets` at `PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE + 1` by walking chunk groups instead of patching assets in `beforeEncode`.
43
-
44
51
  - With `experimental_isLazyBundle: true`, the wrapper is applied to lazy-bundle chunk groups.
45
52
  - Without lazy bundle mode, the wrapper is applied to async main-thread chunk groups generated by dynamic import.
46
53
 
@@ -120,16 +127,16 @@
120
127
  type InlineChunkConfig =
121
128
  | boolean
122
129
  | InlineChunkTest
123
- | { enable?: boolean | 'auto'; test: InlineChunkTest };
130
+ | { enable?: boolean | "auto"; test: InlineChunkTest };
124
131
  ```
125
132
 
126
133
  ```ts
127
- import { defineConfig } from '@lynx-js/rspeedy';
134
+ import { defineConfig } from "@lynx-js/rspeedy";
128
135
 
129
136
  export default defineConfig({
130
137
  output: {
131
138
  inlineScripts: ({ name, size }) => {
132
- return name.includes('foo') && size < 1000;
139
+ return name.includes("foo") && size < 1000;
133
140
  },
134
141
  },
135
142
  });
@@ -184,7 +191,6 @@
184
191
  - feat: fully support MTS ([#569](https://github.com/lynx-family/lynx-stack/pull/569))
185
192
 
186
193
  Now use support the following usage
187
-
188
194
  - mainthread event
189
195
  - mainthread ref
190
196
  - runOnMainThread/runOnBackground
@@ -213,7 +219,7 @@
213
219
  - Shake `useImperativeHandle` on the main-thread by default. ([#153](https://github.com/lynx-family/lynx-stack/pull/153))
214
220
 
215
221
  ```js
216
- import { forwardRef, useImperativeHandle } from '@lynx-js/react';
222
+ import { forwardRef, useImperativeHandle } from "@lynx-js/react";
217
223
 
218
224
  export default forwardRef(function App(_, ref) {
219
225
  useImperativeHandle(ref, () => {
@@ -221,7 +227,7 @@
221
227
  return {
222
228
  name() {
223
229
  // This should be considered as background only
224
- console.info('This should not exist in main-thread');
230
+ console.info("This should not exist in main-thread");
225
231
  },
226
232
  };
227
233
  });
@@ -281,14 +287,14 @@
281
287
  - e8039f2: Add `defineDCE` in plugin options. Often used to define custom macros.
282
288
 
283
289
  ```js
284
- import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';
285
- import { defineConfig } from '@lynx-js/rspeedy';
290
+ import { pluginReactLynx } from "@lynx-js/react-rsbuild-plugin";
291
+ import { defineConfig } from "@lynx-js/rspeedy";
286
292
 
287
293
  export default defineConfig({
288
294
  plugins: [
289
295
  pluginReactLynx({
290
296
  defineDCE: {
291
- __SOME_FALSE_DEFINE__: 'false',
297
+ __SOME_FALSE_DEFINE__: "false",
292
298
  },
293
299
  }),
294
300
  ],
@@ -300,20 +306,20 @@
300
306
  For example, `import` initialized by dead code will be removed:
301
307
 
302
308
  ```js
303
- import { foo } from 'bar';
309
+ import { foo } from "bar";
304
310
 
305
311
  if (__SOME_FALSE_DEFINE__) {
306
312
  foo();
307
- console.log('dead code');
313
+ console.log("dead code");
308
314
  } else {
309
- console.log('reachable code');
315
+ console.log("reachable code");
310
316
  }
311
317
  ```
312
318
 
313
319
  will be transformed to:
314
320
 
315
321
  ```js
316
- console.log('reachable code');
322
+ console.log("reachable code");
317
323
  ```
318
324
 
319
325
  ## 0.6.0
@@ -323,14 +329,14 @@
323
329
  - a30c83d: Add `compat.removeComponentAttrRegex`.
324
330
 
325
331
  ```js
326
- import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';
327
- import { defineConfig } from '@lynx-js/rspeedy';
332
+ import { pluginReactLynx } from "@lynx-js/react-rsbuild-plugin";
333
+ import { defineConfig } from "@lynx-js/rspeedy";
328
334
 
329
335
  export default defineConfig({
330
336
  plugins: [
331
337
  pluginReactLynx({
332
338
  compat: {
333
- removeComponentAttrRegex: 'YOUR REGEX',
339
+ removeComponentAttrRegex: "YOUR REGEX",
334
340
  },
335
341
  }),
336
342
  ],
@@ -1,6 +1,15 @@
1
1
  import type { Compiler } from '@rspack/core';
2
2
  import type { ExtractStrConfig } from '@lynx-js/react/transform';
3
3
  import { LAYERS } from './layer.js';
4
+ interface ElementTemplateBuildInfo {
5
+ templateId: string;
6
+ compiledTemplate: Record<string, unknown>;
7
+ }
8
+ export interface ModuleWithElementTemplateBuildInfo {
9
+ buildInfo?: Record<string, unknown>;
10
+ modules?: Iterable<ModuleWithElementTemplateBuildInfo>;
11
+ }
12
+ export declare function collectElementTemplatesFromModule(module: ModuleWithElementTemplateBuildInfo): ElementTemplateBuildInfo[];
4
13
  /**
5
14
  * The options for ReactWebpackPlugin
6
15
  *
@@ -50,6 +59,12 @@ interface ReactWebpackPluginOptions {
50
59
  * The file path of `@lynx-js/react/worklet-runtime`.
51
60
  */
52
61
  workletRuntimePath: string;
62
+ /**
63
+ * Whether to enable Element Template compilation.
64
+ *
65
+ * @experimental
66
+ */
67
+ experimental_useElementTemplate?: boolean;
53
68
  }
54
69
  /**
55
70
  * ReactWebpackPlugin allows using ReactLynx with webpack
@@ -7,8 +7,22 @@ import invariant from 'tiny-invariant';
7
7
  import { LynxTemplatePlugin } from '@lynx-js/template-webpack-plugin';
8
8
  import { RuntimeGlobals } from '@lynx-js/webpack-runtime-globals';
9
9
  import { LAYERS } from './layer.js';
10
+ import { ELEMENT_TEMPLATE_BUILD_INFO } from './loaders/main-thread.js';
10
11
  import { createLynxProcessEvalResultRuntimeModule } from './LynxProcessEvalResultRuntimeModule.js';
11
12
  const require = createRequire(import.meta.url);
13
+ export function collectElementTemplatesFromModule(module) {
14
+ const elementTemplates = [];
15
+ const templates = module.buildInfo?.[ELEMENT_TEMPLATE_BUILD_INFO];
16
+ if (Array.isArray(templates)) {
17
+ elementTemplates.push(...templates);
18
+ }
19
+ if (module.modules) {
20
+ for (const nestedModule of module.modules) {
21
+ elementTemplates.push(...collectElementTemplatesFromModule(nestedModule));
22
+ }
23
+ }
24
+ return elementTemplates;
25
+ }
12
26
  /**
13
27
  * ReactWebpackPlugin allows using ReactLynx with webpack
14
28
  *
@@ -81,6 +95,7 @@ class ReactWebpackPlugin {
81
95
  experimental_isLazyBundle: false,
82
96
  profile: undefined,
83
97
  workletRuntimePath: '',
98
+ experimental_useElementTemplate: false,
84
99
  }); }
85
100
  /**
86
101
  * The entry point of a webpack plugin.
@@ -120,6 +135,7 @@ class ReactWebpackPlugin {
120
135
  __GLOBAL_PROPS_MODE__: JSON.stringify(options.globalPropsMode),
121
136
  __ENABLE_SSR__: JSON.stringify(options.enableSSR),
122
137
  __DISABLE_CREATE_SELECTOR_QUERY_INCOMPATIBLE_WARNING__: JSON.stringify(options.disableCreateSelectorQueryIncompatibleWarning),
138
+ __USE_ELEMENT_TEMPLATE__: JSON.stringify(options.experimental_useElementTemplate),
123
139
  }).apply(compiler);
124
140
  compiler.hooks.thisCompilation.tap(this.constructor.name, compilation => {
125
141
  const onceForChunkSet = new WeakSet();
@@ -131,7 +147,7 @@ class ReactWebpackPlugin {
131
147
  return;
132
148
  }
133
149
  onceForChunkSet.add(chunk);
134
- if (chunk.name?.includes(':background')) {
150
+ if (!chunk.name?.includes('__main-thread')) {
135
151
  return;
136
152
  }
137
153
  const LynxProcessEvalResultRuntimeModule = createLynxProcessEvalResultRuntimeModule(compiler.webpack);
@@ -222,6 +238,18 @@ class ReactWebpackPlugin {
222
238
  hooks.asyncChunkName.tap(this.constructor.name, (chunkName) => chunkName
223
239
  ?.replaceAll(`-react__background`, '')
224
240
  ?.replaceAll(`-react__main-thread`, ''));
241
+ if (options.experimental_useElementTemplate) {
242
+ hooks.beforeEncode.tap(`${this.constructor.name}.ElementTemplate`, (args) => {
243
+ const elementTemplates = {};
244
+ for (const module of compilation.modules) {
245
+ for (const { templateId, compiledTemplate } of collectElementTemplatesFromModule(module)) {
246
+ elementTemplates[templateId] = compiledTemplate;
247
+ }
248
+ }
249
+ args.encodeData.elementTemplate = elementTemplates;
250
+ return args;
251
+ });
252
+ }
225
253
  });
226
254
  }
227
255
  #updateMainThreadInfo(compilation, name) {
@@ -1,4 +1,5 @@
1
1
  import type { LoaderDefinitionFunction } from '@rspack/core';
2
2
  import type { ReactLoaderOptions } from './options.js';
3
+ export declare const ELEMENT_TEMPLATE_BUILD_INFO = "lynx:element-templates";
3
4
  declare const mainThreadLoader: LoaderDefinitionFunction<ReactLoaderOptions>;
4
5
  export default mainThreadLoader;
@@ -4,6 +4,7 @@
4
4
  import { createRequire } from 'node:module';
5
5
  import { UI_SOURCE_MAP_RECORDS_BUILD_INFO } from '@lynx-js/template-webpack-plugin';
6
6
  import { getMainThreadTransformOptions } from './options.js';
7
+ export const ELEMENT_TEMPLATE_BUILD_INFO = 'lynx:element-templates';
7
8
  const mainThreadLoader = function (content, sourceMap) {
8
9
  const require = createRequire(import.meta.url);
9
10
  const { transformPath = '@lynx-js/react/transform' } = this.getOptions();
@@ -80,6 +81,12 @@ const mainThreadLoader = function (content, sourceMap) {
80
81
  const buildInfo = currentModule?.buildInfo;
81
82
  if (buildInfo) {
82
83
  buildInfo[UI_SOURCE_MAP_RECORDS_BUILD_INFO] = result.uiSourceMapRecords;
84
+ if (result.elementTemplates && result.elementTemplates.length > 0) {
85
+ buildInfo[ELEMENT_TEMPLATE_BUILD_INFO] = result.elementTemplates;
86
+ }
87
+ else {
88
+ delete buildInfo[ELEMENT_TEMPLATE_BUILD_INFO];
89
+ }
83
90
  }
84
91
  this.callback(null, result.code + (this.hot
85
92
  // TODO: temporary fix LEPUS error `$RefreshReg$ is not defined`
@@ -3,8 +3,10 @@ import type { CompatVisitorConfig, DefineDceVisitorConfig, JsxTransformerConfig,
3
3
  export declare const JSX_IMPORT_SOURCE: {
4
4
  MAIN_THREAD: string;
5
5
  BACKGROUND: string;
6
+ ELEMENT_TEMPLATE: string;
6
7
  };
7
8
  export declare const RUNTIME_PKG = "@lynx-js/react/internal";
9
+ export declare const ELEMENT_TEMPLATE_RUNTIME_PKG = "@lynx-js/react/element-template";
8
10
  /**
9
11
  * The options of the ReactLynx plugin.
10
12
  * @public
@@ -46,6 +48,12 @@ export interface ReactLoaderOptions {
46
48
  * The engine version.
47
49
  */
48
50
  engineVersion?: string | undefined;
51
+ /**
52
+ * Whether to enable Element Template compilation.
53
+ *
54
+ * @experimental
55
+ */
56
+ experimental_useElementTemplate?: boolean | undefined;
49
57
  }
50
58
  export declare function getMainThreadTransformOptions(this: LoaderContext<ReactLoaderOptions>, inputSourceMap: string | undefined): TransformNodiffOptions;
51
59
  export declare function getBackgroundTransformOptions(this: LoaderContext<ReactLoaderOptions>, inputSourceMap: string | undefined): TransformNodiffOptions;
@@ -6,9 +6,11 @@ const PLUGIN_NAME = 'react:webpack';
6
6
  export const JSX_IMPORT_SOURCE = {
7
7
  MAIN_THREAD: '@lynx-js/react/lepus',
8
8
  BACKGROUND: '@lynx-js/react',
9
+ ELEMENT_TEMPLATE: '@lynx-js/react/element-template',
9
10
  };
10
11
  const PUBLIC_RUNTIME_PKG = '@lynx-js/react';
11
12
  export const RUNTIME_PKG = '@lynx-js/react/internal';
13
+ export const ELEMENT_TEMPLATE_RUNTIME_PKG = '@lynx-js/react/element-template';
12
14
  const OLD_RUNTIME_PKG = '@lynx-js/react-runtime';
13
15
  const COMPONENT_PKG = '@lynx-js/react-components';
14
16
  function normalizeSlashes(file) {
@@ -16,7 +18,8 @@ function normalizeSlashes(file) {
16
18
  }
17
19
  function getCommonOptions(inputSourceMap) {
18
20
  const filename = normalizeSlashes(path.relative(this.rootContext, this.resourcePath));
19
- const { compat, enableRemoveCSSScope, enableUiSourceMap, inlineSourcesContent, isDynamicComponent, engineVersion, defineDCE = { define: {} }, } = this.getOptions();
21
+ const { compat, enableRemoveCSSScope, enableUiSourceMap, inlineSourcesContent, isDynamicComponent, engineVersion, experimental_useElementTemplate, defineDCE = { define: {} }, } = this.getOptions();
22
+ const useElementTemplate = experimental_useElementTemplate === true;
20
23
  const syntax = (/\.[mc]?tsx?$/.exec(this.resourcePath))
21
24
  ? 'typescript'
22
25
  : 'ecmascript';
@@ -61,7 +64,7 @@ function getCommonOptions(inputSourceMap) {
61
64
  ...(inputSourceMap && { inputSourceMap }),
62
65
  sourceMapColumns: this.sourceMap && !this.hot,
63
66
  inlineSourcesContent: inlineSourcesContent ?? !this.hot,
64
- snapshot: {
67
+ snapshot: useElementTemplate ? false : {
65
68
  // TODO: config
66
69
  preserveJsx: false,
67
70
  // In standalone lazy bundle mode, we do not support HMR now.
@@ -75,6 +78,16 @@ function getCommonOptions(inputSourceMap) {
75
78
  filename,
76
79
  isDynamicComponent: isDynamicComponent ?? false,
77
80
  },
81
+ elementTemplate: useElementTemplate
82
+ ? {
83
+ preserveJsx: false,
84
+ runtimePkg: ELEMENT_TEMPLATE_RUNTIME_PKG,
85
+ jsxImportSource: JSX_IMPORT_SOURCE.ELEMENT_TEMPLATE,
86
+ filename,
87
+ target: 'JS',
88
+ isDynamicComponent: isDynamicComponent ?? false,
89
+ }
90
+ : false,
78
91
  engineVersion: engineVersion ?? '',
79
92
  syntaxConfig: JSON.stringify({
80
93
  syntax,
@@ -100,6 +113,7 @@ function getCommonOptions(inputSourceMap) {
100
113
  export function getMainThreadTransformOptions(inputSourceMap) {
101
114
  const commonOptions = getCommonOptions.call(this, inputSourceMap);
102
115
  const { shake } = this.getOptions();
116
+ const useElementTemplate = typeof commonOptions.elementTemplate === 'object';
103
117
  return {
104
118
  ...commonOptions,
105
119
  compat: typeof commonOptions.compat === 'object'
@@ -108,11 +122,18 @@ export function getMainThreadTransformOptions(inputSourceMap) {
108
122
  target: 'LEPUS',
109
123
  }
110
124
  : false,
111
- snapshot: {
125
+ snapshot: useElementTemplate ? false : {
112
126
  ...commonOptions.snapshot,
113
127
  jsxImportSource: JSX_IMPORT_SOURCE.MAIN_THREAD,
114
128
  target: 'LEPUS',
115
129
  },
130
+ elementTemplate: useElementTemplate
131
+ ? {
132
+ ...commonOptions.elementTemplate,
133
+ jsxImportSource: JSX_IMPORT_SOURCE.ELEMENT_TEMPLATE,
134
+ target: 'LEPUS',
135
+ }
136
+ : false,
116
137
  dynamicImport: {
117
138
  layer: `react__main-thread`,
118
139
  runtimePkg: RUNTIME_PKG,
@@ -141,6 +162,8 @@ export function getMainThreadTransformOptions(inputSourceMap) {
141
162
  PUBLIC_RUNTIME_PKG,
142
163
  `${PUBLIC_RUNTIME_PKG}/legacy-react-runtime`,
143
164
  RUNTIME_PKG,
165
+ ELEMENT_TEMPLATE_RUNTIME_PKG,
166
+ `${ELEMENT_TEMPLATE_RUNTIME_PKG}/internal`,
144
167
  ...typeof commonOptions.compat === 'object'
145
168
  ? commonOptions.compat.oldRuntimePkg
146
169
  : [],
@@ -178,6 +201,7 @@ export function getMainThreadTransformOptions(inputSourceMap) {
178
201
  }
179
202
  export function getBackgroundTransformOptions(inputSourceMap) {
180
203
  const commonOptions = getCommonOptions.call(this, inputSourceMap);
204
+ const useElementTemplate = typeof commonOptions.elementTemplate === 'object';
181
205
  return {
182
206
  ...commonOptions,
183
207
  compat: typeof commonOptions.compat === 'object'
@@ -190,10 +214,17 @@ export function getBackgroundTransformOptions(inputSourceMap) {
190
214
  layer: `react__background`,
191
215
  runtimePkg: RUNTIME_PKG,
192
216
  },
193
- snapshot: {
217
+ snapshot: useElementTemplate ? false : {
194
218
  ...commonOptions.snapshot,
195
219
  jsxImportSource: JSX_IMPORT_SOURCE.BACKGROUND,
196
220
  },
221
+ elementTemplate: useElementTemplate
222
+ ? {
223
+ ...commonOptions.elementTemplate,
224
+ jsxImportSource: JSX_IMPORT_SOURCE.BACKGROUND,
225
+ target: 'JS',
226
+ }
227
+ : false,
197
228
  defineDCE: {
198
229
  define: {
199
230
  ...commonOptions.defineDCE?.define,
@@ -3,13 +3,13 @@
3
3
  // LICENSE file in the root directory of this source tree.
4
4
  import { createRequire } from 'node:module';
5
5
  import path from 'node:path';
6
- import { JSX_IMPORT_SOURCE, RUNTIME_PKG } from './options.js';
6
+ import { ELEMENT_TEMPLATE_RUNTIME_PKG, JSX_IMPORT_SOURCE, RUNTIME_PKG, } from './options.js';
7
7
  function normalizeSlashes(file) {
8
8
  return file.replaceAll(path.win32.sep, '/');
9
9
  }
10
10
  function testingLoader(content) {
11
11
  const require = createRequire(import.meta.url);
12
- const { compat = false, defineDCE = { define: {} }, engineVersion = '', shake = false, transformPath = '@lynx-js/react/transform', } = this.getOptions();
12
+ const { compat = false, defineDCE = { define: {} }, engineVersion = '', experimental_useElementTemplate = false, shake = false, transformPath = '@lynx-js/react/transform', } = this.getOptions();
13
13
  const { transformReactLynxSync } = require(transformPath);
14
14
  const filename = normalizeSlashes(path.relative(this.rootContext, this.resourcePath));
15
15
  const normalizedCompat = typeof compat === 'object'
@@ -33,13 +33,22 @@ function testingLoader(content) {
33
33
  pluginName: '',
34
34
  filename: this.resourcePath,
35
35
  sourcemap: true,
36
- snapshot: {
36
+ snapshot: experimental_useElementTemplate ? false : {
37
37
  preserveJsx: false,
38
38
  runtimePkg: RUNTIME_PKG,
39
39
  jsxImportSource: JSX_IMPORT_SOURCE.BACKGROUND,
40
40
  filename,
41
41
  target: 'MIXED',
42
42
  },
43
+ elementTemplate: experimental_useElementTemplate
44
+ ? {
45
+ preserveJsx: false,
46
+ runtimePkg: ELEMENT_TEMPLATE_RUNTIME_PKG,
47
+ jsxImportSource: JSX_IMPORT_SOURCE.ELEMENT_TEMPLATE,
48
+ filename,
49
+ target: 'MIXED',
50
+ }
51
+ : false,
43
52
  // snapshot: true,
44
53
  directiveDCE: false,
45
54
  defineDCE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lynx-js/react-webpack-plugin-canary",
3
- "version": "0.9.2",
3
+ "version": "0.9.3-canary-20260522-11ef105e",
4
4
  "description": "A webpack plugin for ReactLynx",
5
5
  "keywords": [
6
6
  "webpack",
@@ -43,10 +43,10 @@
43
43
  "swc-loader": "^0.2.7",
44
44
  "webpack": "^5.105.2",
45
45
  "@lynx-js/css-extract-webpack-plugin": "npm:@lynx-js/css-extract-webpack-plugin-canary@0.7.1",
46
- "@lynx-js/template-webpack-plugin": "npm:@lynx-js/template-webpack-plugin-canary@0.11.0",
47
- "@lynx-js/react": "npm:@lynx-js/react-canary@0.120.0",
48
- "@lynx-js/test-tools": "0.0.0",
49
- "@lynx-js/vitest-setup": "0.0.0"
46
+ "@lynx-js/react": "npm:@lynx-js/react-canary@0.121.1-canary-20260522-11ef105e",
47
+ "@lynx-js/template-webpack-plugin": "npm:@lynx-js/template-webpack-plugin-canary@0.11.2-canary-20260522-11ef105e",
48
+ "@lynx-js/vitest-setup": "0.0.0",
49
+ "@lynx-js/test-tools": "0.0.0"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "@lynx-js/template-webpack-plugin": "*"