diff --git a/.changeset/hip-toes-admire.md b/.changeset/hip-toes-admire.md new file mode 100644 index 0000000000..d2e6a83b30 --- /dev/null +++ b/.changeset/hip-toes-admire.md @@ -0,0 +1,5 @@ +--- +"@lynx-js/react": patch +--- + +Auto import `@lynx-js/react/experimental/lazy/import` when using `import(url)` diff --git a/packages/react/runtime/lazy/import.js b/packages/react/runtime/lazy/import.js index 6e938fefce..366b1a630b 100644 --- a/packages/react/runtime/lazy/import.js +++ b/packages/react/runtime/lazy/import.js @@ -22,34 +22,40 @@ Object.defineProperty(target, sExportsReact, { value: ReactAPIs, enumerable: false, writable: false, + configurable: true, }); Object.defineProperty(target, sExportsReactLepus, { value: ReactLepusAPIs, enumerable: false, writable: false, + configurable: true, }); Object.defineProperty(target, sExportsReactInternal, { value: ReactInternal, enumerable: false, writable: false, + configurable: true, }); Object.defineProperty(target, sExportsJSXRuntime, { value: ReactJSXRuntime, enumerable: false, writable: false, + configurable: true, }); Object.defineProperty(target, sExportsJSXDevRuntime, { value: ReactJSXDevRuntime, enumerable: false, writable: false, + configurable: true, }); Object.defineProperty(target, sExportsLegacyReactRuntime, { value: ReactLegacyReactRuntime, enumerable: false, writable: false, + configurable: true, }); diff --git a/packages/react/transform/__test__/fixture.spec.js b/packages/react/transform/__test__/fixture.spec.js index b76716e8e5..526103b463 100644 --- a/packages/react/transform/__test__/fixture.spec.js +++ b/packages/react/transform/__test__/fixture.spec.js @@ -870,7 +870,8 @@ describe('dynamic import', () => { ); expect(result.code).toMatchInlineSnapshot(` - "import { __dynamicImport } from "@lynx-js/react/internal"; + "import "@lynx-js/react/experimental/lazy/import"; + import { __dynamicImport } from "@lynx-js/react/internal"; (async function() { await import(); await import(0); diff --git a/packages/react/transform/src/swc_plugin_dynamic_import/mod.rs b/packages/react/transform/src/swc_plugin_dynamic_import/mod.rs index a6145db747..7f467a13f6 100644 --- a/packages/react/transform/src/swc_plugin_dynamic_import/mod.rs +++ b/packages/react/transform/src/swc_plugin_dynamic_import/mod.rs @@ -235,6 +235,24 @@ where with: Default::default(), })), ); + + prepend_stmt( + &mut n.body, + ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl { + span: DUMMY_SP, + phase: ImportPhase::Evaluation, + specifiers: vec![], + src: Box::new(Str { + span: DUMMY_SP, + raw: None, + value: format!("{}/experimental/lazy/import", self.opts.runtime_pkg) + .replace("/internal", "") + .into(), + }), + type_only: Default::default(), + with: Default::default(), + })), + ); } } } @@ -283,4 +301,30 @@ mod tests { })(); "# ); + + test!( + module, + Syntax::Es(EsSyntax { + jsx: true, + ..Default::default() + }), + |_| visit_mut_pass(DynamicImportVisitor::new( + DynamicImportVisitorConfig { + layer: "test".into(), + ..Default::default() + }, + Some(SingleThreadedComments::default()) + )), + should_not_import_lazy, + r#" + (async function () { + await import("./index.js"); + await import(`./locales/${name}`); + await import("ftp://www/a.js"); + + await import("./index.js", { with: { type: "component" } }); + await import("ftp://www/a.js", { with: { type: "component" } }); + })(); + "# + ); } diff --git a/packages/react/transform/tests/__swc_snapshots__/src/swc_plugin_dynamic_import/mod.rs/should_not_import_lazy.js b/packages/react/transform/tests/__swc_snapshots__/src/swc_plugin_dynamic_import/mod.rs/should_not_import_lazy.js new file mode 100644 index 0000000000..21155543b4 --- /dev/null +++ b/packages/react/transform/tests/__swc_snapshots__/src/swc_plugin_dynamic_import/mod.rs/should_not_import_lazy.js @@ -0,0 +1,15 @@ +(async function() { + await import("./index.js"); + await import(`./locales/${name}`); + await import("ftp://www/a.js"); + await import("./index.js", { + with: { + type: "component" + } + }); + await import("ftp://www/a.js", { + with: { + type: "component" + } + }); +})(); diff --git a/packages/react/transform/tests/__swc_snapshots__/src/swc_plugin_dynamic_import/mod.rs/should_transform_import_call.js b/packages/react/transform/tests/__swc_snapshots__/src/swc_plugin_dynamic_import/mod.rs/should_transform_import_call.js index c516f641a5..0917c5ccd3 100644 --- a/packages/react/transform/tests/__swc_snapshots__/src/swc_plugin_dynamic_import/mod.rs/should_transform_import_call.js +++ b/packages/react/transform/tests/__swc_snapshots__/src/swc_plugin_dynamic_import/mod.rs/should_transform_import_call.js @@ -1,3 +1,4 @@ +import "@lynx-js/react/experimental/lazy/import"; import { __dynamicImport } from "@lynx-js/react/internal"; (async function() { await import("./index.js"); diff --git a/packages/webpack/react-webpack-plugin/test/cases/code-splitting/lazy-imports/index.jsx b/packages/webpack/react-webpack-plugin/test/cases/code-splitting/lazy-imports/index.jsx new file mode 100644 index 0000000000..3d2e63dfdb --- /dev/null +++ b/packages/webpack/react-webpack-plugin/test/cases/code-splitting/lazy-imports/index.jsx @@ -0,0 +1,43 @@ +/// + +const sExportsReact = Symbol.for('__REACT_LYNX_EXPORTS__(@lynx-js/react)'); +const sExportsReactLepus = Symbol.for( + '__REACT_LYNX_EXPORTS__(@lynx-js/react/lepus)', +); +const sExportsReactInternal = Symbol.for( + '__REACT_LYNX_EXPORTS__(@lynx-js/react/internal)', +); +const sExportsJSXRuntime = Symbol.for( + '__REACT_LYNX_EXPORTS__(@lynx-js/react/jsx-runtime)', +); +const sExportsJSXDevRuntime = Symbol.for( + '__REACT_LYNX_EXPORTS__(@lynx-js/react/jsx-dev-runtime)', +); +const sExportsLegacyReactRuntime = Symbol.for( + '__REACT_LYNX_EXPORTS__(@lynx-js/react/legacy-react-runtime)', +); + +export {}; + +it('should have experimental/lazy/import imported', async () => { + try { + await import('https://www/a.js'); + } catch { + // ignore error + } + if (__BACKGROUND__) { + expect(lynx[sExportsReact]).not.toBeUndefined(); + expect(lynx[sExportsReactLepus]).not.toBeUndefined(); + expect(lynx[sExportsReactInternal]).not.toBeUndefined(); + expect(lynx[sExportsJSXRuntime]).not.toBeUndefined(); + expect(lynx[sExportsJSXDevRuntime]).not.toBeUndefined(); + expect(lynx[sExportsLegacyReactRuntime]).not.toBeUndefined(); + } else { + expect(globalThis[sExportsReact]).not.toBeUndefined(); + expect(globalThis[sExportsReactLepus]).not.toBeUndefined(); + expect(globalThis[sExportsReactInternal]).not.toBeUndefined(); + expect(globalThis[sExportsJSXRuntime]).not.toBeUndefined(); + expect(globalThis[sExportsJSXDevRuntime]).not.toBeUndefined(); + expect(globalThis[sExportsLegacyReactRuntime]).not.toBeUndefined(); + } +}); diff --git a/packages/webpack/react-webpack-plugin/test/cases/code-splitting/lazy-imports/rspack.config.js b/packages/webpack/react-webpack-plugin/test/cases/code-splitting/lazy-imports/rspack.config.js new file mode 100644 index 0000000000..c1ad2d8e00 --- /dev/null +++ b/packages/webpack/react-webpack-plugin/test/cases/code-splitting/lazy-imports/rspack.config.js @@ -0,0 +1,7 @@ +import { createConfig } from '../../../create-react-config.js'; + +/** @type {import('@rspack/core').Configuration} */ +export default { + context: __dirname, + ...createConfig(), +}; diff --git a/packages/webpack/react-webpack-plugin/test/cases/code-splitting/lazy-imports/test.config.cjs b/packages/webpack/react-webpack-plugin/test/cases/code-splitting/lazy-imports/test.config.cjs new file mode 100644 index 0000000000..2fa53abe09 --- /dev/null +++ b/packages/webpack/react-webpack-plugin/test/cases/code-splitting/lazy-imports/test.config.cjs @@ -0,0 +1,7 @@ +/** @type {import("@lynx-js/test-tools").TConfigCaseConfig} */ +module.exports = { + bundlePath: [ + 'main:main-thread.js', + 'main:background.js', + ], +};