feat(angular): use a mfe.config.js for mfe (#9495)

This commit is contained in:
Colum Ferry 2022-03-25 09:31:12 +00:00 committed by GitHub
parent 85721e42f0
commit 8bae893d4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 139 additions and 130 deletions

View File

@ -2,36 +2,26 @@
exports[`app --mfe should add a remote application and add it to a specified host applications webpack config that contains a remote application already 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
name: 'app1',
remotes: ['remote1','remote2',]
});"
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;
exports[`app --mfe should add a remote application and add it to a specified host applications webpack config when no other remote has been added to it 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
name: 'app1',
remotes: ['remote1',]
});"
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;
exports[`app --mfe should generate a Module Federation correctly for a each app 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
name: 'my-app',
remotes: []
});"
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;
exports[`app --mfe should generate a Module Federation correctly for a each app 2`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
name: 'my-app',
exposes: {
'./Module': 'apps/my-app/src/app/remote-entry/entry.module.ts',
},
});"
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;
exports[`app at the root should accept numbers in the path 1`] = `"src/9-websites/my-app"`;

View File

@ -2,28 +2,18 @@
exports[`convertToWithMF should migrate a standard previous generated host config correctly 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
name: 'host1',
remotes: [['remote1', 'http://localhost:4201']],
});"
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;
exports[`convertToWithMF should migrate a standard previous generated remote config correctly 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
name: 'remote1',
exposes: {
'./Module': 'apps/remote1/src/app/remote-entry/entry.module.ts',
},
});"
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;
exports[`convertToWithMF should migrate a standard previous generated remote config using object shared syntax correctly 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
name: 'remote1',
exposes: {
'./Module': 'apps/remote1/src/app/remote-entry/entry.module.ts',
},
});"
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;

View File

@ -1,4 +1,4 @@
import { logger, Tree } from '@nrwl/devkit';
import { joinPathFragments, logger, Tree } from '@nrwl/devkit';
import type { Schema } from './schema';
import { readProjectConfiguration, formatFiles } from '@nrwl/devkit';
@ -43,12 +43,13 @@ export default async function convertToWithMF(tree: Tree, schema: Schema) {
`This Micro Frontend configuration conversion will overwrite "${schema.project}"'s current webpack config. If you have anything custom that is not related to Micro Frontends, it will be lost. You should be able to see the changes in your version control system.`
);
const updatedWebpackConfig = writeNewWebpackConfig(
const [updatedWebpackConfig, mfeConfig] = writeNewWebpackConfig(
webpackAst,
isHostRemoteConfig(webpackAst),
schema.project
);
tree.write(pathToWebpackConfig, updatedWebpackConfig);
tree.write(joinPathFragments(project.root, 'mfe.config.js'), mfeConfig);
await formatFiles(tree);
}

View File

@ -1,37 +1,57 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`writeNewWebpackConfig should convert config that is both remote and host correctly 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
Array [
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./mfe.config');
module.exports = withModuleFederation(config);",
"
module.exports = {
name: 'both1',
remotes: [['remote1', 'http://localhost:4201']],
exposes: {
'./Module': 'apps/both/src/app/remote-entry/entry.module.ts'
},
});"
};",
]
`;
exports[`writeNewWebpackConfig should convert config that is neither remote and host correctly 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
Array [
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./mfe.config');
module.exports = withModuleFederation(config);",
"
module.exports = {
name: 'neither',
});"
};",
]
`;
exports[`writeNewWebpackConfig should convert host config correctly 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
Array [
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./mfe.config');
module.exports = withModuleFederation(config);",
"
module.exports = {
name: 'host1',
remotes: [['remote1', 'http://localhost:4201']],
});"
};",
]
`;
exports[`writeNewWebpackConfig should convert remote config correctly 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
Array [
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./mfe.config');
module.exports = withModuleFederation(config);",
"
module.exports = {
name: 'remote1',
exposes: {
'./Module': 'apps/remote1/src/app/remote-entry/entry.module.ts'
},
});"
};",
]
`;

View File

@ -10,38 +10,42 @@ export function writeNewWebpackConfig(
mfType: IsHostRemoteConfigResult,
projectName: string
) {
let webpackConfig = '';
const webpackConfig = `const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./mfe.config');
module.exports = withModuleFederation(config);`;
let mfeConfig = '';
if (!mfType) {
webpackConfig = `const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
mfeConfig = `
module.exports = {
name: '${projectName}',
});`;
};`;
} else if (mfType === 'host') {
const remotes = hostRemotesToString(ast);
webpackConfig = `const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
mfeConfig = `
module.exports = {
name: '${projectName}',
remotes: ${remotes},
});`;
};`;
} else if (mfType === 'remote') {
const exposedModules = getExposedModulesFromRemote(ast);
webpackConfig = `const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
mfeConfig = `
module.exports = {
name: '${projectName}',
exposes: ${exposedModules},
});`;
};`;
} else if (mfType === 'both') {
const remotes = hostRemotesToString(ast);
const exposedModules = getExposedModulesFromRemote(ast);
webpackConfig = `const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
mfeConfig = `
module.exports = {
name: '${projectName}',
remotes: ${remotes},
exposes: ${exposedModules},
});`;
};`;
}
return webpackConfig;
return [webpackConfig, mfeConfig];
}
function hostRemotesToString(ast: SourceFile) {

View File

@ -2,26 +2,18 @@
exports[`MFE Host App Generator should generate a host mfe app with a remote 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
name: 'remote',
exposes: {
'./Module': 'apps/remote/src/app/remote-entry/entry.module.ts',
},
});"
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;
exports[`MFE Host App Generator should generate a host mfe app with a remote 2`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
name: 'test',
remotes: ['remote',]
});"
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;
exports[`MFE Host App Generator should generate a host mfe app with no remotes 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
name: 'test',
remotes: []
});"
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;

