diff --git a/.changeset/eager-bats-refuse.md b/.changeset/eager-bats-refuse.md new file mode 100644 index 0000000000..d08b38596d --- /dev/null +++ b/.changeset/eager-bats-refuse.md @@ -0,0 +1,21 @@ +--- +"@lynx-js/react-webpack-plugin": patch +--- + +Shake `useImperativeHandle` on the main-thread by default. + +```js +import { forwardRef, useImperativeHandle } from '@lynx-js/react'; + +export default forwardRef(function App(_, ref) { + useImperativeHandle(ref, () => { + // This should be considered as background only + return { + name() { + // This should be considered as background only + console.info('This should not exist in main-thread'); + }, + }; + }); +}); +``` diff --git a/packages/webpack/react-webpack-plugin/src/loaders/options.ts b/packages/webpack/react-webpack-plugin/src/loaders/options.ts index 1f29721470..1d1b08dd32 100644 --- a/packages/webpack/react-webpack-plugin/src/loaders/options.ts +++ b/packages/webpack/react-webpack-plugin/src/loaders/options.ts @@ -244,6 +244,7 @@ export function getMainThreadTransformOptions( 'useLayoutEffect', '__runInJS', 'useLynxGlobalEventListener', + 'useImperativeHandle', ...(shake?.removeCallParams ?? []), ], }, diff --git a/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/index.js b/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/index.js new file mode 100644 index 0000000000..b456141e6c --- /dev/null +++ b/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/index.js @@ -0,0 +1,34 @@ +/// + +import fs from 'node:fs/promises'; + +import './react.js'; +import './lynx-js-react.js'; + +it('should have useEffect func in background thread script', async () => { + if (__LEPUS__) { + return; + } + const expected = eval( + `['This', 'should', 'not', 'exist', 'in', 'main-thread'].join(' ', )`, + ); + const content = await fs.readFile(__filename, 'utf-8'); + + expect(content).toContain(expected); +}); + +it('should not have useEffect func in main thread script', async () => { + if (__JS__) { + return; + } + const expected = eval( + `['This', 'should', 'not', 'exist', 'in', 'main-thread'].join(' ', )`, + ); + + const content = await fs.readFile( + __filename, + 'utf-8', + ); + + expect(content).not.toContain(expected); +}); diff --git a/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/lynx-js-react.js b/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/lynx-js-react.js new file mode 100644 index 0000000000..1fad8b3531 --- /dev/null +++ b/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/lynx-js-react.js @@ -0,0 +1,30 @@ +import React, { forwardRef, useImperativeHandle } from '@lynx-js/react'; +import { useEffect } from '@lynx-js/react'; +import { useEffect as useMyEffect } from '@lynx-js/react'; + +export default forwardRef(function App(_, ref) { + function bar() { + console.info('This should not exist in main-thread'); + } + useImperativeHandle(ref, () => { + console.info('This should not exist in main-thread'); + return { + name() { + console.info('This should not exist in main-thread'); + }, + bar, + baz: 'This should not exist in main-thread', + }; + }); + useEffect(() => { + console.info('This should not exist in main-thread'); + }, []); + + useMyEffect(() => { + // TODO: import alias is not removed + }); + + React.useEffect(() => { + // TODO: default import is not removed + }); +}); diff --git a/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/react.js b/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/react.js new file mode 100644 index 0000000000..f2709b6c9a --- /dev/null +++ b/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/react.js @@ -0,0 +1,30 @@ +import React, { forwardRef, useImperativeHandle } from 'react'; +import { useEffect } from 'react'; +import { useEffect as useMyEffect } from 'react'; + +export default forwardRef(function App(_, ref) { + function bar() { + console.info('This should not exist in main-thread'); + } + useImperativeHandle(ref, () => { + console.info('This should not exist in main-thread'); + return { + name() { + console.info('This should not exist in main-thread'); + }, + bar, + baz: 'This should not exist in main-thread', + }; + }); + useEffect(() => { + console.info('This should not exist in main-thread'); + }, []); + + useMyEffect(() => { + // TODO: import alias is not removed + }); + + React.useEffect(() => { + // TODO: default import is not removed + }); +}); diff --git a/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/rspack.config.js b/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/rspack.config.js new file mode 100644 index 0000000000..d75e332f3c --- /dev/null +++ b/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/rspack.config.js @@ -0,0 +1,16 @@ +import { createConfig } from '../../../create-react-config.js'; + +const defaultConfig = createConfig(); + +/** @type {import('@rspack/core').Configuration} */ +export default { + context: __dirname, + ...defaultConfig, + resolve: { + ...defaultConfig.resolve, + alias: { + 'react': '@lynx-js/react', + '@lynx-js/react-runtime': '@lynx-js/react', + }, + }, +}; diff --git a/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/test.config.cjs b/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/test.config.cjs new file mode 100644 index 0000000000..2fa53abe09 --- /dev/null +++ b/packages/webpack/react-webpack-plugin/test/cases/main-thread/use-imperative-handle/test.config.cjs @@ -0,0 +1,7 @@ +/** @type {import("@lynx-js/test-tools").TConfigCaseConfig} */ +module.exports = { + bundlePath: [ + 'main:main-thread.js', + 'main:background.js', + ], +};