diff --git a/generators/angular/__snapshots__/generator.spec.ts.snap b/generators/angular/__snapshots__/generator.spec.ts.snap index 047f73cb510e..2ad9e3baf5ef 100644 --- a/generators/angular/__snapshots__/generator.spec.ts.snap +++ b/generators/angular/__snapshots__/generator.spec.ts.snap @@ -700,6 +700,23 @@ exports[`generator - angular gateway-jwt-skipUserManagement(true)-withAdminUi(fa "source": "TaskParameter[source]", }, ], + "mergeClientPackageJson": [ + { + "devDependencies": { + "@angular-builders/custom-webpack": null, + "browser-sync-webpack-plugin": null, + "copy-webpack-plugin": null, + "eslint-webpack-plugin": null, + "webpack-bundle-analyzer": null, + "webpack-merge": null, + "webpack-notifier": null, + }, + "overrides": { + "browser-sync": "BROWSER_SYNC_VERSION", + "webpack": "WEBPACK_VERSION", + }, + }, + ], } `; @@ -1626,6 +1643,25 @@ exports[`generator - angular gateway-oauth2-withAdminUi(true)-skipJhipsterDepend "source": "TaskParameter[source]", }, ], + "mergeClientPackageJson": [ + { + "devDependencies": { + "@angular-builders/custom-webpack": null, + "browser-sync-webpack-plugin": null, + "copy-webpack-plugin": null, + "eslint-webpack-plugin": null, + "folder-hash": null, + "merge-jsons-webpack-plugin": null, + "webpack-bundle-analyzer": null, + "webpack-merge": null, + "webpack-notifier": null, + }, + "overrides": { + "browser-sync": "BROWSER_SYNC_VERSION", + "webpack": "WEBPACK_VERSION", + }, + }, + ], } `; @@ -2313,6 +2349,23 @@ exports[`generator - angular microservice-jwt-skipUserManagement(false)-withAdmi "config": "targetOptions.target === 'serve' ? {} : require('./webpack.microfrontend')(config, options, targetOptions)", }, ], + "mergeClientPackageJson": [ + { + "devDependencies": { + "@angular-builders/custom-webpack": null, + "browser-sync-webpack-plugin": null, + "copy-webpack-plugin": null, + "eslint-webpack-plugin": null, + "webpack-bundle-analyzer": null, + "webpack-merge": null, + "webpack-notifier": null, + }, + "overrides": { + "browser-sync": "BROWSER_SYNC_VERSION", + "webpack": "WEBPACK_VERSION", + }, + }, + ], } `; @@ -3006,6 +3059,25 @@ exports[`generator - angular microservice-oauth2-withAdminUi(true)-skipJhipsterD "config": "targetOptions.target === 'serve' ? {} : require('./webpack.microfrontend')(config, options, targetOptions)", }, ], + "mergeClientPackageJson": [ + { + "devDependencies": { + "@angular-builders/custom-webpack": null, + "browser-sync-webpack-plugin": null, + "copy-webpack-plugin": null, + "eslint-webpack-plugin": null, + "folder-hash": null, + "merge-jsons-webpack-plugin": null, + "webpack-bundle-analyzer": null, + "webpack-merge": null, + "webpack-notifier": null, + }, + "overrides": { + "browser-sync": "BROWSER_SYNC_VERSION", + "webpack": "WEBPACK_VERSION", + }, + }, + ], } `; @@ -4100,6 +4172,25 @@ exports[`generator - angular monolith-jwt-skipUserManagement(false)-withAdminUi( "source": "TaskParameter[source]", }, ], + "mergeClientPackageJson": [ + { + "devDependencies": { + "@angular-builders/custom-webpack": null, + "browser-sync-webpack-plugin": null, + "copy-webpack-plugin": null, + "eslint-webpack-plugin": null, + "folder-hash": null, + "merge-jsons-webpack-plugin": null, + "webpack-bundle-analyzer": null, + "webpack-merge": null, + "webpack-notifier": null, + }, + "overrides": { + "browser-sync": "BROWSER_SYNC_VERSION", + "webpack": "WEBPACK_VERSION", + }, + }, + ], } `; @@ -4773,6 +4864,23 @@ exports[`generator - angular monolith-oauth2-withAdminUi(false)-skipJhipsterDepe "source": "TaskParameter[source]", }, ], + "mergeClientPackageJson": [ + { + "devDependencies": { + "@angular-builders/custom-webpack": null, + "browser-sync-webpack-plugin": null, + "copy-webpack-plugin": null, + "eslint-webpack-plugin": null, + "webpack-bundle-analyzer": null, + "webpack-merge": null, + "webpack-notifier": null, + }, + "overrides": { + "browser-sync": "BROWSER_SYNC_VERSION", + "webpack": "WEBPACK_VERSION", + }, + }, + ], } `; @@ -5455,6 +5563,23 @@ exports[`generator - angular monolith-session-skipUserManagement(true)-withAdmin "source": "TaskParameter[source]", }, ], + "mergeClientPackageJson": [ + { + "devDependencies": { + "@angular-builders/custom-webpack": null, + "browser-sync-webpack-plugin": null, + "copy-webpack-plugin": null, + "eslint-webpack-plugin": null, + "webpack-bundle-analyzer": null, + "webpack-merge": null, + "webpack-notifier": null, + }, + "overrides": { + "browser-sync": "BROWSER_SYNC_VERSION", + "webpack": "WEBPACK_VERSION", + }, + }, + ], } `; diff --git a/generators/angular/files-angular.ts b/generators/angular/files-angular.ts index be7992e6c055..f5e444347642 100644 --- a/generators/angular/files-angular.ts +++ b/generators/angular/files-angular.ts @@ -28,7 +28,6 @@ export const files = { common: [ clientRootTemplatesBlock({ templates: [ - 'angular.json', { sourceFile: 'eslint.config.js.jhi.angular', destinationFile: ctx => `${ctx.eslintConfigFile}.jhi.angular` }, 'ngsw-config.json', 'package.json', @@ -36,6 +35,14 @@ export const files = { 'tsconfig.app.json', 'tsconfig.spec.json', 'jest.conf.js', + ], + }), + ], + webpack: [ + clientRootTemplatesBlock({ + condition: ctx => ctx.clientBundlerWebpack, + templates: [ + 'angular.json', 'webpack/environment.js', 'webpack/proxy.conf.js', 'webpack/webpack.custom.js', @@ -43,6 +50,19 @@ export const files = { ], }), ], + esbuild: [ + clientRootTemplatesBlock({ + condition: ctx => ctx.clientBundlerExperimentalEsbuild, + templates: [ + { sourceFile: 'angular.json.esbuild', destinationFile: 'angular.json' }, + 'proxy.conf.json', + 'build-plugins/esbuild-copy.mjs', + 'build-plugins/esbuild-define.mjs', + 'build-plugins/swagger-ui.mjs', + 'build-plugins/vite-middleware.mjs', + ], + }), + ], sass: [ { ...clientSrcTemplatesBlock(), @@ -67,7 +87,7 @@ export const files = { ], microfrontend: [ clientRootTemplatesBlock({ - condition: generator => generator.microfrontend, + condition: generator => generator.clientBundlerWebpack && generator.microfrontend, templates: ['webpack/webpack.microfrontend.js'], }), { diff --git a/generators/angular/generator.ts b/generators/angular/generator.ts index ad54b8d083ec..786cf0a881e9 100644 --- a/generators/angular/generator.ts +++ b/generators/angular/generator.ts @@ -266,6 +266,32 @@ export default class AngularGenerator extends BaseApplicationGenerator { get postWriting() { return this.asPostWritingTaskGroup({ + clientBundler({ application, source }) { + if (application.clientBundlerExperimentalEsbuild) { + source.mergeClientPackageJson!({ + devDependencies: { + '@angular-builders/custom-esbuild': null, + }, + }); + } else { + source.mergeClientPackageJson!({ + devDependencies: { + '@angular-builders/custom-webpack': null, + 'browser-sync-webpack-plugin': null, + 'copy-webpack-plugin': null, + 'eslint-webpack-plugin': null, + 'webpack-bundle-analyzer': null, + 'webpack-merge': null, + 'webpack-notifier': null, + ...(application.enableTranslation ? { 'folder-hash': null, 'merge-jsons-webpack-plugin': null } : {}), + }, + overrides: { + 'browser-sync': application.nodeDependencies['browser-sync'], + webpack: application.nodeDependencies.webpack, + }, + }); + } + }, addWebsocketDependencies({ application, source }) { const { authenticationTypeSession, communicationSpringWebsocket, nodeDependencies } = application; const dependencies = {}; diff --git a/generators/angular/resources/package.json b/generators/angular/resources/package.json index 2e05800ae2c1..556a67ae51d5 100644 --- a/generators/angular/resources/package.json +++ b/generators/angular/resources/package.json @@ -21,6 +21,7 @@ "devDependencies": { "@angular-architects/module-federation": "18.0.6", "@angular-architects/module-federation-runtime": "18.0.6", + "@angular-builders/custom-esbuild": "18.0.0", "@angular-builders/custom-webpack": "18.0.0", "@angular-builders/jest": "18.0.0", "@angular/cli": "18.2.12", diff --git a/generators/angular/templates/angular.json.esbuild.ejs b/generators/angular/templates/angular.json.esbuild.ejs new file mode 100644 index 000000000000..0a9833de2008 --- /dev/null +++ b/generators/angular/templates/angular.json.esbuild.ejs @@ -0,0 +1,142 @@ +<%# + 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. +-%> +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "<%= dasherizedBaseName %>": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + }, + "@schematics/angular:application": { + "strict": true + } + }, + "root": "", + "sourceRoot": "<%= this.relativeDir(clientRootDir, clientSrcDir).replace(/\/$/, "") %>", + "prefix": "<%= jhiPrefixDashed %>", + "architect": { + "build": { + "builder": "@angular-builders/custom-esbuild:application", + "options": { + "plugins": ["./build-plugins/esbuild-define.mjs", "./build-plugins/esbuild-copy.mjs"], + "outputPath": { + "base": "<%= this.relativeDir(clientRootDir, clientDistDir) %>", + "browser": "", + "server": "node-server", + "media": "resources" + }, + "index": "<%= this.relativeDir(clientRootDir, clientSrcDir) %>index.html", + "browser": "<%= this.relativeDir(clientRootDir, clientSrcDir) %>main.ts", + "polyfills": [ +<%_ if (communicationSpringWebsocket) { _%> + "./<%= this.relativeDir(clientRootDir, clientSrcDir) %>sockjs-client.polyfill", +<%_ } _%> + "zone.js" + ], + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "<%= this.relativeDir(clientRootDir, clientSrcDir) %>content", + "<%= this.relativeDir(clientRootDir, clientSrcDir) %>favicon.ico", + "<%= this.relativeDir(clientRootDir, clientSrcDir) %>manifest.webapp", + "<%= this.relativeDir(clientRootDir, clientSrcDir) %>robots.txt" + ], + "styles": [ + "<%= this.relativeDir(clientRootDir, clientSrcDir) %>content/scss/vendor.scss", + "<%= this.relativeDir(clientRootDir, clientSrcDir) %>content/scss/global.scss" + ], + "scripts": [] + }, + "configurations": { + "production": { + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "namedChunks": <%= Boolean(microfrontend) %>, + "extractLicenses": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ] + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true, + "fileReplacements": [ + { + "replace": "<%= this.relativeDir(clientRootDir, clientSrcDir) %>environments/environment.ts", + "with": "<%= this.relativeDir(clientRootDir, clientSrcDir) %>environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-builders/custom-esbuild:dev-server", + "options": { + "buildTarget": "<%= dasherizedBaseName %>:build:development", + "middlewares": ["build-plugins/vite-middleware.mjs"], + "open": true, + "proxyConfig": "proxy.conf.json", + "port": <%= devServerPort %> + }, + "configurations": { + "production": { + "buildTarget": "<%= dasherizedBaseName %>:build:production" + }, + "development": { + "buildTarget": "<%= dasherizedBaseName %>:build:development" + } + }, + "defaultConfiguration": "development" + }, + "test": { + "builder": "@angular-builders/jest:run", + "options": { + "configPath": "jest.conf.js", + "tsConfig": "tsconfig.spec.json" + } + } + } + } + }, + "cli": { + "cache": { + "enabled": true, + "path": "./<%= this.relativeDir(clientRootDir, temporaryDir) %>angular/", + "environment": "all" + }, + "packageManager": "<%= clientPackageManager %>" + } +} diff --git a/generators/angular/templates/build-plugins/esbuild-copy.mjs.ejs b/generators/angular/templates/build-plugins/esbuild-copy.mjs.ejs new file mode 100644 index 000000000000..fbf55e6179c9 --- /dev/null +++ b/generators/angular/templates/build-plugins/esbuild-copy.mjs.ejs @@ -0,0 +1,19 @@ +// @ts-check +import { copyFile, mkdir } from 'node:fs/promises'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { getSwaggerUiFileMap } from './swagger-ui.mjs'; + +/** @type {import('esbuild').Plugin} */ +export default { + name: 'copy:files', + async setup() { + const destDir = join(fileURLToPath(new URL('..', import.meta.url)), 'target/classes/static', 'swagger-ui'); + const swaggerFiles = await getSwaggerUiFileMap(); + for (const [dest, src] of Object.entries(swaggerFiles)) { + const destFile = join(destDir, dest); + await mkdir(dirname(destFile), { recursive: true }); + await copyFile(src, destFile); + } + }, +}; diff --git a/generators/angular/templates/build-plugins/esbuild-define.mjs.ejs b/generators/angular/templates/build-plugins/esbuild-define.mjs.ejs new file mode 100644 index 000000000000..1700e0233024 --- /dev/null +++ b/generators/angular/templates/build-plugins/esbuild-define.mjs.ejs @@ -0,0 +1,12 @@ +// @ts-check +/** @type {import('esbuild').Plugin} */ +export default { + name: 'define:vars', + setup(build) { + build.initialOptions.define ??= {}; + Object.assign(build.initialOptions.define, { + SERVER_API_URL: JSON.stringify(''), + __VERSION__: JSON.stringify(process.env.APP_VERSION ?? 'unknown'), + }); + }, +}; diff --git a/generators/angular/templates/build-plugins/swagger-ui.mjs.ejs b/generators/angular/templates/build-plugins/swagger-ui.mjs.ejs new file mode 100644 index 000000000000..b506d1ea974f --- /dev/null +++ b/generators/angular/templates/build-plugins/swagger-ui.mjs.ejs @@ -0,0 +1,19 @@ +// @ts-check +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { globby } from 'globby'; + +const lookup = async (pattern, sourcesDir, destDir) => { + const files = await globby(pattern, { cwd: sourcesDir }); + return Object.fromEntries(files.map(file => [join(destDir, file), join(sourcesDir, file)])); +}; + +export const getSwaggerUiFileMap = async () => { + const destDir = 'swagger-ui'; + const { getAbsoluteFSPath } = await import('swagger-ui-dist'); + return { + [`${destDir}/axios.min.js`]: join(dirname(fileURLToPath(import.meta.resolve('axios/package.json'))), 'dist/axios.min.js'), + ...(await lookup('*.{js,css,png}', getAbsoluteFSPath(), destDir)), + ...(await lookup('**/*.*', join(fileURLToPath(new URL('.', import.meta.url)), '../<%= clientSrcDir %>/swagger-ui/'), destDir)), + }; +}; diff --git a/generators/angular/templates/build-plugins/vite-middleware.mjs.ejs b/generators/angular/templates/build-plugins/vite-middleware.mjs.ejs new file mode 100644 index 000000000000..a5a4dc04a404 --- /dev/null +++ b/generators/angular/templates/build-plugins/vite-middleware.mjs.ejs @@ -0,0 +1,18 @@ +import { readFile } from 'node:fs/promises'; +import { getSwaggerUiFileMap } from './swagger-ui.mjs'; + +const swaggerFiles = await getSwaggerUiFileMap(); +swaggerFiles['swagger-ui'] = swaggerFiles['swagger-ui/index.html']; +const cache = new Map(); + +export default async (req, res, next) => { + const file = swaggerFiles[req.url.slice(1)]; + if (file) { + if (!cache.has(file)) { + cache.set(file, await readFile(file, 'utf8')); + } + res.end(cache.get(file)); + } else { + next(); + } +}; diff --git a/generators/angular/templates/jest.conf.js.ejs b/generators/angular/templates/jest.conf.js.ejs index 4b876b35e520..ad6f7bbaedab 100644 --- a/generators/angular/templates/jest.conf.js.ejs +++ b/generators/angular/templates/jest.conf.js.ejs @@ -21,13 +21,12 @@ const { pathsToModuleNameMapper } = require('ts-jest'); const { compilerOptions: { paths = {}, baseUrl = './' }, } = require('./tsconfig.json'); -const environment = require('./webpack/environment'); module.exports = { transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$|dayjs/esm<% if (applicationTypeGateway && microfrontend) { %>|@angular-architects/module-federation-runtime<% } %>)'], resolver: 'jest-preset-angular/build/resolvers/ng-jest-resolver.js', globals: { - ...environment, + __VERSION__: 'test', }, roots: ['', `/${baseUrl}`], modulePaths: [`/${baseUrl}`], diff --git a/generators/angular/templates/package.json.ejs b/generators/angular/templates/package.json.ejs index b17015881b73..31beef44b3cf 100644 --- a/generators/angular/templates/package.json.ejs +++ b/generators/angular/templates/package.json.ejs @@ -74,7 +74,6 @@ "@angular/cli": "<%= nodeDependencies['@angular/cli'] %>", "@angular/compiler-cli": "<%= nodeDependencies['@angular/common'] %>", "@angular/service-worker": "<%= nodeDependencies['@angular/common'] %>", - "@angular-builders/custom-webpack": "<%= nodeDependencies['@angular-builders/custom-webpack'] %>", "@angular-builders/jest": "<%= nodeDependencies['@angular-builders/jest'] %>", "@angular-devkit/build-angular": "<%= nodeDependencies['@angular/cli'] %>", "@eslint/js": null, @@ -82,13 +81,10 @@ "@types/node": "<%= nodeDependencies['@types/node'] %>", "angular-eslint": "<%= nodeDependencies['angular-eslint'] %>", "browser-sync": "<%= nodeDependencies['browser-sync'] %>", - "browser-sync-webpack-plugin": "<%= nodeDependencies['browser-sync-webpack-plugin'] %>", "buffer": "<%= nodeDependencies['buffer'] %>", - "copy-webpack-plugin": "<%= nodeDependencies['copy-webpack-plugin'] %>", "eslint": "<%= nodeDependencies['eslint'] %>", "eslint-config-prettier": "<%= nodeDependencies['eslint-config-prettier'] %>", "eslint-plugin-prettier": "<%= nodeDependencies['eslint-plugin-prettier'] %>", - "eslint-webpack-plugin": "<%= nodeDependencies['eslint-webpack-plugin'] %>", "globals": "<%= nodeDependencies.globals %>", "jest": "<%= nodeDependencies['jest'] %>", "jest-environment-jsdom": "<%= nodeDependencies['jest'] %>", @@ -103,14 +99,7 @@ "swagger-ui-dist": "<%= nodeDependencies['swagger-ui-dist'] %>", "ts-jest": "<%= nodeDependencies['ts-jest'] %>", "typescript": "<%= nodeDependencies['typescript'] %>", - "typescript-eslint": "<%= nodeDependencies['typescript-eslint'] %>", - "webpack-bundle-analyzer": "<%= nodeDependencies['webpack-bundle-analyzer'] %>", - "webpack-merge": "<%= nodeDependencies['webpack-merge'] %>", - "webpack-notifier": "<%= nodeDependencies['webpack-notifier'] %>" - }, - "overrides": { - "browser-sync": "<%= nodeDependencies['browser-sync'] %>", - "webpack": "<%= nodeDependencies['webpack'] %>" + "typescript-eslint": "<%= nodeDependencies['typescript-eslint'] %>" }, "engines": { "node": ">=<%= nodeVersion %>" diff --git a/generators/angular/templates/proxy.conf.json.ejs b/generators/angular/templates/proxy.conf.json.ejs new file mode 100644 index 000000000000..deb20ebae515 --- /dev/null +++ b/generators/angular/templates/proxy.conf.json.ejs @@ -0,0 +1,27 @@ +<%# + 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 (communicationSpringWebsocket) { _%> + "/websocket": { + "target": "ws://localhost:8080", + "ws": true + }, +<%_ } _%> + "^/(api|management|v3/api-docs<% if (databaseTypeSql && devDatabaseTypeH2Any) { %>|h2-console<% } %><% if (authenticationTypeOauth2) { %>|oauth2|login<% } %><% if (authenticationTypeSession) { %>|login<% } %><% if (applicationTypeGateway || applicationTypeMicroservice) { %>|services<% } %>)": "http://localhost:<%= applicationTypeMicroservice ? gatewayServerPort : serverPort %>" +} diff --git a/generators/app/__snapshots__/generator.spec.ts.snap b/generators/app/__snapshots__/generator.spec.ts.snap index 2a9652edc222..d87aa167dacf 100644 --- a/generators/app/__snapshots__/generator.spec.ts.snap +++ b/generators/app/__snapshots__/generator.spec.ts.snap @@ -258,6 +258,7 @@ exports[`generator - app with default config should match snapshot 1`] = ` "cliName": undefined, "clientBundler": "webpack", "clientBundlerAny": true, + "clientBundlerExperimentalEsbuild": false, "clientBundlerVite": false, "clientBundlerWebpack": true, "clientDistDir": "target/classes/static/", @@ -645,6 +646,7 @@ exports[`generator - app with default config should match snapshot 1`] = ` "nodeDependencies": { "@angular-architects/module-federation": "ANGULAR_ARCHITECTS_MODULE_FEDERATION_VERSION", "@angular-architects/module-federation-runtime": "ANGULAR_ARCHITECTS_MODULE_FEDERATION_RUNTIME_VERSION", + "@angular-builders/custom-esbuild": "ANGULAR_BUILDERS_CUSTOM_ESBUILD_VERSION", "@angular-builders/custom-webpack": "ANGULAR_BUILDERS_CUSTOM_WEBPACK_VERSION", "@angular-builders/jest": "ANGULAR_BUILDERS_JEST_VERSION", "@angular/cli": "ANGULAR_CLI_VERSION", @@ -922,6 +924,7 @@ exports[`generator - app with gateway should match snapshot 1`] = ` "cliName": undefined, "clientBundler": "webpack", "clientBundlerAny": true, + "clientBundlerExperimentalEsbuild": false, "clientBundlerVite": false, "clientBundlerWebpack": true, "clientDistDir": "target/classes/static/", @@ -1305,6 +1308,7 @@ exports[`generator - app with gateway should match snapshot 1`] = ` "nodeDependencies": { "@angular-architects/module-federation": "ANGULAR_ARCHITECTS_MODULE_FEDERATION_VERSION", "@angular-architects/module-federation-runtime": "ANGULAR_ARCHITECTS_MODULE_FEDERATION_RUNTIME_VERSION", + "@angular-builders/custom-esbuild": "ANGULAR_BUILDERS_CUSTOM_ESBUILD_VERSION", "@angular-builders/custom-webpack": "ANGULAR_BUILDERS_CUSTOM_WEBPACK_VERSION", "@angular-builders/jest": "ANGULAR_BUILDERS_JEST_VERSION", "@angular/cli": "ANGULAR_CLI_VERSION", @@ -1581,6 +1585,7 @@ exports[`generator - app with microservice should match snapshot 1`] = ` "cliName": undefined, "clientBundler": undefined, "clientBundlerAny": true, + "clientBundlerExperimentalEsbuild": false, "clientBundlerVite": false, "clientBundlerWebpack": false, "clientDistDir": "target/classes/static/", diff --git a/generators/client/command.ts b/generators/client/command.ts index e64ec89440f2..281e25850b18 100644 --- a/generators/client/command.ts +++ b/generators/client/command.ts @@ -82,7 +82,7 @@ const command = { type: String, hide: true, }, - choices: ['webpack', 'vite'], + choices: ['webpack', 'vite', 'experimentalEsbuild'], scope: 'storage', }, microfrontend: { diff --git a/generators/java/generators/node/generator.ts b/generators/java/generators/node/generator.ts index fd83fefbb891..37255e80f003 100644 --- a/generators/java/generators/node/generator.ts +++ b/generators/java/generators/node/generator.ts @@ -54,6 +54,7 @@ export default class NodeGenerator extends BaseApplicationGenerator { async javaNodeBuildPaths({ application }) { application.javaNodeBuildPaths ??= []; const { + clientBundlerExperimentalEsbuild, clientBundlerVite, clientBundlerWebpack, clientFrameworkAngular, @@ -69,6 +70,9 @@ export default class NodeGenerator extends BaseApplicationGenerator { javaNodeBuildPaths.push(srcMainWebapp, clientDistDir!, 'package-lock.json', 'package.json', 'tsconfig.json'); if (clientFrameworkAngular) { javaNodeBuildPaths.push('tsconfig.app.json'); + if (clientBundlerExperimentalEsbuild) { + javaNodeBuildPaths.push('build-plugins/'); + } } else if (clientFrameworkReact) { javaNodeBuildPaths.push('.postcss.config.js'); } else if (clientFrameworkVue) { diff --git a/lib/jhipster/default-application-options.ts b/lib/jhipster/default-application-options.ts index dcb3564b7d24..b20fc3be7c38 100644 --- a/lib/jhipster/default-application-options.ts +++ b/lib/jhipster/default-application-options.ts @@ -119,11 +119,11 @@ export function getConfigForClientApplication(options: ApplicationDefaults = {}) options[CLIENT_THEME_VARIANT] = 'primary'; } if (clientFramework === 'vue') { - options.clientBundler = options.microfrontend || options.applicationType === 'microservice' ? 'webpack' : 'vite'; + options.clientBundler ??= options.microfrontend || options.applicationType === 'microservice' ? 'webpack' : 'vite'; } else if (clientFramework === 'react') { - options.clientBundler = 'webpack'; + options.clientBundler ??= 'webpack'; } else if (clientFramework === 'angular') { - options.clientBundler = 'webpack'; + options.clientBundler ??= 'webpack'; } return options; }