Skip to content
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

refactor: split federation runtime plugin into separate plugins #803

Merged
merged 16 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 15 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/wet-countries-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@callstack/repack": minor
---

Refactor FederationRuntimePlugin into two separate plugins for more granular control over the MF2 runtime behaviour (CorePlugin & ResolverPlugin)
4 changes: 2 additions & 2 deletions apps/tester-federation-v2/ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
PODS:
- boost (1.84.0)
- callstack-repack (5.0.0-rc.0):
- callstack-repack (5.0.0-rc.2):
- DoubleConversion
- glog
- hermes-engine
Expand Down Expand Up @@ -1895,7 +1895,7 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
boost: 1dca942403ed9342f98334bf4c3621f011aa7946
callstack-repack: 75464b0e26467fc4a7236373399bc0fc2281f495
callstack-repack: 3106db24c24f7a76a380230ff7794d225cecb760
DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385
FBLazyVector: 7075bb12898bc3998fd60f4b7ca422496cc2cdf7
fmt: 10c6e61f4be25dc963c36bd73fc7b1705fe975be
Expand Down
5 changes: 0 additions & 5 deletions apps/tester-federation-v2/rspack.config.host-app.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// @ts-check
import { createRequire } from 'node:module';
import path from 'node:path';
import * as Repack from '@callstack/repack';
import rspack from '@rspack/core';

const dirname = Repack.getDirname(import.meta.url);
const { resolve } = createRequire(import.meta.url);

