Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
9 changes: 9 additions & 0 deletions .changeset/gentle-melons-deliver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@module-federation/dynamic-remote-type-hints-plugin': patch
'@module-federation/third-party-dts-extractor': patch
'@module-federation/dts-plugin': patch
'@module-federation/runtime': patch
'@module-federation/sdk': patch
---

feat: support dynamic remote type hints
3 changes: 2 additions & 1 deletion apps/runtime-demo/3005-runtime-host/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"react-refresh": "0.14.0"
},
"dependencies": {
"antd": "4.24.15"
"antd": "4.24.15",
"@module-federation/dynamic-remote-type-hints-plugin": "workspace:*"
}
}
9 changes: 8 additions & 1 deletion apps/runtime-demo/3005-runtime-host/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ const {
const { composePlugins, withNx } = require('@nx/webpack');
const { withReact } = require('@nx/react');

const runtimePlugins = [path.join(__dirname, './runtimePlugin.ts')];
if (process.env.NODE_ENV === 'development') {
runtimePlugins.push(
require.resolve('@module-federation/dynamic-remote-type-hints-plugin'),
);
}

module.exports = composePlugins(withNx(), withReact(), (config, context) => {
config.watchOptions = {
ignored: ['**/node_modules/**', '**/@mf-types/**', '**/dist/**'],
Expand Down Expand Up @@ -50,7 +57,7 @@ module.exports = composePlugins(withNx(), withReact(), (config, context) => {
requiredVersion: '^18.2.0',
},
},
runtimePlugins: [path.join(__dirname, './runtimePlugin.ts')],
runtimePlugins: runtimePlugins,
}),
);
config.optimization.runtimeChunk = false;
Expand Down
2 changes: 1 addition & 1 deletion apps/runtime-demo/3006-runtime-remote/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ module.exports = composePlugins(
// e.g. `config.plugins.push(new MyPlugin())`
config.output = {
...config.output,
publicPath: 'http://localhost:3006/',
publicPath: 'http://127.0.0.1:3006/',
scriptType: 'text/javascript',
};
config.optimization = {
Expand Down
2 changes: 1 addition & 1 deletion apps/runtime-demo/3007-runtime-remote/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ module.exports = composePlugins(
// e.g. `config.plugins.push(new MyPlugin())`
config.output = {
...config.output,
publicPath: 'http://localhost:3007/',
publicPath: 'http://127.0.0.1:3007/',
scriptType: 'text/javascript',
};
config.optimization = {
Expand Down
16 changes: 16 additions & 0 deletions packages/dts-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@
"import": "./dist/core.js",
"require": "./dist/core.js"
},
"./create-websocket": {
"types": "./dist/create-websocket.d.ts",
"import": "./dist/esm/create-websocket.js",
"require": "./dist/create-websocket.js"
},
"./server-actions": {
"types": "./dist/server-actions.d.ts",
"import": "./dist/esm/server-actions.js",
"require": "./dist/server-actions.js"
},
"./*": "./*"
},
"typesVersions": {
Expand All @@ -31,6 +41,12 @@
],
"core": [
"./dist/core.d.ts"
],
"create-websocket": [
"./dist/create-websocket.d.ts"
],
"server-actions": [
"./dist/server-actions.d.ts"
]
}
},
Expand Down
2 changes: 1 addition & 1 deletion packages/dts-plugin/src/core/configurations/hostPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const buildApiTypeUrl = (zipUrl?: string) => {
return zipUrl.replace('.zip', '.d.ts');
};

const retrieveRemoteInfo = (options: {
export const retrieveRemoteInfo = (options: {
hostOptions: Required<HostOptions>;
remoteAlias: string;
remote: string;
Expand Down
6 changes: 3 additions & 3 deletions packages/dts-plugin/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export {
UpdateMode,
} from './constant';

export { DTSManagerOptions } from './interfaces/DTSManagerOptions';
export { HostOptions } from './interfaces/HostOptions';
export { RemoteOptions } from './interfaces/RemoteOptions';
export type { DTSManagerOptions } from './interfaces/DTSManagerOptions';
export type { HostOptions } from './interfaces/HostOptions';
export type { RemoteOptions } from './interfaces/RemoteOptions';
export * as rpc from './rpc/index';
160 changes: 120 additions & 40 deletions packages/dts-plugin/src/core/lib/DTSManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
inferAutoPublicPath,
} from '@module-federation/sdk';
import cloneDeepWith from 'lodash.clonedeepwith';
import { ThirdPartyExtractor } from '@module-federation/third-party-dts-extractor';

import { retrieveRemoteConfig } from '../configurations/remotePlugin';
import { createTypesArchive, downloadTypesArchive } from './archiveHandler';
Expand All @@ -16,7 +17,10 @@ import {
retrieveMfAPITypesPath,
retrieveMfTypesPath,
} from './typeScriptCompiler';
import { retrieveHostConfig } from '../configurations/hostPlugin';
import {
retrieveHostConfig,
retrieveRemoteInfo,
} from '../configurations/hostPlugin';
import { DTSManagerOptions } from '../interfaces/DTSManagerOptions';
import { HostOptions, RemoteInfo } from '../interfaces/HostOptions';
import {
Expand All @@ -34,14 +38,17 @@ interface UpdateTypesOptions {
updateMode: UpdateMode;
remoteName?: string;
remoteTarPath?: string;
remoteInfo?: RemoteInfo;
once?: boolean;
}

class DTSManager {
options: DTSManagerOptions;
runtimePkgs: string[];
remoteAliasMap: Record<string, Required<RemoteInfo>>;
loadedRemoteAPIAlias: string[];
loadedRemoteAPIAlias: Set<string>;
extraOptions: Record<string, any>;
updatedRemoteInfos: Record<string, Required<RemoteInfo>>;

constructor(options: DTSManagerOptions) {
this.options = cloneDeepWith(options, (_value, key) => {
Expand All @@ -55,9 +62,10 @@ class DTSManager {
'@module-federation/enhanced/runtime',
'@module-federation/runtime-tools',
];
this.loadedRemoteAPIAlias = [];
this.loadedRemoteAPIAlias = new Set();
this.remoteAliasMap = {};
this.extraOptions = options?.extraOptions || {};
this.updatedRemoteInfos = {};
}

generateAPITypes(mapComponentsToExpose: Record<string, string>) {
Expand Down Expand Up @@ -249,7 +257,7 @@ class DTSManager {
);
const filePath = path.join(destinationPath, REMOTE_API_TYPES_FILE_NAME);
fs.writeFileSync(filePath, apiTypeFile);
this.loadedRemoteAPIAlias.push(remoteInfo.alias);
this.loadedRemoteAPIAlias.add(remoteInfo.alias);
} catch (err) {
fileLog(
`Unable to download "${remoteInfo.name}" api types, ${err}`,
Expand All @@ -260,13 +268,34 @@ class DTSManager {
}

consumeAPITypes(hostOptions: Required<HostOptions>) {
if (!this.loadedRemoteAPIAlias.length) {
const apiTypeFileName = path.join(
hostOptions.context,
hostOptions.typesFolder,
HOST_API_TYPES_FILE_NAME,
);
try {
const existedFile = fs.readFileSync(apiTypeFileName, 'utf-8');
const existedImports = new ThirdPartyExtractor('').collectTypeImports(
existedFile,
);
existedImports.forEach((existedImport) => {
const alias = existedImport
.split('./')
.slice(1)
.join('./')
.replace('/apis.d.ts', '');
this.loadedRemoteAPIAlias.add(alias);
});
} catch (err) {
//noop
}
if (!this.loadedRemoteAPIAlias.size) {
return;
}
const packageTypes: string[] = [];
const remoteKeys: string[] = [];

const importTypeStr = this.loadedRemoteAPIAlias
const importTypeStr = [...this.loadedRemoteAPIAlias]
.sort()
.map((alias, index) => {
const remoteKey = `RemoteKeys_${index}`;
Expand Down Expand Up @@ -392,46 +421,97 @@ class DTSManager {
}

async updateTypes(options: UpdateTypesOptions): Promise<void> {
// can use remoteTarPath directly in the future
const { remoteName, updateMode } = options;
const hostName = this.options?.host?.moduleFederationConfig?.name;

if (updateMode === UpdateMode.POSITIVE && remoteName === hostName) {
if (!this.options.remote) {
return;
}
this.generateTypes();
} else {
const { remoteAliasMap } = this;
if (!this.options.host) {
return;
}
const { hostOptions, mapRemotesToDownload } = retrieveHostConfig(
this.options.host,
try {
// can use remoteTarPath directly in the future
const {
remoteName,
updateMode,
remoteInfo: updatedRemoteInfo,
once,
} = options;
const hostName = this.options?.host?.moduleFederationConfig?.name;
fileLog(
`updateTypes options:, ${JSON.stringify(options, null, 2)}`,
'consumeTypes',
'info',
);
if (updateMode === UpdateMode.POSITIVE && remoteName === hostName) {
if (!this.options.remote) {
return;
}
this.generateTypes();
} else {
const { remoteAliasMap } = this;
if (!this.options.host) {
return;
}
const { hostOptions, mapRemotesToDownload } = retrieveHostConfig(
this.options.host,
);

const loadedRemoteInfo = Object.values(remoteAliasMap).find(
(i) => i.name === remoteName,
);
const loadedRemoteInfo = Object.values(remoteAliasMap).find(
(i) => i.name === remoteName,
);

if (!loadedRemoteInfo) {
const remoteInfo = Object.values(mapRemotesToDownload).find((item) => {
return item.name === remoteName;
});
if (remoteInfo) {
if (!this.remoteAliasMap[remoteInfo.alias]) {
const requiredRemoteInfo =
await this.requestRemoteManifest(remoteInfo);
this.remoteAliasMap[remoteInfo.alias] = requiredRemoteInfo;
}
await this.consumeTargetRemotes(
hostOptions,
this.remoteAliasMap[remoteInfo.alias],
if (!loadedRemoteInfo) {
const remoteInfo = Object.values(mapRemotesToDownload).find(
(item) => {
return item.name === remoteName;
},
);
if (remoteInfo) {
if (!this.remoteAliasMap[remoteInfo.alias]) {
const requiredRemoteInfo =
await this.requestRemoteManifest(remoteInfo);
this.remoteAliasMap[remoteInfo.alias] = requiredRemoteInfo;
}
await this.consumeTargetRemotes(
hostOptions,
this.remoteAliasMap[remoteInfo.alias],
);
} else if (updatedRemoteInfo) {
const consumeDynamicRemoteTypes = async () => {
const [_destinationFolder, destinationPath] =
await this.consumeTargetRemotes(
hostOptions,
this.updatedRemoteInfos[updatedRemoteInfo.name],
);
await this.downloadAPITypes(
this.updatedRemoteInfos[updatedRemoteInfo.name],
destinationPath,
);
this.consumeAPITypes(hostOptions);
};
if (!this.updatedRemoteInfos[updatedRemoteInfo.name]) {
const parsedRemoteInfo = retrieveRemoteInfo({
hostOptions: hostOptions,
remoteAlias: updatedRemoteInfo.alias || updatedRemoteInfo.name,
remote: updatedRemoteInfo.url,
});
fileLog(`start request manifest`, 'consumeTypes', 'info');
this.updatedRemoteInfos[updatedRemoteInfo.name] =
await this.requestRemoteManifest(parsedRemoteInfo);
fileLog(
`end request manifest, this.updatedRemoteInfos[updatedRemoteInfo.name]: ${JSON.stringify(
this.updatedRemoteInfos[updatedRemoteInfo.name],
null,
2,
)}`,
'consumeTypes',
'info',
);
await consumeDynamicRemoteTypes();
}
if (!once && this.updatedRemoteInfos[updatedRemoteInfo.name]) {
await consumeDynamicRemoteTypes();
}
}
} else {
await this.consumeTargetRemotes(hostOptions, loadedRemoteInfo);
}
} else {
await this.consumeTargetRemotes(hostOptions, loadedRemoteInfo);
}
} catch (err) {
fileLog(`updateTypes fail, ${err}`, 'updateTypes', 'error');
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/dts-plugin/src/core/lib/DtsWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class DtsWorker {
});
this.removeUnSerializationOptions();
this.rpcWorker = createRpcWorker(
path.resolve(__dirname, './forkGenerateDts.js'),
path.resolve(__dirname, './fork-generate-dts.js'),
{},
undefined,
true,
Expand Down
2 changes: 1 addition & 1 deletion packages/dts-plugin/src/dev-worker/DevWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class DevWorker {
});
this.removeUnSerializationOptions();
this._rpcWorker = rpc.createRpcWorker(
path.resolve(__dirname, './forkDevWorker.js'),
path.resolve(__dirname, './fork-dev-worker.js'),
{},
undefined,
false,
Expand Down
6 changes: 5 additions & 1 deletion packages/dts-plugin/src/dev-worker/forkDevWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@ async function updateCallback({
updateMode,
name,
remoteTypeTarPath,
remoteInfo,
once,
}: UpdateCallbackOptions): Promise<void> {
const { disableHotTypesReload, disableLiveReload } = cacheOptions || {};
fileLog(
`sync remote module ${name}, types to vmok ${cacheOptions?.name},typesManager.updateTypes run`,
`sync remote module ${name}, types to ${cacheOptions?.name},typesManager.updateTypes run`,
'forkDevWorker',
'info',
);
Expand All @@ -98,6 +100,8 @@ async function updateCallback({
updateMode,
remoteName: name,
remoteTarPath: remoteTypeTarPath,
remoteInfo,
once,
});
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/dts-plugin/src/dev-worker/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { createDevWorker } from './createDevWorker';
export { DevWorker, DevWorkerOptions } from './DevWorker';
export { DevWorker, type DevWorkerOptions } from './DevWorker';
Loading