diff --git a/README.md b/README.md
index 1c3fdc5..04dd061 100644
--- a/README.md
+++ b/README.md
@@ -7,8 +7,50 @@ compiled remote types from other federated microapps into _src/@types/remotes_ f
Global type definitions from _src/@types/*.d.ts_ are included in compilation.
-By default, types are compiled after every webpack compilation. Remote types are downloaded
-on webpack build startup and after compilation with a 1-minute interval when idle.
+- [@touk/federated-types](https://github.com/touk/federated-types)
+ a fork of [pixability/federated-types](https://github.com/pixability/federated-types)
+- [ruanyl/dts-loader](https://github.com/ruanyl/dts-loader)
+- [ruanyl/webpack-remote-types-plugin](https://github.com/ruanyl/webpack-remote-types-plugin), a wmf remotes-aware
+ downloader
+ of typings that can be used also with files emitted using `@touk/federated-types`
+ . [Example](https://github.com/jrandeniya/federated-types-sample).
+- [@module-federation/typescript](https://app.privjs.com/buy/packageDetail?pkg=@module-federation/typescript)
+ from the creator of Webpack Module Federation, Zack Jackson (aka [ScriptAlchemy](https://twitter.com/ScriptedAlchemy))
+
+Zack Jackson was asked for help with
+[several issues](https://github.com/module-federation/module-federation-examples/issues/20#issuecomment-1153131082)
+around his plugin. There was a hope that he can suggest some solutions to the exposed problems, to no avail.
+After a month of waiting this package was built.
+
+## Feature comparison tables
+
+| Feature | @touk/
federated-types | ruanyl/dts-loader | ruanyl/webpack-remote-types-plugin | @module-federation/typescript | @cloudbeds/wmf-types-plugin |
+|------------------------------------|---------------------------|-------------------|------------------------------------|-------------------------------|-----------------------------|
+| Webpack Plugin | - | + | + | + | + |
+| Standalone | + | - | - | - | + |
+| Polyrepo support | - | + | + | + | + |
+| Runtime microapp imports | - | - | - | - | + |
+| Support typings from node_modules | - | - | - | - | + |
+| Webpack aliases | - | - | - | - | + |
+| Exposed aliases | + | + | + | - | + |
+| Excessive recompilation prevention | - | - | - | - | + |
+
+*_Runtime microapp imports_ refers to templated remote URLs that are resolved in runtime using
+[module-federation/external-remotes-plugin](https://github.com/module-federation/external-remotes-plugin)
+
+*_Synchronization_ refers to [webpack compile hooks](https://webpack.js.org/api/compiler-hooks/)
+
+*_Excessive recompilation_ refers to the fact that the plugin is not smart enough to detect when the typings file is changed.
+Every time a `d.ts` file is downloaded, webpack recompiles the whole bundle because the watcher compares the timestamp only, which is updated on every download.
+
+| Package | Emitted destination | Download destination | Synchronization/[compile hooks](https://webpack.js.org/api/compiler-hooks/) |
+|------------------------------------|------------------------------------------------------|----------------------|----------------------------------------------------------------------------------------------------------|
+| @touk/federated-types | file in
`node_modules/@types/__federated_types` | - | - |
+| ruanyl/dts-loader | folders in
`.wp_federation` | - | - |
+| ruanyl/webpack-remote-types-plugin | - | `types/[name]-dts` | download on `beforeRun` and `watchRun` |
+| @module-federation/typescript | folders in
`dist/@mf-typescript` | `@mf-typescript` | compile and download on `afterCompile` (leads to double compile),
redo every 1 minute when idle |
+| @cloudbeds/wmf-types-plugin | file in
`dist/@types` | `@remote-types` | download on startup,
compile `afterEmit`,
download every 1 minute or custom interval when idle |
+
## Installation
diff --git a/src/constants.ts b/src/constants.ts
index 7f84e45..b29797c 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -12,3 +12,7 @@ export const CLOUDBEDS_DEV_FRONTEND_ASSETS_DOMAIN = 'https://cb-front.cloudbeds-
export const CLOUDBEDS_MFD_COMMON_MANIFEST_FILE_NAME = 'mfd-common-remote-entry.json';
export const CLOUDBEDS_REMOTES_MANIFEST_FILE_NAME = 'remote-entries.json';
export const CLOUDBEDS_DEPLOYMENT_ENV_WITH_DISABLED_REMOTE_TYPES_DOWNLOAD = 'devbox';
+
+export enum CloudbedsMicrofrontend {
+ Common = 'mfdCommon',
+}
diff --git a/src/helpers/cloudbedsRemoteManifests.ts b/src/helpers/cloudbedsRemoteManifests.ts
index f7b935c..c76f854 100644
--- a/src/helpers/cloudbedsRemoteManifests.ts
+++ b/src/helpers/cloudbedsRemoteManifests.ts
@@ -2,6 +2,7 @@ import {
CLOUDBEDS_DEV_FRONTEND_ASSETS_DOMAIN,
CLOUDBEDS_MFD_COMMON_MANIFEST_FILE_NAME,
CLOUDBEDS_REMOTES_MANIFEST_FILE_NAME,
+ CloudbedsMicrofrontend,
} from '../constants';
import { ModuleFederationTypesPluginOptions, RemoteManifestUrls } from '../types';
@@ -12,7 +13,7 @@ export function getRemoteManifestUrls(options?: ModuleFederationTypesPluginOptio
baseUrl = `${CLOUDBEDS_DEV_FRONTEND_ASSETS_DOMAIN}/remotes/dev-ga`;
}
return {
- mfdCommon: `${baseUrl}/${CLOUDBEDS_MFD_COMMON_MANIFEST_FILE_NAME}`,
+ [CloudbedsMicrofrontend.Common]: `${baseUrl}/${CLOUDBEDS_MFD_COMMON_MANIFEST_FILE_NAME}`,
registry: `${baseUrl}/${CLOUDBEDS_REMOTES_MANIFEST_FILE_NAME}`,
...options?.remoteManifestUrls,
}
diff --git a/src/helpers/compileTypes.ts b/src/helpers/compileTypes.ts
index c5272d6..ed0ac66 100644
--- a/src/helpers/compileTypes.ts
+++ b/src/helpers/compileTypes.ts
@@ -59,6 +59,40 @@ export function compileTypes(exposedComponents: string[], outFile: string): Comp
};
}
+export function includeTypesFromNodeModules(federationConfig: FederationConfig, typings: string): string {
+ const logger = getLogger();
+ let typingsWithNpmPackages = typings;
+
+ const exposedNpmPackages = Object.entries(federationConfig.exposes)
+ .filter(([, path]) => !path.startsWith('.') || path.startsWith('./node_modules/'))
+ .map(([exposedModuleKey, exposeTargetPath]) => [
+ exposedModuleKey.replace(/^\.\//, ''),
+ exposeTargetPath.replace('./node_modules/', ''),
+ ]);
+
+ // language=TypeScript
+ const createNpmModule = (exposedModuleKey: string, packageName: string) => `
+ declare module "${federationConfig.name}/${exposedModuleKey}" {
+ export * from "${packageName}"
+ }
+ `;
+
+ if (exposedNpmPackages.length) {
+ logger.log('Including typings for npm packages:', exposedNpmPackages);
+ }
+
+ try {
+ exposedNpmPackages.forEach(([exposedModuleKey, packageName]) => {
+ typingsWithNpmPackages += `\n${createNpmModule(exposedModuleKey, packageName)}`;
+ });
+ } catch (err) {
+ logger.warn('Typings was not included for npm package:', (err as Dict)?.url);
+ logger.log(err);
+ }
+
+ return typingsWithNpmPackages;
+}
+
export function rewritePathsWithExposedFederatedModules(
federationConfig: FederationConfig,
outFile: string,
@@ -86,12 +120,12 @@ export function rewritePathsWithExposedFederatedModules(
// Replace and prefix paths by exposed remote names
moduleImportPaths.forEach((importPath) => {
- const [exposePath, ...aliases] = Object.keys(federationConfig.exposes)
+ const [exposedModuleKey, ...exposedModuleNameAliases] = Object.keys(federationConfig.exposes)
.filter(key => federationConfig.exposes[key].endsWith(substituteAliases(importPath)))
.map(key => key.replace(/^\.\//, ''));
- let federatedModulePath = exposePath
- ? `${federationConfig.name}/${exposePath}`
+ let federatedModulePath = exposedModuleKey
+ ? `${federationConfig.name}/${exposedModuleKey}`
: `@not-for-import/${federationConfig.name}/${importPath}`;
federatedModulePath = federatedModulePath.replace(/\/index$/, '')
@@ -105,7 +139,7 @@ export function rewritePathsWithExposedFederatedModules(
typingsUpdated = [
typingsUpdated.replace(RegExp(`"${importPath}"`, 'g'), `"${federatedModulePath}"`),
- ...aliases.map(createAliasModule),
+ ...exposedModuleNameAliases.map(createAliasModule),
].join('\n');
});
diff --git a/src/plugin.spec.ts b/src/plugin.spec.ts
index 153878e..d5764f7 100644
--- a/src/plugin.spec.ts
+++ b/src/plugin.spec.ts
@@ -3,6 +3,7 @@ import webpack, { Compilation, Compiler } from 'webpack';
import { downloadTypes } from './helpers/downloadTypes';
import { ModuleFederationTypesPlugin } from './plugin';
import { ModuleFederationPluginOptions, ModuleFederationTypesPluginOptions } from './types';
+import { CloudbedsMicrofrontend } from './constants';
jest.mock('./helpers/downloadTypes');
@@ -48,15 +49,15 @@ describe('ModuleFederationTypesPlugin', () => {
test('remoteManifestUrls setting initiates download of remote entry manifest files on startup', () => {
const moduleFederationPluginOptions = {
- name: 'mfdCommon',
+ name: 'mfdDashboard',
remotes: {
- mfdCommon: 'mfdCommon@[mfdCommon]/remoteEntry.js',
- mfdTranslations: 'mfdTranslations@[mfdTranslations]/remoteEntry.js',
+ [CloudbedsMicrofrontend.Common]: `${CloudbedsMicrofrontend.Common}@[mfdCommonUrl]/remoteEntry.js`,
+ mfdTranslations: 'mfdTranslations@[mfdTranslationsUrl]/remoteEntry.js',
}
};
const typesPluginOptions = {
remoteManifestUrls: {
- mfdCommon: 'https://example.com/mfd-common-remote-entries.json',
+ [CloudbedsMicrofrontend.Common]: 'https://example.com/mfd-common-remote-entries.json',
registry: 'https://example.com/remote-entries.json',
}
};
diff --git a/src/plugin.ts b/src/plugin.ts
index 2316b0a..09bea9d 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -8,7 +8,11 @@ import {
DIR_EMITTED_TYPES
} from './constants';
import { getRemoteManifestUrls } from './helpers/cloudbedsRemoteManifests';
-import { compileTypes, rewritePathsWithExposedFederatedModules } from './helpers/compileTypes';
+import {
+ compileTypes,
+ includeTypesFromNodeModules,
+ rewritePathsWithExposedFederatedModules
+} from './helpers/compileTypes';
import { downloadTypes } from './helpers/downloadTypes';
import { getLoggerHint, setLogger } from './helpers/logger';
import { isEveryUrlValid } from './helpers/validation';
@@ -62,7 +66,8 @@ export class ModuleFederationTypesPlugin implements WebpackPluginInstance {
const compileTypesHook = () => {
const { isSuccess, typeDefinitions } = compileTypes(exposes as string[], outFile);
if (isSuccess) {
- rewritePathsWithExposedFederatedModules(federationPluginOptions as FederationConfig, outFile, typeDefinitions);
+ const typings = includeTypesFromNodeModules(federationPluginOptions as FederationConfig, typeDefinitions);
+ rewritePathsWithExposedFederatedModules(federationPluginOptions as FederationConfig, outFile, typings);
} else {
logger.warn('Failed to compile types for exposed modules.', getLoggerHint(compiler));
}
diff --git a/src/remote-npm-package-typings.ts b/src/remote-npm-package-typings.ts
new file mode 100644
index 0000000..5f1cc75
--- /dev/null
+++ b/src/remote-npm-package-typings.ts
@@ -0,0 +1,3 @@
+export const remoteNpmPackageTypings = {
+ '@cloudbeds/ui-library': ['@chakra-ui'],
+};