diff --git a/generators/client/command.ts b/generators/client/command.ts index b52503537712..272bbd485fc5 100644 --- a/generators/client/command.ts +++ b/generators/client/command.ts @@ -63,6 +63,14 @@ const command = { ], scope: 'storage', }, + clientBundler: { + cli: { + type: String, + hide: true, + }, + choices: ['webpack', 'rsbuild', 'vite'], + scope: 'storage', + }, microfrontend: { description: 'Enable microfrontend support', cli: { diff --git a/generators/client/files-common.ts b/generators/client/files-common.ts index 5f975616225d..6db7ddb69eac 100644 --- a/generators/client/files-common.ts +++ b/generators/client/files-common.ts @@ -25,7 +25,7 @@ export const files = { templates: ['README.md.jhi.client', '.prettierignore.jhi.client'], }, clientRootTemplatesBlock({ - condition: generator => generator.microfrontend && (generator.clientFrameworkVue || generator.clientFrameworkReact), + condition: generator => generator.microfrontend && generator.clientFrameworkReact, templates: ['webpack/webpack.microfrontend.js.jhi'], }), { diff --git a/generators/client/generator.ts b/generators/client/generator.ts index e42da8e2b3d9..87e12d052aa5 100644 --- a/generators/client/generator.ts +++ b/generators/client/generator.ts @@ -252,9 +252,9 @@ export default class JHipsterClientGenerator extends BaseApplicationGenerator { source.addWebpackConfig({ config: `${conditional}require('./webpack.microfrontend')(config, options, targetOptions)`, }); - } else if (application.clientFrameworkVue || application.clientFrameworkReact) { + } else if (application.clientFrameworkReact) { source.addWebpackConfig({ config: "require('./webpack.microfrontend')({ serve: options.env.WEBPACK_SERVE })" }); - } else { + } else if (!application.clientFrameworkVue) { throw new Error(`Client framework ${application.clientFramework} doesn't support microfrontends`); } }, 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 76180541fd09..dde9de0e4f7f 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 @@ -68,10 +68,8 @@ task webapp_test(type: NpmTask) { .withPathSensitivity(PathSensitivity.RELATIVE) <%_ if (microfrontend) { _%> - def webpackDevFiles = fileTree("<%= CLIENT_WEBPACK_DIR %>") - webpackDevFiles.exclude("webpack.prod.js") - inputs.files(webpackDevFiles) - .withPropertyName("webpack-dir") + inputs.files('rsbuild.config.ts', 'rsbuild.module-federation.config.ts') + .withPropertyName("rsbuild") .withPathSensitivity(PathSensitivity.RELATIVE) <%_ } else { _%> 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..8443800a1f6b --- /dev/null +++ b/generators/javascript/generators/rsbuild/generator.ts @@ -0,0 +1,129 @@ +/** + * 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 }) { + this.packageJson.merge({ + devDependencies: { + '@rsbuild/core': 'latest', + }, + scripts: { + start: 'rsbuild dev', + build: 'rsbuild build', + 'webapp:build:dev': `${application.clientPackageManager} run build -- --mode=development`, + 'webapp:build:prod': `${application.clientPackageManager} run build -- --mode=production`, + 'webapp:dev': `${application.clientPackageManager} run 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..e955d62143f6 --- /dev/null +++ b/generators/javascript/generators/rsbuild/templates/rsbuild.config.ts.jhi.ejs @@ -0,0 +1,57 @@ +<%# + 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, + }); +_&> +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { defineConfig } from '@rsbuild/core'; +<&- fragments.importsSection() -&> + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +export default defineConfig({ + root: path.join(__dirname, '<%- this.relativeDir(clientRootDir, clientSrcDir) %>'), + output: { + cleanDistPath: true, + distPath: { + root: path.join(__dirname, './<%= this.relativeDir(clientRootDir, clientDistDir) %>'), + }, + }, + html: { + template: './index.html', + scriptLoading: 'defer', + tags: [ + { + tag: 'base', + attrs: { href: '/' }, + }, + ], + }, + plugins: [ +<&- fragments.pluginsSection() -&> + ], +<&- fragments.configSection() -&> +}); diff --git a/generators/maven/generators/frontend-plugin/generator.ts b/generators/maven/generators/frontend-plugin/generator.ts index bb6b3c2b3811..aa72e866c72b 100644 --- a/generators/maven/generators/frontend-plugin/generator.ts +++ b/generators/maven/generators/frontend-plugin/generator.ts @@ -58,7 +58,7 @@ export default class FrontendPluginGenerator extends BaseApplicationGenerator { } else if (clientFrameworkVue) { checksumIncludedFiles.push('.postcssrc.js', 'tsconfig.app.json'); if (microfrontend) { - checksumIncludedFiles.push('webpack/*.*'); + checksumIncludedFiles.push('rsbuild.config.ts', 'rsbuild.module-federation.config.ts'); } else { checksumIncludedFiles.push('vite.config.ts'); } 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 5fa690b4919b..6820c134aff3 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 @@ -194,17 +194,15 @@ public class SecurityConfiguration { <%_ if (!skipClient) { _%> .requestMatchers(mvc.pattern("/index.html"), mvc.pattern("/*.js"), mvc.pattern("/*.txt"), mvc.pattern("/*.json"), mvc.pattern("/*.map"), mvc.pattern("/*.css")).permitAll() .requestMatchers(mvc.pattern("/*.ico"), mvc.pattern("/*.png"), mvc.pattern("/*.svg"), mvc.pattern("/*.webapp")).permitAll() - <%_ if (clientFrameworkVue) { _%> + <%_ if (clientBundlerRsbuild) { _%> + .requestMatchers(mvc.pattern("/static/**")).permitAll() + <%_ } else if (clientBundlerVite) { _%> .requestMatchers(mvc.pattern("/assets/**")).permitAll() - <%_ if (microfrontend) { _%> - .requestMatchers(mvc.pattern("/app/**")).permitAll() - .requestMatchers(mvc.pattern("/i18n/**")).permitAll() - <%_ } _%> <%_ } else { _%> .requestMatchers(mvc.pattern("/app/**")).permitAll() .requestMatchers(mvc.pattern("/i18n/**")).permitAll() - <%_ } _%> .requestMatchers(mvc.pattern("/content/**")).permitAll() + <%_ } _%> .requestMatchers(mvc.pattern("/swagger-ui/**")).permitAll() <%_ } _%> <%_ if (authenticationTypeJwt) { _%> @@ -227,7 +225,11 @@ public class SecurityConfiguration { <%_ if (applicationTypeGateway) { _%> <%_ if (microfrontend) { _%> // microfrontend resources are loaded by webpack without authentication, they need to be public + <%_ if (clientBundlerRsbuild) { _%> + .requestMatchers(mvc.pattern("/services/*/static/**")).permitAll() + <%_ } _%> .requestMatchers(mvc.pattern("/services/*/*.js")).permitAll() + .requestMatchers(mvc.pattern("/services/*/content/*.js")).permitAll() .requestMatchers(mvc.pattern("/services/*/*.txt")).permitAll() .requestMatchers(mvc.pattern("/services/*/*.json")).permitAll() .requestMatchers(mvc.pattern("/services/*/*.js.map")).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 dc987fc12acb..9f79e9ee2f8a 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 @@ -214,19 +214,18 @@ public class SecurityConfiguration { public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http .securityMatcher(new NegatedServerWebExchangeMatcher(new OrServerWebExchangeMatcher( - <%_ if (clientFrameworkVue) { _%> pathMatchers( + <%_ if (clientBundlerRsbuild) { _%> + "/static/**", + <%_ } else if (clientBundlerVite) { _%> "/assets/**", - <%_ if (microfrontend) { _%> + <%_ } else { _%> "/app/**", "/i18n/**", "/content/**", - <%_ } _%> + <%_ } _%> "/swagger-ui/**" ) - <%_ } else { _%> - pathMatchers("/app/**", "/i18n/**", "/content/**", "/swagger-ui/**") - <%_ } _%> ))) <%_ if (!applicationTypeMicroservice) { _%> .cors(withDefaults()) @@ -286,7 +285,11 @@ public class SecurityConfiguration { <%_ if (applicationTypeGateway) { _%> <%_ if (microfrontend) { _%> // microfrontend resources are loaded by webpack without authentication, they need to be public + <%_ if (clientBundlerRsbuild) { _%> + .pathMatchers("/services/*/static/**").permitAll() + <%_ } _%> .pathMatchers("/services/*/*.js").permitAll() + .pathMatchers("/services/*/content/*.js").permitAll() .pathMatchers("/services/*/*.txt").permitAll() .pathMatchers("/services/*/*.json").permitAll() .pathMatchers("/services/*/*.js.map").permitAll() diff --git a/generators/vue/__snapshots__/generator.spec.ts.snap b/generators/vue/__snapshots__/generator.spec.ts.snap index 81c96d44f772..f73e715083be 100644 --- a/generators/vue/__snapshots__/generator.spec.ts.snap +++ b/generators/vue/__snapshots__/generator.spec.ts.snap @@ -1092,9 +1092,6 @@ exports[`generator - vue microservice-jwt-skipUserManagement(false)-withAdminUi( "clientRoot/src/main/webapp/app/entities/entities-menu.component.ts": { "stateCleared": "modified", }, - "clientRoot/src/main/webapp/app/entities/entities-menu.spec.ts": { - "stateCleared": "modified", - }, "clientRoot/src/main/webapp/app/entities/entities-menu.vue": { "stateCleared": "modified", }, @@ -1395,9 +1392,6 @@ exports[`generator - vue microservice-jwt-skipUserManagement(false)-withAdminUi( "clientRoot/src/main/webapp/manifest.webapp": { "stateCleared": "modified", }, - "clientRoot/src/main/webapp/microfrontends/entities-menu-test.vue": { - "stateCleared": "modified", - }, "clientRoot/src/main/webapp/microfrontends/entities-menu.component-test.ts": { "stateCleared": "modified", }, @@ -1431,22 +1425,13 @@ 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": { + "package.json": { "stateCleared": "modified", }, - "clientRoot/webpack/webpack.microfrontend.js": { + "rsbuild.config.ts": { "stateCleared": "modified", }, - "clientRoot/webpack/webpack.prod.js": { + "rsbuild.module-federation.config.ts": { "stateCleared": "modified", }, } @@ -1478,6 +1463,12 @@ exports[`generator - vue microservice-oauth2-withAdminUi(true)-skipJhipsterDepen "package.json": { "stateCleared": "modified", }, + "rsbuild.config.ts": { + "stateCleared": "modified", + }, + "rsbuild.module-federation.config.ts": { + "stateCleared": "modified", + }, "src/main/webapp/404.html": { "stateCleared": "modified", }, @@ -1907,24 +1898,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 e70af0479916..655aedc206c8 100644 --- a/generators/vue/files-vue.ts +++ b/generators/vue/files-vue.ts @@ -37,17 +37,10 @@ export const vueFiles = { }), ], microfrontend: [ - clientRootTemplatesBlock({ + { condition: generator => generator.microfrontend, - templates: [ - 'webpack/config.js', - 'webpack/webpack.common.js', - 'webpack/webpack.dev.js', - 'webpack/webpack.prod.js', - 'webpack/vue.utils.js', - 'webpack/webpack.microfrontend.js.jhi.vue', - ], - }), + templates: ['rsbuild.module-federation.config.ts', 'rsbuild.config.ts.jhi.vue'], + }, { condition: generator => generator.microfrontend, ...clientApplicationTemplatesBlock(), @@ -56,14 +49,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.enableTranslation && 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 e2d65daa2091..f33c409d5379 100644 --- a/generators/vue/generator.ts +++ b/generators/vue/generator.ts @@ -31,7 +31,6 @@ import { getTypescriptKeyType as getTSKeyType, generateTestEntityId as getTestEntityId, } from '../client/support/index.js'; -import { createNeedleCallback } from '../base/support/index.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 +53,24 @@ export default class VueGenerator extends BaseApplicationGenerator { } } + get composing() { + return this.asComposingTaskGroup({ + async composing() { + if ( + this.jhipsterConfig.microfrontend || + this.jhipsterConfig.applicationType === 'microservice' || + (this.jhipsterConfig.microfrontends ?? []).length > 0 + ) { + await this.composeWithJHipster('jhipster:javascript:rsbuild'); + } + }, + }); + } + + get [BaseApplicationGenerator.COMPOSING]() { + return this.delegateTasksToBlueprint(() => this.composing); + } + get loading() { return this.asLoadingTaskGroup({ loadPackageJson({ application }) { @@ -85,21 +102,8 @@ export default class VueGenerator extends BaseApplicationGenerator { webappEnumerationsDir: app => `${app.clientWebappDir}shared/model/enumerations/`, }); }, - prepareForTemplates({ application, source }) { + prepareForTemplates({ application }) { application.addPrettierExtensions?.(['html', 'vue', 'css', 'scss']); - - source.addWebpackConfig = args => { - const webpackPath = `${application.clientRootDir}webpack/webpack.common.js`; - const ignoreNonExisting = this.sharedData.getControl().ignoreNeedlesError && 'Webpack configuration file not found'; - this.editFile( - webpackPath, - { ignoreNonExisting }, - createNeedleCallback({ - needle: 'jhipster-needle-add-webpack-config', - contentToAdd: `,${args.config}`, - }), - ); - }; }, }); } @@ -175,7 +179,16 @@ export default class VueGenerator extends BaseApplicationGenerator { get postWriting() { return this.asPostWritingTaskGroup({ addIndexAsset({ source, application }) { - if (application.microfrontend) return; + if (application.microfrontend) { + this.packageJson.merge({ + devDependencies: { + '@rsbuild/plugin-sass': 'latest', + '@rsbuild/plugin-vue': 'latest', + }, + }); + return; + } + source.addExternalResourceToRoot!({ resource: '', comment: 'Workaround https://github.com/axios/axios/issues/5622', diff --git a/generators/vue/support/update-languages.ts b/generators/vue/support/update-languages.ts index fdf3eb45c3d5..bbd67554db1e 100644 --- a/generators/vue/support/update-languages.ts +++ b/generators/vue/support/update-languages.ts @@ -88,7 +88,7 @@ export default function updateLanguagesTask(this: BaseGenerator, taskParam: Upda updateLanguagesInPipeTask.call(this, taskParam); updateLanguagesInConfigTask.call(this, taskParam); if (taskParam.application.microfrontend) { - updateLanguagesInWebpackTask.call(this, taskParam); + // updateLanguagesInWebpackTask.call(this, taskParam); } updateLanguagesInDayjsConfigurationTask.call(this, taskParam, { configurationFile: `${taskParam.application.clientSrcDir}app/shared/config/dayjs.ts`, diff --git a/generators/vue/templates/package.json.ejs b/generators/vue/templates/package.json.ejs index bae1f6baa4f9..e0fad3934a47 100644 --- a/generators/vue/templates/package.json.ejs +++ b/generators/vue/templates/package.json.ejs @@ -59,29 +59,12 @@ "@module-federation/utilities": "<%= nodeDependencies['@module-federation/utilities'] %>", <%_ } _%> <%_ if (microfrontend) { _%> - "@originjs/vite-plugin-federation": "1.3.3", - "browser-sync-webpack-plugin": "<%= nodeDependencies['browser-sync-webpack-plugin'] %>", - "copy-webpack-plugin": "<%= nodeDependencies['copy-webpack-plugin'] %>", - "css-loader": "<%= nodeDependencies['css-loader'] %>", - "css-minimizer-webpack-plugin": "<%= nodeDependencies['css-minimizer-webpack-plugin'] %>", - "html-webpack-plugin": "<%= nodeDependencies['html-webpack-plugin'] %>", <%_ if (enableTranslation) { _%> "folder-hash": "<%= nodeDependencies['folder-hash'] %>", "merge-jsons-webpack-plugin": "<%= nodeDependencies['merge-jsons-webpack-plugin'] %>", <%_ } _%> - "mini-css-extract-plugin": "<%= nodeDependencies['mini-css-extract-plugin'] %>", - "postcss-loader": "<%= nodeDependencies['postcss-loader'] %>", - "sass-loader": "<%= nodeDependencies['sass-loader'] %>", - "terser-webpack-plugin": "<%= nodeDependencies['terser-webpack-plugin'] %>", - "ts-loader": "<%= nodeDependencies['ts-loader'] %>", "vue-loader": "<%= nodeDependencies['vue-loader'] %>", "vue-style-loader": "<%= nodeDependencies['vue-style-loader'] %>", - "webpack": "<%= nodeDependencies['webpack'] %>", - "webpack-bundle-analyzer": "<%= nodeDependencies['webpack-bundle-analyzer'] %>", - "webpack-cli": "<%= nodeDependencies['webpack-cli'] %>", - "webpack-dev-server": "<%= nodeDependencies['webpack-dev-server'] %>", - "webpack-merge": "<%= nodeDependencies['webpack-merge'] %>", - "workbox-webpack-plugin": "<%= nodeDependencies['workbox-webpack-plugin'] %>", <%_ } _%> "@eslint/js": null, "@pinia/testing": "<%= nodeDependencies['@pinia/testing'] %>", @@ -125,13 +108,8 @@ "default_environment": "prod" }, "scripts": { -<%_ if (microfrontend) { %> - "prettier:check": "prettier --check \"{,src/**/,webpack/,.blueprint/**/}*.{<%= prettierExtensions %>}\"", - "prettier:format": "prettier --write \"{,src/**/,webpack/,.blueprint/**/}*.{<%= prettierExtensions %>}\"", -<%_ } else { %> "prettier:check": "prettier --check \"{,src/**/,.blueprint/**/}*.{<%= prettierExtensions %>}\"", "prettier:format": "prettier --write \"{,src/**/,.blueprint/**/}*.{<%= prettierExtensions %>}\"", -<%_ } %> "lint": "eslint .", "lint:fix": "eslint . --fix", "cleanup": "rimraf <%= this.relativeDir(clientRootDir, temporaryDir) %>", @@ -148,25 +126,16 @@ "test:watch": "<%= clientPackageManager %> run vitest", "watch": "concurrently npm:start<% if(!skipServer) { %> npm:backend:start<% } %>", "webapp:build": "<%= clientPackageManager %> run clean-www && <%= clientPackageManager %> run webapp:build:dev --", -<%_ if (microfrontend) { %> - "webapp:build:dev": "<%= clientPackageManager %> run webpack -- --mode development --env stats=minimal", - "webapp:build:prod": "<%= clientPackageManager %> run webpack -- --mode production --env stats=minimal", - "webapp:dev": "<%= clientPackageManager %> run webpack-dev-server -- --mode development --env stats=normal", -<%_ } else { %> +<%_ if (!microfrontend) { %> + "vite-serve": "vite", + "vite-build": "vite build", "webapp:build:dev": "<%= clientPackageManager %> run vite-build", "webapp:build:prod": "<%= clientPackageManager %> run vite-build", "webapp:dev": "<%= clientPackageManager %> run vite-serve", "webapp:serve": "<%= clientPackageManager %> run vite-serve", <%_ } %> "webapp:prod": "<%= clientPackageManager %> run clean-www && <%= clientPackageManager %> run webapp:build:prod --", - "webapp:test": "<%= clientPackageManager %> run test --", -<%_ if (microfrontend) { %> - "webpack-dev-server": "webpack serve --config webpack/webpack.common.js", - "webpack": "webpack --config webpack/webpack.common.js" -<%_ } else { %> - "vite-serve": "vite", - "vite-build": "vite build" -<%_ } %> + "webapp:test": "<%= clientPackageManager %> run test --" }, "browserslist": [ "> 1%", 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..30a725303692 --- /dev/null +++ b/generators/vue/templates/rsbuild.config.ts.jhi.vue.ejs @@ -0,0 +1,83 @@ +<%# + 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 './rsbuild.module-federation.config.js'; +<%_ } _%> +<&_ } -&> + +<&_ 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/rsbuild.module-federation.config.ts.ejs b/generators/vue/templates/rsbuild.module-federation.config.ts.ejs new file mode 100644 index 000000000000..33c6c53a6c6e --- /dev/null +++ b/generators/vue/templates/rsbuild.module-federation.config.ts.ejs @@ -0,0 +1,52 @@ +<%# + 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 packageJson from './package.json' assert { type: 'json' }; +import type { Rspack } from '@rsbuild/core'; + +// 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 }]), + ); + +export const mfConfig = { + name: '<%= lowercaseBaseName %>', +<%_ if (applicationTypeMicroservice) { _%> + exposes: { + './entities-router': './app/router/entities', + './entities-menu': './app/entities/entities-menu', + }, +<%_ } _%> + filename: 'remoteEntry.js', + shared: { + ...shareDependencies(), + ...shareMappings( + '@/shared/security/authority', + '@/shared/alert/alert.service', + '@/locale/translation.service', + ), + }, +} satisfies Rspack.ModuleFederationPluginOptions; diff --git a/generators/vue/templates/src/main/webapp/app/main.ts.ejs b/generators/vue/templates/src/main/webapp/app/main.ts.ejs index 86c4dffc215b..26abcebc1252 100644 --- a/generators/vue/templates/src/main/webapp/app/main.ts.ejs +++ b/generators/vue/templates/src/main/webapp/app/main.ts.ejs @@ -186,7 +186,7 @@ const app = createApp({ provide('trackerService', useTrackerService({ authenticated })); <%_ } _%> -<%_ if (applicationTypeMicroservice && microfrontend) { _%> +<%_ if (enableTranslation && applicationTypeMicroservice && microfrontend) { _%> provide('microfrontendI18n', false); <%_ } _%> }, diff --git a/generators/vue/templates/vite.config.mts.ejs b/generators/vue/templates/vite.config.mts.ejs index 572fd11ac6a2..733884e36a47 100644 --- a/generators/vue/templates/vite.config.mts.ejs +++ b/generators/vue/templates/vite.config.mts.ejs @@ -21,20 +21,10 @@ import { existsSync } from 'node:fs'; import { normalizePath } from 'vite' import { -<%_ if (microfrontend) { _%> - mergeConfig, -<%_ } _%> defineConfig, } from 'vite'; import vue from '@vitejs/plugin-vue'; import { viteStaticCopy } from 'vite-plugin-static-copy'; -<%_ if (microfrontend) { _%> -import federation from "@originjs/vite-plugin-federation"; - - <%_ if (applicationTypeGateway) { _%> -const sharedAppVersion = '0.0.0'; - <%_ } _%> -<%_ } _%> const getFileFromRepo = (file: string) => existsSync(fileURLToPath(new URL(`../node_modules/${file}`, import.meta.url))) @@ -122,63 +112,6 @@ let config = defineConfig({ }, }); -<%_ if (microfrontend) { _%> -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/generators/vue/templates/webpack/config.js.ejs b/generators/vue/templates/webpack/config.js.ejs deleted file mode 100644 index fe10699613d9..000000000000 --- a/generators/vue/templates/webpack/config.js.ejs +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -<%_ if (buildToolUnknown) { _%> -const packageJson = require('./../package.json'); -<%_ } _%> - -module.exports = { - serverApiUrl: '', -<%_ if (buildToolUnknown) { _%> - version: packageJson.version, -<%_ } else { _%> - // APP_VERSION is passed as an environment variable from the Gradle / Maven build tasks. - version: process.env.APP_VERSION || 'DEV', -<%_ } _%> - - dev: { - hotReload: <%= !microfrontend %>, - - // https://webpack.js.org/configuration/devtool/#development - devtool: 'cheap-module-source-map', - - // If you have problems debugging vue-files in devtools, - // set this to false - it *may* help - // https://vue-loader.vuejs.org/en/options.html#cachebusting - cacheBusting: true, - - cssSourceMap: true, - }, - - build: { - productionSourceMap: true, - // https://webpack.js.org/configuration/devtool/#production - devtool: 'source-map', - - // Gzip off by default as many popular static hosts such as - // Surge or Netlify already gzip all static assets for you. - // Before setting to `true`, make sure to: - // npm install --save-dev compression-webpack-plugin - productionGzip: false, - productionGzipExtensions: ['js', 'css'], - - // Run the build command with an extra argument to - // View the bundle analyzer report after build finishes: - // `npm run build --report` - // Set to `true` or `false` to always turn it on or off - bundleAnalyzerReport: process.env.npm_config_report, - }, -}; diff --git a/generators/vue/templates/webpack/vue.utils.js.ejs b/generators/vue/templates/webpack/vue.utils.js.ejs deleted file mode 100644 index 6552b8c4165e..000000000000 --- a/generators/vue/templates/webpack/vue.utils.js.ejs +++ /dev/null @@ -1,92 +0,0 @@ -'use strict'; -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const sass = require('sass'); - -const config = require('./config'); - -const sourceMapEnabled = production => (production ? config.build.productionSourceMap : config.dev.cssSourceMap); - -const cssLoaders = options => { - options = options || {}; - - const cssLoader = { - loader: 'css-loader', - options: { - url: false, - sourceMap: options.sourceMap, - esModule: false, - }, - }; - - const postcssLoader = { - loader: 'postcss-loader', - options: { - sourceMap: options.sourceMap, - }, - }; - - // generate loader string to be used with extract text plugin - function generateLoaders(loader, loaderOptions) { - const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]; - - if (loader) { - loaders.push({ - loader: `${loader}-loader`, - options: { ...loaderOptions, sourceMap: options.sourceMap }, - }); - } - - // Extract CSS when that option is specified - // (which is the case during production build) - return [options.extract ? MiniCssExtractPlugin.loader : 'vue-style-loader'].concat(loaders); - } - - // https://vue-loader.vuejs.org/en/configurations/extract-css.html - return { - css: generateLoaders(), - postcss: generateLoaders(), - less: generateLoaders('less'), - sass: generateLoaders('sass', { indentedSyntax: true, implementation: sass }), - scss: generateLoaders('sass', { implementation: sass }), - stylus: generateLoaders('stylus'), - styl: generateLoaders('stylus'), - }; -}; - -// Generate loaders for standalone style files (outside of .vue) -const styleLoaders = options => { - const output = []; - const loaders = cssLoaders(options); - - for (const extension in loaders) { - const loader = loaders[extension]; - output.push({ - test: new RegExp(`\\.${extension}$`), - use: loader, - }); - } - - return output; -}; - -const vueLoaderConfig = production => ({ - loaders: cssLoaders({ - sourceMap: sourceMapEnabled(production), - extract: production, - }), - cssSourceMap: sourceMapEnabled(production), - cacheBusting: config.dev.cacheBusting, - transformToRequire: { - video: ['src', 'poster'], - source: 'src', - img: 'src', - image: 'xlink:href', - }, - hotReload: config.dev.hotReload, -}); - -module.exports = { - cssLoaders, - styleLoaders, - vueLoaderConfig, -}; diff --git a/generators/vue/templates/webpack/webpack.common.js.ejs b/generators/vue/templates/webpack/webpack.common.js.ejs deleted file mode 100644 index fcaa45cf8d4b..000000000000 --- a/generators/vue/templates/webpack/webpack.common.js.ejs +++ /dev/null @@ -1,185 +0,0 @@ -'use strict'; -const path = require('path'); -const { merge } = require('webpack-merge'); -const { VueLoaderPlugin } = require('vue-loader'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -<%_ if (enableTranslation) { _%> -const { hashElement } = require('folder-hash'); -const MergeJsonWebpackPlugin = require('merge-jsons-webpack-plugin'); -<%_ } _%> - -const { DefinePlugin } = require('webpack'); -const { vueLoaderConfig } = require('./vue.utils'); -const config = require('./config'); - -function resolve(dir = '') { - return path.join(__dirname, '..', dir); -} - -module.exports = async (env, options) => { - const development = options.mode === 'development'; -<%_ if (enableTranslation) { _%> - const languagesHash = await hashElement(resolve('<%= this.relativeDir(clientRootDir, clientSrcDir) %>i18n'), { - algo: 'md5', - encoding: 'hex', - files: { include: ['*.json'] }, - }); - -<%_ } _%> - return merge( - { - mode: options.mode, - context: resolve(), -<%_ if (applicationTypeGateway && microfrontend) { _%> - experiments: { - topLevelAwait: true, - }, -<%_ } _%> - entry: { - app: './<%= this.relativeDir(clientRootDir, clientSrcDir) %>app/<%= microfrontend ? 'index' : 'main' %>.ts', - }, - output: { - path: resolve('<%= this.relativeDir(clientRootDir, clientDistDir) %>'), - }, - resolve: { - extensions: ['.ts', '.js', '.vue', '.json'], - alias: { - vue$: '@vue/compat/dist/vue.esm-bundler.js', - '@': resolve('<%= this.relativeDir(clientRootDir, clientSrcDir) %>app'), - }, - }, - devServer: { -<%_ if (microfrontend) { _%> - hot: config.dev.hotReload, -<%_ } _%> - static: { - directory: './<%= this.relativeDir(clientRootDir, clientDistDir) %>', - }, - 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, - }, - cache: { - // 1. Set cache type to filesystem - type: 'filesystem', - cacheDirectory: resolve('<%= this.relativeDir(clientRootDir, temporaryDir) %>webpack'), - buildDependencies: { - // 2. Add your config as buildDependency to get cache invalidation on config change - config: [ - __filename, - path.resolve(__dirname, 'config.js'), - path.resolve(__dirname, 'vue.utils.js'), - path.resolve(__dirname, `webpack.${development ? 'dev' : 'prod'}.js`), - path.resolve(__dirname, '../.postcssrc.js'), - path.resolve(__dirname, '../tsconfig.json'), - ], - }, - }, - module: { - rules: [ - { - test: /\.vue$/, - loader: 'vue-loader', - options: { - ...vueLoaderConfig(!development), - }, - }, - { - test: /\.ts$/, - use: [ - { - loader: 'ts-loader', - options: { - appendTsSuffixTo: ['\\.vue$'], - happyPackMode: true, - transpileOnly: true, - configFile: 'tsconfig.app.json', - }, - }, - ], - include: [resolve('src'), resolve('test')], - }, - { - test: /\.(png|jpe?g|gif|svg|mp4|webm|ogg|mp3|wav|flac|aac|woff2?|eot|ttf|otf)/, - type: 'asset/resource', - }, - ], - }, - plugins: [ - new DefinePlugin({ -<%_ if (enableTranslation) { _%> - I18N_HASH: JSON.stringify(languagesHash.hash), -<%_ } _%> - APP_VERSION: JSON.stringify(config.version), - SERVER_API_URL: JSON.stringify(config.serverApiUrl), - __VUE_PROD_DEVTOOLS__: false, - }), - new HtmlWebpackPlugin({ - base: '/', - template: './<%= this.relativeDir(clientRootDir, clientSrcDir) %>index.html', - }), - new VueLoaderPlugin(), - new CopyWebpackPlugin({ - patterns: [ - { - // https://github.com/swagger-api/swagger-ui/blob/v4.6.1/swagger-ui-dist-package/README.md - context: require('swagger-ui-dist').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' }, - ], - })<% if (enableTranslation) { %>, - new MergeJsonWebpackPlugin({ - output: { - groupBy: [ - // jhipster-needle-i18n-language-webpack - JHipster will add/remove languages in this array - ], - }, - }),<% } %> - ], - }, - await require(`./webpack.${development ? 'dev' : 'prod'}`)(env, options) - // jhipster-needle-add-webpack-config - JHipster will add custom config - ); -}; diff --git a/generators/vue/templates/webpack/webpack.dev.js.ejs b/generators/vue/templates/webpack/webpack.dev.js.ejs deleted file mode 100644 index 56341dd4d9a9..000000000000 --- a/generators/vue/templates/webpack/webpack.dev.js.ejs +++ /dev/null @@ -1,70 +0,0 @@ -<%# - 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. --%> -'use strict'; -const BrowserSyncPlugin = require('browser-sync-webpack-plugin'); - -const { styleLoaders } = require('./vue.utils'); -const config = require('./config'); - -module.exports = (env, options) => { - const devConfig = { - module: { - rules: styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }), - }, - // cheap-module-eval-source-map is faster for development - devtool: config.dev.devtool, - output: { - filename: 'app/[name].[contenthash].bundle.js', - chunkFilename: 'app/[id].[chunkhash].chunk.js', - }, - optimization: { - moduleIds: 'named', - }, - plugins: [], - }; - if (!options.env.WEBPACK_SERVE) return devConfig; - devConfig.plugins.push( - new BrowserSyncPlugin( - { - host: 'localhost', - port: 9000, - proxy: { - target: `http://localhost:${options.watch ? '<%= applicationTypeMicroservice ? gatewayServerPort : serverPort %>' : '<%= devServerPort %>'}`, - ws: true, - }, - socket: { - clients: { - heartbeatTimeout: 60000, - }, - }, - /* - ,ghostMode: { // uncomment this part to disable BrowserSync ghostMode; https://github.com/jhipster/generator-jhipster/issues/11116 - clicks: false, - location: false, - forms: false, - scroll: false - } */ - }, - { - reload: true, - }, - ), - ); - return devConfig; -}; diff --git a/generators/vue/templates/webpack/webpack.microfrontend.js.jhi.vue.ejs b/generators/vue/templates/webpack/webpack.microfrontend.js.jhi.vue.ejs deleted file mode 100644 index e30946d381c2..000000000000 --- a/generators/vue/templates/webpack/webpack.microfrontend.js.jhi.vue.ejs +++ /dev/null @@ -1,45 +0,0 @@ -<%# - 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.configSection) { -&> -<%_ if (applicationTypeGateway && clientFrameworkVue) { _%> - resolve: { - fallback: { - // Workaround https://github.com/module-federation/universe/issues/1575 - path: false, - }, - }, -<%_ } _%> -<&_ } -&> - -<&_ if (fragment.moduleFederationSection) { -&> -<%_ if (applicationTypeMicroservice) { _%> - exposes: { - './entities-router': './<%= this.relativeDir(clientRootDir, clientSrcDir) %>app/router/entities', - './entities-menu': './<%= this.relativeDir(clientRootDir, clientSrcDir) %>app/entities/entities-menu', - }, -<%_ } _%> - shared: { - ...shareDependencies(), - ...shareMappings( - '@/shared/security/authority', - '@/shared/alert/alert.service', - '@/locale/translation.service', - ), - }, -<&_ } -&> diff --git a/generators/vue/templates/webpack/webpack.prod.js.ejs b/generators/vue/templates/webpack/webpack.prod.js.ejs deleted file mode 100644 index 18dba308038f..000000000000 --- a/generators/vue/templates/webpack/webpack.prod.js.ejs +++ /dev/null @@ -1,126 +0,0 @@ -<%# - 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. --%> -'use strict'; -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); -const WorkboxPlugin = require('workbox-webpack-plugin'); -const TerserPlugin = require('terser-webpack-plugin'); - -const { styleLoaders } = require('./vue.utils'); -const config = require('./config'); - -const webpackConfig = { - module: { - rules: styleLoaders({ - sourceMap: config.build.productionSourceMap, - extract: true, - usePostCSS: true, - }), - }, - devtool: config.build.productionSourceMap ? config.build.devtool : false, - output: { - filename: 'app/[name].[contenthash].bundle.js', - chunkFilename: 'app/[id].[chunkhash].chunk.js', - }, - optimization: { - moduleIds: 'deterministic', - minimizer: [ - '...', - new CssMinimizerPlugin({ - parallel: true, - }), - ], - splitChunks: { - cacheGroups: { - commons: { - test: /[\\/]node_modules[\\/]/, - name: 'vendors', - chunks: 'all', - }, - }, - }, - }, - plugins: [ - new TerserPlugin({ - terserOptions: { - compress: { - arrows: false, - collapse_vars: false, - comparisons: false, - computed_props: false, - hoist_funs: false, - hoist_props: false, - hoist_vars: false, - inline: false, - loops: false, - negate_iife: false, - properties: false, - reduce_funcs: false, - reduce_vars: false, - switches: false, - toplevel: false, - typeofs: false, - booleans: true, - if_return: true, - sequences: true, - unused: true, - conditionals: true, - dead_code: true, - evaluate: true, - }, - mangle: { - safari10: true, - }, - }, - parallel: true, - extractComments: false, - }), - // extract css into its own file - new MiniCssExtractPlugin({ - filename: 'content/[name].[contenthash].css', - chunkFilename: 'content/[id].css', - }), - new WorkboxPlugin.GenerateSW({ - clientsClaim: true, - skipWaiting: true, - exclude: [/swagger-ui/], - }), - ], -}; - -if (config.build.productionGzip) { - const CompressionWebpackPlugin = require('compression-webpack-plugin'); - - webpackConfig.plugins.push( - new CompressionWebpackPlugin({ - asset: '[path].gz[query]', - algorithm: 'gzip', - test: new RegExp(`\\.(${config.build.productionGzipExtensions.join('|')})$`), - threshold: 10240, - minRatio: 0.8, - }), - ); -} - -if (config.build.bundleAnalyzerReport) { - const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; - webpackConfig.plugins.push(new BundleAnalyzerPlugin()); -} - -module.exports = async () => webpackConfig; diff --git a/lib/jhipster/default-application-options.ts b/lib/jhipster/default-application-options.ts index 02350746f4db..567e7c122525 100644 --- a/lib/jhipster/default-application-options.ts +++ b/lib/jhipster/default-application-options.ts @@ -76,7 +76,9 @@ const { GRADLE_ENTERPRISE_HOST, } = OptionNames; -const commonDefaultOptions: Partial = { +type ApplicationDefaults = Partial; + +const commonDefaultOptions: ApplicationDefaults = { [AUTHENTICATION_TYPE]: JWT, [BUILD_TOOL]: MAVEN, [DTO_SUFFIX]: OptionValues[DTO_SUFFIX], @@ -89,8 +91,8 @@ const commonDefaultOptions: Partial = { [WEBSOCKET]: (OptionValues[WEBSOCKET] as Record).no, }; -export function getConfigWithDefaults(customOptions: string | Record = {}) { - const applicationType = typeof customOptions === 'string' ? customOptions : customOptions.applicationType; +export function getConfigWithDefaults(customOptions: ApplicationDefaults = {}) { + const applicationType = customOptions.applicationType; if (applicationType === GATEWAY) { return getConfigForGatewayApplication(customOptions); } @@ -100,7 +102,7 @@ export function getConfigWithDefaults(customOptions: string | Record prop + '-bar', prop2: 'won\'t override' }, * ); */ - applicationDefaults: (...defaults: Record[]) => void; + applicationDefaults: (...defaults: (Partial & Record)[]) => void; }; type TaskParamWithApplication> = TaskParamWithControl & { 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 }