View File

@ -2,28 +2,18 @@
exports[`MFE Remote App Generator should generate a remote mfe app with a host 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
name: 'host',
remotes: ['test',]
});"
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;
exports[`MFE Remote App Generator should generate a remote mfe app with a host 2`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
name: 'test',
exposes: {
'./Module': 'apps/test/src/app/remote-entry/entry.module.ts',
},
});"
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;
exports[`MFE Remote App Generator should generate a remote mfe app with no host 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
name: 'test',
exposes: {
'./Module': 'apps/test/src/app/remote-entry/entry.module.ts',
},
});"
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;

View File

@ -31,35 +31,43 @@ export class AppModule { }
`;
exports[`Init MFE should add a remote application and add it to a specified host applications webpack config that contains a remote application already 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
"module.exports = {
name: 'app1',
remotes: ['remote1','remote2',]
});"
}"
`;
exports[`Init MFE should add a remote application and add it to a specified host applications webpack config when no other remote has been added to it 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
"module.exports = {
name: 'app1',
remotes: ['remote1',]
});"
}"
`;
exports[`Init MFE should create webpack configs correctly 1`] = `
exports[`Init MFE should create webpack and mfe configs correctly 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;
exports[`Init MFE should create webpack and mfe configs correctly 2`] = `
"module.exports = {
name: 'app1',
remotes: []
});"
}"
`;
exports[`Init MFE should create webpack configs correctly 2`] = `
exports[`Init MFE should create webpack and mfe configs correctly 3`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
const config = require('./mfe.config');
module.exports = withModuleFederation(config);"
`;
exports[`Init MFE should create webpack and mfe configs correctly 4`] = `
"module.exports = {
name: 'remote1',
exposes: {
'./Module': 'apps/remote1/src/app/remote-entry/entry.module.ts',
},
});"
}"
`;

View File

@ -0,0 +1,7 @@
module.exports = {
name: '<%= name %>',<% if(type === 'host') { %>
remotes: [<% remotes.forEach(function(remote) { %>'<%= remote.remoteName %>',<% }); %>]<% } %><% if(type === 'remote') { %>
exposes: {
'./Module': '<%= projectRoot %>/src/app/remote-entry/entry.module.ts',
},<% } %>
}

View File

@ -1,8 +1,3 @@
const { withModuleFederation } = require('@nrwl/angular/module-federation');
module.exports = withModuleFederation({
name: '<%= name %>',<% if(type === 'host') { %>
remotes: [<% remotes.forEach(function(remote) { %>'<%= remote.remoteName %>',<% }); %>]<% } %><% if(type === 'remote') { %>
exposes: {
'./Module': '<%= projectRoot %>/src/app/remote-entry/entry.module.ts',
},<% } %>
});
const config = require('./mfe.config');
module.exports = withModuleFederation(config);

View File

@ -20,17 +20,19 @@ export function checkIsCommaNeeded(mfeRemoteText: string) {
export function addRemoteToHost(host: Tree, options: Schema) {
if (options.mfeType === 'remote' && options.host) {
const hostProject = readProjectConfiguration(host, options.host);
const hostWebpackPath =
hostProject.targets['build'].options.customWebpackConfig?.path;
const hostMfeConfigPath = joinPathFragments(
hostProject.root,
'mfe.config.js'
);
if (!hostWebpackPath || !host.exists(hostWebpackPath)) {
if (!hostMfeConfigPath || !host.exists(hostMfeConfigPath)) {
throw new Error(
`The selected host application, ${options.host}, does not contain a webpack.config.js. Are you sure it has been set up as a host application?`
`The selected host application, ${options.host}, does not contain a mfe.config.js. Are you sure it has been set up as a host application?`
);
}
const hostWebpackConfig = host.read(hostWebpackPath, 'utf-8');
const webpackAst = tsquery.ast(hostWebpackConfig);
const hostMFEConfig = host.read(hostMfeConfigPath, 'utf-8');
const webpackAst = tsquery.ast(hostMFEConfig);
const mfRemotesNode = tsquery(
webpackAst,
'Identifier[name=remotes] ~ ArrayLiteralExpression',
@ -40,11 +42,11 @@ export function addRemoteToHost(host: Tree, options: Schema) {
const endOfPropertiesPos = mfRemotesNode.getEnd() - 1;
const isCommaNeeded = checkIsCommaNeeded(mfRemotesNode.getText());
const updatedConfig = `${hostWebpackConfig.slice(0, endOfPropertiesPos)}${
const updatedConfig = `${hostMFEConfig.slice(0, endOfPropertiesPos)}${
isCommaNeeded ? ',' : ''
}'${options.appName}',${hostWebpackConfig.slice(endOfPropertiesPos)}`;
}'${options.appName}',${hostMFEConfig.slice(endOfPropertiesPos)}`;
host.write(hostWebpackPath, updatedConfig);
host.write(hostMfeConfigPath, updatedConfig);
const declarationFilePath = joinPathFragments(
hostProject.sourceRoot,

View File

@ -9,6 +9,7 @@ export function generateWebpackConfig(
remotesWithPorts: { remoteName: string; port: number }[]
) {
if (
host.exists(`${appRoot}/mfe.config.js`) ||
host.exists(`${appRoot}/webpack.config.js`) ||
host.exists(`${appRoot}/webpack.prod.config.js`)
) {

View File

@ -23,7 +23,7 @@ describe('Init MFE', () => {
['app1', 'host'],
['remote1', 'remote'],
])(
'should create webpack configs correctly',
'should create webpack and mfe configs correctly',
async (app, type: 'host' | 'remote') => {
// ACT
await setupMfe(host, {
@ -32,14 +32,18 @@ describe('Init MFE', () => {
});
// ASSERT
expect(host.exists(`apps/${app}/mfe.config.js`)).toBeTruthy();
expect(host.exists(`apps/${app}/webpack.config.js`)).toBeTruthy();
expect(host.exists(`apps/${app}/webpack.prod.config.js`)).toBeTruthy();
const webpackContetnts = host.read(
const webpackContents = host.read(
`apps/${app}/webpack.config.js`,
'utf-8'
);
expect(webpackContetnts).toMatchSnapshot();
expect(webpackContents).toMatchSnapshot();
const mfeConfigContents = host.read(`apps/${app}/mfe.config.js`, 'utf-8');
expect(mfeConfigContents).toMatchSnapshot();
}
);
@ -127,9 +131,9 @@ describe('Init MFE', () => {
});
// ASSERT
const webpackContents = host.read(`apps/app1/webpack.config.js`, 'utf-8');
const mfeConfigContents = host.read(`apps/app1/mfe.config.js`, 'utf-8');
expect(webpackContents).toContain(`'remote1'`);
expect(mfeConfigContents).toContain(`'remote1'`);
});
it('should update the implicit dependencies of the host when --remotes flag supplied', async () => {
// ACT
@ -163,8 +167,8 @@ describe('Init MFE', () => {
});
// ASSERT
const hostWebpackConfig = host.read('apps/app1/webpack.config.js', 'utf-8');
expect(hostWebpackConfig).toMatchSnapshot();
const hostMfeConfig = host.read('apps/app1/mfe.config.js', 'utf-8');
expect(hostMfeConfig).toMatchSnapshot();
});
it('should add a remote application and add it to a specified host applications webpack config that contains a remote application already', async () => {
@ -194,8 +198,8 @@ describe('Init MFE', () => {
});
// ASSERT
const hostWebpackConfig = host.read('apps/app1/webpack.config.js', 'utf-8');
expect(hostWebpackConfig).toMatchSnapshot();
const hostMfeConfig = host.read('apps/app1/mfe.config.js', 'utf-8');
expect(hostMfeConfig).toMatchSnapshot();
});
it('should add a remote application and add it to a specified host applications router config', async () => {

View File

@ -161,7 +161,12 @@ function mapRemotes(remotes: MFERemotes) {
for (const remote of remotes) {
if (Array.isArray(remote)) {
mappedRemotes[remote[0]] = remote[1];
const remoteLocation = remote[1].match(/remoteEntry\.(js|mjs)/)
? remote[1]
: `${
remote[1].endsWith('/') ? remote[1].slice(0, -1) : remote[1]
}/remoteEntry.mjs`;
mappedRemotes[remote[0]] = remoteLocation;
} else if (typeof remote === 'string') {
mappedRemotes[remote] = determineRemoteUrl(remote);
}