Skip to content

feat: sink React dependencies and switch to TSC compiler #3919

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
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
7 changes: 1 addition & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"lint": "nx run-many --target=lint",
"test": "nx run-many --target=test",
"build": "NX_TUI=false nx run-many --target=build --parallel=5 --projects=tag:type:pkg",
"build:pkg": "NX_TUI=false nx run-many --targets=build --projects=tag:type:pkg --skip-nx-cache",
"build:pkg": "rm -rf ./node_modules/.cache && NX_TUI=false nx run-many --targets=build --projects=tag:type:pkg --skip-nx-cache",
"test:pkg": "NX_TUI=false nx run-many --targets=test --projects=tag:type:pkg --skip-nx-cache",
"lint-fix": "nx format:write --uncommitted",
"trigger-release": "node -e 'import(\"open\").then(open => open.default(\"https://github.com/module-federation/core/actions/workflows/trigger-release.yml\"))'",
Expand Down Expand Up @@ -83,9 +83,6 @@
"lodash.get": "4.4.2",
"openai": "^4.72.0",
"rambda": "7.5.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-router-dom": "6.26.2",
"regenerator-runtime": "0.14.1",
"sharp": "^0.33.4",
"storybook": "9.0.9",
Expand Down Expand Up @@ -155,8 +152,6 @@
"@types/node": "18.16.9",
"@types/node-fetch": "2.6.11",
"@types/pidusage": "2.0.5",
"@types/react": "18.3.11",
"@types/react-dom": "18.3.0",
"@types/webpack-sources": "3.2.3",
"@typescript-eslint/eslint-plugin": "7.18.0",
"@typescript-eslint/parser": "7.18.0",
Expand Down
37 changes: 37 additions & 0 deletions packages/bridge/bridge-react/.swcrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"jsc": {
"target": "es2017",
"parser": {
"syntax": "typescript",
"tsx": true,
"decorators": true,
"dynamicImport": true
},
"transform": {
"react": {
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
"throwIfNamespace": true,
"development": false,
"useBuiltins": false,
"refresh": false,
"runtime": "classic"
},
"decoratorMetadata": true,
"legacyDecorator": true
},
"keepClassNames": true,
"externalHelpers": true,
"loose": true
},
"module": {
"type": "es6"
},
"sourceMaps": true,
"exclude": [
"jest.config.ts",
"./src/jest-setup.ts$",
"./**/jest-setup.ts$",
".*.js$"
]
}
34 changes: 24 additions & 10 deletions packages/bridge/bridge-react/__tests__/bridge.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import React from 'react';
import { assert, describe, it } from 'vitest';
import {
describe,
it,
expect,
beforeEach,
afterEach,
jest,
} from '@jest/globals';
import { createBridgeComponent, createRemoteAppComponent } from '../src';
import {
act,
Expand Down Expand Up @@ -28,11 +35,12 @@ describe('bridge', () => {
rootComponent: Component,
})();

lifeCycle.render({
dom: containerInfo?.container,
await act(async () => {
lifeCycle.render({
dom: containerInfo?.container,
});
await sleep(200);
});

await sleep(200);
expect(document.querySelector('#container')!.innerHTML).toContain(
'<div>life cycle render</div>',
);
Expand Down Expand Up @@ -66,7 +74,9 @@ describe('bridge', () => {
);
expect(getHtml(container)).toMatch('loading');

await sleep(200);
await act(async () => {
await sleep(200);
});
expect(getHtml(container)).toMatch('life cycle render');
expect(getHtml(container)).toMatch('hello world');
});
Expand Down Expand Up @@ -97,14 +107,16 @@ describe('bridge', () => {
);
expect(getHtml(container)).toMatch('loading');

await sleep(200);
await act(async () => {
await sleep(200);
});
expect(getHtml(container)).toMatch('life cycle render');
expect(getHtml(container)).toMatch('hello world');
expect(ref.current).not.toBeNull();
});

it('createRemoteAppComponent with custom createRoot prop', async () => {
const renderMock = vi.fn();
const renderMock = jest.fn();

function Component({ props }: { props?: Record<string, any> }) {
return <div>life cycle render {props?.msg}</div>;
Expand All @@ -114,7 +126,7 @@ describe('bridge', () => {
createRoot: () => {
return {
render: renderMock,
unmount: vi.fn(),
unmount: jest.fn(),
};
},
});
Expand All @@ -131,7 +143,9 @@ describe('bridge', () => {
const { container } = render(<RemoteComponent />);
expect(getHtml(container)).toMatch('loading');

await sleep(200);
await act(async () => {
await sleep(200);
});
expect(renderMock).toHaveBeenCalledTimes(1);
});
});
49 changes: 25 additions & 24 deletions packages/bridge/bridge-react/__tests__/createLazyComponent.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
// Mocking dependencies
jest.mock('@module-federation/runtime', () => ({
getInstance: jest.fn(),
}));
jest.mock('../src/lazy/utils', () => ({
getLoadedRemoteInfos: jest.fn(),
getDataFetchMapKey: jest.fn(),
fetchData: jest.fn(),
}));

import React, { Suspense } from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { describe, it, expect, jest, beforeEach } from '@jest/globals';
import {
createLazyComponent,
collectSSRAssets,
} from '../src/lazy/createLazyComponent';
import * as runtime from '@module-federation/runtime';
import * as utils from '../src/lazy/utils';

// Mocking dependencies
vi.mock('@module-federation/runtime');
vi.mock('../src/lazy/utils');

const mockGetInstance = runtime.getInstance as vi.Mock;
const mockGetLoadedRemoteInfos = utils.getLoadedRemoteInfos as vi.Mock;
const mockGetDataFetchMapKey = utils.getDataFetchMapKey as vi.Mock;
const mockFetchData = utils.fetchData as vi.Mock;

const MockComponent = () => <div>Mock Component</div>;
const LoadingComponent = () => <div>Loading...</div>;
const ErrorComponent = () => <div>Error!</div>;
Expand All @@ -25,14 +26,14 @@ describe('createLazyComponent', () => {
let mockInstance: any;

beforeEach(() => {
vi.clearAllMocks();
jest.clearAllMocks();
mockInstance = {
name: 'host-app',
options: { version: '1.0.0' },
getModuleInfo: vi.fn(),
getModuleInfo: jest.fn(),
};
mockGetInstance.mockReturnValue(mockInstance);
mockGetLoadedRemoteInfos.mockReturnValue({
(runtime.getInstance as any).mockReturnValue(mockInstance);
(utils.getLoadedRemoteInfos as any).mockReturnValue({
name: 'remoteApp',
alias: 'remote',
expose: './Component',
Expand All @@ -52,11 +53,11 @@ describe('createLazyComponent', () => {
},
entryGlobalName: 'remoteApp',
});
mockGetDataFetchMapKey.mockReturnValue('data-fetch-key');
(utils.getDataFetchMapKey as any).mockReturnValue('data-fetch-key');
});

it('should render loading component then the actual component', async () => {
const loader = vi.fn().mockResolvedValue({
const loader = jest.fn().mockResolvedValue({
default: MockComponent,
[Symbol.for('mf_module_id')]: 'remoteApp/Component',
});
Expand All @@ -82,9 +83,9 @@ describe('createLazyComponent', () => {
});

it('should render fallback component on data fetch error', async () => {
mockFetchData.mockRejectedValue(new Error('Data fetch failed'));
(utils.fetchData as any).mockRejectedValue(new Error('Data fetch failed'));
const LazyComponentWithDataFetch = createLazyComponent({
loader: vi.fn().mockResolvedValue({
loader: jest.fn().mockResolvedValue({
default: MockComponent,
[Symbol.for('mf_module_id')]: 'remoteApp/Component',
}),
Expand All @@ -101,14 +102,14 @@ describe('createLazyComponent', () => {
});

it('should fetch data and pass it to the component', async () => {
const loader = vi.fn().mockResolvedValue({
const loader = jest.fn().mockResolvedValue({
default: (props: { mfData: any }) => (
<div>Data: {JSON.stringify(props.mfData)}</div>
),
[Symbol.for('mf_module_id')]: 'remoteApp/Component',
});
const mockData = { message: 'Hello' };
mockFetchData.mockResolvedValue(mockData);
(utils.fetchData as any).mockResolvedValue(mockData);

const LazyComponent = createLazyComponent({
loader,
Expand All @@ -131,12 +132,12 @@ describe('collectSSRAssets', () => {
let mockInstance: any;

beforeEach(() => {
vi.clearAllMocks();
jest.clearAllMocks();
mockInstance = {
name: 'host-app',
options: { version: '1.0.0' },
};
mockGetInstance.mockReturnValue(mockInstance);
(runtime.getInstance as any).mockReturnValue(mockInstance);
});

it('should return an empty array if instance is not available', () => {
Expand All @@ -148,7 +149,7 @@ describe('collectSSRAssets', () => {
});

it('should return an empty array if module info is not found', () => {
mockGetLoadedRemoteInfos.mockReturnValue(undefined);
(utils.getLoadedRemoteInfos as any).mockReturnValue(undefined);
const assets = collectSSRAssets({
id: 'test/expose',
instance: mockInstance,
Expand All @@ -157,7 +158,7 @@ describe('collectSSRAssets', () => {
});

it('should collect CSS and JS assets for SSR', () => {
mockGetLoadedRemoteInfos.mockReturnValue({
(utils.getLoadedRemoteInfos as any).mockReturnValue({
name: 'remoteApp',
expose: './Component',
snapshot: {
Expand Down
Loading
Loading