Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/deep-heads-talk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lynx-js/web-explorer": patch
---

feat: use nativeModulesPath instead of nativeModulesMap to lynx-view.
40 changes: 40 additions & 0 deletions .changeset/hot-adults-wear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
"@lynx-js/web-platform-rsbuild-plugin": minor
---

feat: add new parameter `nativeModulesPath` to `pluginWebPlatform({})`.

After this commit, you can use `nativeModulesPath` to package custom nativeModules directly into the worker, and no longer need to pass `nativeModulesMap` to lynx-view.

Here is an example:

- `native-modules.ts`:

```ts
// index.native-modules.ts
export default {
CustomModule: function(NativeModules, NativeModulesCall) {
return {
async getColor(data, callback) {
const color = await NativeModulesCall('getColor', data);
callback(color);
},
};
},
};
```

- plugin config:

```ts
// rsbuild.config.ts
import { pluginWebPlatform } from '@lynx-js/web-platform-rsbuild-plugin';
import { defineConfig } from '@rsbuild/core';

export default defineConfig({
plugins: [pluginWebPlatform({
// replace with your actual native-modules file path
nativeModulesPath: path.resolve(__dirname, './index.native-modules.ts'),
})],
});
```
5 changes: 5 additions & 0 deletions .changeset/thick-pots-clap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lynx-js/web-worker-runtime": patch
---

feat: provide comments for `@lynx-js/web-platform-rsbuild-plugin`.
1 change: 1 addition & 0 deletions packages/web-platform/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
{ "path": "./web-worker-rpc/tsconfig.json" },
{ "path": "./web-mainthread-apis/tsconfig.json" },
{ "path": "./web-core/tsconfig.json" },
{ "path": "./web-rsbuild-plugin/tsconfig.json" },
/** packages-end */
],
"include": [],
Expand Down
12 changes: 12 additions & 0 deletions packages/web-platform/web-explorer/index.native-modules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default {
ExplorerModule: function(NativeModules, NativeModulesCall) {
return {
openSchema(value) {
NativeModulesCall('openSchema', value);
},
openScan() {
NativeModulesCall('openScan');
},
};
},
};
19 changes: 0 additions & 19 deletions packages/web-platform/web-explorer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,6 @@ const qrScanner = new QrScanner(video, (result) => {
highlightCodeOutline: true,
});

const nativeModulesMap = {
ExplorerModule: URL.createObjectURL(
new Blob(
[`export default function(NativeModules, NativeModulesCall) {
return {
openSchema(value) {
NativeModulesCall('openSchema', value);
},
openScan() {
NativeModulesCall('openScan');
},
};
}`],
{ type: 'text/javascript' },
),
),
};

