diff --git a/generators/javascript/generators/prettier/templates/.prettierignore.jhi.ejs b/generators/javascript/generators/prettier/templates/.prettierignore.jhi.ejs index 03b20a35aa15..8e07e3d4f48a 100644 --- a/generators/javascript/generators/prettier/templates/.prettierignore.jhi.ejs +++ b/generators/javascript/generators/prettier/templates/.prettierignore.jhi.ejs @@ -19,5 +19,8 @@ node_modules package-lock.json .git +<%_ if (clientFrameworkVue && microfrontend) { _%> +'.__mf__temp', +<%_ } _%> <&- fragments.render({ join: '\n\n' }) &> diff --git a/generators/vue/generator.ts b/generators/vue/generator.ts index bb99f50b8928..47686cf263a2 100644 --- a/generators/vue/generator.ts +++ b/generators/vue/generator.ts @@ -209,8 +209,11 @@ export default class VueGenerator extends BaseApplicationGenerator { if (!microfrontend) return; if (clientBundlerVite) { this.packageJson.merge({ + dependencies: applicationTypeGateway ? { + '@module-federation/runtime': null, + } : {}, devDependencies: { - '@originjs/vite-plugin-federation': '1.3.6', + '@module-federation/vite': null, }, }); } else if (clientBundlerWebpack) { diff --git a/generators/vue/resources/package.json b/generators/vue/resources/package.json index cdfc9cfe0f5e..4186f2ab6280 100644 --- a/generators/vue/resources/package.json +++ b/generators/vue/resources/package.json @@ -3,6 +3,7 @@ "@fortawesome/fontawesome-svg-core": "6.6.0", "@fortawesome/free-solid-svg-icons": "6.6.0", "@fortawesome/vue-fontawesome": "3.0.8", + "@module-federation/runtime": "0.6.9", "@stomp/rx-stomp": "2.0.0", "@vuelidate/core": "2.0.3", "@vuelidate/validators": "2.0.4", @@ -23,6 +24,7 @@ "devDependencies": { "@eslint/js": "9.12.0", "@module-federation/utilities": "3.1.15", + "@module-federation/vite": "1.1.1", "@pinia/testing": "0.1.6", "@tsconfig/node18": "18.2.4", "@types/node": "20.11.25", diff --git a/generators/vue/templates/eslint.config.js.jhi.vue.ejs b/generators/vue/templates/eslint.config.js.jhi.vue.ejs index 7a5a36fa442b..5a2b441fd314 100644 --- a/generators/vue/templates/eslint.config.js.jhi.vue.ejs +++ b/generators/vue/templates/eslint.config.js.jhi.vue.ejs @@ -22,7 +22,13 @@ import vue from 'eslint-plugin-vue'; <&_ } -&> <&_ if (fragment.configSection) { -&> - { ignores: ['<%- this.relativeDir(clientRootDir, clientDistDir) %>', '<%- temporaryDir %>'] }, + { ignores: [ +<%_ if (microfrontend) { _%> + '.__mf__temp', +<%_ } _%> + '<%- this.relativeDir(clientRootDir, clientDistDir) %>', + '<%- temporaryDir %>', + ] }, js.configs.recommended, ...tseslint.configs.recommended.map(config => config.name === 'typescript-eslint/base' ? config : { ...config, files: [ '**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts' ] }, diff --git a/generators/vue/templates/src/main/webapp/app/core/jhi-navbar/jhi-navbar.component.ts.ejs b/generators/vue/templates/src/main/webapp/app/core/jhi-navbar/jhi-navbar.component.ts.ejs index 320eb2781652..8489f80842a4 100644 --- a/generators/vue/templates/src/main/webapp/app/core/jhi-navbar/jhi-navbar.component.ts.ejs +++ b/generators/vue/templates/src/main/webapp/app/core/jhi-navbar/jhi-navbar.component.ts.ejs @@ -12,7 +12,11 @@ import EntitiesMenu from '@/entities/entities-menu.vue'; import { useStore } from '@/store'; import { useRouter } from 'vue-router'; <%_ if (applicationTypeGateway && microfrontend) { _%> + <%_ if (clientBundlerVite) { _%> +import { loadRemote } from '@module-federation/runtime'; + <%_ } else { _%> import { importRemote } from '@module-federation/utilities'; + <%_ } _%> <%_ } _%> export default defineComponent({ @@ -23,11 +27,16 @@ export default defineComponent({ <%_ if (applicationTypeGateway && microfrontend) { _%> <%_ for (const remote of microfrontends) { _%> '<%= remote.lowercaseBaseName %>-menu': defineAsyncComponent(() => { + <%_ if (clientBundlerVite) { _%> + return loadRemote(`<%= remote.lowercaseBaseName %>/entities-menu`) + .catch(() => import('@/core/error/error-loading.vue')); + <%_ } else { _%> return importRemote({ url: `./<%= remote.endpointPrefix %>`, scope: '<%= remote.lowercaseBaseName %>', module: './entities-menu', }).catch(() => import('@/core/error/error-loading.vue')); + <%_ } _%> }), <%_ } _%> <%_ } _%> diff --git a/generators/vue/templates/src/main/webapp/app/router/index.ts.ejs b/generators/vue/templates/src/main/webapp/app/router/index.ts.ejs index 38579df39062..9641092a2184 100644 --- a/generators/vue/templates/src/main/webapp/app/router/index.ts.ejs +++ b/generators/vue/templates/src/main/webapp/app/router/index.ts.ejs @@ -1,6 +1,10 @@ import { createRouter as createVueRouter, createWebHistory<% if (applicationTypeGateway && microfrontend) { %>, type RouteRecordRaw<% } %> } from 'vue-router'; <%_ if (applicationTypeGateway && microfrontend) { _%> + <%_ if (clientBundlerVite) { _%> +import { loadRemote, registerRemotes } from '@module-federation/runtime'; + <%_ } else { _%> import { importRemote } from '@module-federation/utilities'; + <%_ } _%> <%_ } _%> const Home = () => import('@/core/home/home.vue'); @@ -44,13 +48,29 @@ export const createRouter = () => createVueRouter({ const router = createRouter(); <%_ if (applicationTypeGateway && microfrontend) { _%> + <%_ if (clientBundlerVite) { _%> +registerRemotes([ + <%_ for (const remote of microfrontends) { _%> + { + name: '<%= remote.lowercaseBaseName %>', + entry: './<%= remote.endpointPrefix %>/remoteEntry.js', + type: 'module', + }, + <%_ } _%> +]); + <%_ } _%> + export const lazyRoutes = Promise.all([ <%_ for (const remote of microfrontends) { _%> + <%_ if (clientBundlerVite) { _%> + loadRemote(`<%= remote.lowercaseBaseName %>/entities-router`) + <%_ } else { _%> importRemote({ url: `./<%= remote.endpointPrefix %>`, scope: '<%= remote.lowercaseBaseName %>', module: './entities-router', }) + <%_ } _%> .then(<%= remote.lowercaseBaseName %>Router => { router.addRoute(<%= remote.lowercaseBaseName %>Router.default as RouteRecordRaw); return <%= remote.lowercaseBaseName %>Router.default; diff --git a/generators/vue/templates/vite.config.mts.ejs b/generators/vue/templates/vite.config.mts.ejs index 5b53609660c0..bedacddeabc3 100644 --- a/generators/vue/templates/vite.config.mts.ejs +++ b/generators/vue/templates/vite.config.mts.ejs @@ -19,20 +19,12 @@ import { fileURLToPath, URL } from 'node:url'; import { normalizePath } from 'vite' -import { -<%_ if (microfrontend && clientBundlerVite) { _%> - mergeConfig, -<%_ } _%> - defineConfig, -} from 'vite'; +import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import { viteStaticCopy } from 'vite-plugin-static-copy'; <%_ if (microfrontend && clientBundlerVite) { _%> -import federation from "@originjs/vite-plugin-federation"; - - <%_ if (applicationTypeGateway) { _%> -const sharedAppVersion = '0.0.0'; - <%_ } _%> +import { federation } from '@module-federation/vite'; +import federationConfig from './module-federation.config.cjs'; <%_ } _%> const { getAbsoluteFSPath } = await import('swagger-ui-dist'); @@ -55,6 +47,9 @@ let config = defineConfig({ }, ], }), +<%_ if (microfrontend && clientBundlerVite) { _%> + federation(federationConfig), +<%_ } _%> ], root: fileURLToPath(new URL('./<%- this.relativeDir(clientRootDir, clientSrcDir) %>', import.meta.url)), publicDir: fileURLToPath(new URL('./<%- this.relativeDir(clientRootDir, clientDistDir) %>public', import.meta.url)), @@ -62,6 +57,11 @@ let config = defineConfig({ build: { emptyOutDir: true, outDir: fileURLToPath(new URL('./<%- this.relativeDir(clientRootDir, clientDistDir) %>', import.meta.url)), +<%_ if (microfrontend && clientBundlerVite) { _%> + modulePreload: false, + // Allow topLevelAwait by default, Use 'vite-plugin-top-level-await' plugin for lower target + target: ['chrome89', 'edge89', 'firefox89', 'safari15'], +<%_ } _%> rollupOptions: { input: { app: fileURLToPath(new URL('./<%- this.relativeDir(clientRootDir, clientSrcDir) %>index.html', import.meta.url)), @@ -116,63 +116,6 @@ let config = defineConfig({ }, }); -<%_ if (microfrontend && clientBundlerVite) { _%> -config = mergeConfig(config, { - build: { - modulePreload: false, - minify: false, - target: ['chrome89', 'edge89', 'firefox89', 'safari15'], - }, - plugins: [ - federation({ - name: '<%= lowercaseBaseName %>', - <%_ if (applicationTypeGateway) { _%> - remotes: { - <%_ for (const remote of microfrontends) { _%> - '@<%= remote.lowercaseBaseName %>': `/<%= remote.endpointPrefix %>/assets/remoteEntry.js`, - <%_ } _%> - }, - <%_ } _%> - <%_ if (applicationTypeMicroservice) { _%> - exposes: { - './entities-router': './<%= this.relativeDir(clientRootDir, clientSrcDir) %>app/router/entities', - './entities-menu': './<%= this.relativeDir(clientRootDir, clientSrcDir) %>app/entities/entities-menu.vue', - }, - <%_ } _%> - shared: { - '@vuelidate/core': {}, - '@vuelidate/validators': {}, - axios: {}, - // 'bootstrap-vue': {}, - vue: { - packagePath: '@vue/compat/dist/vue.esm-bundler.js', - }, - 'vue-i18n': {}, - 'vue-router': {}, - pinia: {}, - '@/shared/security/authority': { - packagePath: './<%= this.relativeDir(clientRootDir, clientSrcDir) %>app/shared/security/authority', - <%_ if (applicationTypeGateway) { _%> - version: sharedAppVersion, - <%_ } _%> - }, - '@/shared/alert/alert.service': { - packagePath: './<%= this.relativeDir(clientRootDir, clientSrcDir) %>app/shared/alert/alert.service', - <%_ if (applicationTypeGateway) { _%> - version: sharedAppVersion, - <%_ } _%> - }, - '@/locale/translation.service': { - packagePath: './<%= this.relativeDir(clientRootDir, clientSrcDir) %>app/locale/translation.service', - <%_ if (applicationTypeGateway) { _%> - version: sharedAppVersion, - <%_ } _%> - }, - }, - }), - ], -}); -<%_ } _%> // jhipster-needle-add-vite-config - JHipster will add custom config export default config; diff --git a/lib/jhipster/default-application-options.ts b/lib/jhipster/default-application-options.ts index 4853618c54cd..767a49e69cf5 100644 --- a/lib/jhipster/default-application-options.ts +++ b/lib/jhipster/default-application-options.ts @@ -120,7 +120,7 @@ export function getConfigForClientApplication(options: ApplicationDefaults = {}) options[CLIENT_THEME_VARIANT] = 'primary'; } if (clientFramework === 'vue') { - options.clientBundler = options.microfrontend || options.applicationType === 'microservice' ? 'webpack' : 'vite'; + options.clientBundler = 'vite'; } else if (clientFramework === 'react') { options.clientBundler = 'webpack'; } else if (clientFramework === 'angular') {