/** @type {(env: import('@callstack/repack').EnvOptions) => import('@rspack/core').Configuration} */
export default (env) => {
Expand Down Expand Up @@ -121,9 +119,6 @@ export default (env) => {
MiniApp: `MiniApp@http://localhost:8082/${platform}/mf-manifest.json`,
},
dts: false,
runtimePlugins: [
resolve('@callstack/repack/federation-runtime-plugin'),
],
shared: {
react: {
singleton: true,
Expand Down
5 changes: 0 additions & 5 deletions apps/tester-federation-v2/webpack.config.host-app.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// @ts-check
import { createRequire } from 'node:module';
import path from 'node:path';
import * as Repack from '@callstack/repack';
import webpack from 'webpack';

const dirname = Repack.getDirname(import.meta.url);
const { resolve } = createRequire(import.meta.url);

/** @type {(env: import('@callstack/repack').EnvOptions) => import('webpack').Configuration} */
export default (env) => {
Expand Down Expand Up @@ -105,9 +103,6 @@ export default (env) => {
remotes: {
MiniApp: `MiniApp@http://localhost:8082/${platform}/mf-manifest.json`,
},
runtimePlugins: [
resolve('@callstack/repack/federation-runtime-plugin'),
],
shared: {
react: {
singleton: true,
Expand Down
1 change: 0 additions & 1 deletion packages/repack/client.d.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/repack/client.js

This file was deleted.

1 change: 1 addition & 0 deletions packages/repack/client/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from '../dist/modules/ScriptManager';
1 change: 1 addition & 0 deletions packages/repack/client/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from '../dist/modules/ScriptManager';
2 changes: 0 additions & 2 deletions packages/repack/federation-runtime-plugin.d.ts

This file was deleted.

2 changes: 0 additions & 2 deletions packages/repack/federation-runtime-plugin.js

This file was deleted.

1 change: 1 addition & 0 deletions packages/repack/mf/core-plugin.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '../dist/modules/FederationRuntimePlugins/CorePlugin';
2 changes: 2 additions & 0 deletions packages/repack/mf/core-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import RepackCorePlugin from '../dist/modules/FederationRuntimePlugins/CorePlugin';
export default RepackCorePlugin;
1 change: 1 addition & 0 deletions packages/repack/mf/resolver-plugin.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '../dist/modules/FederationRuntimePlugins/ResolverPlugin';
2 changes: 2 additions & 0 deletions packages/repack/mf/resolver-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import RepackResolverPlugin from '../dist/modules/FederationRuntimePlugins/ResolverPlugin';
export default RepackResolverPlugin;
42 changes: 9 additions & 33 deletions packages/repack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,14 @@
"description": "A toolkit to build your React Native application with Rspack or Webpack.",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"react-native": "",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./client": {
"types": "./dist/modules/ScriptManager/index.d.ts",
"default": "./dist/modules/ScriptManager/index.js"
},
"./federation-runtime-plugin": {
"types": "./dist/modules/FederationRuntimePlugin.d.ts",
"default": "./dist/modules/FederationRuntimePlugin.js"
},
"./commands/webpack": {
"types": "./dist/commands/webpack/index.d.ts",
"default": "./dist/commands/webpack/index.js"
},
"./commands/rspack": {
"types": "./dist/commands/rspack/index.d.ts",
"default": "./dist/commands/rspack/index.js"
},
"./assets-loader": {
"types": "./dist/loaders/assetsLoader/index.d.ts",
"default": "./dist/loaders/assetsLoader/index.js"
},
"./flow-loader": {
"types": "./dist/loaders/flowLoader/index.d.ts",
"default": "./dist/loaders/flowLoader/index.js"
},
".": "./dist/index.js",
"./client": "./client/index.js",
"./commands/rspack": "./dist/commands/rspack/index.js",
"./commands/webpack": "./dist/commands/webpack/index.js",
"./assets-loader": "./dist/loaders/assetsLoader/index.js",
"./flow-loader": "./dist/loaders/flowLoader/index.js",
"./mf/*": "./mf/*.js",
"./package.json": "./package.json"
},
"files": [
Expand All @@ -42,10 +20,8 @@
"!android/build",
"ios",
"!ios/build",
"client.js",
"client.d.ts",
"federation-runtime-plugin.js",
"federation-runtime-plugin.d.ts",
"client",
"mf",
"callstack-repack.podspec",
"src/modules/ScriptManager/NativeScriptManager.ts"
],
Expand Down
53 changes: 0 additions & 53 deletions packages/repack/src/modules/FederationRuntimePlugin.ts

This file was deleted.

32 changes: 32 additions & 0 deletions packages/repack/src/modules/FederationRuntimePlugins/CorePlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';
import type * as RepackClient from '../ScriptManager';

const RepackCorePlugin: () => FederationRuntimePlugin = () => ({
name: 'repack-core-plugin',
loadEntry: async ({ remoteInfo }) => {
const client = require('../ScriptManager') as typeof RepackClient;
const { ScriptManager, getWebpackContext } = client;
const { entry, entryGlobalName } = remoteInfo;

try {
await ScriptManager.shared.loadScript(
entryGlobalName,
undefined,
getWebpackContext(),
entry
);

// @ts-ignore
if (!globalThis[entryGlobalName]) {
throw new Error();
}

// @ts-ignore
return globalThis[entryGlobalName];
} catch {
console.error(`Failed to load remote entry: ${entryGlobalName}`);
}
},
});

export default RepackCorePlugin;
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';
import type * as RepackClient from '../ScriptManager';

export type RepackResolverPluginConfiguration =
| Omit<RepackClient.ScriptLocator, 'url'>
| ((url: string) => Promise<RepackClient.ScriptLocator>);

const createScriptLocator = async (
entryUrl: string,
config?: RepackResolverPluginConfiguration
) => {
if (typeof config === 'function') {
const locator = await config(entryUrl);
return locator;
}
if (typeof config === 'object') {
return { url: entryUrl, ...config };
}
return { url: entryUrl };
};

const RepackResolverPlugin: (
config?: RepackResolverPluginConfiguration
) => FederationRuntimePlugin = (config) => ({
name: 'repack-resolver-plugin',
afterResolve(args) {
const { ScriptManager } =
require('../ScriptManager') as typeof RepackClient;
const { remoteInfo } = args;

ScriptManager.shared.addResolver(
async (scriptId, caller, referenceUrl) => {
// entry container
if (scriptId === remoteInfo.entryGlobalName) {
const locator = await createScriptLocator(remoteInfo.entry, config);
return locator;
}
// entry chunks
if (referenceUrl && caller === remoteInfo.entryGlobalName) {
const publicPath = remoteInfo.entry.split('/').slice(0, -1).join('/');
const bundlePath = scriptId + referenceUrl.split(scriptId)[1];
const url = publicPath + '/' + bundlePath;

const locator = await createScriptLocator(url, config);
return locator;
}
},
{ key: remoteInfo.entryGlobalName }
);

return args;
},
});

export default RepackResolverPlugin;
26 changes: 19 additions & 7 deletions packages/repack/src/plugins/ModuleFederationPluginV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { isRspackCompiler } from './utils/isRspackCompiler';
*/
export interface ModuleFederationPluginV2Config
extends MF.ModuleFederationPluginOptions {
/** Whether to disable adding default runtime plugins to the configuration. */
disableDefaultRuntimePlugins?: boolean;
/** Enable or disable adding React Native deep imports to shared dependencies. Defaults to true */
reactNativeDeepImports?: boolean;
}
Expand Down Expand Up @@ -83,11 +85,14 @@ export interface ModuleFederationPluginV2Config
export class ModuleFederationPluginV2 implements RspackPluginInstance {
public config: MF.ModuleFederationPluginOptions;
private deepImports: boolean;
private disableDefaultRuntimePlugins: boolean;

constructor(pluginConfig: ModuleFederationPluginV2Config) {
const { reactNativeDeepImports, ...config } = pluginConfig;
const { disableDefaultRuntimePlugins, reactNativeDeepImports, ...config } =
pluginConfig;
this.config = config;
this.deepImports = reactNativeDeepImports ?? true;
this.disableDefaultRuntimePlugins = disableDefaultRuntimePlugins ?? false;
jbroma marked this conversation as resolved.
Show resolved Hide resolved
}

private ensureModuleFederationPackageInstalled(context: string) {
Expand All @@ -105,9 +110,14 @@ export class ModuleFederationPluginV2 implements RspackPluginInstance {
context: string,
runtimePlugins: string[] | undefined = []
) {
const repackRuntimePlugin = require.resolve(
'../modules/FederationRuntimePlugin'
);
if (this.disableDefaultRuntimePlugins) {
return runtimePlugins;
}

const defaultRuntimePlugins = [
require.resolve('../modules/FederationRuntimePlugins/CorePlugin'),
require.resolve('../modules/FederationRuntimePlugins/ResolverPlugin'),
];

const plugins = runtimePlugins
.map((pluginPath) => {
Expand All @@ -121,11 +131,13 @@ export class ModuleFederationPluginV2 implements RspackPluginInstance {
})
.filter((pluginPath) => !!pluginPath) as string[];

if (!plugins.includes(repackRuntimePlugin)) {
return [repackRuntimePlugin, ...runtimePlugins];
for (const plugin of defaultRuntimePlugins) {
if (!plugins.includes(plugin)) {
plugins.unshift(plugin);
}
}

return runtimePlugins;
return plugins;
}

private getModuleFederationPlugin(compiler: Compiler) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ const mockPlugin = MFPluginRspack as unknown as jest.Mock<
typeof MFPluginRspack
>;

const runtimePluginPath = require.resolve(
'../../modules/FederationRuntimePlugin'
const corePluginPath = require.resolve(
'../../modules/FederationRuntimePlugins/CorePlugin'
);
const resolverPluginPath = require.resolve(
'../../modules/FederationRuntimePlugins/ResolverPlugin'
);

describe('ModuleFederationPlugin', () => {
Expand Down Expand Up @@ -136,22 +139,24 @@ describe('ModuleFederationPlugin', () => {
expect(config.shared['@react-native/'].eager).toBe(false);
});

it('should add FederationRuntimePlugin to runtime plugins', () => {
it('should add CorePlugin & ResolverPlugin to runtime plugins by default', () => {
new ModuleFederationPluginV2({ name: 'test' }).apply(mockCompiler);

const config = mockPlugin.mock.calls[0][0];
expect(config.runtimePlugins).toContain(runtimePluginPath);
expect(config.runtimePlugins).toContain(corePluginPath);
expect(config.runtimePlugins).toContain(resolverPluginPath);
});

it('should not add FederationRuntimePlugin to runtime plugins when already present', () => {
it('should not add duplicate default runtime plugins when already present', () => {
new ModuleFederationPluginV2({
name: 'test',
runtimePlugins: [runtimePluginPath],
runtimePlugins: [corePluginPath, resolverPluginPath],
}).apply(mockCompiler);

const config = mockPlugin.mock.calls[0][0];
expect(config.runtimePlugins).toContain(runtimePluginPath);
expect(config.runtimePlugins).toHaveLength(1);
expect(config.runtimePlugins).toContain(corePluginPath);
expect(config.runtimePlugins).toContain(resolverPluginPath);
expect(config.runtimePlugins).toHaveLength(2);
});

it('should use loaded-first as default shareStrategy', () => {
Expand Down
Loading