feat(angular): add withModuleFederationForSSR function (#13172)
This commit is contained in:
parent
54670c93e0
commit
e70fd48880
@ -162,6 +162,7 @@ It only uses language primitives and immutable objects
|
|||||||
- [isStandaloneProject](../../devkit/index#isstandaloneproject)
|
- [isStandaloneProject](../../devkit/index#isstandaloneproject)
|
||||||
- [joinPathFragments](../../devkit/index#joinpathfragments)
|
- [joinPathFragments](../../devkit/index#joinpathfragments)
|
||||||
- [mapRemotes](../../devkit/index#mapremotes)
|
- [mapRemotes](../../devkit/index#mapremotes)
|
||||||
|
- [mapRemotesForSSR](../../devkit/index#mapremotesforssr)
|
||||||
- [moveFilesToNewDirectory](../../devkit/index#movefilestonewdirectory)
|
- [moveFilesToNewDirectory](../../devkit/index#movefilestonewdirectory)
|
||||||
- [names](../../devkit/index#names)
|
- [names](../../devkit/index#names)
|
||||||
- [normalizePath](../../devkit/index#normalizepath)
|
- [normalizePath](../../devkit/index#normalizepath)
|
||||||
@ -1495,6 +1496,27 @@ Federation.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### mapRemotesForSSR
|
||||||
|
|
||||||
|
▸ **mapRemotesForSSR**(`remotes`, `remoteEntryExt`, `determineRemoteUrl`): `Record`<`string`, `string`\>
|
||||||
|
|
||||||
|
Map remote names to a format that can be understood and used by Module
|
||||||
|
Federation.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| :------------------- | :-------------------------------------- | :------------------------------------------------------- |
|
||||||
|
| `remotes` | [`Remotes`](../../devkit/index#remotes) | The remotes to map |
|
||||||
|
| `remoteEntryExt` | `"js"` \| `"mjs"` | The file extension of the remoteEntry file |
|
||||||
|
| `determineRemoteUrl` | (`remote`: `string`) => `string` | The function used to lookup the URL of the served remote |
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`Record`<`string`, `string`\>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### moveFilesToNewDirectory
|
### moveFilesToNewDirectory
|
||||||
|
|
||||||
▸ **moveFilesToNewDirectory**(`tree`, `oldDir`, `newDir`): `void`
|
▸ **moveFilesToNewDirectory**(`tree`, `oldDir`, `newDir`): `void`
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -1 +1,2 @@
|
|||||||
export { withModuleFederation } from '../src/utils/mf/with-module-federation';
|
export { withModuleFederation } from '../src/utils/mf/with-module-federation';
|
||||||
|
export { withModuleFederationForSSR } from '../src/utils/mf/with-module-federation-ssr';
|
||||||
|
|||||||
94
packages/angular/src/utils/mf/utils.ts
Normal file
94
packages/angular/src/utils/mf/utils.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import {
|
||||||
|
applyAdditionalShared,
|
||||||
|
applySharedFunction,
|
||||||
|
createProjectGraphAsync,
|
||||||
|
getDependentPackagesForProject,
|
||||||
|
mapRemotes,
|
||||||
|
mapRemotesForSSR,
|
||||||
|
ModuleFederationConfig,
|
||||||
|
ProjectGraph,
|
||||||
|
readCachedProjectGraph,
|
||||||
|
SharedLibraryConfig,
|
||||||
|
sharePackages,
|
||||||
|
shareWorkspaceLibraries,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
|
||||||
|
export function applyDefaultEagerPackages(
|
||||||
|
sharedConfig: Record<string, SharedLibraryConfig>
|
||||||
|
) {
|
||||||
|
const DEFAULT_PACKAGES_TO_LOAD_EAGERLY = [
|
||||||
|
'@angular/localize',
|
||||||
|
'@angular/localize/init',
|
||||||
|
];
|
||||||
|
for (const pkg of DEFAULT_PACKAGES_TO_LOAD_EAGERLY) {
|
||||||
|
if (!sharedConfig[pkg]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedConfig[pkg] = { ...sharedConfig[pkg], eager: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_NPM_PACKAGES_TO_AVOID = ['zone.js', '@nrwl/angular/mf'];
|
||||||
|
export const DEFAULT_ANGULAR_PACKAGES_TO_SHARE = [
|
||||||
|
'@angular/animations',
|
||||||
|
'@angular/common',
|
||||||
|
];
|
||||||
|
|
||||||
|
export async function getModuleFederationConfig(
|
||||||
|
mfConfig: ModuleFederationConfig,
|
||||||
|
determineRemoteUrl: (remote: string) => string,
|
||||||
|
options: { isServer: boolean } = { isServer: false }
|
||||||
|
) {
|
||||||
|
let projectGraph: ProjectGraph<any>;
|
||||||
|
try {
|
||||||
|
projectGraph = readCachedProjectGraph();
|
||||||
|
} catch (e) {
|
||||||
|
projectGraph = await createProjectGraphAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
const dependencies = getDependentPackagesForProject(
|
||||||
|
projectGraph,
|
||||||
|
mfConfig.name
|
||||||
|
);
|
||||||
|
const sharedLibraries = shareWorkspaceLibraries(
|
||||||
|
dependencies.workspaceLibraries
|
||||||
|
);
|
||||||
|
|
||||||
|
const npmPackages = sharePackages(
|
||||||
|
Array.from(
|
||||||
|
new Set([
|
||||||
|
...DEFAULT_ANGULAR_PACKAGES_TO_SHARE,
|
||||||
|
...dependencies.npmPackages.filter(
|
||||||
|
(pkg) => !DEFAULT_NPM_PACKAGES_TO_AVOID.includes(pkg)
|
||||||
|
),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFAULT_NPM_PACKAGES_TO_AVOID.forEach((pkgName) => {
|
||||||
|
if (pkgName in npmPackages) {
|
||||||
|
delete npmPackages[pkgName];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const sharedDependencies = {
|
||||||
|
...sharedLibraries.getLibraries(),
|
||||||
|
...npmPackages,
|
||||||
|
};
|
||||||
|
|
||||||
|
applyDefaultEagerPackages(sharedDependencies);
|
||||||
|
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, 'mjs', determineRemoteUrl);
|
||||||
|
return { sharedLibraries, sharedDependencies, mappedRemotes };
|
||||||
|
}
|
||||||
68
packages/angular/src/utils/mf/with-module-federation-ssr.ts
Normal file
68
packages/angular/src/utils/mf/with-module-federation-ssr.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph';
|
||||||
|
import { ModuleFederationConfig } from '@nrwl/devkit';
|
||||||
|
import { getModuleFederationConfig } from './utils';
|
||||||
|
|
||||||
|
function determineRemoteUrl(remote: string) {
|
||||||
|
const remoteProjectConfiguration = readCachedProjectConfiguration(remote);
|
||||||
|
let publicHost = '';
|
||||||
|
try {
|
||||||
|
publicHost = remoteProjectConfiguration.targets.serve.options.publicHost;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot automatically determine URL of remote (${remote}). Looked for property "publicHost" 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', 'http://localhost:4201']]\``
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return `${
|
||||||
|
publicHost.endsWith('/') ? publicHost.slice(0, -1) : publicHost
|
||||||
|
}/server/remoteEntry.js`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function withModuleFederationForSSR(
|
||||||
|
options: ModuleFederationConfig
|
||||||
|
) {
|
||||||
|
const { sharedLibraries, sharedDependencies, mappedRemotes } =
|
||||||
|
await getModuleFederationConfig(options, determineRemoteUrl, {
|
||||||
|
isServer: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (config) => ({
|
||||||
|
...(config ?? {}),
|
||||||
|
target: false,
|
||||||
|
output: {
|
||||||
|
...(config.output ?? {}),
|
||||||
|
uniqueName: options.name,
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
...(config.optimization ?? {}),
|
||||||
|
runtimeChunk: false,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
...(config.resolve ?? {}),
|
||||||
|
alias: {
|
||||||
|
...(config.resolve?.alias ?? {}),
|
||||||
|
...sharedLibraries.getAliases(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
...(config.plugins ?? []),
|
||||||
|
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(),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -1,16 +1,5 @@
|
|||||||
import {
|
import { ModuleFederationConfig } from '@nrwl/devkit';
|
||||||
applyAdditionalShared,
|
import { getModuleFederationConfig } from './utils';
|
||||||
applySharedFunction,
|
|
||||||
createProjectGraphAsync,
|
|
||||||
getDependentPackagesForProject,
|
|
||||||
mapRemotes,
|
|
||||||
ModuleFederationConfig,
|
|
||||||
ProjectGraph,
|
|
||||||
readCachedProjectGraph,
|
|
||||||
SharedLibraryConfig,
|
|
||||||
sharePackages,
|
|
||||||
shareWorkspaceLibraries,
|
|
||||||
} from '@nrwl/devkit';
|
|
||||||
import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph';
|
import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph';
|
||||||
import ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
|
import ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
|
||||||
|
|
||||||
@ -30,78 +19,9 @@ function determineRemoteUrl(remote: string) {
|
|||||||
}/remoteEntry.mjs`;
|
}/remoteEntry.mjs`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyDefaultEagerPackages(
|
|
||||||
sharedConfig: Record<string, SharedLibraryConfig>
|
|
||||||
) {
|
|
||||||
const DEFAULT_PACKAGES_TO_LOAD_EAGERLY = [
|
|
||||||
'@angular/localize',
|
|
||||||
'@angular/localize/init',
|
|
||||||
];
|
|
||||||
for (const pkg of DEFAULT_PACKAGES_TO_LOAD_EAGERLY) {
|
|
||||||
if (!sharedConfig[pkg]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedConfig[pkg] = { ...sharedConfig[pkg], eager: true };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function withModuleFederation(options: ModuleFederationConfig) {
|
export async function withModuleFederation(options: ModuleFederationConfig) {
|
||||||
const DEFAULT_NPM_PACKAGES_TO_AVOID = ['zone.js', '@nrwl/angular/mf'];
|
const { sharedLibraries, sharedDependencies, mappedRemotes } =
|
||||||
const DEFAULT_ANGULAR_PACKAGES_TO_SHARE = [
|
await getModuleFederationConfig(options, determineRemoteUrl);
|
||||||
'@angular/animations',
|
|
||||||
'@angular/common',
|
|
||||||
];
|
|
||||||
|
|
||||||
let projectGraph: ProjectGraph<any>;
|
|
||||||
try {
|
|
||||||
projectGraph = readCachedProjectGraph();
|
|
||||||
} catch (e) {
|
|
||||||
projectGraph = await createProjectGraphAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
const dependencies = getDependentPackagesForProject(
|
|
||||||
projectGraph,
|
|
||||||
options.name
|
|
||||||
);
|
|
||||||
const sharedLibraries = shareWorkspaceLibraries(
|
|
||||||
dependencies.workspaceLibraries
|
|
||||||
);
|
|
||||||
|
|
||||||
const npmPackages = sharePackages(
|
|
||||||
Array.from(
|
|
||||||
new Set([
|
|
||||||
...DEFAULT_ANGULAR_PACKAGES_TO_SHARE,
|
|
||||||
...dependencies.npmPackages.filter(
|
|
||||||
(pkg) => !DEFAULT_NPM_PACKAGES_TO_AVOID.includes(pkg)
|
|
||||||
),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
DEFAULT_NPM_PACKAGES_TO_AVOID.forEach((pkgName) => {
|
|
||||||
if (pkgName in npmPackages) {
|
|
||||||
delete npmPackages[pkgName];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const sharedDependencies = {
|
|
||||||
...sharedLibraries.getLibraries(),
|
|
||||||
...npmPackages,
|
|
||||||
};
|
|
||||||
|
|
||||||
applyDefaultEagerPackages(sharedDependencies);
|
|
||||||
applySharedFunction(sharedDependencies, options.shared);
|
|
||||||
applyAdditionalShared(
|
|
||||||
sharedDependencies,
|
|
||||||
options.additionalShared,
|
|
||||||
projectGraph
|
|
||||||
);
|
|
||||||
|
|
||||||
const mappedRemotes =
|
|
||||||
!options.remotes || options.remotes.length === 0
|
|
||||||
? {}
|
|
||||||
: mapRemotes(options.remotes, 'mjs', determineRemoteUrl);
|
|
||||||
|
|
||||||
return (config) => ({
|
return (config) => ({
|
||||||
...(config ?? {}),
|
...(config ?? {}),
|
||||||
|
|||||||
@ -305,6 +305,7 @@ export {
|
|||||||
applySharedFunction,
|
applySharedFunction,
|
||||||
applyAdditionalShared,
|
applyAdditionalShared,
|
||||||
mapRemotes,
|
mapRemotes,
|
||||||
|
mapRemotesForSSR,
|
||||||
getNpmPackageSharedConfig,
|
getNpmPackageSharedConfig,
|
||||||
shareWorkspaceLibraries,
|
shareWorkspaceLibraries,
|
||||||
sharePackages,
|
sharePackages,
|
||||||
|
|||||||
@ -34,3 +34,39 @@ export function mapRemotes(
|
|||||||
|
|
||||||
return mappedRemotes;
|
return mappedRemotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map remote names to a format that can be understood and used by Module
|
||||||
|
* Federation.
|
||||||
|
*
|
||||||
|
* @param remotes - The remotes to map
|
||||||
|
* @param remoteEntryExt - The file extension of the remoteEntry file
|
||||||
|
* @param determineRemoteUrl - The function used to lookup the URL of the served remote
|
||||||
|
*/
|
||||||
|
export function mapRemotesForSSR(
|
||||||
|
remotes: Remotes,
|
||||||
|
remoteEntryExt: 'js' | 'mjs',
|
||||||
|
determineRemoteUrl: (remote: string) => string
|
||||||
|
): Record<string, string> {
|
||||||
|
const mappedRemotes = {};
|
||||||
|
|
||||||
|
for (const remote of remotes) {
|
||||||
|
if (Array.isArray(remote)) {
|
||||||
|
const [remoteName, remoteLocation] = remote;
|
||||||
|
const remoteLocationExt = extname(remoteLocation);
|
||||||
|
mappedRemotes[remoteName] = `${remoteName}@${
|
||||||
|
['.js', '.mjs'].includes(remoteLocationExt)
|
||||||
|
? remoteLocation
|
||||||
|
: `${
|
||||||
|
remoteLocation.endsWith('/')
|
||||||
|
? remoteLocation.slice(0, -1)
|
||||||
|
: remoteLocation
|
||||||
|
}/remoteEntry.${remoteEntryExt}`
|
||||||
|
}`;
|
||||||
|
} else if (typeof remote === 'string') {
|
||||||
|
mappedRemotes[remote] = `${remote}@${determineRemoteUrl(remote)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappedRemotes;
|
||||||
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ const IGNORE_MATCHES_IN_PACKAGE = {
|
|||||||
'@ngrx/router-store',
|
'@ngrx/router-store',
|
||||||
'@ngrx/store',
|
'@ngrx/store',
|
||||||
'@storybook/angular',
|
'@storybook/angular',
|
||||||
|
'@module-federation/node',
|
||||||
'rxjs',
|
'rxjs',
|
||||||
'semver',
|
'semver',
|
||||||
// installed dynamically by the library generator
|
// installed dynamically by the library generator
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user