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/javascript/generators/rspack/__snapshots__/generator.spec.ts.snap b/generators/javascript/generators/rspack/__snapshots__/generator.spec.ts.snap new file mode 100644 index 000000000000..f6127b8cfe4e --- /dev/null +++ b/generators/javascript/generators/rspack/__snapshots__/generator.spec.ts.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`generator - javascript:rspack with defaults options should call source snapshot 1`] = `{}`; + +exports[`generator - javascript:rspack with defaults options should match files snapshot 1`] = ` +{ + ".yo-rc.json": { + "stateCleared": "modified", + }, + "package.json": { + "stateCleared": "modified", + }, + "rspack.config.mjs.jhi": { + "stateCleared": "modified", + }, +} +`; diff --git a/generators/javascript/generators/rspack/command.ts b/generators/javascript/generators/rspack/command.ts new file mode 100644 index 000000000000..de9446fa3118 --- /dev/null +++ b/generators/javascript/generators/rspack/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/rspack/generator.spec.ts b/generators/javascript/generators/rspack/generator.spec.ts new file mode 100644 index 000000000000..1552e18ce2ae --- /dev/null +++ b/generators/javascript/generators/rspack/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/rspack/generator.ts b/generators/javascript/generators/rspack/generator.ts new file mode 100644 index 000000000000..6ce83916f3e0 --- /dev/null +++ b/generators/javascript/generators/rspack/generator.ts @@ -0,0 +1,130 @@ +/** + * 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: ['rspack.config.mjs.jhi'] }], + context: application, + }); + }, + }); + } + + get [BaseGenerator.WRITING]() { + return this.delegateTasksToBlueprint(() => this.writing); + } + + get postWriting() { + return this.asPostWritingTaskGroup({ + addScripts({ application }) { + this.packageJson.merge({ + devDependencies: { + '@rspack/core': 'latest', + '@rspack/cli': 'latest', + }, + scripts: { + start: 'rspack serve', + build: 'rspack 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/rspack/index.ts b/generators/javascript/generators/rspack/index.ts new file mode 100644 index 000000000000..1cfadd692bb6 --- /dev/null +++ b/generators/javascript/generators/rspack/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/rspack/templates/rspack.config.mjs.jhi.ejs b/generators/javascript/generators/rspack/templates/rspack.config.mjs.jhi.ejs new file mode 100644 index 000000000000..704604e5e0e1 --- /dev/null +++ b/generators/javascript/generators/rspack/templates/rspack.config.mjs.jhi.ejs @@ -0,0 +1,48 @@ +<%# + 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({ + context: __dirname, + output: { + path: './<%= this.relativeDir(clientRootDir, clientDistDir) %>', + uniqueName: '<%= lowercaseBaseName %>', + }, + html: { + template: './<%= this.relativeDir(clientRootDir, clientSrcDir) %>index.html', + }, + plugins: [ +<&- fragments.pluginsSection() -&> + ], +<&- fragments.configSection() -&> +}); diff --git a/generators/vue/__snapshots__/generator.spec.ts.snap b/generators/vue/__snapshots__/generator.spec.ts.snap index 81c96d44f772..c64550a1a29b 100644 --- a/generators/vue/__snapshots__/generator.spec.ts.snap +++ b/generators/vue/__snapshots__/generator.spec.ts.snap @@ -1002,6 +1002,9 @@ exports[`generator - vue microservice-jwt-skipUserManagement(false)-withAdminUi( "clientRoot/package.json": { "stateCleared": "modified", }, + "clientRoot/rspack.module-federation.config.mjs": { + "stateCleared": "modified", + }, "clientRoot/src/main/webapp/404.html": { "stateCleared": "modified", }, @@ -1431,22 +1434,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": { + "clientRoot/webpack/webpack.microfrontend.js": { "stateCleared": "modified", }, - "clientRoot/webpack/webpack.microfrontend.js": { + "package.json": { "stateCleared": "modified", }, - "clientRoot/webpack/webpack.prod.js": { + "rspack.config.mjs": { "stateCleared": "modified", }, } @@ -1478,6 +1472,12 @@ exports[`generator - vue microservice-oauth2-withAdminUi(true)-skipJhipsterDepen "package.json": { "stateCleared": "modified", }, + "rspack.config.mjs": { + "stateCleared": "modified", + }, + "rspack.module-federation.config.mjs": { + "stateCleared": "modified", + }, "src/main/webapp/404.html": { "stateCleared": "modified", }, @@ -1907,24 +1907,9 @@ 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..797aa7ed2502 100644 --- a/generators/vue/files-vue.ts +++ b/generators/vue/files-vue.ts @@ -39,14 +39,7 @@ 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: ['rspack.module-federation.config.mjs', 'rspack.config.mjs.jhi.vue'], }), { condition: generator => generator.microfrontend, diff --git a/generators/vue/generator.ts b/generators/vue/generator.ts index e2d65daa2091..31ad425dbb67 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:rspack'); + } + }, + }); + } + + get [BaseApplicationGenerator.COMPOSING]() { + return this.delegateTasksToBlueprint(() => this.composing); + } + get loading() { return this.asLoadingTaskGroup({ loadPackageJson({ application }) { @@ -87,19 +104,6 @@ export default class VueGenerator extends BaseApplicationGenerator { }, prepareForTemplates({ application, source }) { 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}`, - }), - ); - }; }, }); } 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/rspack.config.mjs.jhi.vue.ejs b/generators/vue/templates/rspack.config.mjs.jhi.vue.ejs new file mode 100644 index 000000000000..b6aa3ab3a97b --- /dev/null +++ b/generators/vue/templates/rspack.config.mjs.jhi.vue.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. +-%> +<&_ if (fragment.importsSection) { -&> +import { basename, dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { pluginVue } from '@rsbuild/plugin-vue'; +<%_ if (microfrontend) { _%> +import { mfConfig } from './rsbuild.module-federation.config.js'; +<%_ } _%> +<&_ } -&> + +<&_ if (fragment.pluginsSection) { -&> + pluginVue(), +<&_ } -&> + +<&_ if (fragment.configSection) { -&> + port: <%= devServerPort %>, + entry: { + app: './<%= this.relativeDir(clientRootDir, clientSrcDir) %>app/<%= microfrontend ? 'index' : 'main' %>.ts', + }, + experiments: { + css: true, + }, + resolve: { + extensions: ['.ts', '.js', '.vue', '.json'], + alias: { + vue$: '@vue/compat/dist/vue.esm-bundler.js', + '@': path.resolve(__dirname, './<%= this.relativeDir(clientRootDir, clientSrcDir) %>app'), + }, + }, + devServer: { + 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, + }, +<%_ if (microfrontend) { _%> + moduleFederation: { + options: mfConfig, + }, +<%_ } _%> +<&_ } -&> diff --git a/generators/vue/templates/rspack.module-federation.config.mjs.ejs b/generators/vue/templates/rspack.module-federation.config.mjs.ejs new file mode 100644 index 000000000000..4627ccee8f82 --- /dev/null +++ b/generators/vue/templates/rspack.module-federation.config.mjs.ejs @@ -0,0 +1,51 @@ +<%# + 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 { dependencies } from './package.json'; + +// Microfrontend api, should match across gateway and microservices. +const apiVersion = '0.0.1'; + +const sharedDefaults = { singleton: true, strictVersion: true, requiredVersion: apiVersion }; +const shareMappings = (...mappings) => Object.fromEntries(mappings.map(map => [map, { ...sharedDefaults, version: apiVersion }])); + +const shareDependencies = ({ skipList = [] } = {}) => + Object.fromEntries( + Object.entries(dependencies) + .filter(([dependency]) => !skipList.includes(dependency)) + .map(([dependency, version]) => [dependency, { ...sharedDefaults, version, requiredVersion: version }]), + ); + +/** @type {import('@rsbuild/core').Rspack.ModuleFederationPluginOptions} */ +export const mfConfig = { +<%_ if (applicationTypeMicroservice) { _%> + exposes: { + './entities-router': './<%= this.relativeDir(clientRootDir, clientSrcDir) %>app/router/entities', + './entities-menu': './<%= this.relativeDir(clientRootDir, clientSrcDir) %>app/entities/entities-menu', + }, +<%_ } _%> + filename: 'remoteEntry.js', + shared: { + ...shareDependencies(), + ...shareMappings( + '@/shared/security/authority', + '@/shared/alert/alert.service', + '@/locale/translation.service', + ), + }, +}; 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;