diff --git a/generators/app/__snapshots__/generator.spec.ts.snap b/generators/app/__snapshots__/generator.spec.ts.snap index a1144f5b66d5..df725a182bc8 100644 --- a/generators/app/__snapshots__/generator.spec.ts.snap +++ b/generators/app/__snapshots__/generator.spec.ts.snap @@ -256,6 +256,7 @@ exports[`generator - app with default config should match snapshot 1`] = ` "cjsExtension": ".cjs", "clientBundler": "webpack", "clientBundlerAny": true, + "clientBundlerRsbuild": false, "clientBundlerVite": false, "clientBundlerWebpack": true, "clientDistDir": "target/classes/static/", @@ -900,6 +901,7 @@ exports[`generator - app with gateway should match snapshot 1`] = ` "cjsExtension": ".cjs", "clientBundler": "webpack", "clientBundlerAny": true, + "clientBundlerRsbuild": false, "clientBundlerVite": false, "clientBundlerWebpack": true, "clientDistDir": "target/classes/static/", @@ -1539,6 +1541,7 @@ exports[`generator - app with microservice should match snapshot 1`] = ` "cjsExtension": ".cjs", "clientBundler": undefined, "clientBundlerAny": true, + "clientBundlerRsbuild": false, "clientBundlerVite": false, "clientBundlerWebpack": false, "clientDistDir": "target/classes/static/", diff --git a/generators/client/command.ts b/generators/client/command.ts index e64ec89440f2..9b91fe3a2b4f 100644 --- a/generators/client/command.ts +++ b/generators/client/command.ts @@ -82,7 +82,7 @@ const command = { type: String, hide: true, }, - choices: ['webpack', 'vite'], + choices: ['webpack', 'vite', 'rsbuild'], scope: 'storage', }, microfrontend: { diff --git a/generators/gradle/generators/node-gradle/templates/buildSrc/src/main/groovy/jhipster.node-gradle-conventions.gradle.ejs b/generators/gradle/generators/node-gradle/templates/buildSrc/src/main/groovy/jhipster.node-gradle-conventions.gradle.ejs index 14f7dd732ac8..695a36ccb79f 100644 --- a/generators/gradle/generators/node-gradle/templates/buildSrc/src/main/groovy/jhipster.node-gradle-conventions.gradle.ejs +++ b/generators/gradle/generators/node-gradle/templates/buildSrc/src/main/groovy/jhipster.node-gradle-conventions.gradle.ejs @@ -71,6 +71,12 @@ task webapp_test(type: NpmTask) { .withPropertyName("postcssrc") .withPathSensitivity(PathSensitivity.RELATIVE) <%_ } _%> + <%_ if (clientBundlerRsbuild && clientFrameworkBuiltIn) { _%> + + inputs.files('rsbuild.config.ts') + .withPropertyName("rsbuild") + .withPathSensitivity(PathSensitivity.RELATIVE) + <%_ } _%> <%_ if (clientBundlerVite && clientFrameworkBuiltIn) { _%> inputs.files("vite.config.mts") 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/maven/generators/frontend-plugin/generator.ts b/generators/maven/generators/frontend-plugin/generator.ts index ac68d698f07f..d793a3b88deb 100644 --- a/generators/maven/generators/frontend-plugin/generator.ts +++ b/generators/maven/generators/frontend-plugin/generator.ts @@ -43,6 +43,7 @@ export default class FrontendPluginGenerator extends BaseApplicationGenerator { clientFrameworkBuiltIn, clientBundlerVite, clientBundlerWebpack, + clientBundlerRsbuild, microfrontend, srcMainWebapp, } = application; @@ -69,6 +70,8 @@ export default class FrontendPluginGenerator extends BaseApplicationGenerator { checksumIncludedFiles.push('webpack/*.*'); } else if (clientBundlerVite) { checksumIncludedFiles.push('vite.config.mts'); + } else if (clientBundlerRsbuild) { + checksumIncludedFiles.push('rsbuild.config.ts'); } } source.addMavenDefinition!({ 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 2608def03185..7759e4e9c976 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 @@ -196,6 +196,8 @@ public class SecurityConfiguration { .requestMatchers(mvc.pattern("/*.ico"), mvc.pattern("/*.png"), mvc.pattern("/*.svg"), mvc.pattern("/*.webapp")).permitAll() <%_ if (clientBundlerVite) { _%> .requestMatchers(mvc.pattern("/assets/**")).permitAll() + <%_ } else if (clientBundlerRsbuild) { _%> + .requestMatchers(mvc.pattern("/static/**")).permitAll() <%_ } else { _%> .requestMatchers(mvc.pattern("/app/**")).permitAll() .requestMatchers(mvc.pattern("/i18n/**")).permitAll() @@ -225,6 +227,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 ea732caf90c1..ffecc6b61b4f 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 @@ -217,6 +217,8 @@ public class SecurityConfiguration { pathMatchers( <%_ if (clientBundlerVite) { _%> "/assets/**", + <%_ } else if (clientBundlerRsbuild) { _%> + "/static/**", <%_ } else { _%> "/app/**", "/i18n/**", @@ -286,6 +288,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 978209a6225e..0de950facd29 100644 --- a/generators/vue/__snapshots__/generator.spec.ts.snap +++ b/generators/vue/__snapshots__/generator.spec.ts.snap @@ -1440,25 +1440,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", }, } @@ -1493,6 +1478,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", }, @@ -1922,24 +1910,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 dcf63b6c2ce7..191cd9a412da 100644 --- a/generators/vue/files-vue.ts +++ b/generators/vue/files-vue.ts @@ -51,6 +51,10 @@ 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(), @@ -59,14 +63,15 @@ export const vueFiles = { { condition: generator => generator.microfrontend, ...clientSrcTemplatesBlock(), - templates: [ - 'microfrontends/entities-menu.component-test.ts', - 'microfrontends/entities-menu-test.vue', - 'microfrontends/entities-router-test.ts', - ], + 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.applicationTypeMicroservice, + 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 04cca3fb6b3c..a3028a28d553 100644 --- a/generators/vue/generator.ts +++ b/generators/vue/generator.ts @@ -31,7 +31,7 @@ import { getTypescriptKeyType as getTSKeyType, generateTestEntityId as getTestEntityId, } 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'; @@ -54,6 +54,20 @@ export default class VueGenerator extends BaseApplicationGenerator { } } + 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 }) { @@ -205,7 +219,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({ @@ -250,6 +265,27 @@ export default class VueGenerator extends BaseApplicationGenerator { : {}), }, }); + } else if (clientBundlerRsbuild) { + this.packageJson.merge({ + devDependencies: { + ...(applicationTypeGateway + ? { + '@module-federation/utilities': null, + } + : undefined), + '@module-federation/enhanced': null, + '@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 5b53609660c0..3ab74646ebfb 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 4853618c54cd..5b89820e3099 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 = options.microfrontend || options.applicationType === 'microservice' ? 'rsbuild' : 'vite'; } else if (clientFramework === 'react') { options.clientBundler = 'webpack'; } else if (clientFramework === 'angular') { 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 }