diff --git a/.changeset/bright-brooms-divide.md b/.changeset/bright-brooms-divide.md new file mode 100644 index 0000000000..0784200303 --- /dev/null +++ b/.changeset/bright-brooms-divide.md @@ -0,0 +1,6 @@ +--- +'@module-federation/dts-plugin': minor +'@module-federation/sdk': minor +--- + +Adding `remoteBasePath` to `DtsHostOptions` enabling types to be resolved from a relative remote path. diff --git a/packages/dts-plugin/src/core/configurations/hostPlugin.test.ts b/packages/dts-plugin/src/core/configurations/hostPlugin.test.ts index fe5cc1c0e0..084d38425a 100644 --- a/packages/dts-plugin/src/core/configurations/hostPlugin.test.ts +++ b/packages/dts-plugin/src/core/configurations/hostPlugin.test.ts @@ -41,6 +41,7 @@ describe('hostPlugin', () => { abortOnError: true, consumeAPITypes: false, runtimePkgs: [], + remoteBasePath: 'file:', }); expect(mapRemotesToDownload).toStrictEqual({ @@ -66,6 +67,7 @@ describe('hostPlugin', () => { abortOnError: true, consumeAPITypes: false, runtimePkgs: [], + remoteBasePath: 'file:', }; const { hostOptions, mapRemotesToDownload } = @@ -143,5 +145,54 @@ describe('hostPlugin', () => { }, }); }); + + it('uses the provided remote base path for relative remote urls', () => { + const subpathModuleFederationConfig = { + ...moduleFederationConfig, + remotes: { + moduleFederationTypescript: '/subpatha/mf-manifest.json', + }, + }; + + const { mapRemotesToDownload } = retrieveHostConfig({ + moduleFederationConfig: subpathModuleFederationConfig, + remoteBasePath: 'http://localhost:3000', + }); + + expect(mapRemotesToDownload).toStrictEqual({ + moduleFederationTypescript: { + alias: 'moduleFederationTypescript', + apiTypeUrl: 'http://localhost:3000/subpatha/@mf-types.d.ts', + name: '/subpatha/mf-manifest.json', + url: 'http://localhost:3000/subpatha/mf-manifest.json', + zipUrl: 'http://localhost:3000/subpatha/@mf-types.zip', + }, + }); + }); + + it('ignores the provided remote base path for absolute remote urls', () => { + const subpathModuleFederationConfig = { + ...moduleFederationConfig, + remotes: { + moduleFederationTypescript: + 'http://localhost:3333/subpatha/mf-manifest.json', + }, + }; + + const { mapRemotesToDownload } = retrieveHostConfig({ + moduleFederationConfig: subpathModuleFederationConfig, + remoteBasePath: 'http://localhost:3000', + }); + + expect(mapRemotesToDownload).toStrictEqual({ + moduleFederationTypescript: { + alias: 'moduleFederationTypescript', + apiTypeUrl: 'http://localhost:3333/subpatha/@mf-types.d.ts', + name: 'http://localhost:3333/subpatha/mf-manifest.json', + url: 'http://localhost:3333/subpatha/mf-manifest.json', + zipUrl: 'http://localhost:3333/subpatha/@mf-types.zip', + }, + }); + }); }); }); diff --git a/packages/dts-plugin/src/core/configurations/hostPlugin.ts b/packages/dts-plugin/src/core/configurations/hostPlugin.ts index bc2f113cec..192d6ded20 100644 --- a/packages/dts-plugin/src/core/configurations/hostPlugin.ts +++ b/packages/dts-plugin/src/core/configurations/hostPlugin.ts @@ -8,6 +8,8 @@ import { utils } from '@module-federation/managers'; import { HostOptions, RemoteInfo } from '../interfaces/HostOptions'; import { validateOptions } from '../lib/utils'; +const fileBase = 'file:'; + const defaultOptions = { typesFolder: '@mf-types', remoteTypesFolder: '@mf-types', @@ -18,18 +20,31 @@ const defaultOptions = { abortOnError: true, consumeAPITypes: false, runtimePkgs: [], + remoteBasePath: fileBase, } satisfies Partial; -const buildZipUrl = (hostOptions: Required, url: string) => { - const remoteUrl = new URL(url, 'file:'); - - const pathnameWithoutEntry = remoteUrl.pathname - .split('/') - .slice(0, -1) - .join('/'); - remoteUrl.pathname = `${pathnameWithoutEntry}/${hostOptions.remoteTypesFolder}.zip`; +const resolveRelativeUrl = ( + hostOptions: Required, + entryUrl: string, + relativeFile?: string, +) => { + let remoteUrl = new URL(entryUrl, hostOptions.remoteBasePath); + if (relativeFile) { + const pathnameWithoutEntry = remoteUrl.pathname + .split('/') + .slice(0, -1) + .join('/'); + remoteUrl.pathname = `${pathnameWithoutEntry}/${relativeFile}`; + } + return remoteUrl.protocol === fileBase ? remoteUrl.pathname : remoteUrl.href; +}; - return remoteUrl.protocol === 'file:' ? remoteUrl.pathname : remoteUrl.href; +const buildZipUrl = (hostOptions: Required, url: string) => { + return resolveRelativeUrl( + hostOptions, + url, + `${hostOptions.remoteTypesFolder}.zip`, + ); }; const buildApiTypeUrl = (zipUrl?: string) => { @@ -63,7 +78,7 @@ export const retrieveRemoteInfo = (options: { return { name: parsedInfo.name || remoteAlias, - url: url, + url: resolveRelativeUrl(hostOptions, url), zipUrl, apiTypeUrl: buildApiTypeUrl(zipUrl), alias: remoteAlias, diff --git a/packages/sdk/src/types/plugins/ModuleFederationPlugin.ts b/packages/sdk/src/types/plugins/ModuleFederationPlugin.ts index 5ffa3b16fb..3d6b74efc8 100644 --- a/packages/sdk/src/types/plugins/ModuleFederationPlugin.ts +++ b/packages/sdk/src/types/plugins/ModuleFederationPlugin.ts @@ -140,6 +140,10 @@ export interface DtsHostOptions { maxRetries?: number; consumeAPITypes?: boolean; runtimePkgs?: string[]; + /** + * Used as the default base path for relative remote URLs when locating types + */ + remoteBasePath?: string; } export interface DtsRemoteOptions {