Skip to content

Commit c57d87f

Browse files
committed
feat(mf): add type option to ng-add
- type=legacy (default): creates the used webpack.config.js - type=host|dynamic-host|remote: uses withWebpackConfig helper to create a concise webpack.config.js In the case of dynamic-host a mf.manifest.json is created and loaded in the bootstrap.ts
1 parent 5615917 commit c57d87f

File tree

8 files changed

+113
-48
lines changed

8 files changed

+113
-48
lines changed

libs/mf-runtime/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@angular-architects/module-federation-runtime",
33
"license": "MIT",
4-
"version": "14.3.0-beta.3",
4+
"version": "14.3.0-beta.4",
55
"peerDependencies": {
66
"@angular/common": ">=14.0.0-next.15",
77
"@angular/core": ">=14.0.0-next.15"

libs/mf-tools/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"name": "@angular-architects/module-federation-tools",
3-
"version": "14.3.0-beta.3",
3+
"version": "14.3.0-beta.4",
44
"license": "MIT",
55
"peerDependencies": {
66
"@angular/common": ">=14.0.0-next.15",
77
"@angular/core": ">=14.0.0-next.15",
88
"@angular/router": ">=14.0.0-next.15",
9-
"@angular-architects/module-federation": "^14.3.0-beta.3",
9+
"@angular-architects/module-federation": "^14.3.0-beta.4",
1010
"@angular/platform-browser": ">=14.0.0-next.15",
1111
"rxjs": ">= 6.0.0"
1212
},

libs/mf/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@angular-architects/module-federation",
3-
"version": "14.3.0-beta.3",
3+
"version": "14.3.0-beta.4",
44
"license": "MIT",
55
"repository": {
66
"type": "GitHub",
@@ -17,7 +17,7 @@
1717
"schematics": "./collection.json",
1818
"builders": "./builders.json",
1919
"dependencies": {
20-
"@angular-architects/module-federation-runtime": "14.3.0-beta.3",
20+
"@angular-architects/module-federation-runtime": "14.3.0-beta.4",
2121
"word-wrap": "^1.2.3",
2222
"callsite": "^1.0.0",
2323
"node-fetch": "^2.6.7",

libs/mf/src/schematics/mf/files/src/index.ts.template

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');
2+
3+
module.exports = withModuleFederationPlugin({
4+
<% if (type === 'remote') { %>
5+
name: 'mfe1',
6+
7+
exposes: {
8+
'./Component': './projects/mfe1/src/app/app.component.ts',
9+
},
10+
<% } else if (type === 'host') { %>
11+
remotes: {<% for (key in remoteMap) { %>
12+
"<%=key%>": "<%=remoteMap[key]%>",<% } %>
13+
},
14+
<% } %>
15+
shared: {
16+
...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }),
17+
},
18+
19+
sharedMappings: [],
20+
});

libs/mf/src/schematics/mf/schema.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export interface MfSchematicSchema {
22
project: string;
33
port: string;
44
nxBuilders: boolean | undefined;
5+
type: 'host' | 'dynamic-host' | 'remote' | 'legacy'
56
}

libs/mf/src/schematics/mf/schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
"index": 1
2626
}
2727
},
28+
"type": {
29+
"enum": ["host", "dynamic-host", "remote", "legacy"],
30+
"type": "string",
31+
"default": "legacy"
32+
},
2833
"nxBuilders": {
2934
"type": "boolean",
3035
"description": "Use builders provided by Nx instead of ngx-build-plus? Defaults to true for Nx workspaces and false for CLI workspaces."

libs/mf/src/schematics/mf/schematic.ts

Lines changed: 82 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ import {
22
chain,
33
Rule,
44
Tree,
5+
url,
6+
apply,
7+
mergeWith,
8+
template,
9+
move
510
} from '@angular-devkit/schematics';
611

712
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
@@ -18,38 +23,6 @@ import { MfSchematicSchema } from './schema';
1823

1924
import { addPackageJsonDependency, getPackageJsonDependency, NodeDependencyType } from '@schematics/angular/utility/dependencies';
2025

21-
// export async function npmInstall(packageName: string) {
22-
// await new Promise<boolean>((resolve) => {
23-
// console.log('Installing packages...');
24-
// spawn('npm', ['install', packageName, '-D'])
25-
// .on('close', (code: number) => {
26-
// if (code === 0) {
27-
// console.log('Packages installed successfully ✅');
28-
// resolve(true);
29-
// } else {
30-
// throw new Error(
31-
// `Error installing '${packageName}'`
32-
// );
33-
// }
34-
// });
35-
// });
36-
// }
37-
38-
// export async function yarnAdd(packageName: string) {
39-
// await new Promise<boolean>((resolve) => {
40-
// spawn('npm', ['install', packageName, '-D'])
41-
// .on('close', (code: number) => {
42-
// if (code === 0) {
43-
// resolve(true);
44-
// } else {
45-
// throw new Error(
46-
// `Error installing '${packageName}'`
47-
// );
48-
// }
49-
// });
50-
// });
51-
// }
52-
5326
export function add(options: MfSchematicSchema): Rule {
5427
return config(options);
5528
}
@@ -90,7 +63,7 @@ const ssrEngine = new Engine();
9063
}
9164
}
9265

