Skip to content

Commit b5e4e02

Browse files
committed
add migration guide
1 parent 2fe3dc8 commit b5e4e02

File tree

3 files changed

+76
-25
lines changed

3 files changed

+76
-25
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Migration Guide: Module Federation to Native Federation for Angular
2+
3+
## Motivation
4+
5+
Since Angular 17, the CLI ships with an esbuild-based builder that is remarkable faster than the original webpack-based solution. This new builder is used for newly generated projects and beginning with Angular 18 ng updates also migrates existing projects.
6+
7+
Native Federation for Angular is a thin wrapper around the esbuild builder that allows to use the proven mental model of Module Federation.
8+
9+
## Prerequisites
10+
11+
- Update your solution to the newest Angular and CLI version
12+
- Update your solution to the newest version of ``@angular-architects/module-federation``
13+
- Have a look to our [FAQs about sharing packages with Native Federation](share-faq.md)
14+
15+
## Migration for Angular CLI projects
16+
17+
## Migration for Nx projects
18+
19+
## Issues
20+
21+
We have tested this guide with several projects. However, e

libs/native-federation/docs/update18.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Make sure you have the following `postinstall` script in your `package.json`:
2727
},
2828
```
2929

30-
**Remarks:** This script is just a temporary solution. It won't be necessary in future versions.
30+
**Remarks:** This script is just a temporary solution. It won't be necessary in future versions.
3131

3232
Run the `postinstall` script once manually for initialization:
3333

libs/native-federation/src/schematics/init/schematic.ts

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type NormalizedOptions = {
2929
projectRoot: string;
3030
projectSourceRoot: string;
3131
manifestPath: string;
32+
manifestRelPath: string;
3233
projectConfig: any;
3334
main: string;
3435
port: number;
@@ -65,14 +66,15 @@ export default function config(options: MfSchematicSchema): Rule {
6566
const workspaceFileName = getWorkspaceFileName(tree);
6667
const workspace = JSON.parse(tree.read(workspaceFileName).toString('utf8'));
6768

68-
const normalized = normalizeOptions(options, workspace);
69+
const normalized = normalizeOptions(options, workspace, tree);
6970

7071
const {
7172
polyfills,
7273
projectName,
7374
projectRoot,
7475
projectSourceRoot,
7576
manifestPath,
77+
manifestRelPath,
7678
main,
7779
} = normalized;
7880

@@ -90,11 +92,11 @@ export default function config(options: MfSchematicSchema): Rule {
9092

9193
const generateRule = !exists
9294
? await generateFederationConfig(
93-
remoteMap,
94-
projectRoot,
95-
projectSourceRoot,
96-
options
97-
)
95+
remoteMap,
96+
projectRoot,
97+
projectSourceRoot,
98+
options
99+
)
98100
: noop;
99101

100102
updateWorkspaceConfig(tree, normalized, workspace, workspaceFileName);
@@ -112,11 +114,11 @@ export default function config(options: MfSchematicSchema): Rule {
112114

113115
context.addTask(new NodePackageInstallTask());
114116

115-
return chain([generateRule, makeMainAsync(main, options, remoteMap)]);
117+
return chain([generateRule, makeMainAsync(main, options, remoteMap, manifestRelPath)]);
116118
};
117119
}
118120

119-
export function patchAngularBuild(tree) {
121+
export function patchAngularBuild(tree: Tree) {
120122
const packagePath = 'node_modules/@angular/build/package.json';
121123
const privatePath = 'node_modules/@angular/build/private.js';
122124

@@ -125,17 +127,26 @@ export function patchAngularBuild(tree) {
125127
}
126128

127129
const packageJson = JSON.parse(
128-
tree.read(packagePath)
130+
tree.read(packagePath).toString('utf8')
129131
);
130132
patchAngularBuildPackageJson(packageJson);
131133
tree.overwrite(
132134
packagePath,
133135
JSON.stringify(packageJson, null, 2)
134136
);
135-
tree.overwrite(
136-
privatePath,
137-
privateEntrySrc
138-
);
137+
138+
if (!tree.exists(privatePath)) {
139+
tree.create(
140+
privatePath,
141+
privateEntrySrc
142+
);
143+
}
144+
else {
145+
tree.overwrite(
146+
privatePath,
147+
privateEntrySrc
148+
);
149+
}
139150

140151
}
141152

@@ -164,18 +175,14 @@ function updateWorkspaceConfig(
164175
delete originalBuild.configurations?.development?.vendorChunk;
165176
}
166177

167-
// if (originalBuild.options.browser) {
168-
// const browser = originalBuild.options.browser;
169-
// delete originalBuild.options.browser;
170-
// originalBuild.options.main = browser;
171-
// }
172-
173178
if (originalBuild.options.main) {
174179
const main = originalBuild.options.main;
175180
delete originalBuild.options.main;
176181
originalBuild.options.browser = main;
177182
}
178183

184+
delete originalBuild.options.commonChunk;
185+
179186
projectConfig.architect.esbuild = originalBuild;
180187

181188
projectConfig.architect.build = {
@@ -197,6 +204,8 @@ function updateWorkspaceConfig(
197204
serve.options ??= {};
198205
serve.options.port = port;
199206

207+
delete serve.options.commonChunk;
208+
200209
const serveProd = projectConfig.architect.serve.configurations?.production;
201210
if (serveProd) {
202211
serveProd.buildTarget = `${projectName}:esbuild:production`;
@@ -237,7 +246,8 @@ function updateWorkspaceConfig(
237246

238247
function normalizeOptions(
239248
options: MfSchematicSchema,
240-
workspace: any
249+
workspace: any,
250+
tree: Tree
241251
): NormalizedOptions {
242252
if (!options.project) {
243253
options.project = workspace.defaultProject;
@@ -271,10 +281,22 @@ function normalizeOptions(
271281
'/'
272282
);
273283

274-
const manifestPath = path
275-
.join(projectRoot, 'public/federation.manifest.json')
284+
const publicPath = path.join(projectRoot, 'public').replace(/\\/g, '/');
285+
286+
let manifestPath = path
287+
.join(publicPath, 'federation.manifest.json')
276288
.replace(/\\/g, '/');
277289

290+
let manifestRelPath = 'public/federation.manifest.json';
291+
292+
if (!tree.exists(publicPath)) {
293+
manifestPath = path
294+
.join(projectRoot, 'src/assets/federation.manifest.json')
295+
.replace(/\\/g, '/');
296+
297+
manifestRelPath = 'assets/federation.manifest.json';
298+
}
299+
278300
const main =
279301
projectConfig.architect.build.options.main ||
280302
projectConfig.architect.build.options.browser;
@@ -283,13 +305,20 @@ function normalizeOptions(
283305
projectConfig.architect.build.options.polyfills = [];
284306
}
285307

308+
if (typeof projectConfig.architect.build.options.polyfills === 'string') {
309+
projectConfig.architect.build.options.polyfills = [
310+
projectConfig.architect.build.options.polyfills
311+
]
312+
}
313+
286314
const polyfills = projectConfig.architect.build.options.polyfills;
287315
return {
288316
polyfills,
289317
projectName,
290318
projectRoot,
291319
projectSourceRoot,
292320
manifestPath,
321+
manifestRelPath,
293322
projectConfig,
294323
main,
295324
port: +(options.port || 4200),
@@ -353,7 +382,8 @@ function generateRemoteMap(workspace: any, projectName: string) {
353382
function makeMainAsync(
354383
main: string,
355384
options: MfSchematicSchema,
356-
remoteMap: unknown
385+
remoteMap: unknown,
386+
manifestRelPath: string,
357387
): Rule {
358388
return async function (tree) {
359389
const mainPath = path.dirname(main);
@@ -371,7 +401,7 @@ function makeMainAsync(
371401
if (options.type === 'dynamic-host') {
372402
newMainContent = `import { initFederation } from '@angular-architects/native-federation';
373403
374-
initFederation('federation.manifest.json')
404+
initFederation('${manifestRelPath}')
375405
.catch(err => console.error(err))
376406
.then(_ => import('./bootstrap'))
377407
.catch(err => console.error(err));

0 commit comments

Comments
 (0)