UNPKG

gen-jhipster

Version:

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

326 lines (325 loc) 15.3 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, fieldTypes } from "../../lib/jhipster/index.js"; import { upperFirstCamelCase } from "../../lib/utils/index.js"; import { createNeedleCallback } from "../base-core/support/index.js"; import { ClientApplicationGenerator } from "../client/generator.js"; import { generateEntityClientEnumImports as getClientEnumImportsFormat, generateEntityClientFields as getHydratedEntityClientFields, generateEntityClientImports as formatEntityClientImports, } 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 cleanupOldFilesTask from "./cleanup.js"; import { cleanupEntitiesFiles, postWriteEntitiesFiles, writeEntitiesFiles } from "./entity-files-react.js"; import { writeFiles } from "./files-react.js"; import { isTranslatedReactFile, translateReactFilesTransform } from "./support/index.js"; const { CommonDBTypes } = fieldTypes; const TYPE_BOOLEAN = CommonDBTypes.BOOLEAN; const { REACT } = clientFrameworkTypes; export default class ReactGenerator extends ClientApplicationGenerator { async beforeQueue() { if (!this.fromBlueprint) { await this.composeWithBlueprints(); } await this.dependsOnBootstrap('react'); if (!this.delegateToBlueprint) { await this.dependsOnJHipster('jhipster:client:i18n'); await this.dependsOnJHipster('client'); await this.dependsOnJHipster('languages'); } } 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 [ClientApplicationGenerator.COMPOSING]() { return this.delegateTasksToBlueprint(() => this.composing); } get preparing() { return this.asPreparingTaskGroup({ loadPackageJson({ application }) { this.loadNodeDependenciesFromPackageJson(application.nodeDependencies, this.fetchFromInstalledJHipster('react', 'resources', 'package.json')); }, applicationDefaults({ application, applicationDefaults }) { application.prettierExtensions.push('html', 'tsx', 'css', 'scss'); if (application.clientBundlerWebpack) { application.prettierFolders.push('webpack/'); } 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}**/`); } applicationDefaults({ __override__: true, webappEnumerationsDir: app => `${app.clientSrcDir}app/shared/model/enumerations/`, }); }, async javaNodeBuildPaths({ application }) { const { clientBundlerWebpack, javaNodeBuildPaths } = application; javaNodeBuildPaths?.push('.postcss.config.js', 'tsconfig.json'); if (clientBundlerWebpack) { javaNodeBuildPaths?.push('webpack/'); } }, prepareForTemplates({ application, source }) { source.addWebpackConfig = args => { const webpackPath = `${application.clientRootDir}webpack/webpack.common.js`; const ignoreNonExisting = this.ignoreNeedlesError && 'Webpack configuration file not found'; this.editFile(webpackPath, { ignoreNonExisting }, createNeedleCallback({ needle: 'jhipster-needle-add-webpack-config', contentToAdd: `,${args.config}`, })); }; source.addClientStyle = ({ style, comment }) => { comment = comment ? `/* ========================================================================== ${comment} ========================================================================== */ ` : ''; this.editFile(`${application.clientSrcDir}app/app.scss`, createNeedleCallback({ needle: 'scss-add-main', contentToAdd: `${comment}${style}`, contentToCheck: style, })); }; source.addEntitiesToClient = ({ application, entities }) => { const entityRoutes = `${application.clientSrcDir}app/entities/routes.tsx`; const entityReducers = `${application.clientSrcDir}app/entities/reducers.ts`; const entityMenu = `${application.clientSrcDir}app/entities/menu.tsx`; for (const entity of entities) { const { entityAngularName, entityInstance, entityFolderName, entityFileName, entityPage, entityUrl, entityTranslationKeyMenuPath, entityNameHumanized, } = entity; this.editFile(entityMenu, createNeedleCallback({ needle: 'add-entity-to-menu', contentToAdd: `<MenuItem icon="asterisk" to="/${entityPage}"> ${application.enableTranslation ? `<Translate contentKey="${entityTranslationKeyMenuPath}" />` : `${entityNameHumanized}`} </MenuItem>`, })); this.editFile(entityRoutes, createNeedleCallback({ needle: 'add-route-import', contentToAdd: `import ${entityAngularName} from './${entityFolderName}';`, }), createNeedleCallback({ needle: 'add-route-path', contentToAdd: `<Route path="/${entityUrl}/*" element={<${entityAngularName} />} />`, })); this.editFile(entityReducers, createNeedleCallback({ needle: 'add-reducer-import', contentToAdd: `import ${entityInstance} from 'app/entities/${entityFolderName}/${entityFileName}.reducer';`, }), createNeedleCallback({ needle: 'add-reducer-combine', contentToAdd: `${entityInstance},`, })); } }; }, }); } get [ClientApplicationGenerator.PREPARING]() { return this.delegateTasksToBlueprint(() => this.preparing); } get preparingEachEntity() { return this.asPreparingEachEntityTaskGroup({ react({ application, entity }) { entity.entityReactState = application.applicationTypeMonolith ? entity.entityInstance : `${application.lowercaseBaseName}.${entity.entityInstance}`; }, }); } get [ClientApplicationGenerator.PREPARING_EACH_ENTITY]() { return this.delegateTasksToBlueprint(() => this.preparingEachEntity); } get preparingEachEntityField() { return this.asPreparingEachEntityFieldTaskGroup({ react({ field }) { field.fieldValidateRulesPatternReact ??= field.fieldValidateRulesPattern?.replace(/'/g, String.raw `\'`); }, }); } get [ClientApplicationGenerator.PREPARING_EACH_ENTITY_FIELD]() { return this.delegateTasksToBlueprint(() => this.preparingEachEntityField); } get default() { return this.asDefaultTaskGroup({ queueTranslateTransform({ application }) { if (!application.enableTranslation) { this.queueTransformStream({ name: 'translating react application', filter: file => isFileStateModified(file) && file.path.startsWith(this.destinationPath()) && isTranslatedReactFile(file), refresh: false, }, translateReactFilesTransform(application.getWebappTranslation)); } }, }); } get [ClientApplicationGenerator.DEFAULT]() { return this.delegateTasksToBlueprint(() => this.default); } get writing() { return this.asWritingTaskGroup({ async cleanup({ control }) { await control.cleanupFiles({ '9.0.0-alpha.0': [ // Try to remove possibles old eslint config files 'eslint.config.js', 'eslint.config.mjs', 'postcss.config.js', ], }); }, cleanupOldFilesTask, writeEslintClientRootConfigFile, writeFiles, }); } get [ClientApplicationGenerator.WRITING]() { return this.delegateTasksToBlueprint(() => this.writing); } get writingEntities() { return { cleanupEntitiesFiles, writeEntitiesFiles, }; } get [ClientApplicationGenerator.WRITING_ENTITIES]() { return this.delegateTasksToBlueprint(() => this.writingEntities); } get postWriting() { return this.asPostWritingTaskGroup({ clientBundler({ application, source }) { source.mergeClientPackageJson({ overrides: { 'react-redux-loading-bar': { react: '$react', 'react-dom': '$react-dom', }, }, }); if (application.clientRootDir) { this.packageJson.merge({ overrides: { 'react-redux-loading-bar': { react: '$react', 'react-dom': '$react-dom', }, }, }); } }, addMicrofrontendDependencies({ application, source }) { if (!application.microfrontend) return; const { applicationTypeGateway } = application; if (applicationTypeGateway) { source.mergeClientPackageJson({ devDependencies: { '@module-federation/utilities': null }, }); } source.mergeClientPackageJson({ devDependencies: { '@module-federation/enhanced': null }, }); }, addWebsocketDependencies({ application, source }) { const { communicationSpringWebsocket, nodeDependencies } = application; if (communicationSpringWebsocket) { source.mergeClientPackageJson({ dependencies: { rxjs: nodeDependencies.rxjs, 'sockjs-client': nodeDependencies['sockjs-client'], 'webstomp-client': nodeDependencies['webstomp-client'], }, }); } }, sonar({ application, source }) { const { clientI18nDir, clientDistDir, clientSrcDir, temporaryDir } = application; source.addSonarProperties?.([ { key: 'sonar.test.inclusions', value: `${clientSrcDir}app/**/*.spec.ts, ${clientSrcDir}app/**/*.spec.tsx`, valueSep: ', ' }, { key: 'sonar.testExecutionReportPaths', value: `${temporaryDir}test-results/jest/TESTS-results-sonar.xml` }, { key: 'sonar.javascript.lcov.reportPaths', value: `${temporaryDir}test-results/lcov.info` }, { key: 'sonar.exclusions', value: `${clientSrcDir}content/**/*.*, ${clientI18nDir}*.ts, ${clientDistDir}**/*.*`, valueSep: ', ', }, ]); }, }); } get [ClientApplicationGenerator.POST_WRITING]() { return this.delegateTasksToBlueprint(() => this.postWriting); } get postWritingEntities() { return { postWriteEntitiesFiles, }; } get [ClientApplicationGenerator.POST_WRITING_ENTITIES]() { return this.delegateTasksToBlueprint(() => this.postWritingEntities); } get end() { return this.asEndTaskGroup({ end({ application }) { this.log.ok(`React ${application.nodeDependencies.react} application generated successfully.`); this.log.log(chalk.green(` Start your Webpack development server with: ${chalk.yellow.bold(`${application.nodePackageManager} start`)} `)); }, }); } get [ClientApplicationGenerator.END]() { return this.delegateTasksToBlueprint(() => this.end); } /** * @private * Generate Entity Client Field Default Values */ generateEntityClientFieldDefaultValues(fields) { const defaultVariablesValues = {}; fields.forEach(field => { const { fieldType, fieldName } = field; if (fieldType === TYPE_BOOLEAN) { defaultVariablesValues[fieldName] = `${fieldName}: false,`; } }); return defaultVariablesValues; } generateEntityClientFields(primaryKey, fields, relationships, dto, customDateType, embedded = false) { return getHydratedEntityClientFields(primaryKey, fields, relationships, dto, customDateType, embedded, REACT); } generateEntityClientImports(relationships, dto) { return formatEntityClientImports(relationships, dto, REACT); } generateEntityClientEnumImports(fields) { return getClientEnumImportsFormat(fields, REACT); } /** * get the an upperFirst camelCase value. * @param {string} value string to convert */ upperFirstCamelCase(value) { return upperFirstCamelCase(value); } }