From 1aaf4706aa4cb530c35610c51d9d4b15cd8b2101 Mon Sep 17 00:00:00 2001 From: Marcelo Shima Date: Mon, 7 Oct 2024 14:18:58 -0300 Subject: [PATCH 1/2] vue: @module-federation/utilities dependency is gateway only --- generators/vue/generator.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/generators/vue/generator.ts b/generators/vue/generator.ts index 99d8586c9b5b..08199e60e7ca 100644 --- a/generators/vue/generator.ts +++ b/generators/vue/generator.ts @@ -238,19 +238,25 @@ export default class VueGenerator extends BaseApplicationGenerator { }); } }, - addMicrofrontendDependencies({ application, source }) { - const { clientBundlerVite, clientBundlerWebpack, enableTranslation, microfrontend } = application; + addMicrofrontendDependencies({ application }) { + const { applicationTypeGateway, clientBundlerVite, clientBundlerWebpack, enableTranslation, microfrontend } = application; if (!microfrontend) return; if (clientBundlerVite) { - source.mergeClientPackageJson!({ + this.packageJson.merge({ devDependencies: { '@originjs/vite-plugin-federation': '1.3.6', }, }); } else if (clientBundlerWebpack) { - source.mergeClientPackageJson!({ + if (applicationTypeGateway) { + this.packageJson.merge({ + devDependencies: { + '@module-federation/utilities': null, + }, + }); + } + this.packageJson.merge({ devDependencies: { - '@module-federation/enhanced': null, 'browser-sync-webpack-plugin': null, 'copy-webpack-plugin': null, 'css-loader': null, From 9b46e91efe4de69580d4318492dcdfa8155cb942 Mon Sep 17 00:00:00 2001 From: Marcelo Shima Date: Mon, 7 Oct 2024 15:30:58 -0300 Subject: [PATCH 2/2] vue: migrate microfrontend to rsbuild --- generators/client/command.ts | 2 +- .../__snapshots__/generator.spec.ts.snap | 17 +++ .../javascript/generators/rsbuild/command.ts | 26 ++++ .../generators/rsbuild/generator.spec.ts | 53 +++++++ .../generators/rsbuild/generator.ts | 131 ++++++++++++++++++ .../javascript/generators/rsbuild/index.ts | 20 +++ .../templates/rsbuild.config.ts.jhi.ejs | 86 ++++++++++++ .../SecurityConfiguration_imperative.java.ejs | 4 + .../SecurityConfiguration_reactive.java.ejs | 5 + .../vue/__snapshots__/generator.spec.ts.snap | 40 +----- generators/vue/files-vue.ts | 16 ++- generators/vue/generator.ts | 39 +++++- .../module-federation.config.cjs.ejs | 4 +- .../templates/rsbuild.config.ts.jhi.vue.ejs | 85 ++++++++++++ generators/vue/templates/vite.config.mts.ejs | 20 +-- lib/jhipster/default-application-options.ts | 2 +- .../blog-store.jdl | 4 + 17 files changed, 498 insertions(+), 56 deletions(-) create mode 100644 generators/javascript/generators/rsbuild/__snapshots__/generator.spec.ts.snap create mode 100644 generators/javascript/generators/rsbuild/command.ts create mode 100644 generators/javascript/generators/rsbuild/generator.spec.ts create mode 100644 generators/javascript/generators/rsbuild/generator.ts create mode 100644 generators/javascript/generators/rsbuild/index.ts create mode 100644 generators/javascript/generators/rsbuild/templates/rsbuild.config.ts.jhi.ejs create mode 100644 generators/vue/templates/rsbuild.config.ts.jhi.vue.ejs diff --git a/generators/client/command.ts b/generators/client/command.ts index f02a3562d731..c53ef4f31b5d 100644 --- a/generators/client/command.ts +++ b/generators/client/command.ts @@ -82,7 +82,7 @@ const command = { type: String, hide: true, }, - choices: ['webpack', 'vite', 'experimentalEsbuild'], + choices: ['webpack', 'vite', 'experimentalEsbuild', 'rsbuild'], scope: 'storage', }, devServerPort: { diff --git a/generators/javascript/generators/rsbuild/__snapshots__/generator.spec.ts.snap b/generators/javascript/generators/rsbuild/__snapshots__/generator.spec.ts.snap new file mode 100644 index 000000000000..f5dfc067cbc8 --- /dev/null +++ b/generators/javascript/generators/rsbuild/__snapshots__/generator.spec.ts.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`generator - javascript:rsbuild with defaults options should call source snapshot 1`] = `{}`; + +exports[`generator - javascript:rsbuild with defaults options should match files snapshot 1`] = ` +{ + ".yo-rc.json": { + "stateCleared": "modified", + }, + "package.json": { + "stateCleared": "modified", + }, + "rsbuild.config.ts.jhi": { + "stateCleared": "modified", + }, +} +`; diff --git a/generators/javascript/generators/rsbuild/command.ts b/generators/javascript/generators/rsbuild/command.ts new file mode 100644 index 000000000000..de9446fa3118 --- /dev/null +++ b/generators/javascript/generators/rsbuild/command.ts @@ -0,0 +1,26 @@ +/** + * 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. + */ +import type { JHipsterCommandDefinition } from '../../../../lib/command/types.js'; + +const command = { + configs: {}, + import: [], +} as const satisfies JHipsterCommandDefinition; + +export default command; diff --git a/generators/javascript/generators/rsbuild/generator.spec.ts b/generators/javascript/generators/rsbuild/generator.spec.ts new file mode 100644 index 000000000000..1552e18ce2ae --- /dev/null +++ b/generators/javascript/generators/rsbuild/generator.spec.ts @@ -0,0 +1,53 @@ +/** + * 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. + */ +import { basename, dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { before, describe, expect, it } from 'esmocha'; + +import { shouldSupportFeatures, testBlueprintSupport } from '../../../../test/support/tests.js'; +import { defaultHelpers as helpers, result } from '../../../../lib/testing/index.js'; +import Generator from './index.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const generator = `${basename(resolve(__dirname, '../../'))}:${basename(__dirname)}`; + +describe(`generator - ${generator}`, () => { + shouldSupportFeatures(Generator); + describe('blueprint support', () => testBlueprintSupport(generator)); + + describe('with defaults options', () => { + before(async () => { + await helpers.runJHipster(generator).withMockedJHipsterGenerators().withMockedSource().withSharedApplication({}).withJHipsterConfig(); + }); + + it('should match files snapshot', () => { + expect(result.getStateSnapshot()).toMatchSnapshot(); + }); + + it('should call source snapshot', () => { + expect(result.sourceCallsArg).toMatchSnapshot(); + }); + + it('should compose with generators', () => { + expect(result.composedMockedGenerators).toMatchInlineSnapshot(`[]`); + }); + }); +}); diff --git a/generators/javascript/generators/rsbuild/generator.ts b/generators/javascript/generators/rsbuild/generator.ts new file mode 100644 index 000000000000..01dc364cedff --- /dev/null +++ b/generators/javascript/generators/rsbuild/generator.ts @@ -0,0 +1,131 @@ +/** + * 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. + */ +import BaseGenerator from '../../../../generators/base-application/index.js'; + +export default class RspackGenerator extends BaseGenerator { + constructor(args, options, features) { + super(args, options, { queueCommandTasks: true, ...features }); + } + + async beforeQueue() { + if (!this.fromBlueprint) { + await this.composeWithBlueprints(); + } + + if (!this.delegateToBlueprint) { + await this.dependsOnBootstrapApplication(); + } + } + + get preparing() { + return this.asPreparingTaskGroup({}); + } + + get [BaseGenerator.PREPARING]() { + return this.delegateTasksToBlueprint(() => this.preparing); + } + + get postPreparing() { + return this.asPostPreparingTaskGroup({}); + } + + get [BaseGenerator.POST_PREPARING]() { + return this.delegateTasksToBlueprint(() => this.postPreparing); + } + + get preparingEachEntity() { + return this.asPreparingEachEntityTaskGroup({}); + } + + get [BaseGenerator.PREPARING_EACH_ENTITY]() { + return this.delegateTasksToBlueprint(() => this.preparingEachEntity); + } + + get preparingEachEntityField() { + return this.asPreparingEachEntityFieldTaskGroup({}); + } + + get [BaseGenerator.PREPARING_EACH_ENTITY_FIELD]() { + return this.delegateTasksToBlueprint(() => this.preparingEachEntityField); + } + + get preparingEachEntityRelationship() { + return this.asPreparingEachEntityRelationshipTaskGroup({}); + } + + get [BaseGenerator.PREPARING_EACH_ENTITY_RELATIONSHIP]() { + return this.delegateTasksToBlueprint(() => this.preparingEachEntityRelationship); + } + + get postPreparingEachEntity() { + return this.asPostPreparingEachEntityTaskGroup({}); + } + + get [BaseGenerator.POST_PREPARING_EACH_ENTITY]() { + return this.delegateTasksToBlueprint(() => this.postPreparingEachEntity); + } + + get default() { + return this.asDefaultTaskGroup({}); + } + + get [BaseGenerator.DEFAULT]() { + return this.delegateTasksToBlueprint(() => this.default); + } + + get writing() { + return this.asWritingTaskGroup({ + async writeFiles({ application }) { + await this.writeFiles({ + blocks: [{ templates: ['rsbuild.config.ts.jhi'] }], + context: application, + }); + }, + }); + } + + get [BaseGenerator.WRITING]() { + return this.delegateTasksToBlueprint(() => this.writing); + } + + get postWriting() { + return this.asPostWritingTaskGroup({ + addScripts({ application }) { + const { clientPackageManager } = application; + this.packageJson.merge({ + devDependencies: { + '@rsbuild/core': 'latest', + }, + scripts: { + start: 'rsbuild dev', + build: 'rsbuild build', + 'webapp:build:dev': `${clientPackageManager} run build -- --mode=development`, + 'webapp:build:prod': `${clientPackageManager} run build -- --mode=production`, + 'webapp:dev': `${clientPackageManager} run start`, + 'webapp:serve': `${clientPackageManager} start`, + }, + }); + }, + }); + } + + get [BaseGenerator.POST_WRITING]() { + return this.delegateTasksToBlueprint(() => this.postWriting); + } +} diff --git a/generators/javascript/generators/rsbuild/index.ts b/generators/javascript/generators/rsbuild/index.ts new file mode 100644 index 000000000000..1cfadd692bb6 --- /dev/null +++ b/generators/javascript/generators/rsbuild/index.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ +export { default } from './generator.js'; +export { default as command } from './command.js'; diff --git a/generators/javascript/generators/rsbuild/templates/rsbuild.config.ts.jhi.ejs b/generators/javascript/generators/rsbuild/templates/rsbuild.config.ts.jhi.ejs new file mode 100644 index 000000000000..2b51f1cfbc43 --- /dev/null +++ b/generators/javascript/generators/rsbuild/templates/rsbuild.config.ts.jhi.ejs @@ -0,0 +1,86 @@ +<%# + 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. +-%> +<&_ + // Register sections and max allowed fragments, 0 for unlimited. + fragments.registerSections({ + importsSection: 0, + pluginsSection: 0, + configSection: 0, + configsSection: 0, + }); +_&> +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { mergeRsbuildConfig } from '@rsbuild/core'; +import { getAbsoluteFSPath } from 'swagger-ui-dist'; +<&- fragments.importsSection() -&> + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +export default mergeRsbuildConfig({ + root: path.join(__dirname, '<%- this.relativeDir(clientRootDir, clientSrcDir) %>'), + output: { +<%_ if (microfrontend) { _%> + assetPrefix: 'auto', +<%_ } _%> + cleanDistPath: true, + distPath: { + root: path.join(__dirname, './<%= this.relativeDir(clientRootDir, clientDistDir) %>'), + }, + }, + copy: [ + { + // https://github.com/swagger-api/swagger-ui/blob/v4.6.1/swagger-ui-dist-package/README.md + context: getAbsoluteFSPath(), + from: '*.{js,css,html,png}', + to: 'swagger-ui/', + globOptions: { ignore: ['**/index.html'] }, + }, + { + from: path.join(path.dirname(require.resolve('axios/package.json')), 'dist/axios.min.js'), + to: 'swagger-ui/', + }, + { from: './<%= this.relativeDir(clientRootDir, clientSrcDir) %>swagger-ui/', to: 'swagger-ui/' }, + { from: './<%= this.relativeDir(clientRootDir, clientSrcDir) %>content/', to: 'content/' }, + { from: './<%= this.relativeDir(clientRootDir, clientSrcDir) %>favicon.ico', to: 'favicon.ico' }, + { + from: './<%= this.relativeDir(clientRootDir, clientSrcDir) %>manifest.webapp', + to: 'manifest.webapp', + }, + // jhipster-needle-add-assets-to-webpack - JHipster will add/remove third-party resources in this array + { from: './<%= this.relativeDir(clientRootDir, clientSrcDir) %>robots.txt', to: 'robots.txt' }, + ], + html: { + template: './index.html', + scriptLoading: 'defer', + tags: [ + { + tag: 'base', + attrs: { href: '/' }, + }, + ], + }, + plugins: [ +<&- fragments.pluginsSection() -&> + ], +<&- fragments.configSection() -&> +}, +<&- fragments.configsSection() -&> +); diff --git a/generators/spring-boot/templates/src/main/java/_package_/config/SecurityConfiguration_imperative.java.ejs b/generators/spring-boot/templates/src/main/java/_package_/config/SecurityConfiguration_imperative.java.ejs index a01beea70bdf..391406e4b7ae 100644 --- a/generators/spring-boot/templates/src/main/java/_package_/config/SecurityConfiguration_imperative.java.ejs +++ b/generators/spring-boot/templates/src/main/java/_package_/config/SecurityConfiguration_imperative.java.ejs @@ -191,6 +191,8 @@ public class SecurityConfiguration { <%_ } else if (clientBundlerExperimentalEsbuild) { _%> .requestMatchers(mvc.pattern("/content/**")).permitAll() .requestMatchers(mvc.pattern("/resources/**")).permitAll() + <%_ } else if (clientBundlerRsbuild) { _%> + .requestMatchers(mvc.pattern("/static/**")).permitAll() <%_ } else { _%> .requestMatchers(mvc.pattern("/app/**")).permitAll() .requestMatchers(mvc.pattern("/i18n/**")).permitAll() @@ -220,6 +222,8 @@ public class SecurityConfiguration { // microfrontend resources are loaded by webpack without authentication, they need to be public <%_ if (clientBundlerVite) { _%> .requestMatchers(mvc.pattern("/services/*/assets/**")).permitAll() + <%_ } else if (clientBundlerRsbuild) { _%> + .requestMatchers(mvc.pattern("/services/*/static/**")).permitAll() <%_ } _%> .requestMatchers(mvc.pattern("/services/*/*.js")).permitAll() .requestMatchers(mvc.pattern("/services/*/*.txt")).permitAll() diff --git a/generators/spring-boot/templates/src/main/java/_package_/config/SecurityConfiguration_reactive.java.ejs b/generators/spring-boot/templates/src/main/java/_package_/config/SecurityConfiguration_reactive.java.ejs index 4b60e495f0fd..47db8fbf55f3 100644 --- a/generators/spring-boot/templates/src/main/java/_package_/config/SecurityConfiguration_reactive.java.ejs +++ b/generators/spring-boot/templates/src/main/java/_package_/config/SecurityConfiguration_reactive.java.ejs @@ -220,6 +220,8 @@ public class SecurityConfiguration { <%_ } else if (clientBundlerExperimentalEsbuild) { _%> "/content/**", "/resources/**", + <%_ } else if (clientBundlerRsbuild) { _%> + "/static/**", <%_ } else { _%> "/app/**", "/i18n/**", @@ -289,6 +291,9 @@ public class SecurityConfiguration { <%_ if (clientBundlerVite) { _%> .pathMatchers("/services/*/assets/**").permitAll() .pathMatchers("/services/*/*.js").permitAll() + <%_ } else if (clientBundlerRsbuild) { _%> + .pathMatchers("/services/*/static/**").permitAll() + .pathMatchers("/services/*/*.js").permitAll() <%_ } else { _%> .pathMatchers("/services/*/*.js").permitAll() .pathMatchers("/services/*/*.txt").permitAll() diff --git a/generators/vue/__snapshots__/generator.spec.ts.snap b/generators/vue/__snapshots__/generator.spec.ts.snap index 6bb863c6493a..8046d873d5f1 100644 --- a/generators/vue/__snapshots__/generator.spec.ts.snap +++ b/generators/vue/__snapshots__/generator.spec.ts.snap @@ -1476,25 +1476,10 @@ exports[`generator - vue microservice-jwt-skipUserManagement(false)-withAdminUi( "clientRoot/vitest.config.mts": { "stateCleared": "modified", }, - "clientRoot/webpack/config.js": { - "stateCleared": "modified", - }, - "clientRoot/webpack/vue.utils.js": { - "stateCleared": "modified", - }, - "clientRoot/webpack/webpack.common.js": { - "stateCleared": "modified", - }, - "clientRoot/webpack/webpack.dev.js": { - "stateCleared": "modified", - }, - "clientRoot/webpack/webpack.microfrontend.js": { - "stateCleared": "modified", - }, - "clientRoot/webpack/webpack.prod.js": { + "package.json": { "stateCleared": "modified", }, - "package.json": { + "rsbuild.config.ts": { "stateCleared": "modified", }, } @@ -1573,6 +1558,9 @@ exports[`generator - vue microservice-oauth2-withAdminUi(true)-skipJhipsterDepen "package.json": { "stateCleared": "modified", }, + "rsbuild.config.ts": { + "stateCleared": "modified", + }, "src/main/webapp/404.html": { "stateCleared": "modified", }, @@ -1990,24 +1978,6 @@ exports[`generator - vue microservice-oauth2-withAdminUi(true)-skipJhipsterDepen "vitest.config.mts": { "stateCleared": "modified", }, - "webpack/config.js": { - "stateCleared": "modified", - }, - "webpack/vue.utils.js": { - "stateCleared": "modified", - }, - "webpack/webpack.common.js": { - "stateCleared": "modified", - }, - "webpack/webpack.dev.js": { - "stateCleared": "modified", - }, - "webpack/webpack.microfrontend.js": { - "stateCleared": "modified", - }, - "webpack/webpack.prod.js": { - "stateCleared": "modified", - }, } `; diff --git a/generators/vue/files-vue.ts b/generators/vue/files-vue.ts index c6d31d2cf8da..191cd9a412da 100644 --- a/generators/vue/files-vue.ts +++ b/generators/vue/files-vue.ts @@ -51,13 +51,27 @@ export const vueFiles = { 'webpack/vue.utils.js', ], }), + clientRootTemplatesBlock({ + condition: ctx => ctx.microfrontend && ctx.clientBundlerRsbuild, + templates: ['rsbuild.config.ts.jhi.vue'], + }), { condition: generator => generator.microfrontend, ...clientApplicationTemplatesBlock(), templates: ['index.ts', 'core/error/error-loading.vue'], }, { - condition: generator => generator.applicationTypeMicroservice, + condition: generator => generator.microfrontend, + ...clientSrcTemplatesBlock(), + templates: ['microfrontends/entities-menu.component-test.ts', 'microfrontends/entities-router-test.ts'], + }, + { + condition: generator => generator.microfrontend, + ...clientSrcTemplatesBlock(), + templates: ['microfrontends/entities-menu-test.vue'], + }, + { + condition: generator => generator.enableTranslation && generator.applicationTypeMicroservice, ...clientApplicationTemplatesBlock(), templates: ['entities/entities-menu.spec.ts'], }, diff --git a/generators/vue/generator.ts b/generators/vue/generator.ts index 08199e60e7ca..450879c62f5b 100644 --- a/generators/vue/generator.ts +++ b/generators/vue/generator.ts @@ -29,7 +29,7 @@ import { generateEntityClientEnumImports as getClientEnumImportsFormat, generateEntityClientFields as getHydratedEntityClientFields, } from '../client/support/index.js'; -import { createNeedleCallback } from '../base/support/index.js'; +import { createNeedleCallback } from '../base/support/needles.js'; import { writeEslintClientRootConfigFile } from '../javascript/generators/eslint/support/tasks.js'; import { cleanupEntitiesFiles, postWriteEntityFiles, writeEntityFiles } from './entity-files-vue.js'; import cleanupOldFilesTask from './cleanup.js'; @@ -69,6 +69,20 @@ export default class VueGenerator extends BaseApplicationGenerator { return this.delegateTasksToBlueprint(() => this.configuring); } + get composing() { + return this.asComposingTaskGroup({ + async composing() { + if (this.jhipsterConfigWithDefaults.clientBundler === 'rsbuild') { + await this.composeWithJHipster('jhipster:javascript:rsbuild'); + } + }, + }); + } + + get [BaseApplicationGenerator.COMPOSING]() { + return this.delegateTasksToBlueprint(() => this.composing); + } + get loading() { return this.asLoadingTaskGroup({ loadPackageJson({ application }) { @@ -239,7 +253,8 @@ export default class VueGenerator extends BaseApplicationGenerator { } }, addMicrofrontendDependencies({ application }) { - const { applicationTypeGateway, clientBundlerVite, clientBundlerWebpack, enableTranslation, microfrontend } = application; + const { applicationTypeGateway, clientBundlerRsbuild, clientBundlerVite, clientBundlerWebpack, enableTranslation, microfrontend } = + application; if (!microfrontend) return; if (clientBundlerVite) { this.packageJson.merge({ @@ -283,6 +298,26 @@ export default class VueGenerator extends BaseApplicationGenerator { : {}), }, }); + } else if (clientBundlerRsbuild) { + this.packageJson.merge({ + devDependencies: { + ...(applicationTypeGateway + ? { + '@module-federation/utilities': null, + } + : undefined), + '@rsbuild/plugin-sass': 'latest', + '@rsbuild/plugin-vue': 'latest', + 'vue-loader': null, + 'vue-style-loader': null, + ...(application.enableTranslation + ? { + 'folder-hash': null, + 'merge-jsons-webpack-plugin': null, + } + : {}), + }, + }); } }, addIndexAsset({ source, application }) { diff --git a/generators/vue/templates/module-federation.config.cjs.ejs b/generators/vue/templates/module-federation.config.cjs.ejs index a7d7eff0ef7a..3ed3cfdfb6c5 100644 --- a/generators/vue/templates/module-federation.config.cjs.ejs +++ b/generators/vue/templates/module-federation.config.cjs.ejs @@ -36,8 +36,8 @@ module.exports = { name: '<%= lowercaseBaseName %>', <%_ if (applicationTypeMicroservice) { _%> exposes: { - './entities-router': './<%- this.relativeDir(clientRootDir, clientSrcDir) %>app/router/entities.ts', - './entities-menu': './<%- this.relativeDir(clientRootDir, clientSrcDir) %>app/entities/entities-menu.vue', + './entities-router': './app/router/entities.ts', + './entities-menu': './app/entities/entities-menu.vue', }, <%_ } _%> filename: 'remoteEntry.js', diff --git a/generators/vue/templates/rsbuild.config.ts.jhi.vue.ejs b/generators/vue/templates/rsbuild.config.ts.jhi.vue.ejs new file mode 100644 index 000000000000..12ed011bb611 --- /dev/null +++ b/generators/vue/templates/rsbuild.config.ts.jhi.vue.ejs @@ -0,0 +1,85 @@ +<%# + 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. +-%> +<&_ if (fragment.importsSection) { -&> +import { pluginVue } from '@rsbuild/plugin-vue'; +import { pluginSass } from '@rsbuild/plugin-sass'; +<%_ if (microfrontend) { _%> +import mfConfig from './module-federation.config.cjs'; + +console.log(mfConfig); +<%_ } _%> +<&_ } -&> + +<&_ if (fragment.pluginsSection) { -&> + pluginVue(), + pluginSass(), +<&_ } -&> + +<&_ if (fragment.configSection) { -&> + source: { + entry: { + index: './app/<%= microfrontend ? 'index' : 'main' %>.ts', + }, + define: { + I18N_HASH: '"generated_hash"', + SERVER_API_URL: '"/"', + APP_VERSION: `"${process.env.APP_VERSION ? process.env.APP_VERSION : 'DEV'}"`, + }, + alias: { + vue$: '@vue/compat/dist/vue.esm-bundler.js', + '@': path.resolve(__dirname, './<%= this.relativeDir(clientRootDir, clientSrcDir) %>app'), + }, + }, + server: { + port: <%= devServerPort %>, + proxy: [ + { + context: [ + '/api', + '/services', + '/management', + '/v3/api-docs', + '/h2-console', +<%_ if (authenticationTypeOauth2) { _%> + '/oauth2', + '/login', +<%_ } _%> + '/auth' + ], + target: 'http://localhost:<%= applicationTypeMicroservice ? gatewayServerPort : serverPort %>', + secure: false, + }, +<%_ if (communicationSpringWebsocket) { _%> + { + context: [ + '/websocket' + ], + target: 'ws://localhost:<%= applicationTypeMicroservice ? gatewayServerPort : serverPort %>', + ws: true + } +<%_ } _%> + ], + historyApiFallback: true, + }, +<%_ if (microfrontend) { _%> + moduleFederation: { + options: mfConfig, + }, +<%_ } _%> +<&_ } -&> diff --git a/generators/vue/templates/vite.config.mts.ejs b/generators/vue/templates/vite.config.mts.ejs index 1feef5c7686f..6f41de9bbd3f 100644 --- a/generators/vue/templates/vite.config.mts.ejs +++ b/generators/vue/templates/vite.config.mts.ejs @@ -19,12 +19,7 @@ import { fileURLToPath, URL } from 'node:url'; import { normalizePath } from 'vite' -import { -<%_ if (microfrontend && clientBundlerVite) { _%> - mergeConfig, -<%_ } _%> - defineConfig, -} from 'vite'; +import { mergeConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import { viteStaticCopy } from 'vite-plugin-static-copy'; <%_ if (microfrontend && clientBundlerVite) { _%> @@ -38,8 +33,7 @@ const sharedAppVersion = '0.0.0'; const { getAbsoluteFSPath } = await import('swagger-ui-dist'); const swaggerUiPath = getAbsoluteFSPath(); -// eslint-disable-next-line prefer-const -let config = defineConfig({ +export default mergeConfig({ plugins: [ vue(), viteStaticCopy({ @@ -114,10 +108,9 @@ let config = defineConfig({ ]), ), }, -}); - +}, <%_ if (microfrontend && clientBundlerVite) { _%> -config = mergeConfig(config, { +{ build: { modulePreload: false, minify: false, @@ -171,8 +164,7 @@ config = mergeConfig(config, { }, }), ], -}); +}, <%_ } _%> // 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 c16b62c59bf9..285789c466d5 100644 --- a/lib/jhipster/default-application-options.ts +++ b/lib/jhipster/default-application-options.ts @@ -119,7 +119,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 ??= options.microfrontend || options.applicationType === 'microservice' ? 'rsbuild' : 'vite'; options.devServerPort ??= options.clientBundler === 'webpack' ? 9060 : 9000; } else if (clientFramework === 'react') { options.clientBundler ??= 'webpack'; diff --git a/test-integration/jdl-samples/ms-mf-vue-consul-oauth2-mysql-memcached/blog-store.jdl b/test-integration/jdl-samples/ms-mf-vue-consul-oauth2-mysql-memcached/blog-store.jdl index 88761d6c8e14..c0758e54fec5 100644 --- a/test-integration/jdl-samples/ms-mf-vue-consul-oauth2-mysql-memcached/blog-store.jdl +++ b/test-integration/jdl-samples/ms-mf-vue-consul-oauth2-mysql-memcached/blog-store.jdl @@ -31,6 +31,7 @@ application { serviceDiscoveryType consul testFrameworks [cypress] microfrontends [blog, notification] + enableTranslation false } entities UserData, Product } @@ -51,6 +52,7 @@ application { serverPort 8081 serviceDiscoveryType consul testFrameworks [cypress] + enableTranslation false } entities Blog, Post, Tag } @@ -72,6 +74,7 @@ application { serverPort 8082 serviceDiscoveryType consul testFrameworks [cypress] + enableTranslation false } entities Product } @@ -94,6 +97,7 @@ application { serverPort 8083 serviceDiscoveryType consul testFrameworks [cypress] + enableTranslation false } entities Notification }