UNPKG

gen-jhipster

Version:

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

283 lines (282 loc) 13.9 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 { readFile } from 'node:fs/promises'; import { extname } from 'node:path'; import { upperFirst } from 'lodash-es'; import { create as createMemFs } from 'mem-fs'; import { create as createMemFsEditor } from 'mem-fs-editor'; import { downloadJdlFile } from "../../cli/download.js"; import EnvironmentBuilder from "../../cli/environment-builder.js"; import { CLI_NAME } from "../../cli/utils.js"; import { createImporterFromContent } from "../../lib/jdl/jdl-importer.js"; import { mergeYoRcContent } from "../../lib/utils/yo-rc.js"; import BaseGenerator from "../base/index.js"; import { normalizeBlueprintName } from "../base/internal/blueprint.js"; import { updateApplicationEntitiesTransform } from "../base-application/support/update-application-entities-transform.js"; import { GENERATOR_JHIPSTER, JHIPSTER_CONFIG_DIR } from "../generator-constants.js"; import { addApplicationIndex, allNewApplications, customizeForMicroservices } from "./internal/index.js"; /** * Add jdl extension to the file */ const toJdlFile = (file) => { if (!extname(file)) { return `${file}.jdl`; } return file; }; export default class JdlGenerator extends BaseGenerator { jdlFiles; inline; jdlContents = []; entrypointGenerator = `${CLI_NAME}:app`; entitiesGenerator = 'entities'; workspacesGenerator = 'workspaces'; interactive; jsonOnly; ignoreApplication; ignoreDeployments; skipSampleRepository; force; reproducible; createEnvBuilder = EnvironmentBuilder.createDefaultBuilder; existingProject; applications; exportedApplicationsWithEntities; exportedEntities; exportedDeployments; async beforeQueue() { if (!this.fromBlueprint) { await this.composeWithBlueprints(); } } get initializing() { return this.asInitializingTaskGroup({ loadArguments() { if (this.jdlFiles) { this.log.verboseInfo('Generating jdls', ...this.jdlFiles); } }, existingProject() { this.existingProject = this.jhipsterConfig.baseName !== undefined && this.config.existed; }, checkOptions() { if (!this.skipChecks && !this.inline && !this.jdlFiles?.length) { throw new Error('At least one jdl file is required.'); } }, }); } get [BaseGenerator.INITIALIZING]() { return this.delegateTasksToBlueprint(() => this.initializing); } get configuring() { return this.asConfiguringTaskGroup({ async downloadJdlFiles() { if (this.jdlFiles) { this.jdlFiles = await Promise.all(this.jdlFiles.map(toJdlFile).map(async (filename) => { try { this.readDestination(filename); } catch { this.log.warn(`File not found: ${filename}. Attempting download from jdl-samples repository`); const downloadedFile = await downloadJdlFile(filename, { skipSampleRepository: this.skipSampleRepository }); // The file has null content at mem-fs, update with actual content. this.writeDestination(downloadedFile, (await readFile(downloadedFile)).toString()); return downloadedFile; } return filename; })); } }, readJdlFiles() { if (this.inline) { this.jdlContents.push(this.inline); } for (const jdlFile of this.jdlFiles ?? []) { this.jdlContents.push(this.readDestination(jdlFile)?.toString() ?? ''); } }, async parseJDL() { const importer = createImporterFromContent(this.jdlContents.join('\n'), { applicationName: this.options.baseName ?? (this.existingProject ? this.jhipsterConfig.baseName : undefined), applicationType: this.options.applicationType ?? (this.existingProject ? this.jhipsterConfig.applicationType : undefined), }, this.options.jdlDefinition); const importState = importer.import(); this.exportedDeployments = importState.exportedDeployments; this.exportedEntities = importState.exportedEntities; this.exportedApplicationsWithEntities = importState.exportedApplicationsWithEntities; const applicationsWithEntities = Object.values(importState.exportedApplicationsWithEntities); this.applications = applicationsWithEntities.length === 1 ? applicationsWithEntities : [ ...applicationsWithEntities.filter((app) => app.config.applicationType === 'gateway'), ...applicationsWithEntities.filter((app) => app.config.applicationType !== 'gateway'), ]; }, configure() { const nrApplications = this.applications.length; const allNew = allNewApplications(this.applications); const interactiveFallback = !allNew; this.interactive = this.interactive ?? interactiveFallback; this.force = (this.options.force ?? (nrApplications > 0 && allNew)) ? true : undefined; this.reproducible = allNew; }, customizeApplication() { for (const app of this.applications) { app.config.entities = app.entities.map(entity => entity.name); } if (this.applications.length > 1) { for (const app of this.applications) { app.folder = app.config.baseName; if (!this.interactive && !this.jsonOnly && !this.ignoreApplication) { app.sharedFs = createMemFs(); } } addApplicationIndex(this.applications); } customizeForMicroservices(this.exportedApplicationsWithEntities); }, async generateJson() { if (this.applications.length === 0) { this.writeConfig({ entities: this.exportedEntities }); await this.env.sharedFs.pipeline({ refresh: true }, updateApplicationEntitiesTransform({ destinationPath: this.destinationPath(), throwOnMissingConfig: false })); } else { this.writeConfig(...this.applications.map(app => (this.ignoreApplication ? { ...app, config: undefined } : app))); } }, async generate() { if (this.jsonOnly) { return; } const generatorOptions = { defaults: true, reproducible: this.options.reproducible ?? this.reproducible, force: this.force }; if (this.ignoreApplication !== false && (this.ignoreApplication || this.applications.length === 0)) { if (this.applications.length === 0) { const entities = this.exportedEntities; await this.composeWithJHipster(this.entitiesGenerator, { generatorArgs: entities.map(entity => entity.name), generatorOptions, }); } else { for (const app of this.applications) { await this.composeWithJHipster(this.entitiesGenerator, { generatorArgs: app.entities.map(entity => entity.name), generatorOptions: { ...generatorOptions, destinationRoot: app.folder ? this.destinationPath(app.folder) : undefined, }, }); } } } else if (this.applications.length > 1) { this.log.info(`Generating ${this.applications.length} applications`); await this.composeWithJHipster(this.workspacesGenerator, { generatorOptions: { /** TODO types contains appsFolders which is not correctly handled, {@see file:../workspaces/command.ts} */ workspacesFolders: this.applications.map(app => app.folder), generateApplications: async () => this.runNonInteractive(this.applications, generatorOptions), }, }); } else { this.log.info('Generating 1 application'); await this.composeWithJHipster(this.entrypointGenerator, { generatorOptions }); } }, }); } get [BaseGenerator.CONFIGURING]() { return this.delegateTasksToBlueprint(() => this.configuring); } get end() { return this.asEndTaskGroup({ async generateDeployments() { if (!this.exportedDeployments || this.exportedDeployments.length === 0) { this.log.info('No deployment configured'); return; } if (this.ignoreDeployments) { this.log.info(`Ignoring ${this.exportedDeployments.length} deployments`); return; } this.log.info(`Generating ${this.exportedDeployments.length} deployments`); for (const deployment of this.exportedDeployments) { const deploymentConfig = deployment[GENERATOR_JHIPSTER]; const deploymentType = deploymentConfig.deploymentType; this.log.debug(`Generating deployment: ${JSON.stringify(deploymentConfig, null, 2)}`); await this.composeWithJHipster(deploymentType, { generatorOptions: { destinationRoot: this.destinationPath(deploymentType), force: true, }, }); } }, }); } get [BaseGenerator.END]() { return this.delegateTasksToBlueprint(() => this.end); } async runNonInteractive(applications, options) { await Promise.all(applications.map(async (application) => { const rootCwd = this.destinationPath(); const cwd = application.folder ? this.destinationPath(application.folder) : rootCwd; const adapter = this.env.adapter.newAdapter(); const envOptions = { cwd, logCwd: rootCwd, sharedFs: application.sharedFs, adapter }; const generatorOptions = { ...this.options, ...options, skipPriorities: ['prompting'] }; // Install should happen at the root of the monorepository. Force skip install at childs. if (this.options.monorepository) { generatorOptions.skipInstall = true; } const envBuilder = await this.createEnvBuilder(envOptions); const env = envBuilder.getEnvironment(); await env.run([this.entrypointGenerator], generatorOptions); })); } writeConfig(...applications) { for (const application of applications) { const { folder = '', entities = [], sharedFs } = application; let { config, namespaceConfigs } = application; const appPath = folder ? `${folder}/` : folder; const fs = sharedFs ? createMemFsEditor(sharedFs) : this.fs; if (config) { const configFile = this.destinationPath(`${appPath}.yo-rc.json`); const oldConfig = fs.readJSON(configFile, {}); if (Array.isArray(config.blueprints)) { config = { ...config, blueprints: config.blueprints.map(({ name, ...remaining }) => ({ ...remaining, name: normalizeBlueprintName(name) })), }; } if (namespaceConfigs) { namespaceConfigs = Object.fromEntries(Object.entries(namespaceConfigs).map(([ns, config]) => [normalizeBlueprintName(ns), config])); } fs.writeJSON(configFile, mergeYoRcContent(oldConfig, { ...namespaceConfigs, [GENERATOR_JHIPSTER]: config })); } for (const entity of entities) { const configFile = this.destinationPath(`${appPath}${JHIPSTER_CONFIG_DIR}/${upperFirst(entity.name)}.json`); const oldConfig = fs.readJSON(configFile, {}); fs.writeJSON(configFile, { ...oldConfig, ...entity }); } } } }