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/sour-swans-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@module-federation/cli': patch
---

perf(cli): use jiti to replace modernjs utils to load config
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
"@rollup/plugin-replace": "6.0.1",
"@rslib/core": "^0.10.4",
"@rspack/core": "1.3.9",
"@rstest/core": "^0.6.5",
"@rspack/dev-server": "1.1.1",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^6.0.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Rstest Snapshot v1

exports[`readConfig integration tests > readConfig with real jiti > should handle config with complex TypeScript features 1`] = `
{
"exposes": {
"./Button": "./src/components/Button.tsx",
"./Footer": "./src/components/Footer.tsx",
"./Header": "./src/components/Header.tsx",
},
"filename": "remoteEntry.js",
"name": "complex-mf-app",
"remotes": {
"remote-app-1": "remote-app-1@http://localhost:3001/remoteEntry.js",
"remote-app-2": "remote-app-2@http://localhost:3002/remoteEntry.js",
},
"shared": {
"lodash": {
"singleton": false,
"strictVersion": true,
},
"react": {
"requiredVersion": "^18.0.0",
"singleton": true,
},
"react-dom": {
"requiredVersion": "^18.0.0",
"singleton": true,
},
},
"sharedScope": "default",
}
`;

exports[`readConfig integration tests > readConfig with real jiti > should handle config with complex TypeScript features 2`] = `
{
"exposes": {
"./Button": "./src/components/Button.tsx",
"./Header": "./src/components/Header.tsx",
},
"filename": "remoteEntry.js",
"name": "test-mf-app",
"remotes": {
"remote-app": "remoteApp@http://localhost:3001/remoteEntry.js",
},
"shared": {
"react": {
"requiredVersion": "^18.0.0",
"singleton": true,
},
"react-dom": {
"requiredVersion": "^18.0.0",
"singleton": true,
},
},
}
`;
59 changes: 59 additions & 0 deletions packages/cli/__tests__/fixtures/complex-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Test TypeScript features that jiti should handle
interface SharedConfig {
singleton?: boolean;
requiredVersion?: string;
strictVersion?: boolean;
}

interface RemoteConfig {
url: string;
format?: 'esm' | 'var';
}

enum ExposedModules {
Button = './Button',
Header = './Header',
Footer = './Footer',
}

const sharedDependencies: Record<string, SharedConfig> = {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
lodash: { singleton: false, strictVersion: true },
};

const remoteApps: Record<string, RemoteConfig> = {
'remote-app-1': {
url: 'http://localhost:3001/remoteEntry.js',
format: 'esm',
},
'remote-app-2': {
url: 'http://localhost:3002/remoteEntry.js',
format: 'var',
},
};

export default {
name: 'complex-mf-app',
filename: 'remoteEntry.js',
exposes: {
[ExposedModules.Button]: './src/components/Button.tsx',
[ExposedModules.Header]: './src/components/Header.tsx',
[ExposedModules.Footer]: './src/components/Footer.tsx',
},
shared: sharedDependencies,
remotes: Object.entries(remoteApps).reduce(
(acc, [name, config]) => {
acc[name] = `${name}@${config.url}`;
return acc;
},
{} as Record<string, string>,
),
// Test generic types
sharedScope: createSharedScope<'default'>(),
};

// Test generic function
function createSharedScope<T extends string>(): T {
return 'default' as T;
}
15 changes: 15 additions & 0 deletions packages/cli/__tests__/fixtures/test-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export default {
name: 'test-mf-app',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button.tsx',
'./Header': './src/components/Header.tsx',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
},
remotes: {
'remote-app': 'remoteApp@http://localhost:3001/remoteEntry.js',
},
};
19 changes: 19 additions & 0 deletions packages/cli/__tests__/readConfig.integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { describe, it, expect } from '@rstest/core';
import path from 'path';
import { readConfig } from '../src/utils/readConfig';

describe('readConfig integration tests', () => {
describe('readConfig with real jiti', () => {
it('should handle config with complex TypeScript features', async () => {
const resultComplex = await readConfig(
path.join(__dirname, 'fixtures', 'complex-config.ts'),
);
expect(resultComplex).toMatchSnapshot();

const result = await readConfig(
path.join(__dirname, 'fixtures', 'test-config.ts'),
);
expect(result).toMatchSnapshot();
});
});
});
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"@module-federation/dts-plugin": "workspace:*",
"commander": "11.1.0",
"chalk": "3.0.0",
"@modern-js/node-bundle-require": "2.68.2"
"jiti": "2.4.2"
},
"devDependencies": {
"@types/node": "~16.11.7"
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"parallel": false,
"commands": [
{
"command": "echo 'waiting for adding test case...'",
"command": "rstest run -c packages/cli/rstest.config.ts",
"forwardAllArgs": false
}
]
Expand Down
9 changes: 9 additions & 0 deletions packages/cli/rstest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from '@rstest/core';
import path from 'path';

export default defineConfig({
testEnvironment: 'node',
include: [path.resolve(__dirname, '__tests__/**.test.ts')],
globals: true,
testTimeout: 10000,
});
21 changes: 15 additions & 6 deletions packages/cli/src/utils/readConfig.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import path from 'path';
import { bundle } from '@modern-js/node-bundle-require';
import type { moduleFederationPlugin } from '@module-federation/sdk';
import { pathToFileURL } from 'url';

const { createJiti } = require('jiti');
const DEFAULT_CONFIG_PATH = 'module-federation.config.ts';

export const getConfigPath = (userConfigPath?: string) => {
Expand All @@ -15,8 +14,18 @@ export const getConfigPath = (userConfigPath?: string) => {

export async function readConfig(userConfigPath?: string) {
const configPath = getConfigPath(userConfigPath);
const preBundlePath = await bundle(configPath);
const mfConfig = (await import(pathToFileURL(preBundlePath).href)).default
.default as unknown as moduleFederationPlugin.ModuleFederationPluginOptions;
return mfConfig;
const jit = createJiti(__filename, {
interopDefault: true,
esmResolve: true,
});
const configModule = await jit(configPath);
const resolvedConfig = (
configModule &&
typeof configModule === 'object' &&
'default' in configModule
? (configModule as { default: unknown }).default
: configModule
) as moduleFederationPlugin.ModuleFederationPluginOptions;

return resolvedConfig;
}
2 changes: 1 addition & 1 deletion packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@
"@src/*": ["./src/*"]
}
},
"include": ["src", "../../global.d.ts", "__tests__/**/*"],
"include": ["src", "../../global.d.ts", "__tests__/**/*", "__tests__"],
"exclude": ["node_modules/**/*", "../node_modules"]
}
Loading
Loading