UNPKG

gen-jhipster

Version:

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

602 lines (601 loc) 23.6 kB
/** * Copyright 2013-2021 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 { upperFirst } from 'lodash-es'; import BaseGenerator from '../base/index.js'; import { JHIPSTER_CONFIG_DIR } from '../generator-constants.js'; import { mutateData } from '../../lib/utils/object.js'; import { GENERATOR_BOOTSTRAP_APPLICATION, GENERATOR_BOOTSTRAP_APPLICATION_BASE, GENERATOR_BOOTSTRAP_APPLICATION_CLIENT, GENERATOR_BOOTSTRAP_APPLICATION_SERVER, } from '../generator-list.js'; import { getEntitiesFromDir } from './support/index.js'; import { CUSTOM_PRIORITIES, PRIORITY_NAMES, QUEUES } from './priorities.js'; const { LOADING, PREPARING, POST_PREPARING, CONFIGURING_EACH_ENTITY, LOADING_ENTITIES, PREPARING_EACH_ENTITY, PREPARING_EACH_ENTITY_FIELD, PREPARING_EACH_ENTITY_RELATIONSHIP, POST_PREPARING_EACH_ENTITY, DEFAULT, WRITING, POST_WRITING, WRITING_ENTITIES, POST_WRITING_ENTITIES, PRE_CONFLICTS, INSTALL, END, } = PRIORITY_NAMES; const { CONFIGURING_EACH_ENTITY_QUEUE, LOADING_ENTITIES_QUEUE, PREPARING_EACH_ENTITY_QUEUE, PREPARING_EACH_ENTITY_FIELD_QUEUE, PREPARING_EACH_ENTITY_RELATIONSHIP_QUEUE, POST_PREPARING_EACH_ENTITY_QUEUE, WRITING_ENTITIES_QUEUE, POST_WRITING_ENTITIES_QUEUE, } = QUEUES; const asPriority = BaseGenerator.asPriority; /** * This is the base class for a generator that generates entities. */ export default class BaseApplicationGenerator extends BaseGenerator { static CONFIGURING_EACH_ENTITY = asPriority(CONFIGURING_EACH_ENTITY); static LOADING_ENTITIES = asPriority(LOADING_ENTITIES); static PREPARING_EACH_ENTITY = asPriority(PREPARING_EACH_ENTITY); static PREPARING_EACH_ENTITY_FIELD = asPriority(PREPARING_EACH_ENTITY_FIELD); static PREPARING_EACH_ENTITY_RELATIONSHIP = asPriority(PREPARING_EACH_ENTITY_RELATIONSHIP); static POST_PREPARING_EACH_ENTITY = asPriority(POST_PREPARING_EACH_ENTITY); static WRITING_ENTITIES = asPriority(WRITING_ENTITIES); static POST_WRITING_ENTITIES = asPriority(POST_WRITING_ENTITIES); constructor(args, options, features) { super(args, options, features); if (this.options.help) { return; } this.registerPriorities(CUSTOM_PRIORITIES); /* Add tasks allowing entities priorities to match normal priorities pattern */ this.on('queueOwnTasks', () => { this.log.debug('Queueing entity tasks'); this.queueEntityTasks(); }); if (this.options.applicationWithEntities) { this.log.warn('applicationWithEntities option is deprecated'); // Write new definitions to memfs this.config.set({ ...this.config.getAll(), ...this.options.applicationWithEntities.config, }); if (this.options.applicationWithEntities.entities) { const entities = this.options.applicationWithEntities.entities.map(entity => { const entityName = upperFirst(entity.name); const file = this.getEntityConfigPath(entityName); this.fs.writeJSON(file, { ...this.fs.readJSON(file), ...entity }); return entityName; }); this.jhipsterConfig.entities = [...new Set((this.jhipsterConfig.entities || []).concat(entities))]; } delete this.options.applicationWithEntities; } } dependsOnBootstrapApplication(options) { return this.dependsOnJHipster(GENERATOR_BOOTSTRAP_APPLICATION, options); } dependsOnBootstrapApplicationBase(options) { return this.dependsOnJHipster(GENERATOR_BOOTSTRAP_APPLICATION_BASE, options); } dependsOnBootstrapApplicationServer(options) { return this.dependsOnJHipster(GENERATOR_BOOTSTRAP_APPLICATION_SERVER, options); } dependsOnBootstrapApplicationClient(options) { return this.dependsOnJHipster(GENERATOR_BOOTSTRAP_APPLICATION_CLIENT, options); } /** * Get Entities configuration path * @returns */ getEntitiesConfigPath(...args) { return this.destinationPath(JHIPSTER_CONFIG_DIR, ...args); } /** * Get Entity configuration path * @param entityName Entity name * @returns */ getEntityConfigPath(entityName) { return this.getEntitiesConfigPath(`${upperFirst(entityName)}.json`); } /** * Get all the generator configuration from the .yo-rc.json file * @param entityName - Name of the entity to load. * @param create - Create storage if doesn't exists. */ getEntityConfig(entityName, create = false) { const entityPath = this.getEntityConfigPath(entityName); if (!create && !this.fs.exists(entityPath)) return undefined; return this.createStorage(entityPath); } /** * get sorted list of entity names according to changelog date (i.e. the order in which they were added) */ getExistingEntityNames() { return this.getExistingEntities().map(entity => entity.name); } /** * get sorted list of entities according to changelog date (i.e. the order in which they were added) */ getExistingEntities() { function isBefore(e1, e2) { return (e1.definition.annotations?.changelogDate ?? 0) - (e2.definition.annotations?.changelogDate ?? 0); } const configDir = this.getEntitiesConfigPath(); const entities = []; for (const entityName of [...new Set((this.jhipsterConfig.entities || []).concat(getEntitiesFromDir(configDir)))]) { const definition = this.getEntityConfig(entityName)?.getAll(); if (definition) { entities.push({ name: entityName, definition }); } } entities.sort(isBefore); this.jhipsterConfig.entities = entities.map(({ name }) => name); return entities; } /** * Priority API stub for blueprints. * * Configuring each entity should configure entities. */ get configuringEachEntity() { return {}; } get preparingEachEntity() { return {}; } /** * Priority API stub for blueprints. */ get preparingEachEntityField() { return {}; } /** * Priority API stub for blueprints. */ get preparingEachEntityRelationship() { return {}; } /** * Priority API stub for blueprints. */ get postPreparingEachEntity() { return {}; } /** * Priority API stub for blueprints. */ get writingEntities() { return {}; } /** * Priority API stub for blueprints. */ get postWritingEntities() { return {}; } /** * Utility method to get typed objects for autocomplete. */ asConfiguringEachEntityTaskGroup(taskGroup) { return taskGroup; } /** * Utility method to get typed objects for autocomplete. */ asLoadingEntitiesTaskGroup(taskGroup) { return taskGroup; } /** * Utility method to get typed objects for autocomplete. */ asPreparingEachEntityTaskGroup(taskGroup) { return taskGroup; } /** * Utility method to get typed objects for autocomplete. */ asPreparingEachEntityFieldTaskGroup(taskGroup) { return taskGroup; } /** * Utility method to get typed objects for autocomplete. */ asPreparingEachEntityRelationshipTaskGroup(taskGroup) { return taskGroup; } /** * Utility method to get typed objects for autocomplete. */ asPostPreparingEachEntityTaskGroup(taskGroup) { return taskGroup; } /** * Utility method to get typed objects for autocomplete. */ asWritingEntitiesTaskGroup(taskGroup) { return taskGroup; } /** * Utility method to get typed objects for autocomplete. */ asPostWritingEntitiesTaskGroup(taskGroup) { return taskGroup; } /** * Reset entities fake data seed. * @param {string} seed */ resetEntitiesFakeData(seed) { seed = `${this.sharedData.getApplication().baseName}-${seed}`; this.log.debug(`Resetting entities seed with '${seed}'`); this.sharedData.getEntities().forEach(({ entity }) => { entity.resetFakerSeed(seed); }); } getArgsForPriority(priorityName) { const args = super.getArgsForPriority(priorityName); let firstArg = this.getTaskFirstArgForPriority(priorityName); if (args.length > 0) { firstArg = { ...args[0], ...firstArg }; } return [firstArg]; } /** * @protected */ getTaskFirstArgForPriority(priorityName) { if (![ LOADING, PREPARING, POST_PREPARING, CONFIGURING_EACH_ENTITY, LOADING_ENTITIES, PREPARING_EACH_ENTITY, PREPARING_EACH_ENTITY_FIELD, PREPARING_EACH_ENTITY_RELATIONSHIP, POST_PREPARING_EACH_ENTITY, DEFAULT, WRITING, WRITING_ENTITIES, POST_WRITING, POST_WRITING_ENTITIES, PRE_CONFLICTS, INSTALL, END, ].includes(priorityName)) { return {}; } if (!this.jhipsterConfig.baseName) { throw new Error(`BaseName (${this.jhipsterConfig.baseName}) application not available for priority ${priorityName}`); } const application = this.sharedData.getApplication(); if ([PREPARING, LOADING].includes(priorityName)) { return { application, applicationDefaults: data => mutateData(application, { __override__: false, ...data }), }; } if (LOADING_ENTITIES === priorityName) { return { application, entitiesToLoad: this.getEntitiesDataToLoad(), }; } if ([DEFAULT].includes(priorityName)) { return { application, ...this.getEntitiesDataForPriorities(), }; } if ([WRITING_ENTITIES, POST_WRITING_ENTITIES].includes(priorityName)) { const applicationAndEntities = { application, ...this.getEntitiesDataToWrite(), }; if (priorityName === WRITING_ENTITIES) { return applicationAndEntities; } return { ...applicationAndEntities, source: this.sharedData.getSource(), }; } return { application }; } /** * @private * Get entities to configure. * This method doesn't filter entities. An filtered config can be changed at this priority. * @returns {string[]} */ getEntitiesDataToConfigure() { return this.getExistingEntityNames().map(entityName => { const entityStorage = this.getEntityConfig(entityName, true); return { entityName, entityStorage, entityConfig: entityStorage.createProxy() }; }); } /** * @private * Get entities to load. * This method doesn't filter entities. An filtered config can be changed at this priority. * @returns {string[]} */ getEntitiesDataToLoad() { const application = this.sharedData.getApplication(); const builtInEntities = []; if (application.generateBuiltInUserEntity) { // Reorder User entity to be the first one to be loaded builtInEntities.push('User'); } if (application.generateBuiltInUserEntity && application.generateUserManagement) { // Reorder User entity to be the first one to be loaded builtInEntities.push('UserManagement'); } if (application.generateBuiltInAuthorityEntity) { // Reorder User entity to be the first one to be loaded builtInEntities.push('Authority'); } const entitiesToLoad = [...new Set([...builtInEntities, ...this.getExistingEntityNames()])]; return entitiesToLoad.map(entityName => { const generator = this; if (!this.sharedData.hasEntity(entityName)) { this.sharedData.setEntity(entityName, { name: entityName }); } const entityBootstrap = this.sharedData.getEntity(entityName); return { entityName, get entityStorage() { return generator.getEntityConfig(entityName, true); }, get entityConfig() { return generator.getEntityConfig(entityName, true).createProxy(); }, entityBootstrap, }; }); } /** * @private * Get entities to prepare. * @returns {object[]} */ getEntitiesDataToPrepare() { return this.sharedData.getEntities().map(({ entityName, ...data }) => ({ description: entityName, entityName, ...data, })); } /** * @private * Get entities and fields to prepare. * @returns {object[]} */ getEntitiesFieldsDataToPrepare() { return this.getEntitiesDataToPrepare() .map(({ entity, entityName, ...data }) => { if (!entity.fields) return []; return entity.fields.map(field => ({ entity, entityName, ...data, field, fieldName: field.fieldName, description: `${entityName}#${field.fieldName}`, })); }) .flat(); } /** * @private * Get entities and relationships to prepare. * @returns {object[]} */ getEntitiesRelationshipsDataToPrepare() { return this.getEntitiesDataToPrepare() .map(({ entity, entityName, ...data }) => { if (!entity.relationships) return []; return entity.relationships.map(relationship => ({ entity, entityName, ...data, relationship, relationshipName: relationship.relationshipName, description: `${entityName}#${relationship.relationshipName}`, })); }) .flat(); } /** * @private * Get entities to post prepare. * @returns {object[]} */ getEntitiesDataToPostPrepare() { return this.getEntitiesDataToPrepare(); } /** * @private * Get entities to write. * @returns {object[]} */ getEntitiesDataForPriorities() { const entitiesDefinitions = this.sharedData.getEntities(); return { entities: entitiesDefinitions.map(({ entity }) => entity) }; } /** * @private * Get entities to write. * @returns {object[]} */ getEntitiesDataToWrite() { const { entities = [] } = this.options; const data = this.getEntitiesDataForPriorities(); if (entities.length === 0) return data; const filteredEntities = data.entities.filter(entity => entities.includes(entity.name)); return { ...data, entities: filteredEntities }; } /** * @private * Queue entity tasks. */ queueEntityTasks() { this.queueTask({ queueName: CONFIGURING_EACH_ENTITY_QUEUE, taskName: 'queueConfiguringEachEntity', cancellable: true, method: () => { if (this.options.skipPriorities?.includes(CONFIGURING_EACH_ENTITY)) return; this.log.debug(`Queueing entity tasks ${CONFIGURING_EACH_ENTITY}`); const tasks = this.extractTasksFromPriority(CONFIGURING_EACH_ENTITY, { skip: false }); this.getEntitiesDataToConfigure().forEach(({ entityName, entityStorage, entityConfig }) => { this.log.debug(`Queueing entity tasks ${CONFIGURING_EACH_ENTITY} for ${entityName}`); const args = this.getArgsForPriority(CONFIGURING_EACH_ENTITY); tasks.forEach(task => { this.queueTask({ ...task, args: [{ ...args[0], entityName, entityStorage, entityConfig }], }); }); }); }, }); this.queueTask({ queueName: LOADING_ENTITIES_QUEUE, taskName: 'queueLoadingEntities', cancellable: true, method: () => { if (this.options.skipPriorities?.includes(LOADING_ENTITIES)) return; this.log.debug(`Queueing entity tasks ${LOADING_ENTITIES}`); const tasks = this.extractTasksFromPriority(LOADING_ENTITIES, { skip: false }); this.log.debug(`Queueing entity tasks ${LOADING_ENTITIES}`); const args = this.getArgsForPriority(LOADING_ENTITIES); tasks.forEach(task => { this.queueTask({ ...task, args, }); }); }, }); this.queueTask({ queueName: PREPARING_EACH_ENTITY_QUEUE, taskName: 'queuePreparingEachEntity', cancellable: true, method: () => { if (this.options.skipPriorities?.includes(PREPARING_EACH_ENTITY)) return; this.log.debug(`Queueing entity tasks ${PREPARING_EACH_ENTITY}`); const tasks = this.extractTasksFromPriority(PREPARING_EACH_ENTITY, { skip: false }); this.getEntitiesDataToPrepare().forEach(({ description, ...data }) => { this.log.debug(`Queueing entity tasks ${PREPARING_EACH_ENTITY} for ${description}`); const args = this.getArgsForPriority(PREPARING_EACH_ENTITY); tasks.forEach(task => { this.queueTask({ ...task, args: [{ ...args[0], description, ...data }], }); }); }); }, }); this.queueTask({ queueName: PREPARING_EACH_ENTITY_FIELD_QUEUE, taskName: 'queuePreparingEachEntityField', cancellable: true, method: () => { if (this.options.skipPriorities?.includes(PREPARING_EACH_ENTITY_FIELD)) return; const tasks = this.extractTasksFromPriority(PREPARING_EACH_ENTITY_FIELD, { skip: false }); this.getEntitiesFieldsDataToPrepare().forEach(({ description, ...data }) => { this.log.debug(`Queueing entity tasks ${PREPARING_EACH_ENTITY_FIELD} for ${description}`); const args = this.getArgsForPriority(PREPARING_EACH_ENTITY_FIELD); tasks.forEach(task => { this.queueTask({ ...task, args: [{ ...args[0], description, ...data }], }); }); }); }, }); this.queueTask({ queueName: PREPARING_EACH_ENTITY_RELATIONSHIP_QUEUE, taskName: 'queuePreparingEachEntityRelationship', cancellable: true, method: () => { if (this.options.skipPriorities?.includes(PREPARING_EACH_ENTITY_RELATIONSHIP)) return; const tasks = this.extractTasksFromPriority(PREPARING_EACH_ENTITY_RELATIONSHIP, { skip: false }); this.getEntitiesRelationshipsDataToPrepare().forEach(({ description, ...data }) => { this.log.debug(`Queueing entity tasks ${PREPARING_EACH_ENTITY_RELATIONSHIP} for ${description}`); const args = this.getArgsForPriority(PREPARING_EACH_ENTITY_RELATIONSHIP); tasks.forEach(task => { this.queueTask({ ...task, args: [{ ...args[0], description, ...data }], }); }); }); }, }); this.queueTask({ queueName: POST_PREPARING_EACH_ENTITY_QUEUE, taskName: 'queuePostPreparingEachEntity', cancellable: true, method: () => { if (this.options.skipPriorities?.includes(POST_PREPARING_EACH_ENTITY)) return; const tasks = this.extractTasksFromPriority(POST_PREPARING_EACH_ENTITY, { skip: false }); this.getEntitiesDataToPostPrepare().forEach(({ description, ...data }) => { this.log.debug(`Queueing entity tasks ${POST_PREPARING_EACH_ENTITY} for ${description}`); const args = this.getArgsForPriority(POST_PREPARING_EACH_ENTITY); tasks.forEach(task => { this.queueTask({ ...task, args: [{ ...args[0], description, ...data }], }); }); }); }, }); this.queueTask({ queueName: WRITING_ENTITIES_QUEUE, taskName: 'queueWritingEachEntity', cancellable: true, method: () => { if (this.options.skipWriting || this.options.skipPriorities?.includes(WRITING_ENTITIES)) return; const tasks = this.extractTasksFromPriority(WRITING_ENTITIES, { skip: false }); const args = this.getArgsForPriority(WRITING_ENTITIES); tasks.forEach(task => { this.queueTask({ ...task, args, }); }); }, }); this.queueTask({ queueName: POST_WRITING_ENTITIES_QUEUE, taskName: 'queuePostWritingEachEntity', cancellable: true, method: () => { if (this.options.skipWriting || this.options.skipPriorities?.includes(POST_WRITING_ENTITIES)) return; const tasks = this.extractTasksFromPriority(POST_WRITING_ENTITIES, { skip: false }); const args = this.getArgsForPriority(POST_WRITING_ENTITIES); tasks.forEach(task => { this.queueTask({ ...task, args, }); }); }, }); } }