Skip to content

Commit

Permalink
fix(dts-plugin): support remoteTypeUrls option which allow user to sp…
Browse files Browse the repository at this point in the history
…ecify the remote types url (#3532)
  • Loading branch information
2heal1 authored Feb 26, 2025
1 parent cbd5b7e commit 35d925b
Show file tree
Hide file tree
Showing 17 changed files with 210 additions and 64 deletions.
6 changes: 6 additions & 0 deletions .changeset/clean-pets-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@module-federation/dts-plugin': patch
'@module-federation/sdk': patch
---

feat(dts-plugin): support remoteTypeUrls option which allow user to specify the remote types url
6 changes: 6 additions & 0 deletions .changeset/curly-bears-marry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@module-federation/sdk": patch
"@module-federation/dts-plugin": patch
---

fix(dts-plugin): support parse @scope@manifest-url.json entry
2 changes: 1 addition & 1 deletion .github/workflows/e2e-modern-ssr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
run: |
lsof -ti tcp:3050,3051,3052,3053,3054,3055,3056 | xargs -r kill &&
pnpm run app:modern:dev &
sleep 1 &&
sleep 30 &&
npx wait-on http://127.0.0.1:3050/ &&
npx wait-on http://127.0.0.1:3051/ &&
npx wait-on http://127.0.0.1:3052/ &&
Expand Down
4 changes: 2 additions & 2 deletions apps/website-new/docs/en/configure/dts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ When configuring `generateTypes` to `true`, the following configuration will be
{
"generateAPITypes": true,
"abortOnError": false,
"extractThirdParty": true,
"extractRemoteTypes": true,
"extractThirdParty": false,
"extractRemoteTypes": false,
"compileInChildProcess": true
}
```
Expand Down
4 changes: 2 additions & 2 deletions apps/website-new/docs/zh/configure/dts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ interface DtsRemoteOptions {
{
"generateAPITypes": true,
"abortOnError": false,
"extractThirdParty": true,
"extractRemoteTypes": true,
"extractThirdParty": false,
"extractRemoteTypes": false,
"compileInChildProcess": true
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe('hostPlugin', () => {
abortOnError: true,
consumeAPITypes: false,
runtimePkgs: [],
remoteTypeUrls: {},
});

expect(mapRemotesToDownload).toStrictEqual({
Expand All @@ -66,6 +67,7 @@ describe('hostPlugin', () => {
abortOnError: true,
consumeAPITypes: false,
runtimePkgs: [],
remoteTypeUrls: {},
};

const { hostOptions, mapRemotesToDownload } =
Expand Down
22 changes: 18 additions & 4 deletions packages/dts-plugin/src/core/configurations/hostPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
MANIFEST_EXT,
parseEntry,
ENCODE_NAME_PREFIX,
decodeName,
Expand All @@ -18,6 +17,7 @@ const defaultOptions = {
abortOnError: true,
consumeAPITypes: false,
runtimePkgs: [],
remoteTypeUrls: {},
} satisfies Partial<HostOptions>;

const buildZipUrl = (hostOptions: Required<HostOptions>, url: string) => {
Expand Down Expand Up @@ -45,6 +45,7 @@ export const retrieveRemoteInfo = (options: {
remote: string;
}): RemoteInfo => {
const { hostOptions, remoteAlias, remote } = options;
const { remoteTypeUrls } = hostOptions;
let decodedRemote = remote;
if (decodedRemote.startsWith(ENCODE_NAME_PREFIX)) {
decodedRemote = decodeName(decodedRemote, ENCODE_NAME_PREFIX);
Expand All @@ -59,13 +60,26 @@ export const retrieveRemoteInfo = (options: {
? decodedRemote
: '';

const zipUrl = url ? buildZipUrl(hostOptions, url) : '';
let zipUrl = '';
let apiTypeUrl = '';
const name = parsedInfo.name || remoteAlias;
if (typeof remoteTypeUrls === 'object' && remoteTypeUrls[name]) {
zipUrl = remoteTypeUrls[name].zip;
apiTypeUrl = remoteTypeUrls[name].api;
}
if (!zipUrl && url) {
zipUrl = buildZipUrl(hostOptions, url);
}

if (!apiTypeUrl && zipUrl) {
apiTypeUrl = buildApiTypeUrl(zipUrl);
}

return {
name: parsedInfo.name || remoteAlias,
name,
url: url,
zipUrl,
apiTypeUrl: buildApiTypeUrl(zipUrl),
apiTypeUrl,
alias: remoteAlias,
};
};
Expand Down
61 changes: 46 additions & 15 deletions packages/dts-plugin/src/core/lib/DTSManager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ansiColors from 'ansi-colors';
import path from 'path';
import { rm } from 'fs/promises';
import fs from 'fs';
import fse from 'fs-extra';
import {
MANIFEST_EXT,
Manifest,
Expand Down Expand Up @@ -103,21 +103,35 @@ class DTSManager {

const mfTypesPath = retrieveMfTypesPath(tsConfig, remoteOptions);

if (hasRemotes) {
const tempHostOptions = {
moduleFederationConfig: remoteOptions.moduleFederationConfig,
typesFolder: path.join(mfTypesPath, 'node_modules'),
remoteTypesFolder:
remoteOptions?.hostRemoteTypesFolder || remoteOptions.typesFolder,
deleteTypesFolder: true,
context: remoteOptions.context,
implementation: remoteOptions.implementation,
abortOnError: false,
};
await this.consumeArchiveTypes(tempHostOptions);
if (hasRemotes && this.options.host) {
try {
const { hostOptions } = retrieveHostConfig(this.options.host);
const remoteTypesFolder = path.resolve(
hostOptions.context,
hostOptions.typesFolder,
);

const targetDir = path.join(mfTypesPath, 'node_modules');
if (fs.existsSync(remoteTypesFolder)) {
const targetFolder = path.resolve(remoteOptions.context, targetDir);
await fse.ensureDir(targetFolder);
await fse.copy(remoteTypesFolder, targetFolder, { overwrite: true });
}
} catch (err) {
if (this.options.host?.abortOnError === false) {
fileLog(
`Unable to copy remote types, ${err}`,
'extractRemoteTypes',
'error',
);
} else {
throw err;
}
}
}
}

// it must execute after consumeTypes
async generateTypes() {
try {
const { options } = this;
Expand All @@ -134,6 +148,21 @@ class DTSManager {
return;
}

if (tsConfig.compilerOptions.tsBuildInfoFile) {
try {
const tsBuildInfoFile = path.resolve(
remoteOptions.context,
tsConfig.compilerOptions.tsBuildInfoFile,
);
const mfTypesPath = retrieveMfTypesPath(tsConfig, remoteOptions);
if (!fs.existsSync(mfTypesPath)) {
fs.rmSync(tsBuildInfoFile, { force: true });
}
} catch (e) {
//noop
}
}

await this.extractRemoteTypes({
remoteOptions,
tsConfig,
Expand All @@ -150,7 +179,6 @@ class DTSManager {
apiTypesPath = retrieveMfAPITypesPath(tsConfig, remoteOptions);
fs.writeFileSync(apiTypesPath, apiTypes);
}

try {
if (remoteOptions.deleteTypesFolder) {
await rm(retrieveMfTypesPath(tsConfig, remoteOptions), {
Expand All @@ -167,7 +195,7 @@ class DTSManager {
} catch (error) {
if (this.options.remote?.abortOnError === false) {
if (this.options.displayErrorInTerminal) {
logger.error(`Unable to compile federated types${error}`);
logger.error(`Unable to compile federated types ${error}`);
}
} else {
throw error;
Expand All @@ -182,6 +210,9 @@ class DTSManager {
if (!remoteInfo.url.includes(MANIFEST_EXT)) {
return remoteInfo as Required<RemoteInfo>;
}
if (remoteInfo.zipUrl) {
return remoteInfo as Required<RemoteInfo>;
}
const url = remoteInfo.url;
const res = await axiosGet(url);
const manifestJson = res.data as unknown as Manifest;
Expand Down
2 changes: 0 additions & 2 deletions packages/dts-plugin/src/core/lib/DtsWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ export class DtsWorker {
rpcWorker: RpcWorker<RpcMethod>;
private _options: DtsWorkerOptions;
private _res: Promise<any>;

constructor(options: DtsWorkerOptions) {
this._options = cloneDeepOptions(options);

this.removeUnSerializationOptions();
this.rpcWorker = createRpcWorker(
path.resolve(__dirname, './fork-generate-dts.js'),
Expand Down
6 changes: 5 additions & 1 deletion packages/dts-plugin/src/core/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,15 @@ export const isTSProject = (

export function cloneDeepOptions(options: DTSManagerOptions) {
const excludeKeys = ['manifest', 'async'];
return cloneDeepWith(options, (_value, key) => {

return cloneDeepWith(options, (value, key) => {
// moduleFederationConfig.manifest may have un serialization options
if (typeof key === 'string' && excludeKeys.includes(key)) {
return false;
}
if (typeof value === 'function') {
return false;
}
});
}

Expand Down
47 changes: 34 additions & 13 deletions packages/dts-plugin/src/plugins/ConsumeTypesPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,46 @@ export class ConsumeTypesPlugin implements WebpackPluginInstance {
dtsOptions: moduleFederationPlugin.PluginDtsOptions;
defaultOptions: moduleFederationPlugin.DtsHostOptions;
callback: () => void;
fetchRemoteTypeUrlsResolve: (
options: moduleFederationPlugin.RemoteTypeUrls,
) => void;

constructor(
pluginOptions: moduleFederationPlugin.ModuleFederationPluginOptions,
dtsOptions: moduleFederationPlugin.PluginDtsOptions,
defaultOptions: moduleFederationPlugin.DtsHostOptions,
callback: () => void,
fetchRemoteTypeUrlsResolve: (
options: moduleFederationPlugin.RemoteTypeUrls,
) => void,
) {
this.pluginOptions = pluginOptions;
this.dtsOptions = dtsOptions;
this.defaultOptions = defaultOptions;
this.callback = callback;
this.fetchRemoteTypeUrlsResolve = fetchRemoteTypeUrlsResolve;
}

apply(compiler: Compiler) {
const { dtsOptions, defaultOptions, pluginOptions, callback } = this;
const {
dtsOptions,
defaultOptions,
pluginOptions,
fetchRemoteTypeUrlsResolve,
} = this;

if (isPrd()) {
callback();
fetchRemoteTypeUrlsResolve(undefined);
return;
}

const normalizedConsumeTypes =
normalizeOptions<moduleFederationPlugin.DtsRemoteOptions>(
normalizeOptions<moduleFederationPlugin.DtsHostOptions>(
true,
defaultOptions,
'mfOptions.dts.consumeTypes',
)(dtsOptions.consumeTypes);

if (!normalizedConsumeTypes) {
callback();
fetchRemoteTypeUrlsResolve(undefined);
return;
}

Expand All @@ -56,14 +66,25 @@ export class ConsumeTypesPlugin implements WebpackPluginInstance {
};

validateOptions(finalOptions.host);

const promise = consumeTypes(finalOptions)
.then(() => {
callback();
const fetchRemoteTypeUrlsPromise =
typeof normalizedConsumeTypes.remoteTypeUrls === 'function'
? normalizedConsumeTypes.remoteTypeUrls()
: Promise.resolve(normalizedConsumeTypes.remoteTypeUrls);
const promise = fetchRemoteTypeUrlsPromise.then((remoteTypeUrls) => {
consumeTypes({
...finalOptions,
host: {
...finalOptions.host,
remoteTypeUrls,
},
})
.catch(() => {
callback();
});
.then(() => {
fetchRemoteTypeUrlsResolve(remoteTypeUrls);
})
.catch(() => {
fetchRemoteTypeUrlsResolve(remoteTypeUrls);
});
});

compiler.hooks.thisCompilation.tap('mf:generateTypes', (compilation) => {
compilation.hooks.processAssets.tapPromise(
Expand Down
24 changes: 19 additions & 5 deletions packages/dts-plugin/src/plugins/DevPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,23 @@ export class DevPlugin implements WebpackPluginInstance {
private _options: moduleFederationPlugin.ModuleFederationPluginOptions;
private _devWorker?: DevWorker;
dtsOptions: moduleFederationPlugin.PluginDtsOptions;
fetchTypesPromise: Promise<void>;
generateTypesPromise: Promise<void>;
fetchRemoteTypeUrlsPromise: Promise<
moduleFederationPlugin.DtsHostOptions['remoteTypeUrls'] | undefined
>;

constructor(
options: moduleFederationPlugin.ModuleFederationPluginOptions,
dtsOptions: moduleFederationPlugin.PluginDtsOptions,
fetchTypesPromise: Promise<void>,
generateTypesPromise: Promise<void>,
fetchRemoteTypeUrlsPromise: Promise<
moduleFederationPlugin.DtsHostOptions['remoteTypeUrls'] | undefined
>,
) {
this._options = options;
this.fetchTypesPromise = fetchTypesPromise;
this.generateTypesPromise = generateTypesPromise;
this.dtsOptions = dtsOptions;
this.fetchRemoteTypeUrlsPromise = fetchRemoteTypeUrlsPromise;
}

static ensureLiveReloadEntry(
Expand Down Expand Up @@ -253,11 +260,18 @@ export class DevPlugin implements WebpackPluginInstance {
) {
remote.tsConfigPath = normalizedDtsOptions.tsConfigPath;
}
this.fetchTypesPromise.then(() => {

Promise.all([
this.generateTypesPromise,
this.fetchRemoteTypeUrlsPromise,
]).then(([_, remoteTypeUrls]) => {
this._devWorker = createDevWorker({
name,
remote: remote,
host: host,
host: {
...host,
remoteTypeUrls,
},
extraOptions: extraOptions,
disableLiveReload: normalizedDev.disableHotTypesReload,
disableHotTypesReload: normalizedDev.disableHotTypesReload,
Expand Down
Loading

0 comments on commit 35d925b

Please sign in to comment.