@lynx-js/external-bundle-rsbuild-plugin 0.0.4 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +170 -4
- package/lib/index.d.ts +170 -6
- package/lib/index.js +393 -4
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @lynx-js/external-bundle-rsbuild-plugin
|
|
2
2
|
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [[`3262ca8`](https://github.com/lynx-family/lynx-stack/commit/3262ca88e93f66a1b745d4cc12b98959d20e9413)]:
|
|
8
|
+
- @lynx-js/externals-loading-webpack-plugin@0.1.1
|
|
9
|
+
|
|
10
|
+
## 0.1.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- **BREAKING CHANGE**: ([#2370](https://github.com/lynx-family/lynx-stack/pull/2370))
|
|
15
|
+
|
|
16
|
+
Simplify the API for external bundle builds by `externalsPresets` and `externalsPresetDefinitions`.
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Updated dependencies [[`7b7a0c6`](https://github.com/lynx-family/lynx-stack/commit/7b7a0c6ee35e32f9575436cb36b25f2931f43c05)]:
|
|
21
|
+
- @lynx-js/externals-loading-webpack-plugin@0.1.0
|
|
22
|
+
|
|
3
23
|
## 0.0.4
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
npm install -D @lynx-js/external-bundle-rsbuild-plugin
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
If you want to use the built-in `reactlynx` preset, also install:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -D @lynx-js/react-umd
|
|
24
|
+
```
|
|
25
|
+
|
|
20
26
|
## Usage
|
|
21
27
|
|
|
22
28
|
```ts
|
|
@@ -28,11 +34,20 @@ export default {
|
|
|
28
34
|
plugins: [
|
|
29
35
|
pluginReactLynx(),
|
|
30
36
|
pluginExternalBundle({
|
|
37
|
+
externalsPresets: {
|
|
38
|
+
reactlynx: true,
|
|
39
|
+
},
|
|
31
40
|
externals: {
|
|
32
|
-
lodash: {
|
|
33
|
-
|
|
34
|
-
background: { sectionPath: '
|
|
35
|
-
mainThread: { sectionPath: '
|
|
41
|
+
'lodash-es': {
|
|
42
|
+
bundlePath: 'lodash-es.lynx.bundle',
|
|
43
|
+
background: { sectionPath: 'lodash-es' },
|
|
44
|
+
mainThread: { sectionPath: 'lodash-es__main-thread' },
|
|
45
|
+
async: false,
|
|
46
|
+
},
|
|
47
|
+
'./components': {
|
|
48
|
+
bundlePath: 'comp.lynx.bundle',
|
|
49
|
+
background: { sectionPath: 'component' },
|
|
50
|
+
mainThread: { sectionPath: 'component__main-thread' },
|
|
36
51
|
},
|
|
37
52
|
},
|
|
38
53
|
}),
|
|
@@ -40,6 +55,157 @@ export default {
|
|
|
40
55
|
}
|
|
41
56
|
```
|
|
42
57
|
|
|
58
|
+
If your external bundle request key already matches the produced section names,
|
|
59
|
+
you can use the shorthand string form:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
pluginExternalBundle({
|
|
63
|
+
externalsPresets: {
|
|
64
|
+
reactlynx: true,
|
|
65
|
+
},
|
|
66
|
+
externals: {
|
|
67
|
+
'./App.js': 'comp.lynx.bundle',
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The shorthand expands to the same `bundlePath` plus default:
|
|
73
|
+
|
|
74
|
+
- `libraryName = './App.js'`
|
|
75
|
+
- `background.sectionPath = './App.js'`
|
|
76
|
+
- `mainThread.sectionPath = './App.js__main-thread'`
|
|
77
|
+
- `async = true`
|
|
78
|
+
|
|
79
|
+
If you need custom section names or a custom exported library name, use the
|
|
80
|
+
full object form instead:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
pluginExternalBundle({
|
|
84
|
+
externalsPresets: {
|
|
85
|
+
reactlynx: true,
|
|
86
|
+
},
|
|
87
|
+
externals: {
|
|
88
|
+
'./App.js': {
|
|
89
|
+
libraryName: 'CompLib',
|
|
90
|
+
bundlePath: 'comp.lynx.bundle',
|
|
91
|
+
background: { sectionPath: 'CompLib' },
|
|
92
|
+
mainThread: { sectionPath: 'CompLib__main-thread' },
|
|
93
|
+
async: true,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## `bundlePath` vs `url`
|
|
100
|
+
|
|
101
|
+
Prefer `bundlePath` for bundles that belong to the current project.
|
|
102
|
+
|
|
103
|
+
- `bundlePath`
|
|
104
|
+
- resolves through the runtime public path
|
|
105
|
+
- lets the plugin serve local bundles in development
|
|
106
|
+
- lets the plugin emit managed bundle assets during build
|
|
107
|
+
- `url`
|
|
108
|
+
- uses a fully resolved address directly
|
|
109
|
+
- is better only when the bundle is hosted outside the current build output, such as on a CDN
|
|
110
|
+
|
|
111
|
+
For the built-in `reactlynx` preset, `bundlePath` is especially recommended because the plugin will automatically emit `react.lynx.bundle` by resolving `@lynx-js/react-umd/dev` or `@lynx-js/react-umd/prod`.
|
|
112
|
+
|
|
113
|
+
## Building project-owned external bundles
|
|
114
|
+
|
|
115
|
+
By default, `pluginExternalBundle` reads project-owned external bundles from `dist-external-bundle` before emitting them into the final app output.
|
|
116
|
+
|
|
117
|
+
If you use that default directory, no extra config is needed:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
pluginExternalBundle({
|
|
121
|
+
externals: {
|
|
122
|
+
'./components': {
|
|
123
|
+
bundlePath: 'comp.lynx.bundle',
|
|
124
|
+
background: { sectionPath: 'component' },
|
|
125
|
+
mainThread: { sectionPath: 'component__main-thread' },
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Then:
|
|
132
|
+
|
|
133
|
+
- development serves `dist-external-bundle/comp.lynx.bundle`
|
|
134
|
+
- production emits that bundle into the app output
|
|
135
|
+
|
|
136
|
+
If your local external bundles live somewhere else, set `externalBundleRoot` explicitly.
|
|
137
|
+
|
|
138
|
+
## ReactLynx preset
|
|
139
|
+
|
|
140
|
+
`externalsPresets.reactlynx` expands the standard ReactLynx module requests automatically, so you do not need to write the full externals map by hand.
|
|
141
|
+
|
|
142
|
+
If your app needs business-specific presets, define them next to
|
|
143
|
+
`externalsPresets`:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
pluginExternalBundle({
|
|
147
|
+
externalsPresets: {
|
|
148
|
+
lynxUi: true,
|
|
149
|
+
},
|
|
150
|
+
externalsPresetDefinitions: {
|
|
151
|
+
lynxUi: {
|
|
152
|
+
resolveExternals() {
|
|
153
|
+
return {
|
|
154
|
+
'@lynx-js/lynx-ui': {
|
|
155
|
+
libraryName: ['LynxUI', 'UI'],
|
|
156
|
+
bundlePath: 'lynx-ui.lynx.bundle',
|
|
157
|
+
background: { sectionPath: 'LynxUI' },
|
|
158
|
+
mainThread: { sectionPath: 'LynxUI__main-thread' },
|
|
159
|
+
async: false,
|
|
160
|
+
},
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
})
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
If you want to extend the built-in `reactlynx` preset instead of defining a
|
|
169
|
+
completely separate one, use `extends`:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
pluginExternalBundle({
|
|
173
|
+
externalsPresets: {
|
|
174
|
+
reactlynxPlus: true,
|
|
175
|
+
},
|
|
176
|
+
externalsPresetDefinitions: {
|
|
177
|
+
reactlynxPlus: {
|
|
178
|
+
extends: 'reactlynx',
|
|
179
|
+
resolveExternals() {
|
|
180
|
+
return {
|
|
181
|
+
'@lynx-js/lynx-ui': {
|
|
182
|
+
libraryName: ['LynxUI', 'UI'],
|
|
183
|
+
bundlePath: 'lynx-ui.lynx.bundle',
|
|
184
|
+
background: { sectionPath: 'LynxUI' },
|
|
185
|
+
mainThread: { sectionPath: 'LynxUI__main-thread' },
|
|
186
|
+
async: false,
|
|
187
|
+
},
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
})
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Use it together with `@lynx-js/lynx-bundle-rslib-config` when producing external bundles:
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import { defineExternalBundleRslibConfig } from '@lynx-js/lynx-bundle-rslib-config'
|
|
199
|
+
|
|
200
|
+
export default defineExternalBundleRslibConfig({
|
|
201
|
+
output: {
|
|
202
|
+
externalsPresets: {
|
|
203
|
+
reactlynx: true,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
})
|
|
207
|
+
```
|
|
208
|
+
|
|
43
209
|
## Documentation
|
|
44
210
|
|
|
45
211
|
Visit [Lynx Website](https://lynxjs.org/api/rspeedy/external-bundle-rsbuild-plugin) to view the full documentation.
|
package/lib/index.d.ts
CHANGED
|
@@ -1,16 +1,180 @@
|
|
|
1
|
+
import type { RsbuildPlugin } from '@rsbuild/core';
|
|
2
|
+
import type { ExternalValue, ExternalsLoadingPluginOptions } from '@lynx-js/externals-loading-webpack-plugin';
|
|
1
3
|
/**
|
|
2
|
-
*
|
|
4
|
+
* Options for the built-in `reactlynx` externals preset.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
6
|
+
* @public
|
|
5
7
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
export interface ReactLynxExternalsPresetOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Emit the ReactLynx runtime bundle into the current build output and load it
|
|
11
|
+
* through the generated runtime public path.
|
|
12
|
+
*
|
|
13
|
+
* Prefer this over `url` for normal Rspeedy projects. In addition to letting
|
|
14
|
+
* the runtime resolve the final URL from `publicPath`, the plugin will also
|
|
15
|
+
* copy the corresponding `@lynx-js/react-umd` bundle into the emitted assets,
|
|
16
|
+
* so application bundles can reference it without requiring an extra manual
|
|
17
|
+
* copy step when publishing.
|
|
18
|
+
*
|
|
19
|
+
* @defaultValue `'react.lynx.bundle'`
|
|
20
|
+
*/
|
|
21
|
+
bundlePath?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Package name that provides the ReactLynx runtime bundle.
|
|
24
|
+
*
|
|
25
|
+
* Override this when wrapping the plugin for another distribution, such as
|
|
26
|
+
* `@byted-lynx/react-umd`.
|
|
27
|
+
*
|
|
28
|
+
* @defaultValue `'@lynx-js/react-umd'`
|
|
29
|
+
*/
|
|
30
|
+
reactUmdPackageName?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Override the runtime bundle URL directly.
|
|
33
|
+
*
|
|
34
|
+
* Use this only when the ReactLynx runtime is hosted outside the current
|
|
35
|
+
* build output, for example on a CDN. Unlike `bundlePath`, this does not emit
|
|
36
|
+
* any extra asset into the current compilation.
|
|
37
|
+
*
|
|
38
|
+
* @deprecated Prefer `bundlePath`, which resolves through the runtime public
|
|
39
|
+
* path and enables automatic asset emission.
|
|
40
|
+
*/
|
|
41
|
+
url?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Presets for external bundle dependencies.
|
|
45
|
+
*
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
export interface ExternalsPresets {
|
|
49
|
+
/**
|
|
50
|
+
* Load the ReactLynx runtime bundle and wire its standard module globals.
|
|
51
|
+
*/
|
|
52
|
+
reactlynx?: boolean | ReactLynxExternalsPresetOptions;
|
|
53
|
+
/**
|
|
54
|
+
* Additional custom preset flags.
|
|
55
|
+
*/
|
|
56
|
+
[presetName: string]: boolean | object | undefined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Context passed to externals preset resolvers.
|
|
60
|
+
*
|
|
61
|
+
* @public
|
|
62
|
+
*/
|
|
63
|
+
export interface ExternalsPresetContext {
|
|
64
|
+
/**
|
|
65
|
+
* The current Rsbuild project root path.
|
|
66
|
+
*/
|
|
67
|
+
rootPath: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Definition for a named externals preset.
|
|
71
|
+
*
|
|
72
|
+
* @public
|
|
73
|
+
*/
|
|
74
|
+
export interface ExternalsPresetDefinition {
|
|
75
|
+
/**
|
|
76
|
+
* Other preset names to apply before the current preset.
|
|
77
|
+
*/
|
|
78
|
+
extends?: string | string[];
|
|
79
|
+
/**
|
|
80
|
+
* Resolve external request mappings contributed by this preset.
|
|
81
|
+
*/
|
|
82
|
+
resolveExternals?: (value: boolean | object, context: ExternalsPresetContext) => ExternalsLoadingPluginOptions['externals'];
|
|
83
|
+
/**
|
|
84
|
+
* Resolve managed bundle assets contributed by this preset.
|
|
85
|
+
*/
|
|
86
|
+
resolveManagedAssets?: (value: boolean | object, context: ExternalsPresetContext) => Map<string, string> | Record<string, string>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Available externals preset definitions.
|
|
90
|
+
*
|
|
91
|
+
* @public
|
|
92
|
+
*/
|
|
93
|
+
export type ExternalsPresetDefinitions = Record<string, ExternalsPresetDefinition>;
|
|
94
|
+
/**
|
|
95
|
+
* Built-in externals preset definitions.
|
|
96
|
+
*
|
|
97
|
+
* @public
|
|
98
|
+
*/
|
|
99
|
+
export declare const builtInExternalsPresetDefinitions: ExternalsPresetDefinitions;
|
|
8
100
|
/**
|
|
9
101
|
* Options for the external-bundle-rsbuild-plugin.
|
|
10
102
|
*
|
|
11
103
|
* @public
|
|
12
104
|
*/
|
|
13
|
-
export
|
|
105
|
+
export interface PluginExternalBundleOptions extends Pick<ExternalsLoadingPluginOptions, 'globalObject' | 'timeout'> {
|
|
106
|
+
/**
|
|
107
|
+
* Root directory that stores project-owned external bundles referenced by
|
|
108
|
+
* `bundlePath`.
|
|
109
|
+
*
|
|
110
|
+
* `pluginExternalBundle` uses this directory for both development serving
|
|
111
|
+
* and build-time asset emission. Prefer setting this explicitly when
|
|
112
|
+
* external bundles are built into a separate output folder, such as
|
|
113
|
+
* `dist-external-bundle`.
|
|
114
|
+
*/
|
|
115
|
+
externalBundleRoot?: string;
|
|
116
|
+
/**
|
|
117
|
+
* Additional explicit externals to load.
|
|
118
|
+
*/
|
|
119
|
+
externals?: Record<string, PluginExternalConfig>;
|
|
120
|
+
/**
|
|
121
|
+
* Presets for external libraries.
|
|
122
|
+
*
|
|
123
|
+
* Same as https://rspack.rs/config/externals#externalspresets but for Lynx.
|
|
124
|
+
*/
|
|
125
|
+
externalsPresets?: ExternalsPresets;
|
|
126
|
+
/**
|
|
127
|
+
* Definitions for custom externals presets enabled by `externalsPresets`.
|
|
128
|
+
*
|
|
129
|
+
* Use this to add business-specific presets such as `lynxUi`, or to extend a
|
|
130
|
+
* built-in preset through `extends`.
|
|
131
|
+
*/
|
|
132
|
+
externalsPresetDefinitions?: ExternalsPresetDefinitions;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* External bundle reference accepted by `pluginExternalBundle`.
|
|
136
|
+
*
|
|
137
|
+
* @public
|
|
138
|
+
*/
|
|
139
|
+
export interface PluginExternalValue extends Omit<ExternalValue, 'url'> {
|
|
140
|
+
/**
|
|
141
|
+
* Bundle path resolved against the runtime public path.
|
|
142
|
+
*
|
|
143
|
+
* Prefer this over `url` when the external bundle should be emitted or served
|
|
144
|
+
* as part of the current project. `pluginExternalBundle` can use this
|
|
145
|
+
* information to manage local bundle files, while the runtime keeps the final
|
|
146
|
+
* URL aligned with the active `publicPath`.
|
|
147
|
+
*/
|
|
148
|
+
bundlePath?: string;
|
|
149
|
+
/**
|
|
150
|
+
* Bundle URL.
|
|
151
|
+
*
|
|
152
|
+
* Use this only when the external bundle lives outside the current build
|
|
153
|
+
* output and should not be emitted or served by `pluginExternalBundle`.
|
|
154
|
+
*
|
|
155
|
+
* @deprecated Prefer `bundlePath`, which resolves through the runtime public
|
|
156
|
+
* path and lets higher-level tooling manage asset emission.
|
|
157
|
+
*/
|
|
158
|
+
url?: string;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* External bundle shorthand accepted by `pluginExternalBundle`.
|
|
162
|
+
*
|
|
163
|
+
* When a string is provided, it is treated as `bundlePath` and the plugin will
|
|
164
|
+
* infer:
|
|
165
|
+
* - `libraryName`: the external request key
|
|
166
|
+
* - `background.sectionPath`: the external request key
|
|
167
|
+
* - `mainThread.sectionPath`: `${request}__main-thread`
|
|
168
|
+
*
|
|
169
|
+
* @public
|
|
170
|
+
*/
|
|
171
|
+
export type PluginExternalConfig = PluginExternalValue | string;
|
|
172
|
+
/**
|
|
173
|
+
* Normalize a public bundle path by removing leading slashes.
|
|
174
|
+
*
|
|
175
|
+
* @public
|
|
176
|
+
*/
|
|
177
|
+
export declare function normalizeBundlePath(bundlePath: string): string;
|
|
14
178
|
/**
|
|
15
179
|
* Create a rsbuild plugin for loading external bundles.
|
|
16
180
|
*
|
|
@@ -29,7 +193,7 @@ export type PluginExternalBundleOptions = Pick<ExternalsLoadingPluginOptions, 'e
|
|
|
29
193
|
* pluginExternalBundle({
|
|
30
194
|
* externals: {
|
|
31
195
|
* lodash: {
|
|
32
|
-
*
|
|
196
|
+
* bundlePath: 'lodash.lynx.bundle',
|
|
33
197
|
* background: { sectionPath: 'background' },
|
|
34
198
|
* mainThread: { sectionPath: 'mainThread' },
|
|
35
199
|
* },
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,356 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*
|
|
7
|
+
* A rsbuild plugin for loading external bundles using externals-loading-webpack-plugin.
|
|
8
|
+
*/
|
|
9
|
+
import { createReadStream, existsSync, readFileSync } from 'node:fs';
|
|
10
|
+
import { createRequire } from 'node:module';
|
|
11
|
+
import path from 'node:path';
|
|
4
12
|
import { ExternalsLoadingPlugin } from '@lynx-js/externals-loading-webpack-plugin';
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
|
+
const DEFAULT_REACT_UMD_PACKAGE_NAME = '@lynx-js/react-umd';
|
|
15
|
+
const REACT_LYNX_BUNDLE_FILE_NAME = 'react.lynx.bundle';
|
|
16
|
+
const reactLynxExternalTemplate = {
|
|
17
|
+
'react': {
|
|
18
|
+
libraryName: ['ReactLynx', 'React'],
|
|
19
|
+
background: { sectionPath: 'ReactLynx' },
|
|
20
|
+
mainThread: { sectionPath: 'ReactLynx__main-thread' },
|
|
21
|
+
async: false,
|
|
22
|
+
},
|
|
23
|
+
'@lynx-js/react': {
|
|
24
|
+
libraryName: ['ReactLynx', 'React'],
|
|
25
|
+
background: { sectionPath: 'ReactLynx' },
|
|
26
|
+
mainThread: { sectionPath: 'ReactLynx__main-thread' },
|
|
27
|
+
async: false,
|
|
28
|
+
},
|
|
29
|
+
'@lynx-js/react/internal': {
|
|
30
|
+
libraryName: ['ReactLynx', 'ReactInternal'],
|
|
31
|
+
background: { sectionPath: 'ReactLynx' },
|
|
32
|
+
mainThread: { sectionPath: 'ReactLynx__main-thread' },
|
|
33
|
+
async: false,
|
|
34
|
+
},
|
|
35
|
+
'@lynx-js/react/jsx-dev-runtime': {
|
|
36
|
+
libraryName: ['ReactLynx', 'ReactJSXDevRuntime'],
|
|
37
|
+
background: { sectionPath: 'ReactLynx' },
|
|
38
|
+
mainThread: { sectionPath: 'ReactLynx__main-thread' },
|
|
39
|
+
async: false,
|
|
40
|
+
},
|
|
41
|
+
'@lynx-js/react/jsx-runtime': {
|
|
42
|
+
libraryName: ['ReactLynx', 'ReactJSXRuntime'],
|
|
43
|
+
background: { sectionPath: 'ReactLynx' },
|
|
44
|
+
mainThread: { sectionPath: 'ReactLynx__main-thread' },
|
|
45
|
+
async: false,
|
|
46
|
+
},
|
|
47
|
+
'@lynx-js/react/lepus/jsx-dev-runtime': {
|
|
48
|
+
libraryName: ['ReactLynx', 'ReactJSXLepusDevRuntime'],
|
|
49
|
+
background: { sectionPath: 'ReactLynx' },
|
|
50
|
+
mainThread: { sectionPath: 'ReactLynx__main-thread' },
|
|
51
|
+
async: false,
|
|
52
|
+
},
|
|
53
|
+
'@lynx-js/react/lepus/jsx-runtime': {
|
|
54
|
+
libraryName: ['ReactLynx', 'ReactJSXLepusRuntime'],
|
|
55
|
+
background: { sectionPath: 'ReactLynx' },
|
|
56
|
+
mainThread: { sectionPath: 'ReactLynx__main-thread' },
|
|
57
|
+
async: false,
|
|
58
|
+
},
|
|
59
|
+
'@lynx-js/react/experimental/lazy/import': {
|
|
60
|
+
libraryName: ['ReactLynx', 'ReactLazyImport'],
|
|
61
|
+
background: { sectionPath: 'ReactLynx' },
|
|
62
|
+
mainThread: { sectionPath: 'ReactLynx__main-thread' },
|
|
63
|
+
async: false,
|
|
64
|
+
},
|
|
65
|
+
'@lynx-js/react/legacy-react-runtime': {
|
|
66
|
+
libraryName: ['ReactLynx', 'ReactLegacyRuntime'],
|
|
67
|
+
background: { sectionPath: 'ReactLynx' },
|
|
68
|
+
mainThread: { sectionPath: 'ReactLynx__main-thread' },
|
|
69
|
+
async: false,
|
|
70
|
+
},
|
|
71
|
+
'@lynx-js/react/runtime-components': {
|
|
72
|
+
libraryName: ['ReactLynx', 'ReactComponents'],
|
|
73
|
+
background: { sectionPath: 'ReactLynx' },
|
|
74
|
+
mainThread: { sectionPath: 'ReactLynx__main-thread' },
|
|
75
|
+
async: false,
|
|
76
|
+
},
|
|
77
|
+
'@lynx-js/react/worklet-runtime/bindings': {
|
|
78
|
+
libraryName: ['ReactLynx', 'ReactWorkletRuntime'],
|
|
79
|
+
background: { sectionPath: 'ReactLynx' },
|
|
80
|
+
mainThread: { sectionPath: 'ReactLynx__main-thread' },
|
|
81
|
+
async: false,
|
|
82
|
+
},
|
|
83
|
+
'@lynx-js/react/debug': {
|
|
84
|
+
libraryName: ['ReactLynx', 'ReactDebug'],
|
|
85
|
+
background: { sectionPath: 'ReactLynx' },
|
|
86
|
+
mainThread: { sectionPath: 'ReactLynx__main-thread' },
|
|
87
|
+
async: false,
|
|
88
|
+
},
|
|
89
|
+
preact: {
|
|
90
|
+
libraryName: ['ReactLynx', 'Preact'],
|
|
91
|
+
background: { sectionPath: 'ReactLynx' },
|
|
92
|
+
mainThread: { sectionPath: 'ReactLynx__main-thread' },
|
|
93
|
+
async: false,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
function toManagedBundleAssets(assets) {
|
|
97
|
+
if (!assets) {
|
|
98
|
+
return new Map();
|
|
99
|
+
}
|
|
100
|
+
if (assets instanceof Map) {
|
|
101
|
+
return new Map(assets);
|
|
102
|
+
}
|
|
103
|
+
return new Map(Object.entries(assets));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Built-in externals preset definitions.
|
|
107
|
+
*
|
|
108
|
+
* @public
|
|
109
|
+
*/
|
|
110
|
+
export const builtInExternalsPresetDefinitions = createBuiltInExternalsPresetDefinitions();
|
|
111
|
+
function normalizeReactLynxPreset(preset) {
|
|
112
|
+
if (!preset) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
return preset === true ? {} : preset;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Normalize a public bundle path by removing leading slashes.
|
|
119
|
+
*
|
|
120
|
+
* @public
|
|
121
|
+
*/
|
|
122
|
+
export function normalizeBundlePath(bundlePath) {
|
|
123
|
+
return bundlePath.replace(/^\/+/, '');
|
|
124
|
+
}
|
|
125
|
+
function createBuiltInExternalsPresetDefinitions() {
|
|
126
|
+
return {
|
|
127
|
+
reactlynx: {
|
|
128
|
+
resolveExternals(value) {
|
|
129
|
+
return createReactLynxExternals(normalizeReactLynxPreset(value));
|
|
130
|
+
},
|
|
131
|
+
resolveManagedAssets(value, context) {
|
|
132
|
+
const preset = normalizeReactLynxPreset(value);
|
|
133
|
+
if (!preset || preset.url) {
|
|
134
|
+
return new Map();
|
|
135
|
+
}
|
|
136
|
+
return new Map([
|
|
137
|
+
[
|
|
138
|
+
getDefaultReactLynxBundlePath(preset),
|
|
139
|
+
getReactLynxBundlePath(context.rootPath, preset.reactUmdPackageName ?? DEFAULT_REACT_UMD_PACKAGE_NAME),
|
|
140
|
+
],
|
|
141
|
+
]);
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function getReactLynxBundlePath(rootPath, reactUmdPackageName) {
|
|
147
|
+
const reactUmdExport = process.env['NODE_ENV'] === 'production'
|
|
148
|
+
? `${reactUmdPackageName}/prod`
|
|
149
|
+
: `${reactUmdPackageName}/dev`;
|
|
150
|
+
try {
|
|
151
|
+
return require.resolve(reactUmdExport, { paths: [rootPath] });
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
throw new Error(`external-bundle-rsbuild-plugin requires \`${reactUmdExport}\` when \`externalsPresets.reactlynx\` is enabled. Install a compatible \`${reactUmdPackageName}\` into your devDependencies.`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function mergePresetDefinitions(baseDefinition, extraDefinition) {
|
|
158
|
+
const baseExtends = baseDefinition.extends
|
|
159
|
+
? (Array.isArray(baseDefinition.extends)
|
|
160
|
+
? baseDefinition.extends
|
|
161
|
+
: [baseDefinition.extends])
|
|
162
|
+
: [];
|
|
163
|
+
const extraExtends = extraDefinition.extends
|
|
164
|
+
? (Array.isArray(extraDefinition.extends)
|
|
165
|
+
? extraDefinition.extends
|
|
166
|
+
: [extraDefinition.extends])
|
|
167
|
+
: [];
|
|
168
|
+
return {
|
|
169
|
+
extends: [...baseExtends, ...extraExtends],
|
|
170
|
+
resolveExternals(value, context) {
|
|
171
|
+
return {
|
|
172
|
+
...(baseDefinition.resolveExternals?.(value, context) ?? {}),
|
|
173
|
+
...(extraDefinition.resolveExternals?.(value, context) ?? {}),
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
resolveManagedAssets(value, context) {
|
|
177
|
+
const assets = toManagedBundleAssets(baseDefinition.resolveManagedAssets?.(value, context));
|
|
178
|
+
for (const [bundlePath, sourcePath] of toManagedBundleAssets(extraDefinition.resolveManagedAssets?.(value, context))) {
|
|
179
|
+
assets.set(bundlePath, sourcePath);
|
|
180
|
+
}
|
|
181
|
+
return assets;
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function resolvePresetDefinitions(presetDefinitions) {
|
|
186
|
+
const resolvedDefinitions = {
|
|
187
|
+
...createBuiltInExternalsPresetDefinitions(),
|
|
188
|
+
};
|
|
189
|
+
for (const [presetName, presetDefinition] of Object.entries(presetDefinitions ?? {})) {
|
|
190
|
+
const builtInDefinition = builtInExternalsPresetDefinitions[presetName];
|
|
191
|
+
resolvedDefinitions[presetName] = builtInDefinition
|
|
192
|
+
? mergePresetDefinitions(builtInDefinition, presetDefinition)
|
|
193
|
+
: presetDefinition;
|
|
194
|
+
}
|
|
195
|
+
return resolvedDefinitions;
|
|
196
|
+
}
|
|
197
|
+
function resolvePresetResult(presetName, presetValue, presetDefinitions, resolving, context) {
|
|
198
|
+
const presetDefinition = presetDefinitions[presetName];
|
|
199
|
+
if (!presetDefinition) {
|
|
200
|
+
throw new Error(`Unknown externals preset "${presetName}". Define it in \`externalsPresetDefinitions\` before enabling it in \`externalsPresets\`.`);
|
|
201
|
+
}
|
|
202
|
+
if (resolving.includes(presetName)) {
|
|
203
|
+
throw new Error(`Circular externals preset dependency detected: ${[...resolving, presetName].join(' -> ')}`);
|
|
204
|
+
}
|
|
205
|
+
const externals = {};
|
|
206
|
+
const managedAssets = new Map();
|
|
207
|
+
const nextResolving = [...resolving, presetName];
|
|
208
|
+
const inheritedPresetNames = presetDefinition.extends
|
|
209
|
+
? (Array.isArray(presetDefinition.extends)
|
|
210
|
+
? presetDefinition.extends
|
|
211
|
+
: [presetDefinition.extends])
|
|
212
|
+
: [];
|
|
213
|
+
for (const inheritedPresetName of inheritedPresetNames) {
|
|
214
|
+
const inheritedResult = resolvePresetResult(inheritedPresetName, true, presetDefinitions, nextResolving, context);
|
|
215
|
+
Object.assign(externals, inheritedResult.externals);
|
|
216
|
+
for (const [bundlePath, sourcePath] of inheritedResult.managedAssets) {
|
|
217
|
+
managedAssets.set(bundlePath, sourcePath);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
Object.assign(externals, presetDefinition.resolveExternals?.(presetValue, context) ?? {});
|
|
221
|
+
for (const [bundlePath, sourcePath] of toManagedBundleAssets(presetDefinition.resolveManagedAssets?.(presetValue, context))) {
|
|
222
|
+
managedAssets.set(bundlePath, sourcePath);
|
|
223
|
+
}
|
|
224
|
+
return { externals, managedAssets };
|
|
225
|
+
}
|
|
226
|
+
function resolvePresetExternals(externalsPresets, presetDefinitions, context) {
|
|
227
|
+
const externals = {};
|
|
228
|
+
const managedAssets = new Map();
|
|
229
|
+
for (const [presetName, presetValue] of Object.entries(externalsPresets ?? {})) {
|
|
230
|
+
if (!presetValue) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const resolvedPreset = resolvePresetResult(presetName, presetValue, presetDefinitions, [], context);
|
|
234
|
+
Object.assign(externals, resolvedPreset.externals);
|
|
235
|
+
for (const [bundlePath, sourcePath] of resolvedPreset.managedAssets) {
|
|
236
|
+
managedAssets.set(bundlePath, sourcePath);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return { externals, managedAssets };
|
|
240
|
+
}
|
|
241
|
+
function createReactLynxExternals(preset) {
|
|
242
|
+
const bundleReference = preset?.url
|
|
243
|
+
? { url: preset.url }
|
|
244
|
+
: { bundlePath: getDefaultReactLynxBundlePath(preset) };
|
|
245
|
+
return Object.fromEntries(Object.entries(reactLynxExternalTemplate).map(([request, external]) => [
|
|
246
|
+
request,
|
|
247
|
+
{
|
|
248
|
+
...external,
|
|
249
|
+
...bundleReference,
|
|
250
|
+
},
|
|
251
|
+
]));
|
|
252
|
+
}
|
|
253
|
+
class EmitManagedBundleAssetsPlugin {
|
|
254
|
+
assets;
|
|
255
|
+
constructor(assets) {
|
|
256
|
+
this.assets = assets;
|
|
257
|
+
}
|
|
258
|
+
apply(compiler) {
|
|
259
|
+
compiler.hooks.thisCompilation.tap(EmitManagedBundleAssetsPlugin.name, (compilation) => {
|
|
260
|
+
compilation.hooks.processAssets.tap({
|
|
261
|
+
name: EmitManagedBundleAssetsPlugin.name,
|
|
262
|
+
// Emit managed bundle files after optimization so binary template
|
|
263
|
+
// assets such as `*.template.js` are not treated as JS chunks by
|
|
264
|
+
// later minification stages.
|
|
265
|
+
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT,
|
|
266
|
+
}, () => {
|
|
267
|
+
const { RawSource } = compiler.webpack.sources;
|
|
268
|
+
for (const [bundlePath, sourcePath] of this.assets) {
|
|
269
|
+
if (compilation.getAsset(bundlePath)) {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
if (!existsSync(sourcePath)) {
|
|
273
|
+
throw new Error(`external-bundle-rsbuild-plugin could not find local bundle \`${sourcePath}\` for emitted asset \`${bundlePath}\`.`);
|
|
274
|
+
}
|
|
275
|
+
compilation.emitAsset(bundlePath, new RawSource(readFileSync(sourcePath), false));
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function getDefaultReactLynxBundlePath(preset) {
|
|
282
|
+
return normalizeBundlePath(preset?.bundlePath ?? REACT_LYNX_BUNDLE_FILE_NAME);
|
|
283
|
+
}
|
|
284
|
+
function joinUrlPath(base, bundlePath) {
|
|
285
|
+
const normalizedBase = base ?? '/';
|
|
286
|
+
const normalizedBundlePath = normalizeBundlePath(bundlePath);
|
|
287
|
+
if (normalizedBase === '/') {
|
|
288
|
+
return `/${normalizedBundlePath}`;
|
|
289
|
+
}
|
|
290
|
+
if (/^[a-z][a-z\d+\-.]*:/i.test(normalizedBase)) {
|
|
291
|
+
try {
|
|
292
|
+
return new URL(normalizedBundlePath, normalizedBase).toString();
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
return `${normalizedBase.replace(/\/$/, '')}/${normalizedBundlePath}`;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return `${normalizedBase.replace(/\/$/, '')}/${normalizedBundlePath}`;
|
|
299
|
+
}
|
|
300
|
+
function getExternalBundleRoot(options, api) {
|
|
301
|
+
return path.resolve(api.context.rootPath, options.externalBundleRoot ?? 'dist-external-bundle');
|
|
302
|
+
}
|
|
303
|
+
function getManagedBundleAssets(options, presetManagedAssets, api) {
|
|
304
|
+
const assets = new Map(presetManagedAssets);
|
|
305
|
+
const externalBundleRoot = getExternalBundleRoot(options, api);
|
|
306
|
+
for (const [request, rawExternal] of Object.entries(options.externals ?? {})) {
|
|
307
|
+
const external = normalizePluginExternal(request, rawExternal);
|
|
308
|
+
if (external.url || !external.bundlePath) {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
assets.set(normalizeBundlePath(external.bundlePath), path.resolve(externalBundleRoot, normalizeBundlePath(external.bundlePath)));
|
|
312
|
+
}
|
|
313
|
+
return assets;
|
|
314
|
+
}
|
|
315
|
+
function getLocalBundleAssets(options, presetManagedAssets, api, serverBase) {
|
|
316
|
+
const assets = new Map();
|
|
317
|
+
for (const [bundlePath, sourcePath] of getManagedBundleAssets(options, presetManagedAssets, api)) {
|
|
318
|
+
assets.set(joinUrlPath(serverBase, bundlePath), sourcePath);
|
|
319
|
+
}
|
|
320
|
+
return assets;
|
|
321
|
+
}
|
|
322
|
+
function resolvePluginExternals(externals) {
|
|
323
|
+
if (!externals) {
|
|
324
|
+
return {};
|
|
325
|
+
}
|
|
326
|
+
for (const [request, external] of Object.entries(externals)) {
|
|
327
|
+
const normalizedExternal = normalizePluginExternal(request, external);
|
|
328
|
+
if (normalizedExternal.url || normalizedExternal.bundlePath) {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
throw new Error(`external-bundle-rsbuild-plugin requires \`url\` or \`bundlePath\` for external "${request}".`);
|
|
332
|
+
}
|
|
333
|
+
return Object.fromEntries(Object.entries(externals).map(([request, external]) => [
|
|
334
|
+
request,
|
|
335
|
+
normalizePluginExternal(request, external),
|
|
336
|
+
]));
|
|
337
|
+
}
|
|
338
|
+
function normalizePluginExternal(request, external) {
|
|
339
|
+
if (typeof external !== 'string') {
|
|
340
|
+
return external;
|
|
341
|
+
}
|
|
342
|
+
return {
|
|
343
|
+
bundlePath: external,
|
|
344
|
+
libraryName: request,
|
|
345
|
+
background: {
|
|
346
|
+
sectionPath: request,
|
|
347
|
+
},
|
|
348
|
+
mainThread: {
|
|
349
|
+
sectionPath: `${request}__main-thread`,
|
|
350
|
+
},
|
|
351
|
+
async: true,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
5
354
|
/**
|
|
6
355
|
* Create a rsbuild plugin for loading external bundles.
|
|
7
356
|
*
|
|
@@ -20,7 +369,7 @@ import { ExternalsLoadingPlugin } from '@lynx-js/externals-loading-webpack-plugi
|
|
|
20
369
|
* pluginExternalBundle({
|
|
21
370
|
* externals: {
|
|
22
371
|
* lodash: {
|
|
23
|
-
*
|
|
372
|
+
* bundlePath: 'lodash.lynx.bundle',
|
|
24
373
|
* background: { sectionPath: 'background' },
|
|
25
374
|
* mainThread: { sectionPath: 'mainThread' },
|
|
26
375
|
* },
|
|
@@ -36,18 +385,58 @@ export function pluginExternalBundle(options) {
|
|
|
36
385
|
return {
|
|
37
386
|
name: 'lynx:external-bundle',
|
|
38
387
|
setup(api) {
|
|
388
|
+
const presetDefinitions = resolvePresetDefinitions(options.externalsPresetDefinitions);
|
|
389
|
+
const presetResolution = resolvePresetExternals(options.externalsPresets, presetDefinitions, {
|
|
390
|
+
rootPath: api.context.rootPath,
|
|
391
|
+
});
|
|
392
|
+
api.modifyRsbuildConfig((config, { mergeRsbuildConfig }) => {
|
|
393
|
+
const localBundleAssets = getLocalBundleAssets(options, presetResolution.managedAssets, api, config.server?.base);
|
|
394
|
+
if (localBundleAssets.size === 0) {
|
|
395
|
+
return config;
|
|
396
|
+
}
|
|
397
|
+
return mergeRsbuildConfig(config, {
|
|
398
|
+
dev: {
|
|
399
|
+
setupMiddlewares: [
|
|
400
|
+
(middlewares) => {
|
|
401
|
+
middlewares.unshift((req, res, next) => {
|
|
402
|
+
const bundlePath = req.url
|
|
403
|
+
? localBundleAssets.get(req.url)
|
|
404
|
+
: undefined;
|
|
405
|
+
if (bundlePath && existsSync(bundlePath)) {
|
|
406
|
+
res.setHeader('Content-Type', 'application/octet-stream');
|
|
407
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
408
|
+
createReadStream(bundlePath).pipe(res);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
next();
|
|
412
|
+
});
|
|
413
|
+
return middlewares;
|
|
414
|
+
},
|
|
415
|
+
],
|
|
416
|
+
},
|
|
417
|
+
});
|
|
418
|
+
});
|
|
39
419
|
api.modifyRspackConfig((config) => {
|
|
40
|
-
// Get layer names from react-rsbuild-plugin
|
|
41
420
|
const LAYERS = api.useExposed(Symbol.for('LAYERS'));
|
|
42
421
|
if (!LAYERS) {
|
|
43
|
-
throw new Error('external-bundle-rsbuild-plugin requires exposed `LAYERS`.');
|
|
422
|
+
throw new Error('external-bundle-rsbuild-plugin requires exposed `LAYERS`. Please install a DSL plugin, for example `pluginReactLynx` for ReactLynx.');
|
|
44
423
|
}
|
|
424
|
+
const explicitExternals = resolvePluginExternals(options.externals);
|
|
425
|
+
const externals = {
|
|
426
|
+
...presetResolution.externals,
|
|
427
|
+
...explicitExternals,
|
|
428
|
+
};
|
|
429
|
+
const managedBundleAssets = getManagedBundleAssets(options, presetResolution.managedAssets, api);
|
|
45
430
|
config.plugins = config.plugins || [];
|
|
431
|
+
if (managedBundleAssets.size > 0) {
|
|
432
|
+
config.plugins.push(new EmitManagedBundleAssetsPlugin(managedBundleAssets));
|
|
433
|
+
}
|
|
46
434
|
config.plugins.push(new ExternalsLoadingPlugin({
|
|
47
435
|
backgroundLayer: LAYERS.BACKGROUND,
|
|
48
436
|
mainThreadLayer: LAYERS.MAIN_THREAD,
|
|
49
|
-
externals
|
|
437
|
+
externals,
|
|
50
438
|
globalObject: options.globalObject,
|
|
439
|
+
timeout: options.timeout,
|
|
51
440
|
}));
|
|
52
441
|
return config;
|
|
53
442
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lynx-js/external-bundle-rsbuild-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "An rsbuild plugin for loading lynx external bundles.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rsbuild",
|
|
@@ -34,11 +34,12 @@
|
|
|
34
34
|
"README.md"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@lynx-js/externals-loading-webpack-plugin": "0.
|
|
37
|
+
"@lynx-js/externals-loading-webpack-plugin": "0.1.1"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@microsoft/api-extractor": "7.
|
|
41
|
-
"@rsbuild/core": "1.7.
|
|
40
|
+
"@microsoft/api-extractor": "7.58.2",
|
|
41
|
+
"@rsbuild/core": "1.7.5",
|
|
42
|
+
"@lynx-js/react-umd": "0.119.0"
|
|
42
43
|
},
|
|
43
44
|
"engines": {
|
|
44
45
|
"node": ">=18"
|