feat(react): add withModuleFederationForSSR function (#13276)

This commit is contained in:
Colum Ferry 2022-11-22 12:16:20 +00:00 committed by GitHub
parent b902849bc4
commit 8be2980339
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 138 additions and 53 deletions

View File

@ -1,8 +1,12 @@
import { withModuleFederation } from './src/module-federation/with-module-federation'; import { withModuleFederation } from './src/module-federation/with-module-federation';
import { withModuleFederationForSSR } from './src/module-federation/with-module-federation-ssr';
export { withModuleFederation }; export { withModuleFederation };
export { withModuleFederationForSSR };
// Support for older generated code: `const withModuleFederation = require('@nrwl/react/module-federation')` // Support for older generated code: `const withModuleFederation = require('@nrwl/react/module-federation')`
module.exports = withModuleFederation; module.exports = withModuleFederation;
// Allow newer generated code to work: `const { withModuleFederation } = require(...)`; // Allow newer generated code to work: `const { withModuleFederation } = require(...)`;
module.exports.withModuleFederation = withModuleFederation; module.exports.withModuleFederation = withModuleFederation;
module.exports.withModuleFederationForSSR = withModuleFederationForSSR;

View File

@ -0,0 +1,65 @@
import {
applyAdditionalShared,
applySharedFunction,
createProjectGraphAsync,
getDependentPackagesForProject,
mapRemotes,
mapRemotesForSSR,
ModuleFederationConfig,
ProjectConfiguration,
ProjectGraph,
readCachedProjectGraph,
sharePackages,
shareWorkspaceLibraries,
} from '@nrwl/devkit';
export async function getModuleFederationConfig(
mfConfig: ModuleFederationConfig,
determineRemoteUrl: (remote: string) => string,
options: { isServer: boolean } = { isServer: false }
) {
let projectGraph: ProjectGraph<ProjectConfiguration>;
try {
projectGraph = readCachedProjectGraph();
} catch (e) {
projectGraph = await createProjectGraphAsync();
}
const project = projectGraph.nodes[mfConfig.name]?.data;
if (!project) {
throw Error(
`Cannot find project "${mfConfig.name}". Check that the name is correct in module-federation.config.js`
);
}
const dependencies = getDependentPackagesForProject(
projectGraph,
mfConfig.name
);
const sharedLibraries = shareWorkspaceLibraries(
dependencies.workspaceLibraries
);
const npmPackages = sharePackages(dependencies.npmPackages);
const sharedDependencies = {
...sharedLibraries.getLibraries(),
...npmPackages,
};
applySharedFunction(sharedDependencies, mfConfig.shared);
applyAdditionalShared(
sharedDependencies,
mfConfig.additionalShared,
projectGraph
);
const mapRemotesFunction = options.isServer ? mapRemotesForSSR : mapRemotes;
const mappedRemotes =
!mfConfig.remotes || mfConfig.remotes.length === 0
? {}
: mapRemotesFunction(mfConfig.remotes, 'js', determineRemoteUrl);
return { sharedLibraries, sharedDependencies, mappedRemotes };
}

View File

@ -0,0 +1,64 @@
import { ModuleFederationConfig } from '@nrwl/devkit';
import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph';
import { getModuleFederationConfig } from './utils';
function determineRemoteUrl(remote: string) {
const remoteConfiguration = readCachedProjectConfiguration(remote);
const serveTarget = remoteConfiguration?.targets?.serve;
if (!serveTarget) {
throw new Error(
`Cannot automatically determine URL of remote (${remote}). Looked for property "host" in the project's "serve" target.\n
You can also use the tuple syntax in your webpack config to configure your remotes. e.g. \`remotes: [['remote1', '//localhost:4201']]\``
);
}
const host = serveTarget.options?.host ?? '//localhost';
const port = serveTarget.options?.port ?? 4201;
return `${
host.endsWith('/') ? host.slice(0, -1) : host
}:${port}/server/remoteEntry.js`;
}
export async function withModuleFederationForSSR(
options: ModuleFederationConfig
) {
const reactWebpackConfig = require('../../plugins/webpack');
const { sharedLibraries, sharedDependencies, mappedRemotes } =
await getModuleFederationConfig(options, determineRemoteUrl, {
isServer: true,
});
return (config) => {
config = reactWebpackConfig(config);
config.target = false;
config.output.uniqueName = options.name;
config.optimization = {
runtimeChunk: false,
};
config.plugins.push(
new (require('@module-federation/node').UniversalFederationPlugin)(
{
name: options.name,
filename: 'remoteEntry.js',
exposes: options.exposes,
remotes: mappedRemotes,
shared: {
...sharedDependencies,
},
library: {
type: 'commonjs-module',
},
isServer: true,
},
{}
),
sharedLibraries.getReplacementPlugin()
);
return config;
};
}

View File

@ -1,17 +1,6 @@
import { import { ModuleFederationConfig } from '@nrwl/devkit';
applyAdditionalShared,
applySharedFunction,
createProjectGraphAsync,
getDependentPackagesForProject,
mapRemotes,
ModuleFederationConfig,
ProjectConfiguration,
ProjectGraph,
readCachedProjectGraph,
sharePackages,
shareWorkspaceLibraries,
} from '@nrwl/devkit';
import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph'; import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph';
import { getModuleFederationConfig } from './utils';
import ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); import ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
function determineRemoteUrl(remote: string) { function determineRemoteUrl(remote: string) {
@ -34,42 +23,9 @@ function determineRemoteUrl(remote: string) {
export async function withModuleFederation(options: ModuleFederationConfig) { export async function withModuleFederation(options: ModuleFederationConfig) {
const reactWebpackConfig = require('../../plugins/webpack'); const reactWebpackConfig = require('../../plugins/webpack');
let projectGraph: ProjectGraph<ProjectConfiguration>;
try {
projectGraph = readCachedProjectGraph();
} catch (e) {
projectGraph = await createProjectGraphAsync();
}
const project = projectGraph.nodes[options.name]?.data; const { sharedDependencies, sharedLibraries, mappedRemotes } =
await getModuleFederationConfig(options, determineRemoteUrl);
if (!project) {
throw Error(
`Cannot find project "${options.name}". Check that the name is correct in module-federation.config.js`
);
}
const dependencies = getDependentPackagesForProject(
projectGraph,
options.name
);
const sharedLibraries = shareWorkspaceLibraries(
dependencies.workspaceLibraries
);
const npmPackages = sharePackages(dependencies.npmPackages);
const sharedDependencies = {
...sharedLibraries.getLibraries(),
...npmPackages,
};
applySharedFunction(sharedDependencies, options.shared);
applyAdditionalShared(
sharedDependencies,
options.additionalShared,
projectGraph
);
return (config) => { return (config) => {
config = reactWebpackConfig(config); config = reactWebpackConfig(config);
@ -85,11 +41,6 @@ export async function withModuleFederation(options: ModuleFederationConfig) {
outputModule: true, outputModule: true,
}; };
const mappedRemotes =
!options.remotes || options.remotes.length === 0
? {}
: mapRemotes(options.remotes, 'js', determineRemoteUrl);
config.plugins.push( config.plugins.push(
new ModuleFederationPlugin({ new ModuleFederationPlugin({
name: options.name, name: options.name,

View File

@ -87,6 +87,7 @@ const IGNORE_MATCHES_IN_PACKAGE = {
'stylus-loader', 'stylus-loader',
'swc-loader', 'swc-loader',
'tsconfig-paths-webpack-plugin', 'tsconfig-paths-webpack-plugin',
'@module-federation/node',
], ],
rollup: ['@swc/core'], rollup: ['@swc/core'],
storybook: [ storybook: [