diff --git a/generators/angular/files-angular.ts b/generators/angular/files-angular.ts index f047c5b3953b..6a3608f2b341 100644 --- a/generators/angular/files-angular.ts +++ b/generators/angular/files-angular.ts @@ -71,6 +71,10 @@ export const files = { condition: ctx => ctx.clientBundlerExperimentalEsbuild && ctx.enableTranslation, templates: ['i18n/index.ts'], }), + clientRootTemplatesBlock({ + condition: ctx => ctx.clientBundlerExperimentalEsbuild && ctx.microfrontend, + templates: ['module-federation.config.cjs', 'build-plugins/module-federation-esbuild.mjs'], + }), ], sass: [ { diff --git a/generators/angular/generator.ts b/generators/angular/generator.ts index 7bacb5e3d658..3f748f5b9031 100644 --- a/generators/angular/generator.ts +++ b/generators/angular/generator.ts @@ -302,13 +302,14 @@ export default class AngularGenerator extends BaseApplicationGenerator { get postWriting() { return this.asPostWritingTaskGroup({ clientBundler({ application, source }) { - const { clientBundlerExperimentalEsbuild, enableTranslation, nodeDependencies } = application; + const { clientBundlerExperimentalEsbuild, enableTranslation, nodeDependencies, microfrontend } = application; if (clientBundlerExperimentalEsbuild) { source.mergeClientPackageJson!({ devDependencies: { '@angular-builders/custom-esbuild': null, globby: null, ...(enableTranslation ? { 'folder-hash': null, deepmerge: null } : {}), + ...(microfrontend ? { '@module-federation/esbuild': null, deepmerge: null } : {}), }, }); } else { diff --git a/generators/angular/resources/package.json b/generators/angular/resources/package.json index 1e20cf98d554..414fff8a46fb 100644 --- a/generators/angular/resources/package.json +++ b/generators/angular/resources/package.json @@ -26,6 +26,7 @@ "@angular-builders/jest": "18.0.0", "@angular/cli": "18.2.12", "@eslint/js": "9.16.0", + "@module-federation/esbuild": "0.0.44", "@types/jest": "29.5.14", "@types/node": "20.11.25", "@types/sockjs-client": "1.5.4", diff --git a/generators/angular/templates/angular.json.esbuild.ejs b/generators/angular/templates/angular.json.esbuild.ejs index cafd9605e0e1..028cb2917480 100644 --- a/generators/angular/templates/angular.json.esbuild.ejs +++ b/generators/angular/templates/angular.json.esbuild.ejs @@ -41,6 +41,9 @@ "plugins": [ <%_ if (enableTranslation) { _%> "build-plugins/i18n-esbuild.mjs", +<%_ } _%> +<%_ if (microfrontend) { _%> + "build-plugins/module-federation-esbuild.mjs", <%_ } _%> "build-plugins/define-esbuild.mjs" ], diff --git a/generators/angular/templates/build-plugins/module-federation-esbuild.mjs.ejs b/generators/angular/templates/build-plugins/module-federation-esbuild.mjs.ejs new file mode 100644 index 000000000000..b864fe152dc1 --- /dev/null +++ b/generators/angular/templates/build-plugins/module-federation-esbuild.mjs.ejs @@ -0,0 +1,4 @@ +import { moduleFederationPlugin } from '@module-federation/esbuild/plugin'; +import federationConfig from '../module-federation.config.cjs'; + +export default moduleFederationPlugin(config); diff --git a/generators/angular/templates/module-federation.config.cjs.ejs b/generators/angular/templates/module-federation.config.cjs.ejs new file mode 100644 index 000000000000..1ea6261b3d28 --- /dev/null +++ b/generators/angular/templates/module-federation.config.cjs.ejs @@ -0,0 +1,77 @@ +<%# + Copyright 2013-2024 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> +const packageJson = require('./package.json'); + +// Microfrontend api, should match across gateway and microservices. +const apiVersion = '0.0.1'; + +const sharedDefaults = { singleton: true, strictVersion: true, requiredVersion: apiVersion }; +const shareMappings = (...mappings) => Object.fromEntries(mappings.map(map => [map, { ...sharedDefaults, version: apiVersion }])); + +const shareDependencies = ({ skipList = [] } = {}) => + Object.fromEntries( + Object.entries(packageJson.dependencies) + .filter(([dependency]) => !skipList.includes(dependency)) + .map(([dependency, version]) => [dependency, { ...sharedDefaults, version, requiredVersion: version }]), + ); + +let sharedDependencies = shareDependencies({ skipList: ['@angular/localize', 'zone.js'] }); +sharedDependencies = { + ...sharedDependencies, + '@angular/common/http': sharedDependencies['@angular/common'], + 'rxjs/operators': sharedDependencies.rxjs, +}; + +/** @type {import('@module-federation/runtime').Options} */ +module.exports = { + name: '<%= lowercaseBaseName %>', +<%_ if (applicationTypeMicroservice) { _%> + exposes: { + <%_ if (enableTranslation) { _%> + './translation-module': 'app/shared/language/translation.module.ts', + <%_ } _%> + './entity-navbar-items': 'app/entities/entity-navbar-items.ts', + './entity-routes': 'app/entities/entity.routes.ts', + }, +<%_ } _%> + filename: 'remoteEntry.js', + shareScope: 'default', + shared: { + ...sharedDependencies, + ...shareMappings( + 'app/config/input.constants', + 'app/config/pagination.constants', + 'app/config/translation.config', + 'app/core/auth', + 'app/core/config', + 'app/core/interceptor', + 'app/core/request', + 'app/core/util', + 'app/shared', + 'app/shared/alert', + 'app/shared/auth', + 'app/shared/date', + 'app/shared/language', + 'app/shared/pagination', + 'app/shared/sort', + ), + }, + dts: false, + manifest: false, +}; diff --git a/test-integration/workflow-samples/angular.json b/test-integration/workflow-samples/angular.json index 024a8087f39b..058217e426f9 100644 --- a/test-integration/workflow-samples/angular.json +++ b/test-integration/workflow-samples/angular.json @@ -118,6 +118,7 @@ "jdl-samples": "mf-ng-eureka-jwt-psql-ehcache", "generatorOptions": { "workspaces": true, + "clientBundler": "experimentalEsbuild", "monorepository": true } },