93-
function makeMainAsync(main: string): Rule {
66+
function makeMainAsync(main: string, options: MfSchematicSchema): Rule {
9467
return async function (tree, context) {
9568

9669
const mainPath = path.dirname(main);
@@ -103,8 +76,22 @@ function makeMainAsync(main: string): Rule {
10376

10477
const mainContent = tree.read(main);
10578
tree.create(bootstrapName, mainContent);
106-
tree.overwrite(main, "import('./bootstrap')\n\t.catch(err => console.error(err));\n");
10779

80+
let newMainContent = '';
81+
if (options.type === 'dynamic-host') {
82+
newMainContent = `import { loadManifest } from '@angular-architects/module-federation';
83+
84+
loadManifest("/assets/mf.manifest.json")
85+
.catch(err => console.error(err))
86+
.then(_ => import('./bootstrap'))
87+
.catch(err => console.error(err));
88+
`;
89+
}
90+
else {
91+
newMainContent = "import('./bootstrap')\n\t.catch(err => console.error(err));\n"
92+
}
93+
94+
tree.overwrite(main, newMainContent);
10895
}
10996
}
11097

@@ -115,7 +102,7 @@ export function getWorkspaceFileName(tree: Tree): string {
115102
if (tree.exists('workspace.json')) {
116103
return 'workspace.json';
117104
}
118-
throw new Error('angular.json or workspae.json expected! Did you call this in your project\'s root?');
105+
throw new Error('angular.json or workspace.json expected! Did you call this in your project\'s root?');
119106
}
120107

121108
interface PackageJson {
@@ -162,7 +149,21 @@ function nxBuildersAvailable(tree: Tree): boolean {
162149

163150
}
164151

165-
export default function config (options: MfSchematicSchema): Rule {
152+
async function generateWebpackConfig(remoteMap: Record<string, string>, src: string, options: MfSchematicSchema) {
153+
const tmpl = url('./files');
154+
155+
const applied = apply(tmpl, [
156+
template({
157+
remoteMap,
158+
...options
159+
}),
160+
move(src)
161+
]);
162+
163+
return mergeWith(applied);
164+
}
165+
166+
export default function config(options: MfSchematicSchema): Rule {
166167

167168
return async function (tree, context) {
168169

@@ -191,6 +192,8 @@ export default function config (options: MfSchematicSchema): Rule {
191192

192193
const configPath = path.join(projectRoot, 'webpack.config.js').replace(/\\/g, '/');
193194
const configProdPath = path.join(projectRoot, 'webpack.prod.config.js').replace(/\\/g, '/');
195+
const manifestPath = path.join(projectRoot, 'src/assets/mf.manifest.json').replace(/\\/g, '/');
196+
194197
const port = parseInt(options.port);
195198
const main = projectConfig.architect.build.options.main;
196199

@@ -206,12 +209,25 @@ export default function config (options: MfSchematicSchema): Rule {
206209
throw new Error(`Port must be a number!`);
207210
}
208211

209-
const remotes = generateRemoteConfig(workspace, projectName);
210-
const webpackConfig = createConfig(projectName, remotes, relTsConfigPath, projectRoot, port);
212+
const remoteMap = await generateRemoteMap(workspace, projectName);
213+
214+
let generateRule = null;
215+
216+
if (options.type === 'legacy') {
217+
const remotes = generateRemoteConfig(workspace, projectName);
218+
const webpackConfig = createConfig(projectName, remotes, relTsConfigPath, projectRoot, port);
219+
tree.create(configPath, webpackConfig);
220+
}
221+
else {
222+
generateRule = await generateWebpackConfig(remoteMap, projectRoot, options);
223+
}
211224

212-
tree.create(configPath, webpackConfig);
213225
tree.create(configProdPath, prodConfig);
214226

227+
if (options.type === 'dynamic-host') {
228+
tree.create(manifestPath, JSON.stringify(remoteMap, null, '\t') );
229+
}
230+
215231
if (options.nxBuilders && !nxBuildersAvailable(tree)) {
216232
console.info('To use Nx builders, make sure you have Nx version 12.9 or higher!');
217233
options.nxBuilders = false;
@@ -270,7 +286,6 @@ export default function config (options: MfSchematicSchema): Rule {
270286
projectConfig.architect['extract-i18n'].options.extraWebpackConfig = configPath;
271287
}
272288

273-
274289
updateTsConfig(tree, tsConfigName);
275290

276291
const localTsConfig = path.join(projectRoot, 'tsconfig.app.json');
@@ -298,9 +313,9 @@ export default function config (options: MfSchematicSchema): Rule {
298313
}
299314

300315
return chain([
301-
makeMainAsync(main),
316+
...(generateRule)? [generateRule] : [],
317+
makeMainAsync(main, options),
302318
adjustSSR(projectSourceRoot, ssrMappings),
303-
// externalSchematic('ngx-build-plus', 'ng-add', { project: options.project }),
304319
]);
305320

306321
}
@@ -346,6 +361,31 @@ function generateRemoteConfig(workspace: any, projectName: string) {
346361
return remotes;
347362
}
348363

364+
function generateRemoteMap(workspace: any, projectName: string) {
365+
const result = {};
366+
367+
for (const p in workspace.projects) {
368+
const project = workspace.projects[p];
369+
const projectType = project.projectType ?? 'application';
370+
371+
if (p !== projectName
372+
&& projectType === 'application'
373+
&& project?.architect?.serve
374+
&& project?.architect?.build) {
375+
376+
const pPort = project.architect.serve.options?.port ?? 4200;
377+
result[strings.camelize(p)] = `http://localhost:${pPort}/remoteEntry.js`;
378+
379+
}
380+
}
381+
382+
if (Object.keys(result).length === 0) {
383+
result["mfe1"] = `http://localhost:3000/remoteEntry.js`;
384+
}
385+
386+
return result;
387+
}
388+
349389
export function generateSsrMappings(workspace: any, projectName: string): string {
350390
let remotes = '{\n';
351391

0 commit comments

Comments
 (0)