@lynx-js/react-webpack-plugin-canary 0.12.1 → 0.13.0-canary-20261014-bdb7471c
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 +18 -0
- package/lib/Defines.d.ts +8 -0
- package/lib/Defines.js +33 -5
- package/lib/DefinesInjection.js +37 -0
- package/lib/ReactWebpackPlugin.d.ts +0 -4
- package/lib/ReactWebpackPlugin.js +1 -16
- package/lib/loaders/main-thread.d.ts +4 -0
- package/lib/loaders/main-thread.js +9 -0
- package/package.json +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @lynx-js/react-webpack-plugin
|
|
2
2
|
|
|
3
|
+
## 0.13.0-canary-20261014081315-bdb7471ce6e98461b062d1572df766ecccf6bca4
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Deliver the main-thread runtime through named imports from its init entry, including background-only definitions that carry their registration dependencies into the main-thread graph. Runtime code participates in business defines and release processing. Every supported transform supplies semantic runtime requirements, including for definitions injected from background-only modules; watch rebuilds refresh their dependencies and metadata. ([#4066](https://github.com/lynx-family/lynx-stack/pull/4066))
|
|
8
|
+
|
|
9
|
+
Upgrade the build plugins and `@lynx-js/react` together. This release requires `@lynx-js/react@^0.128.0`; its runtime and bundled transform use the same module-delivery protocol. The refresh plugin adds support for `@lynx-js/react-webpack-plugin@^0.13.0` while retaining its existing supported versions. Older compiler output is unsupported. The prebuilt worklet-runtime chunk, its package entries, and `loadWorkletRuntime` are removed. Combined lazy chunks share their paired main runtime, and standalone lazy bundles bootstrap themselves.
|
|
10
|
+
|
|
11
|
+
The worklet init entry follows `worklet.runtimePkg`: remove a trailing `/internal`, if present, and append `/worklet-runtime/init`. Its `registerWorklet` export uses the existing host registration function when the runtime is already initialized. Default worklet options use `@lynx-js/react/internal` for helpers.
|
|
12
|
+
|
|
13
|
+
Testing-library initializes module-delivered worklets on its emulated main thread in Vitest and Rstest.
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [[`bdb7471`](https://github.com/lynx-family/lynx-stack/commit/bdb7471ce6e98461b062d1572df766ecccf6bca4), [`89b828a`](https://github.com/lynx-family/lynx-stack/commit/89b828aba5e4eff14d8d2b5c44baecc121856c7d), [`16b45de`](https://github.com/lynx-family/lynx-stack/commit/16b45defdc76e822086b5207c0c290d51224da0e)]:
|
|
18
|
+
- @lynx-js/react@0.128.0-canary-20261014081315-bdb7471ce6e98461b062d1572df766ecccf6bca4
|
|
19
|
+
- @lynx-js/template-webpack-plugin@0.16.4-canary-20261014081315-bdb7471ce6e98461b062d1572df766ecccf6bca4
|
|
20
|
+
|
|
3
21
|
## 0.12.1
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/lib/Defines.d.ts
CHANGED
|
@@ -4,6 +4,14 @@ export interface Define {
|
|
|
4
4
|
id: string;
|
|
5
5
|
code: string;
|
|
6
6
|
unmergeable?: boolean;
|
|
7
|
+
imports?: Array<{
|
|
8
|
+
source: string;
|
|
9
|
+
imported: string;
|
|
10
|
+
local: string;
|
|
11
|
+
}>;
|
|
12
|
+
runtimeRequirements?: {
|
|
13
|
+
mainThreadProgrammability: boolean;
|
|
14
|
+
};
|
|
7
15
|
}
|
|
8
16
|
export declare function collectDefines<TModule>(modules: Iterable<TModule>, buildInfoKey: string, getModuleIdentifier: (module: TModule) => string): Define[];
|
|
9
17
|
export declare function selectMissingDefines(candidates: readonly Define[], present: readonly Define[]): Define[];
|
package/lib/Defines.js
CHANGED
|
@@ -32,9 +32,22 @@ export function collectDefines(modules, buildInfoKey, getModuleIdentifier) {
|
|
|
32
32
|
defines.set(define.id, define);
|
|
33
33
|
continue;
|
|
34
34
|
}
|
|
35
|
-
if (seen.code !== define.code
|
|
35
|
+
if (seen.code !== define.code
|
|
36
|
+
|| JSON.stringify(seen.imports?.map(({ source, imported, local }) => [source, imported, local]))
|
|
37
|
+
!== JSON.stringify(define.imports?.map(({ source, imported, local }) => [source, imported, local]))) {
|
|
36
38
|
throw new Error(`Two different definitions share the id ${define.id}.`);
|
|
37
39
|
}
|
|
40
|
+
// Equal code and dependencies identify one executable definition. Keep
|
|
41
|
+
// semantic evidence from any copy instead of making it depend on order.
|
|
42
|
+
if (define.runtimeRequirements !== undefined) {
|
|
43
|
+
defines.set(define.id, {
|
|
44
|
+
...seen,
|
|
45
|
+
runtimeRequirements: {
|
|
46
|
+
mainThreadProgrammability: seen.runtimeRequirements?.mainThreadProgrammability === true
|
|
47
|
+
|| define.runtimeRequirements.mainThreadProgrammability,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
38
51
|
}
|
|
39
52
|
return [...defines.values()];
|
|
40
53
|
}
|
|
@@ -49,15 +62,30 @@ export function renderDefines(defines) {
|
|
|
49
62
|
}
|
|
50
63
|
export function renderDefinesModule(definesForSnapshot, definesForWorklet) {
|
|
51
64
|
const prelude = [`import * as ReactLynx from '@lynx-js/react/internal';`];
|
|
52
|
-
if (definesForWorklet.length > 0) {
|
|
53
|
-
prelude.push(`var loadWorkletRuntime = ReactLynx.loadWorkletRuntime;`);
|
|
54
|
-
}
|
|
55
65
|
const defines = [...definesForWorklet, ...definesForSnapshot];
|
|
66
|
+
const locals = new Set(defines.flatMap(define => define.imports?.map(({ local }) => local) ?? []));
|
|
67
|
+
const imports = new Map();
|
|
68
|
+
let nextImport = 0;
|
|
69
|
+
const boundDefines = defines.map(define => {
|
|
70
|
+
const bindings = (define.imports ?? []).map(({ source, imported, local }) => {
|
|
71
|
+
const key = JSON.stringify([source, imported]);
|
|
72
|
+
let alias = imports.get(key);
|
|
73
|
+
if (alias === undefined) {
|
|
74
|
+
do {
|
|
75
|
+
alias = `__lynxDefineImport${nextImport++}`;
|
|
76
|
+
} while (locals.has(alias));
|
|
77
|
+
imports.set(key, alias);
|
|
78
|
+
prelude.push(`import { ${imported} as ${alias} } from ${JSON.stringify(source)};`);
|
|
79
|
+
}
|
|
80
|
+
return `const ${local} = ${alias};`;
|
|
81
|
+
});
|
|
82
|
+
return { ...define, code: [...bindings, define.code].join('\n') };
|
|
83
|
+
});
|
|
56
84
|
if (defines.some(({ code }) => /\brequire\(/.test(code))) {
|
|
57
85
|
prelude.push(`var require = function () { return ReactLynx; };`);
|
|
58
86
|
}
|
|
59
87
|
return `${prelude.join('\n')}
|
|
60
|
-
${renderDefines(
|
|
88
|
+
${renderDefines(boundDefines)}
|
|
61
89
|
`;
|
|
62
90
|
}
|
|
63
91
|
//# sourceMappingURL=Defines.js.map
|
package/lib/DefinesInjection.js
CHANGED
|
@@ -5,6 +5,7 @@ import * as path from 'node:path';
|
|
|
5
5
|
import { DEFINES_FOR_SNAPSHOT_BUILD_INFO, DEFINES_FOR_WORKLET_BUILD_INFO, collectDefines, renderDefinesModule, selectMissingDefines, } from './Defines.js';
|
|
6
6
|
import { LAYERS } from './layer.js';
|
|
7
7
|
import { boundaryKey, definesImportByBoundary, } from './loaders/defines-import-by-boundary.js';
|
|
8
|
+
import { REACT_RUNTIME_REQUIREMENTS_BUILD_INFO, } from './loaders/main-thread.js';
|
|
8
9
|
export function applyDefinesInjection(compiler, entryPairs, pluginName) {
|
|
9
10
|
const { EntryPlugin, experiments } = compiler.webpack;
|
|
10
11
|
const virtualModules = new experiments.VirtualModulesPlugin({});
|
|
@@ -12,7 +13,21 @@ export function applyDefinesInjection(compiler, entryPairs, pluginName) {
|
|
|
12
13
|
compiler.hooks.finishMake.tapPromise(pluginName, async (compilation) => {
|
|
13
14
|
const { moduleGraph } = compilation;
|
|
14
15
|
const definesImports = definesImportByBoundary(compiler);
|
|
16
|
+
const previousDefinesImports = new Map(definesImports);
|
|
15
17
|
definesImports.clear();
|
|
18
|
+
const requirementsByRequest = new Map();
|
|
19
|
+
compilation.hooks.finishModules.tap(pluginName, modules => {
|
|
20
|
+
// Rebuilding an async boundary does not finish its new dependencies.
|
|
21
|
+
// Transfer compiler metadata only after all injected modules are built.
|
|
22
|
+
for (const module of modules) {
|
|
23
|
+
const resource = module.resource;
|
|
24
|
+
const requirements = resource && requirementsByRequest.get(resource);
|
|
25
|
+
if (requirements) {
|
|
26
|
+
module.buildInfo[REACT_RUNTIME_REQUIREMENTS_BUILD_INFO] =
|
|
27
|
+
requirements;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
16
31
|
const traverse = (roots) => {
|
|
17
32
|
const visited = new Set();
|
|
18
33
|
const asyncBoundaries = new Map();
|
|
@@ -82,8 +97,17 @@ export function applyDefinesInjection(compiler, entryPairs, pluginName) {
|
|
|
82
97
|
throw new Error(`The main thread lacks the worklet definition(s) ${unmergeable.map(({ id }) => id).join(', ')} and they cannot be merged: they close over shared-runtime imports. Make the owning module reachable from the main thread, or avoid closing over a shared import inside the worklet.`);
|
|
83
98
|
}
|
|
84
99
|
if (missingSnapshot.length > 0 || missingWorklet.length > 0) {
|
|
100
|
+
const previousModule = [...compilation.modules].find(module => module.resource === request);
|
|
85
101
|
virtualModules.writeModule(request, renderDefinesModule(missingSnapshot, missingWorklet));
|
|
102
|
+
// Updating the virtual file does not invalidate a module already
|
|
103
|
+
// reused by this compilation. Rebuild it before reusing its imports.
|
|
104
|
+
if (previousModule) {
|
|
105
|
+
await rebuildModule(previousModule);
|
|
106
|
+
}
|
|
86
107
|
await inject(request);
|
|
108
|
+
requirementsByRequest.set(request, {
|
|
109
|
+
mainThreadProgrammability: missingWorklet.some(define => define.runtimeRequirements.mainThreadProgrammability),
|
|
110
|
+
});
|
|
87
111
|
}
|
|
88
112
|
for (const [resource, backgroundBoundary] of background.asyncBoundaries) {
|
|
89
113
|
const mainThreadBoundary = mainThread.asyncBoundaries.get(resource);
|
|
@@ -111,6 +135,19 @@ export function applyDefinesInjection(compiler, entryPairs, pluginName) {
|
|
|
111
135
|
}
|
|
112
136
|
});
|
|
113
137
|
}));
|
|
138
|
+
// A background boundary can lose its last definition or disappear
|
|
139
|
+
// entirely while its main-thread module remains cached and reachable.
|
|
140
|
+
// Rebuild every former owner that no longer needs a synthetic import.
|
|
141
|
+
await Promise.all([...compilation.modules].flatMap(module => {
|
|
142
|
+
const { resource, layer } = module;
|
|
143
|
+
if (!resource) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
const key = boundaryKey(layer, resource);
|
|
147
|
+
return previousDefinesImports.has(key) && !definesImports.has(key)
|
|
148
|
+
? [rebuildModule(module)]
|
|
149
|
+
: [];
|
|
150
|
+
}));
|
|
114
151
|
});
|
|
115
152
|
}
|
|
116
153
|
//# sourceMappingURL=DefinesInjection.js.map
|
|
@@ -65,10 +65,6 @@ interface ReactWebpackPluginOptions {
|
|
|
65
65
|
* @defaultValue `false` when production, `true` when development
|
|
66
66
|
*/
|
|
67
67
|
profile?: boolean | undefined;
|
|
68
|
-
/**
|
|
69
|
-
* The file path of `@lynx-js/react/worklet-runtime`.
|
|
70
|
-
*/
|
|
71
|
-
workletRuntimePath: string;
|
|
72
68
|
/**
|
|
73
69
|
* Whether to enable Element Template compilation.
|
|
74
70
|
*
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Copyright 2024 The Lynx Authors. All rights reserved.
|
|
2
2
|
// Licensed under the Apache License Version 2.0 that can be found in the
|
|
3
3
|
// LICENSE file in the root directory of this source tree.
|
|
4
|
-
import * as fs from 'node:fs';
|
|
5
4
|
import { createRequire } from 'node:module';
|
|
6
5
|
import invariant from 'tiny-invariant';
|
|
7
6
|
import { LynxTemplatePlugin } from '@lynx-js/template-webpack-plugin';
|
|
@@ -187,7 +186,6 @@ class ReactWebpackPlugin {
|
|
|
187
186
|
extractStr: false,
|
|
188
187
|
experimental_isLazyBundle: false,
|
|
189
188
|
profile: undefined,
|
|
190
|
-
workletRuntimePath: '',
|
|
191
189
|
experimental_useElementTemplate: false,
|
|
192
190
|
experimental_transformBuiltinAttributeNames: false,
|
|
193
191
|
lazyBundleFetcher: 'QueryComponent',
|
|
@@ -287,20 +285,7 @@ class ReactWebpackPlugin {
|
|
|
287
285
|
// two layers keep their own chunk. Strip it again here: the resulting
|
|
288
286
|
// chunk groups still make up a single lazy bundle.
|
|
289
287
|
hooks.asyncChunkName.tap(this.constructor.name, (chunkName) => chunkName.replace(/-react__(?:background|main-thread)$/, ''));
|
|
290
|
-
const {
|
|
291
|
-
hooks.beforeEncode.tap(this.constructor.name, (args) => {
|
|
292
|
-
const lepusCode = args.encodeData.lepusCode;
|
|
293
|
-
if (lepusCode.root?.source.source().toString()?.includes('registerWorkletInternal')) {
|
|
294
|
-
lepusCode.chunks.push({
|
|
295
|
-
name: 'worklet-runtime',
|
|
296
|
-
source: new RawSource(fs.readFileSync(options.workletRuntimePath, 'utf8')),
|
|
297
|
-
info: {
|
|
298
|
-
['lynx:main-thread']: true,
|
|
299
|
-
},
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
return args;
|
|
303
|
-
});
|
|
288
|
+
const { ConcatSource } = compiler.webpack.sources;
|
|
304
289
|
if (compiler.options.plugins.some((p) => p instanceof LynxTemplatePlugin)) {
|
|
305
290
|
compilation.hooks.processAssets.tap({
|
|
306
291
|
name: this.constructor.name,
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { LoaderDefinitionFunction } from '@rspack/core';
|
|
2
2
|
import type { ReactLoaderOptions } from './options.js';
|
|
3
3
|
export declare const ELEMENT_TEMPLATE_BUILD_INFO = "lynx:element-templates";
|
|
4
|
+
export declare const REACT_RUNTIME_REQUIREMENTS_BUILD_INFO = "lynx:react-runtime-requirements";
|
|
5
|
+
export interface ReactRuntimeRequirementsBuildInfo {
|
|
6
|
+
mainThreadProgrammability: boolean;
|
|
7
|
+
}
|
|
4
8
|
declare const mainThreadLoader: LoaderDefinitionFunction<ReactLoaderOptions>;
|
|
5
9
|
export default mainThreadLoader;
|
|
@@ -7,6 +7,7 @@ import { DEFINES_FOR_SNAPSHOT_BUILD_INFO, DEFINES_FOR_WORKLET_BUILD_INFO, } from
|
|
|
7
7
|
import { boundaryKey, definesImportByBoundary, } from './defines-import-by-boundary.js';
|
|
8
8
|
import { getMainThreadTransformOptions } from './options.js';
|
|
9
9
|
export const ELEMENT_TEMPLATE_BUILD_INFO = 'lynx:element-templates';
|
|
10
|
+
export const REACT_RUNTIME_REQUIREMENTS_BUILD_INFO = 'lynx:react-runtime-requirements';
|
|
10
11
|
const mainThreadLoader = function (content, sourceMap) {
|
|
11
12
|
const require = createRequire(import.meta.url);
|
|
12
13
|
const { transformPath = '@lynx-js/react/transform' } = this.getOptions();
|
|
@@ -83,6 +84,14 @@ const mainThreadLoader = function (content, sourceMap) {
|
|
|
83
84
|
const buildInfo = currentModule?.buildInfo;
|
|
84
85
|
if (buildInfo) {
|
|
85
86
|
buildInfo[UI_SOURCE_MAP_RECORDS_BUILD_INFO] = result.uiSourceMapRecords;
|
|
87
|
+
if (typeof result.runtimeRequirements !== 'object'
|
|
88
|
+
|| result.runtimeRequirements === null
|
|
89
|
+
|| typeof result.runtimeRequirements.mainThreadProgrammability
|
|
90
|
+
!== 'boolean') {
|
|
91
|
+
throw new TypeError('react-transform must provide runtimeRequirements.mainThreadProgrammability as a boolean; upgrade the build plugins and @lynx-js/react (runtime and transform) together to the module-delivery release (^0.128.0).');
|
|
92
|
+
}
|
|
93
|
+
buildInfo[REACT_RUNTIME_REQUIREMENTS_BUILD_INFO] =
|
|
94
|
+
result.runtimeRequirements;
|
|
86
95
|
if (result.elementTemplates && result.elementTemplates.length > 0) {
|
|
87
96
|
buildInfo[ELEMENT_TEMPLATE_BUILD_INFO] = result.elementTemplates;
|
|
88
97
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lynx-js/react-webpack-plugin-canary",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0-canary-20261014-bdb7471c",
|
|
4
4
|
"description": "A webpack plugin for ReactLynx",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"webpack",
|
|
@@ -38,11 +38,12 @@
|
|
|
38
38
|
"tiny-invariant": "^1.3.3"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
+
"@lynx-js/chunk-loading-webpack-plugin": "npm:@lynx-js/chunk-loading-webpack-plugin-canary@0.4.2",
|
|
41
42
|
"@lynx-js/css-extract-webpack-plugin": "npm:@lynx-js/css-extract-webpack-plugin-canary@0.11.1",
|
|
42
|
-
"@lynx-js/react": "npm:@lynx-js/react-canary@0.
|
|
43
|
-
"@lynx-js/template-webpack-plugin": "npm:@lynx-js/template-webpack-plugin-canary@0.16.
|
|
43
|
+
"@lynx-js/react": "npm:@lynx-js/react-canary@0.128.0-canary-20261014-bdb7471c",
|
|
44
|
+
"@lynx-js/template-webpack-plugin": "npm:@lynx-js/template-webpack-plugin-canary@0.16.4-canary-20261014-bdb7471c",
|
|
44
45
|
"@lynx-js/test-tools": "0.0.0",
|
|
45
|
-
"@microsoft/api-extractor": "7.59.
|
|
46
|
+
"@microsoft/api-extractor": "7.59.4",
|
|
46
47
|
"@rspack/core": "2.2.7",
|
|
47
48
|
"@rspack/lite-tapable": "1.1.5",
|
|
48
49
|
"@rstest/core": "0.12.1",
|
|
@@ -50,6 +51,7 @@
|
|
|
50
51
|
"swc-loader": "^0.2.7"
|
|
51
52
|
},
|
|
52
53
|
"peerDependencies": {
|
|
54
|
+
"@lynx-js/react": "*",
|
|
53
55
|
"@lynx-js/template-webpack-plugin": "*"
|
|
54
56
|
},
|
|
55
57
|
"peerDependenciesMeta": {
|