From f2ee106b5251c7a6a5eb5829d30a45783df2a2e0 Mon Sep 17 00:00:00 2001 From: Qingyu Wang <40660121+colinaaa@users.noreply.github.com> Date: Wed, 26 Mar 2025 22:48:59 +0800 Subject: [PATCH 1/2] fix(rspeedy/core): chunk loading on Web platform --- .changeset/shy-pears-serve.md | 5 ++ .../core/src/plugins/chunkLoading.plugin.ts | 33 ++++++--- .../rspeedy/core/src/plugins/target.plugin.ts | 13 +++- packages/rspeedy/core/src/utils/is-lynx.ts | 11 +++ packages/rspeedy/core/src/utils/is-web.ts | 11 +++ .../test/plugins/chunkLoading.plugin.test.ts | 67 +++++++++++++++++++ .../core/test/plugins/target.plugin.test.ts | 26 +++++++ 7 files changed, 155 insertions(+), 11 deletions(-) create mode 100644 .changeset/shy-pears-serve.md create mode 100644 packages/rspeedy/core/src/utils/is-lynx.ts create mode 100644 packages/rspeedy/core/src/utils/is-web.ts diff --git a/.changeset/shy-pears-serve.md b/.changeset/shy-pears-serve.md new file mode 100644 index 0000000000..924d078361 --- /dev/null +++ b/.changeset/shy-pears-serve.md @@ -0,0 +1,5 @@ +--- +"@lynx-js/rspeedy": patch +--- + +Use `chunkLoading: 'import-scripts'` for Web platform diff --git a/packages/rspeedy/core/src/plugins/chunkLoading.plugin.ts b/packages/rspeedy/core/src/plugins/chunkLoading.plugin.ts index d254704783..38353ee60b 100644 --- a/packages/rspeedy/core/src/plugins/chunkLoading.plugin.ts +++ b/packages/rspeedy/core/src/plugins/chunkLoading.plugin.ts @@ -6,13 +6,25 @@ import type { RsbuildPlugin } from '@rsbuild/core' import { ChunkLoadingWebpackPlugin } from '@lynx-js/chunk-loading-webpack-plugin' +import { isLynx } from '../utils/is-lynx.js' +import { isWeb } from '../utils/is-web.js' + export function pluginChunkLoading(): RsbuildPlugin { return { name: 'lynx:rsbuild:chunk-loading', setup(api) { - api.modifyBundlerChain(chain => { - // dprint-ignore - chain + api.modifyBundlerChain((chain, { environment }) => { + if (isWeb(environment)) { + chain + .output + .chunkLoading('import-scripts') + .end() + return + } + + if (isLynx(environment)) { + // dprint-ignore + chain .plugin('lynx:chunk-loading') .use(ChunkLoadingWebpackPlugin) .end() @@ -23,14 +35,17 @@ export function pluginChunkLoading(): RsbuildPlugin { .chunkFormat('commonjs') .iife(false) .end() + } }) - api.modifyWebpackChain(chain => { - chain - .output - // For webpack, we directly use `chunkLoading: 'lynx'`. - .chunkLoading('lynx') - .end() + api.modifyWebpackChain((chain, { environment }) => { + if (isLynx(environment)) { + chain + .output + // For webpack, we directly use `chunkLoading: 'lynx'`. + .chunkLoading('lynx') + .end() + } }) }, } diff --git a/packages/rspeedy/core/src/plugins/target.plugin.ts b/packages/rspeedy/core/src/plugins/target.plugin.ts index 69c2c60432..68e0b5b3de 100644 --- a/packages/rspeedy/core/src/plugins/target.plugin.ts +++ b/packages/rspeedy/core/src/plugins/target.plugin.ts @@ -5,13 +5,22 @@ import type { RsbuildPlugin } from '@rsbuild/core' import { getESVersionTarget } from '../utils/getESVersionTarget.js' +import { isWeb } from '../utils/is-web.js' export function pluginTarget(): RsbuildPlugin { return { name: 'lynx:rsbuild:target', setup(api) { - api.modifyBundlerChain((options) => { - options.target([getESVersionTarget()]) + api.modifyBundlerChain((options, { environment }) => { + if (isWeb(environment)) { + options.target([ + getESVersionTarget(), + // Add `target: 'web'` to make Rsbuild inject HMR related code. + 'web', + ]) + } else { + options.target([getESVersionTarget()]) + } }) }, } diff --git a/packages/rspeedy/core/src/utils/is-lynx.ts b/packages/rspeedy/core/src/utils/is-lynx.ts new file mode 100644 index 0000000000..94bfe87c58 --- /dev/null +++ b/packages/rspeedy/core/src/utils/is-lynx.ts @@ -0,0 +1,11 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +import type { EnvironmentContext } from '@rsbuild/core' + +export function isLynx(environment: EnvironmentContext | string): boolean { + return typeof environment === 'string' + ? environment === 'lynx' + : environment.name === 'lynx' +} diff --git a/packages/rspeedy/core/src/utils/is-web.ts b/packages/rspeedy/core/src/utils/is-web.ts new file mode 100644 index 0000000000..0fd862b39d --- /dev/null +++ b/packages/rspeedy/core/src/utils/is-web.ts @@ -0,0 +1,11 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +import type { EnvironmentContext } from '@rsbuild/core' + +export function isWeb(environment: EnvironmentContext | string): boolean { + return typeof environment === 'string' + ? environment === 'web' + : environment.name === 'web' +} diff --git a/packages/rspeedy/core/test/plugins/chunkLoading.plugin.test.ts b/packages/rspeedy/core/test/plugins/chunkLoading.plugin.test.ts index a9a6ee245b..9441909dd7 100644 --- a/packages/rspeedy/core/test/plugins/chunkLoading.plugin.test.ts +++ b/packages/rspeedy/core/test/plugins/chunkLoading.plugin.test.ts @@ -36,4 +36,71 @@ describe('Plugins - chunkLoading', () => { ), ).toBeTruthy() }) + + describe('Web', () => { + test('Rspack', async () => { + const rspeedy = await createStubRspeedy({ + environments: { + web: {}, + }, + }) + + const config = await rspeedy.unwrapConfig() + + expect(config.output?.chunkLoading).toBe('import-scripts') + expect(config.output?.chunkFormat).not.toBe('commonjs') + expect(config.output?.iife).not.toBe(false) + }) + + test('Webpack', async () => { + const { webpackProvider } = await import('@rsbuild/webpack') + const rspeedy = await createStubRspeedy({ + provider: webpackProvider, + environments: { + web: {}, + }, + }) + + const config = await rspeedy.unwrapConfig() + + expect(config.output?.chunkLoading).toBe('import-scripts') + expect(config.output?.chunkFormat).not.toBe('commonjs') + expect(config.output?.iife).not.toBe(false) + }) + + test('multiple environments', async () => { + const rspeedy = await createStubRspeedy({ + environments: { + web: {}, + lynx: {}, + }, + }) + + const [webConfig, lynxConfig] = await rspeedy.initConfigs() + + expect(webConfig?.output?.chunkLoading).toBe('import-scripts') + expect(webConfig?.output?.chunkFormat).not.toBe('commonjs') + expect(webConfig?.output?.iife).not.toBe(false) + + expect(lynxConfig?.output?.chunkLoading).toBe('require') + expect(lynxConfig?.output?.chunkFormat).toBe('commonjs') + expect(lynxConfig?.output?.iife).toBe(false) + }) + + test('override with tools.rspack.output.chunkLoading', async () => { + const rspeedy = await createStubRspeedy({ + environments: { + web: { + tools: { + rspack: { output: { chunkLoading: 'import' } }, + }, + }, + }, + }) + + const config = await rspeedy.unwrapConfig() + + expect(config.output?.chunkLoading).toBe('import') + }) + }) }) diff --git a/packages/rspeedy/core/test/plugins/target.plugin.test.ts b/packages/rspeedy/core/test/plugins/target.plugin.test.ts index d01f71ea00..40a7ad754b 100644 --- a/packages/rspeedy/core/test/plugins/target.plugin.test.ts +++ b/packages/rspeedy/core/test/plugins/target.plugin.test.ts @@ -28,4 +28,30 @@ describe('target.plugin', () => { expect(config.target).toEqual(['es2019']) }) + + test('Web', async () => { + const rspeedy = await createStubRspeedy({ + environments: { + web: {}, + }, + }) + + const config = await rspeedy.unwrapConfig() + + expect(config.target).toEqual(['es2019', 'web']) + }) + + test('multiple environments', async () => { + const rspeedy = await createStubRspeedy({ + environments: { + web: {}, + lynx: {}, + }, + }) + + const [webConfig, lynxConfig] = await rspeedy.initConfigs() + + expect(webConfig?.target).toEqual(['es2019', 'web']) + expect(lynxConfig?.target).toEqual(['es2019']) + }) }) From 4caf2ef444586ad4be96198a5510c62830f745c9 Mon Sep 17 00:00:00 2001 From: Qingyu Wang <40660121+colinaaa@users.noreply.github.com> Date: Thu, 27 Mar 2025 12:16:58 +0800 Subject: [PATCH 2/2] test: enable chunk-split tests --- .../web-tests/tests/main-thread-apis.test.ts | 10 ------- .../web-tests/tests/react.spec.ts | 22 ++------------ .../index.jsx | 25 ---------------- .../rspeedy.config.ts | 29 ------------------- .../rspeedy.config.ts | 7 +++-- .../rspeedy.config.ts | 7 +++-- .../rspeedy.config.ts | 7 +++-- .../web-tests/tests/web-elements.spec.ts | 7 +---- 8 files changed, 19 insertions(+), 95 deletions(-) delete mode 100644 packages/web-platform/web-tests/tests/react/config-splitchunk-error-assertPrefix/index.jsx delete mode 100644 packages/web-platform/web-tests/tests/react/config-splitchunk-error-assertPrefix/rspeedy.config.ts diff --git a/packages/web-platform/web-tests/tests/main-thread-apis.test.ts b/packages/web-platform/web-tests/tests/main-thread-apis.test.ts index f5d69ae01e..07038bb711 100644 --- a/packages/web-platform/web-tests/tests/main-thread-apis.test.ts +++ b/packages/web-platform/web-tests/tests/main-thread-apis.test.ts @@ -9,12 +9,6 @@ import { } from '@lynx-js/web-constants'; import { test, expect } from './coverage-fixture.js'; import type { Page } from '@playwright/test'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import fs from 'node:fs/promises'; -import v8toIstanbul from 'v8-to-istanbul'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); const wait = async (ms: number) => { await new Promise((resolve) => { @@ -22,10 +16,6 @@ const wait = async (ms: number) => { }); }; -const getTitle = (titlePath: string[]) => { - return path.join(...[titlePath.pop()!, titlePath.pop()!].reverse()); -}; - test.describe('main thread api tests', () => { test.beforeEach(async ({ page }) => { await page.goto(`/main-thread-test.html`, { diff --git a/packages/web-platform/web-tests/tests/react.spec.ts b/packages/web-platform/web-tests/tests/react.spec.ts index 2dd649a301..31327f408c 100644 --- a/packages/web-platform/web-tests/tests/react.spec.ts +++ b/packages/web-platform/web-tests/tests/react.spec.ts @@ -4,12 +4,6 @@ import { swipe, dragAndHold } from './utils.js'; import { test, expect } from './coverage-fixture.js'; import type { Page } from '@playwright/test'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import fs from 'node:fs/promises'; -import v8toIstanbul from 'v8-to-istanbul'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); const wait = async (ms: number) => { await new Promise((resolve) => { @@ -964,7 +958,7 @@ test.describe('reactlynx3 tests', () => { await expect(target).toHaveCSS('height', '100px'); }, ); - test.fixme( // TODO(@colinaaa): update the template plugin + test( 'config-splitchunk-single-vendor', async ({ page }, { title }) => { await goto(page, title, true); @@ -973,7 +967,7 @@ test.describe('reactlynx3 tests', () => { await expect(target).toHaveCSS('background-color', 'rgb(0, 128, 0)'); // green }, ); - test.fixme( // TODO(@colinaaa): update the template plugin + test( 'config-splitchunk-split-by-experience', async ({ page }, { title }) => { await goto(page, title, true); @@ -982,7 +976,7 @@ test.describe('reactlynx3 tests', () => { await expect(target).toHaveCSS('background-color', 'rgb(0, 128, 0)'); // green }, ); - test.fixme( // TODO(@colinaaa): update the template plugin + test( 'config-splitchunk-split-by-module', async ({ page }, { title }) => { await goto(page, title, true); @@ -992,16 +986,6 @@ test.describe('reactlynx3 tests', () => { }, ); - test.fixme( // TODO(@colinaaa): update the template plugin - 'config-splitchunk-error-assertPrefix', - async ({ page }, { title }) => { - await goto(page, title, true); - await wait(1500); - const target = page.locator('#target'); - await expect(target).toHaveCSS('background-color', 'rgb(0, 128, 0)'); // green - }, - ); - test('config-mode-dev-with-all-in-one', async ({ page }, { title }) => { await goto(page, title, true); await wait(100); diff --git a/packages/web-platform/web-tests/tests/react/config-splitchunk-error-assertPrefix/index.jsx b/packages/web-platform/web-tests/tests/react/config-splitchunk-error-assertPrefix/index.jsx deleted file mode 100644 index 1d8f5fc701..0000000000 --- a/packages/web-platform/web-tests/tests/react/config-splitchunk-error-assertPrefix/index.jsx +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2023 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. -import { root, useEffect, useState } from '@lynx-js/react'; - -function App() { - const [color, setColor] = useState('pink'); - useEffect(() => { - setTimeout(() => { - setColor('green'); - }, 1000); - }, [setColor]); - return ( - - ); -} - -root.render(); diff --git a/packages/web-platform/web-tests/tests/react/config-splitchunk-error-assertPrefix/rspeedy.config.ts b/packages/web-platform/web-tests/tests/react/config-splitchunk-error-assertPrefix/rspeedy.config.ts deleted file mode 100644 index 703efd1075..0000000000 --- a/packages/web-platform/web-tests/tests/react/config-splitchunk-error-assertPrefix/rspeedy.config.ts +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2023 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { defineConfig } from '@lynx-js/rspeedy'; -import { commonConfig } from '../commonConfig.js'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const caseName = path.basename(__dirname); - -const root = path.join(__dirname, '..', '..', '..', 'dist', caseName); -const commonConfigResult = commonConfig(); -commonConfigResult.output!.distPath = { root }; -commonConfigResult.output!.assetPrefix += `error://example.com/`; -export default defineConfig({ - ...commonConfigResult, - source: { - entry: { - [caseName]: path.join(__dirname, 'index.jsx'), - }, - }, - performance: { - chunkSplit: { - // Rspack 1.2.8 introduced a bug that causes the split-by-module strategy to fail. - // strategy: 'split-by-module', - }, - }, -}); diff --git a/packages/web-platform/web-tests/tests/react/config-splitchunk-single-vendor/rspeedy.config.ts b/packages/web-platform/web-tests/tests/react/config-splitchunk-single-vendor/rspeedy.config.ts index b3b9ba4e04..421d2b99db 100644 --- a/packages/web-platform/web-tests/tests/react/config-splitchunk-single-vendor/rspeedy.config.ts +++ b/packages/web-platform/web-tests/tests/react/config-splitchunk-single-vendor/rspeedy.config.ts @@ -22,8 +22,11 @@ export default defineConfig({ }, performance: { chunkSplit: { - // Rspack 1.2.8 introduced a bug that causes the single-vendor strategy to fail. - // strategy: 'single-vendor', + strategy: 'single-vendor', + override: { + // See: https://github.com/web-infra-dev/rspack/issues/9812 + filename: '[name].[contenthash:8].js', + }, }, }, }); diff --git a/packages/web-platform/web-tests/tests/react/config-splitchunk-split-by-experience/rspeedy.config.ts b/packages/web-platform/web-tests/tests/react/config-splitchunk-split-by-experience/rspeedy.config.ts index 77c2ea92f7..c3ffda250a 100644 --- a/packages/web-platform/web-tests/tests/react/config-splitchunk-split-by-experience/rspeedy.config.ts +++ b/packages/web-platform/web-tests/tests/react/config-splitchunk-split-by-experience/rspeedy.config.ts @@ -22,8 +22,11 @@ export default defineConfig({ }, performance: { chunkSplit: { - // Rspack 1.2.8 introduced a bug that causes the split-by-experience strategy to fail. - // strategy: 'split-by-experience', + strategy: 'split-by-experience', + override: { + // See: https://github.com/web-infra-dev/rspack/issues/9812 + filename: '[name].[contenthash:8].js', + }, }, }, }); diff --git a/packages/web-platform/web-tests/tests/react/config-splitchunk-split-by-module/rspeedy.config.ts b/packages/web-platform/web-tests/tests/react/config-splitchunk-split-by-module/rspeedy.config.ts index 71ca2aac18..419e098768 100644 --- a/packages/web-platform/web-tests/tests/react/config-splitchunk-split-by-module/rspeedy.config.ts +++ b/packages/web-platform/web-tests/tests/react/config-splitchunk-split-by-module/rspeedy.config.ts @@ -22,8 +22,11 @@ export default defineConfig({ }, performance: { chunkSplit: { - // Rspack 1.2.8 introduced a bug that causes the split-by-module strategy to fail. - // strategy: 'split-by-module', + strategy: 'split-by-module', + override: { + // See: https://github.com/web-infra-dev/rspack/issues/9812 + filename: '[name].[contenthash:8].js', + }, }, }, }); diff --git a/packages/web-platform/web-tests/tests/web-elements.spec.ts b/packages/web-platform/web-tests/tests/web-elements.spec.ts index 1a86f0b4c1..be534a4b59 100644 --- a/packages/web-platform/web-tests/tests/web-elements.spec.ts +++ b/packages/web-platform/web-tests/tests/web-elements.spec.ts @@ -1,15 +1,10 @@ // Copyright 2024 The Lynx Authors. All rights reserved. // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. -import { swipe, dragAndHold } from './utils'; +import { swipe, dragAndHold } from './utils.js'; import { test, expect } from './coverage-fixture.js'; import type { Page } from '@playwright/test'; import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import fs from 'node:fs/promises'; -import v8toIstanbul from 'v8-to-istanbul'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); const wait = async (ms: number) => { await new Promise((resolve) => {