Skip to content

Commit

Permalink
refactor: split federation runtime plugin into separate plugins (#803)
Browse files Browse the repository at this point in the history
* refactor: split federation runtinme plugin into separate plugins:

* chore: cjs exports

* chore: package exports

* feat: add option to disable injecting runtime plugins

* feat: make resolver plugin configurable

* chore: use named exports

* test: update tests

* fix: plugin import naming

* chore: add changeset

* fix: reconfigure package exports

* chore: remove empty RN condition

* fix: add d.ts files with typings

* chore: update tester apps configs

* fix: use default export

* refactor: change to list of default runtime plugins
  • Loading branch information
jbroma authored Dec 3, 2024
1 parent 502527e commit daffbf7
Show file tree
Hide file tree
Showing 21 changed files with 147 additions and 128 deletions.
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;
30 changes: 22 additions & 8 deletions packages/repack/src/plugins/ModuleFederationPluginV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ import { isRspackCompiler } from './utils/isRspackCompiler';
*/
export interface ModuleFederationPluginV2Config
extends MF.ModuleFederationPluginOptions {
/**
* List of default runtime plugins for Federation Runtime.
* Useful if you want to modify or disable behaviour of runtime plugins.
*
* Defaults to an array containing:
* - '@callstack/repack/mf/core-plugin
* - '@callstack/repack/mf/resolver-plugin
*/
defaultRuntimePlugins?: string[];
/** Enable or disable adding React Native deep imports to shared dependencies. Defaults to true */
reactNativeDeepImports?: boolean;
}
Expand Down Expand Up @@ -83,11 +92,17 @@ export interface ModuleFederationPluginV2Config
export class ModuleFederationPluginV2 implements RspackPluginInstance {
public config: MF.ModuleFederationPluginOptions;
private deepImports: boolean;
private defaultRuntimePlugins: string[];

constructor(pluginConfig: ModuleFederationPluginV2Config) {
const { reactNativeDeepImports, ...config } = pluginConfig;
const { defaultRuntimePlugins, reactNativeDeepImports, ...config } =
pluginConfig;
this.config = config;
this.deepImports = reactNativeDeepImports ?? true;
this.defaultRuntimePlugins = defaultRuntimePlugins ?? [
'@callstack/repack/mf/core-plugin',
'@callstack/repack/mf/resolver-plugin',
];
}

private ensureModuleFederationPackageInstalled(context: string) {
Expand All @@ -105,10 +120,6 @@ export class ModuleFederationPluginV2 implements RspackPluginInstance {
context: string,
runtimePlugins: string[] | undefined = []
) {
const repackRuntimePlugin = require.resolve(
'../modules/FederationRuntimePlugin'
);

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

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

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,9 @@ const mockPlugin = MFPluginRspack as unknown as jest.Mock<
typeof MFPluginRspack
>;

const runtimePluginPath = require.resolve(
'../../modules/FederationRuntimePlugin'
const corePluginPath = require.resolve('@callstack/repack/mf/core-plugin');
const resolverPluginPath = require.resolve(
'@callstack/repack/mf/resolver-plugin'
);

describe('ModuleFederationPlugin', () => {
Expand Down Expand Up @@ -136,22 +137,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

0 comments on commit daffbf7

Please sign in to comment.