UNPKG

gen-jhipster

Version:

VHipster - Spring Boot + Angular/React/Vue in one handy generator

407 lines (406 loc) 21.8 kB
/** * Copyright 2013-2026 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 chalk from 'chalk'; import { isFileStateModified } from 'mem-fs-editor/state'; import { clientFrameworkTypes } from "../../lib/jhipster/index.js"; import { mutateData } from "../../lib/utils/index.js"; import BaseApplicationGenerator from "../base-application/index.js"; import { createNeedleCallback } from "../base-core/support/index.js"; import { generateEntityClientEnumImports as getClientEnumImportsFormat } from "../client/support/index.js"; import { JAVA_WEBAPP_SOURCES_DIR } from "../index.js"; import { writeEslintClientRootConfigFile } from "../javascript-simple-application/generators/eslint/support/tasks.js"; import { defaultLanguage } from "../languages/support/index.js"; import cleanupOldFilesTask from "./cleanup.js"; import { cleanupEntitiesFiles, postWriteEntitiesFiles, writeEntitiesFiles } from "./entity-files-angular.js"; import { writeFiles } from "./files-angular.js"; import { addEntitiesRoute, addIconImport, addItemToAdminMenu, addRoute, addToEntitiesMenu, isTranslatedAngularFile, translateAngularFilesTransform, } from "./support/index.js"; const { ANGULAR } = clientFrameworkTypes; export class AngularApplicationGenerator extends BaseApplicationGenerator { } export default class AngularGenerator extends AngularApplicationGenerator { async beforeQueue() { if (!this.fromBlueprint) { await this.composeWithBlueprints(); } await this.dependsOnBootstrap('angular'); if (!this.delegateToBlueprint) { await this.dependsOnJHipster('jhipster:client:i18n'); await this.dependsOnJHipster('client'); await this.dependsOnJHipster('languages'); } } get configuring() { return this.asConfiguringTaskGroup({ migrateWebpackAndEsbuild({ control }) { if (control.isJhipsterVersionLessThan('9.0.0-alpha.0')) { this.jhipsterConfig.clientBundler ??= 'webpack'; } // @ts-expect-error renamed option if (this.jhipsterConfig.clientBundler === 'experimentalEsbuild') { this.jhipsterConfig.clientBundler = 'esbuild'; } }, }); } get [BaseApplicationGenerator.CONFIGURING]() { return this.delegateTasksToBlueprint(() => this.configuring); } get composing() { return this.asComposingTaskGroup({ async composing() { await this.composeWithJHipster('jhipster:client:common'); if (this.jhipsterConfigWithDefaults.websocket === 'spring-websocket') { await this.composeWithJHipster('jhipster:client:encode-csrf-token'); } }, }); } get [BaseApplicationGenerator.COMPOSING]() { return this.delegateTasksToBlueprint(() => this.composing); } get preparing() { return this.asPreparingTaskGroup({ loadPackageJson({ application }) { this.loadNodeDependenciesFromPackageJson(application.nodeDependencies, this.fetchFromInstalledJHipster('angular', 'resources', 'package.json')); }, applicationDefaults({ application, applicationDefaults }) { applicationDefaults({ __override__: true, webappEnumerationsDir: app => `${app.clientSrcDir}app/entities/enumerations/`, angularLocaleId: app => app.nativeLanguageDefinition.angularLocale ?? defaultLanguage.angularLocale, }); application.prettierExtensions.push('html', 'css', 'scss'); application.prettierFolders.push(application.clientBundlerWebpack ? 'webpack/**/' : 'build-plugins/**/'); if (!application.backendTypeJavaAny && application.clientSrcDir !== JAVA_WEBAPP_SOURCES_DIR) { // When we have a java backend, 'src/**' is already added by java:bootstrap application.prettierFolders.push(`${application.clientSrcDir}**/`); } }, async javaNodeBuildPaths({ application }) { application.javaNodeBuildPaths?.push('angular.json', 'tsconfig.json', 'tsconfig.app.json'); if (application.clientBundlerWebpack) { application.javaNodeBuildPaths?.push('webpack/'); } else if (application.clientBundlerEsbuild) { application.javaNodeBuildPaths?.push('build-plugins/'); if (application.enableI18nRTL) { application.javaNodeBuildPaths?.push('postcss.conf.json'); } } }, addNeedles({ source, application }) { source.addEntitiesToClient = param => { const routeTemplatePath = `${param.application.clientSrcDir}app/entities/entity.routes.ts`; const ignoreNonExistingRoute = chalk.yellow(`Route(s) not added to ${routeTemplatePath}.`); const addRouteCallback = addEntitiesRoute(param); this.editFile(routeTemplatePath, { ignoreNonExisting: ignoreNonExistingRoute }, addRouteCallback); const filePath = `${application.clientSrcDir}app/layouts/navbar/navbar.html`; const ignoreNonExisting = chalk.yellow('Reference to entities not added to menu.'); const editCallback = addToEntitiesMenu(param); this.editFile(filePath, { ignoreNonExisting }, editCallback); if (application.applicationTypeMicroservice) { this.editFile(`${application.clientSrcDir}app/entities/entity-navbar-items.ts`, createNeedleCallback({ needle: 'add-entity-navbar', contentToAdd: param.entities.map(entity => ({ contentToCheck: `route: '/${entity.entityPage}',`, content: `{ name: '${entity.entityAngularName}', route: '/${entity.entityPage}',${application.enableTranslation ? ` translationKey: '${entity.entityTranslationKeyMenuPath}',` : ''} },`, })), })); } }; source.addAdminRoute = (args) => this.editFile(`${application.clientSrcDir}app/admin/admin.routes.ts`, addRoute({ needle: 'add-admin-route', ...args, })); source.addItemToAdminMenu = (args) => { this.editFile(`${application.clientSrcDir}app/layouts/navbar/navbar.html`, addItemToAdminMenu({ enableTranslation: application.enableTranslation, jhiPrefix: application.jhiPrefix, ...args, })); if (args.icon) { source.addIconImport({ icon: args.icon }); } }; source.addIconImport = args => { const iconsPath = `${application.clientSrcDir}app/config/font-awesome-icons.ts`; const ignoreNonExisting = this.ignoreNeedlesError && 'Icon imports not updated with icon'; this.editFile(iconsPath, { ignoreNonExisting }, addIconImport(args)); }; if (application.clientBundlerWebpack) { source.addWebpackConfig = args => { const webpackPath = `${application.clientRootDir}webpack/webpack.custom.js`; const ignoreNonExisting = this.ignoreNeedlesError && 'Webpack configuration file not found'; this.editFile(webpackPath, { ignoreNonExisting }, createNeedleCallback({ needle: 'jhipster-needle-add-webpack-config', contentToAdd: `${args.config},`, })); }; } if (application.clientRootDir) { // Overrides only works if added in root package.json this.packageJson.merge({ overrides: { 'browser-sync': application.nodeDependencies['browser-sync'], webpack: application.nodeDependencies.webpack, }, }); } }, }); } get [BaseApplicationGenerator.PREPARING]() { return this.delegateTasksToBlueprint(() => this.preparing); } get preparingEachEntity() { return this.asPreparingEachEntityTaskGroup({ prepareEntity({ entity }) { const asAuthorities = (authorities) => authorities.length > 0 ? authorities.map(auth => `'${auth}'`).join(', ') : undefined; mutateData(entity, { entityAngularAuthorities: asAuthorities(entity.entityAuthority?.split(',') ?? []), entityAngularReadAuthorities: asAuthorities([ ...(entity.entityAuthority?.split(',') ?? []), ...(entity.entityReadAuthority?.split(',') ?? []), ]), }); entity.generateEntityClientEnumImports = (fields) => getClientEnumImportsFormat(fields, ANGULAR); }, }); } get [BaseApplicationGenerator.PREPARING_EACH_ENTITY]() { return this.delegateTasksToBlueprint(() => this.preparingEachEntity); } get preparingEachEntityField() { return this.asPreparingEachEntityFieldTaskGroup({ prepareField({ field }) { mutateData(field, { fieldTsDefaultValue: ({ fieldTsDefaultValue, defaultValue, fieldTypeCharSequence, fieldTypeTimed }) => { let returnValue; if (fieldTsDefaultValue !== undefined || defaultValue !== undefined) { let fieldDefaultValue; if (fieldTsDefaultValue === undefined) { fieldDefaultValue = defaultValue; } else { fieldDefaultValue = fieldTsDefaultValue; } fieldDefaultValue = String(fieldDefaultValue).replace(/'/g, String.raw `\'`); if (fieldTypeCharSequence) { returnValue = `'${fieldDefaultValue}'`; } else if (fieldTypeTimed) { returnValue = `dayjs('${fieldDefaultValue}')`; } else { returnValue = fieldDefaultValue; } } return returnValue; }, fieldValidateRulesPatternAngular: ({ fieldValidateRulesPattern }) => fieldValidateRulesPattern?.replace(/\\\\/g, '\\').replace(/\\/g, '\\\\').replace(/'/g, "\\'"), }); }, }); } get [BaseApplicationGenerator.PREPARING_EACH_ENTITY_FIELD]() { return this.delegateTasksToBlueprint(() => this.preparingEachEntityField); } get default() { return this.asDefaultTaskGroup({ queueTranslateTransform({ application }) { const { enableTranslation, jhiPrefix } = application; this.queueTransformStream({ name: 'translating angular application', filter: file => isFileStateModified(file) && file.path.startsWith(this.destinationPath()) && isTranslatedAngularFile(file), refresh: false, }, translateAngularFilesTransform(application.getWebappTranslation, { enableTranslation, jhiPrefix })); }, }); } get [BaseApplicationGenerator.DEFAULT]() { return this.delegateTasksToBlueprint(() => this.default); } get writing() { return this.asWritingTaskGroup({ async cleanup({ application, control }) { await control.cleanupFiles({ '8.6.1': ['.eslintrc.json', '.eslintignore'], '8.7.4': [`${application.clientSrcDir}app/app.constants.ts`], '9.0.0-alpha.0': [ // Try to remove possibles old eslint config files 'eslint.config.js', 'eslint.config.mjs', [ application.clientBundlerEsbuild, `${application.clientRootDir}build-plugins/define-esbuild.mjs`, `${application.clientRootDir}build-plugins/i18n-esbuild.mjs`, ], [ !application.microfrontend || !application.applicationTypeMicroservice, `${application.clientSrcDir}app/entities/entity-navbar-items.ts`, ], `${application.clientSrcDir}app/config/uib-pagination.config.ts`, ], '8.0.0-beta.1': [ `${application.clientRootDir}jest.js`, `${application.clientSrcDir}app/shared/shared.module.ts.ejs`, `${application.clientSrcDir}app/core/interceptor/index.ts.ejs`, `${application.clientSrcDir}default-test-providers.ts.ejs`, `${application.clientSrcDir}app/config/error.constants.ts`, `${application.clientSrcDir}app/config/authority.constants.ts`, ], '8.0.0-beta.4': [ `${application.clientSrcDir}app/admin/user-management/user-management.route.ts`, `${application.clientSrcDir}app/admin/user-management/user-management.model.ts`, `${application.clientSrcDir}app/admin/user-management/list/user-management.html`, `${application.clientSrcDir}app/admin/user-management/list/user-management.spec.ts`, `${application.clientSrcDir}app/admin/user-management/list/user-management.ts`, `${application.clientSrcDir}app/admin/user-management/detail/user-management-detail.html`, `${application.clientSrcDir}app/admin/user-management/detail/user-management-detail.spec.ts`, `${application.clientSrcDir}app/admin/user-management/detail/user-management-detail.ts`, `${application.clientSrcDir}app/admin/user-management/update/user-management-update.html`, `${application.clientSrcDir}app/admin/user-management/update/user-management-update.spec.ts`, `${application.clientSrcDir}app/admin/user-management/update/user-management-update.ts`, `${application.clientSrcDir}app/admin/user-management/delete/user-management-delete-dialog.html`, `${application.clientSrcDir}app/admin/user-management/delete/user-management-delete-dialog.spec.ts`, `${application.clientSrcDir}app/admin/user-management/delete/user-management-delete-dialog.ts`, `${application.clientSrcDir}app/admin/user-management/service/user-management.service.spec.ts`, `${application.clientSrcDir}app/admin/user-management/service/user-management.service.ts`, ], }); }, cleanupOldFilesTask, writeEslintClientRootConfigFile, writeFiles, }); } get [BaseApplicationGenerator.WRITING]() { return this.delegateTasksToBlueprint(() => this.writing); } get writingEntities() { return this.asWritingEntitiesTaskGroup({ cleanupEntitiesFiles, writeEntitiesFiles, }); } get [BaseApplicationGenerator.WRITING_ENTITIES]() { return this.delegateTasksToBlueprint(() => this.writingEntities); } get postWriting() { return this.asPostWritingTaskGroup({ clientBundler({ application, source }) { const { clientBundlerEsbuild, enableTranslation, nodeDependencies } = application; if (clientBundlerEsbuild) { source.mergeClientPackageJson({ devDependencies: { '@angular-builders/custom-esbuild': null, '@angular/build': null, tinyglobby: null, ...(enableTranslation ? { '@types/folder-hash': null, 'folder-hash': null, deepmerge: null } : {}), }, }); } else { source.mergeClientPackageJson({ dependencies: enableTranslation ? { '@ngx-translate/http-loader': null, } : {}, devDependencies: { '@angular-builders/custom-webpack': null, 'browser-sync-webpack-plugin': null, 'copy-webpack-plugin': null, 'webpack-bundle-analyzer': null, 'webpack-merge': null, 'webpack-notifier': null, ...(enableTranslation ? { 'folder-hash': null, 'merge-jsons-webpack-plugin': null } : {}), }, overrides: { 'browser-sync': nodeDependencies['browser-sync'], webpack: nodeDependencies.webpack, }, }); } }, addWebsocketDependencies({ application, source }) { const { authenticationTypeSession, communicationSpringWebsocket, nodeDependencies } = application; const dependencies = {}; if (communicationSpringWebsocket) { if (authenticationTypeSession) { dependencies['ngx-cookie-service'] = nodeDependencies['ngx-cookie-service']; } source.mergeClientPackageJson({ dependencies: { 'sockjs-client': nodeDependencies['sockjs-client'], '@stomp/rx-stomp': nodeDependencies['@stomp/rx-stomp'], ...dependencies, }, devDependencies: { '@types/sockjs-client': nodeDependencies['@types/sockjs-client'], }, }); } }, sonar({ application, source }) { const { clientDistDir, clientSrcDir, clientI18nDir, temporaryDir } = application; source.addSonarProperties?.([ { key: 'sonar.test.inclusions', value: `${clientSrcDir}app/**/*.spec.ts`, valueSep: ', ' }, { key: 'sonar.testExecutionReportPaths', value: `${temporaryDir}test-results/TESTS-results-sonar-vitest.xml` }, { key: 'sonar.javascript.lcov.reportPaths', value: `${temporaryDir}test-results/lcov.info` }, { key: 'sonar.exclusions', value: `${clientSrcDir}content/**/*.*, ${clientI18nDir}*.ts, ${clientDistDir}**/*.*`, valueSep: ', ', }, ]); }, }); } get [BaseApplicationGenerator.POST_WRITING]() { return this.delegateTasksToBlueprint(() => this.postWriting); } get postWritingEntities() { return this.asPostWritingEntitiesTaskGroup({ postWriteEntitiesFiles, }); } get [BaseApplicationGenerator.POST_WRITING_ENTITIES]() { return this.delegateTasksToBlueprint(() => this.postWritingEntities); } get end() { return this.asEndTaskGroup({ end({ application }) { this.log.ok(`Angular ${application.nodeDependencies['@angular/common']} application generated successfully.`); this.log.log(chalk.green(` Start your Webpack development server with: ${chalk.yellow.bold(`${application.nodePackageManager} start`)} `)); }, }); } get [BaseApplicationGenerator.END]() { return this.delegateTasksToBlueprint(() => this.end); } }