setLynxViewUrl(homepage);
window.addEventListener('message', (ev) => {
if (ev.data && ev.data.method === 'setLynxViewUrl' && ev.data.url) {
Expand All @@ -65,7 +47,6 @@ function setLynxViewUrl(url: string) {
const theme = window.matchMedia('(prefers-color-scheme: dark)').matches
? 'Dark'
: 'Light';
lynxView.nativeModulesMap = nativeModulesMap;
lynxView.onNativeModulesCall = (nm, data) => {
if (nm === 'openScan') {
lynxView.style.visibility = 'hidden';
Expand Down
8 changes: 7 additions & 1 deletion packages/web-platform/web-explorer/rsbuild.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { defineConfig } from '@rsbuild/core';
import { codecovWebpackPlugin } from '@codecov/webpack-plugin';
import { pluginWebPlatform } from '@lynx-js/web-platform-rsbuild-plugin';
import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin';
import path from 'path';
const codecovEnabled = !!process.env.CI;
console.info('codecov enabled:', codecovEnabled);
export default defineConfig({
Expand Down Expand Up @@ -56,5 +57,10 @@ export default defineConfig({
},
profile: true,
},
plugins: [pluginWebPlatform({ polyfill: false })],
plugins: [
pluginWebPlatform({
polyfill: false,
nativeModulesPath: path.resolve(__dirname, './index.native-modules.ts'),
}),
],
});
48 changes: 46 additions & 2 deletions packages/web-platform/web-rsbuild-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,55 @@ Lynx3 Web Platform rsbuild plugin

## Usage

```javascript
```ts
import { pluginWebPlatform } from '@lynx-js/web-platform-rsbuild-plugin';
import { defineConfig } from '@rsbuild/core';

export default defineConfig({
plugins: [pluginWebPlatform()],
plugins: [pluginWebPlatform({
// replace with your actual native-modules file path
nativeModulesPath: path.resolve(__dirname, './index.native-modules.ts'),
})],
});
```

## Options

```ts
{
/**
* Whether to polyfill the packages about Lynx Web Platform.
*
* If it is true, @lynx-js will be compiled and polyfills will be added.
*
* @default true
*/
polyfill?: boolean;
/**
* The absolute path of the native-modules file.
*
* If you use it, you don't need to pass nativeModulesMap in the lynx-view tag, otherwise it will cause duplicate packaging.
*
* When enabled, nativeModules will be packaged directly into the worker chunk instead of being transferred through Blob.
*/
nativeModulesPath?: string;
}
```

### nativeModulesPath

`native-modules.ts` example:

```ts
// index.native-modules.ts
export default {
CustomModule: function(NativeModules, NativeModulesCall) {
return {
async getColor(data, callback) {
const color = await NativeModulesCall('getColor', data);
callback(color);
},
};
},
};
```
12 changes: 8 additions & 4 deletions packages/web-platform/web-rsbuild-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,22 @@
"README.md"
],
"scripts": {
"build": "rslib build",
"dev": "rslib build --watch",
"test": "vitest"
},
"dependencies": {
"loader-utils": "^3.3.1"
},
"devDependencies": {
"@microsoft/api-extractor": "catalog:",
"@lynx-js/web-core": "workspace:*",
"@lynx-js/web-elements": "workspace:*",
"@rsbuild/core": "catalog:rsbuild",
"@rslib/core": "^0.6.7",
"@types/loader-utils": "^2.0.6",
"@types/webpack": "^5.28.5",
"typescript": "^5.8.3",
"vitest": "^3.1.2"
},
"peerDependencies": {
"@lynx-js/web-core": ">0.13.0",
"@rsbuild/core": "*"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { LoaderContext } from 'webpack';

interface NativeModulesLoaderOptions {
nativeModulesPath: string;
}

export default function(
this: LoaderContext<NativeModulesLoaderOptions>,
source: string,
) {
const options = this.getOptions();
const { nativeModulesPath } = options;
const modifiedSource = source.replace(
/\/\* LYNX_NATIVE_MODULES_IMPORT \*\//g,
`import CUSTOM_NATIVE_MODULES from '${nativeModulesPath}';`,
).replace(
/\/\* LYNX_NATIVE_MODULES_ADD \*\//g,
`Object.entries(CUSTOM_NATIVE_MODULES).map(([moduleName, moduleFunc]) => {
customNativeModules[moduleName] = moduleFunc(
nativeModules,
(name, data) =>
nativeModulesCall(name, data, moduleName),
);
});`,
);

return modifiedSource;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
// LICENSE file in the root directory of this source tree.

import type { RsbuildPlugin } from '@rsbuild/core';
import path from 'path';

const __filename = new URL('', import.meta.url).pathname;
const __dirname = path.dirname(__filename);

/**
* The options for {@link pluginWebPlatform}.
Expand All @@ -13,11 +17,19 @@ export interface PluginWebPlatformOptions {
/**
* Whether to polyfill the packages about Lynx Web Platform.
*
* If it is true, @lynx-js will be compiled and polyfills will be added
* If it is true, @lynx-js will be compiled and polyfills will be added.
*
* @default true
*/
polyfill?: boolean;
/**
* The absolute path of the native-modules file.
*
* If you use it, you don't need to pass nativeModulesMap in the lynx-view tag, otherwise it will cause duplicate packaging.
*
* When enabled, nativeModules will be packaged directly into the worker chunk instead of being transferred through Blob.
*/
nativeModulesPath?: string;
}

/**
Expand All @@ -30,7 +42,10 @@ export interface PluginWebPlatformOptions {
* import { defineConfig } from '@rsbuild/core';
*
* export default defineConfig({
* plugins: [pluginWebPlatform()],
* plugins: [pluginWebPlatform({
* // replace with your actual native-modules file path
* nativeModulesPath: path.resolve(__dirname, './index.native-modules.ts'),
* })],
* })
* ```
*
Expand All @@ -47,6 +62,15 @@ export function pluginWebPlatform(
};
const options = Object.assign({}, defaultPluginOptions, userOptions);

if (
options.nativeModulesPath !== undefined
&& !path.isAbsolute(options.nativeModulesPath)
) {
throw new Error(
'options.nativeModulesPath must be an absolute path.',
);
}

api.modifyRsbuildConfig(config => {
if (options.polyfill === true) {
config.source = {
Expand All @@ -62,6 +86,30 @@ export function pluginWebPlatform(
};
}
});

api.modifyRspackConfig(rspackConfig => {
console.log(path.resolve(
__dirname,
'./loaders/native-modules.js',
));
rspackConfig.module = {
...rspackConfig.module,
rules: [
...(rspackConfig.module?.rules ?? []),
{
test:
/backgroundThread\/background-apis\/createNativeModules\.js$/,
loader: path.resolve(
__dirname,
'./loaders/native-modules.js',
),
options: {
nativeModulesPath: options.nativeModulesPath,
},
},
],
};
});
},
};
}
63 changes: 63 additions & 0 deletions packages/web-platform/web-rsbuild-plugin/tests/bundle.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { createRsbuild } from '@rsbuild/core';
import { describe, expect, test, vi } from 'vitest';
import { pluginWebPlatform } from '../dist/index.js';
import path from 'path';
import type { Stats, NormalModule } from '@rspack/core';

describe('Bundle Build', () => {
test('native-modules bundle', async () => {
const rsbuild = await createRsbuild({
rsbuildConfig: {
source: {
entry: {
main: path.resolve(__dirname, './fixtures/index.ts'),
},
},
output: {
distPath: {
root: path.resolve(__dirname, './dist/native-modules-bundle'),
},
},
plugins: [
pluginWebPlatform({
nativeModulesPath: path.resolve(
__dirname,
'./fixtures/index.native-modules.ts',
),
}),
],
performance: {
chunkSplit: {
strategy: 'all-in-one',
},
},
},
});

let asyncChunkImportCount = 0;
let syncChunkImportCount = 0;
await rsbuild.initConfigs();
const buildInfo = await rsbuild.build();
for (
const i of (buildInfo.stats as Stats).compilation.chunks.values() || []
) {
const modules = (buildInfo.stats as Stats).compilation.chunkGraph
.getChunkModules(i) as NormalModule[];

for (const m of modules) {
if (
m.type === 'javascript/auto'
&& m.userRequest.includes('tests/fixtures/index.native-modules.ts')
) {
if (!i.isOnlyInitial()) {
asyncChunkImportCount++;
} else {
syncChunkImportCount++;
}
}
}
}
expect(asyncChunkImportCount).toBe(1);
expect(syncChunkImportCount).toBe(0);
});
});
Loading
Loading