UNPKG

generator-pyhipster

Version:

Python (Flask) + Angular/React/Vue in one handy generator

615 lines (558 loc) 19.6 kB
/** * Copyright 2013-2022 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. */ const chalk = require('chalk'); const fs = require('fs'); const path = require('path'); const semver = require('semver'); const packagejs = require('../package.json'); const { packageNameToNamespace } = require('./utils'); const BaseGenerator = require('./generator-base'); const { mergeBlueprints, parseBluePrints, loadBlueprintsFromConfiguration, normalizeBlueprintName } = require('../utils/blueprint'); /** * Basic task definition * * @async * @function YeomanTask * @param {...string} cliArgs - Arguments passed to cli. * @return {Promise<any>}. */ /** * Base class for a generator that can be extended through a blueprint. * * @class * @property {import('yeoman-generator/lib/util/storage')} blueprintStorage - Storage for blueprint config (Blueprints only). * @property {object} blueprintConfig - Proxy object for blueprintStorage (Blueprints only). * @property {import('yeoman-generator')} jhipsterContext - JHipster parent generator (Blueprints only). */ module.exports = class JHipsterBaseBlueprintGenerator extends BaseGenerator { constructor(args, options, features) { super(args, options, features); if (this.options.help) { return; } // Add base template folder. this.jhipsterTemplatesFolders = [this.templatePath()]; this.fromBlueprint = this.rootGeneratorName() !== 'generator-jhipster'; if (this.fromBlueprint) { this.blueprintStorage = this._getStorage({ sorted: true }); this.blueprintConfig = this.blueprintStorage.createProxy(); // jhipsterContext is the original generator this.jhipsterContext = this.options.jhipsterContext; try { // Fallback to the original generator if the file does not exists in the blueprint. this.jhipsterTemplatesFolders.push(this.jhipsterTemplatePath()); } catch (error) { this.warning('Error adding current blueprint templates as alternative for JHipster templates.'); this.log(error); } } } /** * Priority API stub for blueprints. * * Initializing priority is used to show logo and tasks related to preparing for prompts, like loading constants. * * @returns {Object.<string, YeomanTask>} generator tasks */ get initializing() { return this._initializing(); } /** * Public API method used by the getter and also by Blueprints * @returns {Object} tasks */ _initializing() { return {}; } /** * Priority API stub for blueprints. * * Prompting priority is used to prompt users for configuration values. * * @returns {Object.<string, YeomanTask>} generator tasks */ get prompting() { return this._prompting(); } /** * Public API method used by the getter and also by Blueprints * @returns {Object} tasks */ _prompting() { return {}; } /** * Priority API stub for blueprints. * * Configuring priority is used to customize and validate the configuration. * * @returns {Object.<string, YeomanTask>} generator tasks */ get configuring() { return this._configuring(); } /** * Public API method used by the getter and also by Blueprints * @returns {Object} tasks */ _configuring() { return {}; } /** * Configuring entities task argument * * @typedef {object} ConfiguringEntityArgument * @property {string} entityName - Entity name. * @property {import('yeoman-generator/lib/util/storage')} entityStorage - Storage for entity. * @property {object} entityConfig - Proxy object for entityStorage. */ /** * Configuring entities task definition * * @async * @function ConfiguringEntityTask * @param {ConfiguringEntityArgument} taskData * @return {Promise<any>}. */ /** * Priority API stub for blueprints. * * Configuring each entity priority is used to customize and validate the entity configuration. * * @returns {Object.<string, ConfiguringEntityTask>} taskGroup */ get configuringEachEntity() { return {}; } /** * Priority API stub for blueprints. * * Composing should be used to compose with others generators. * * @returns {Object.<string, requestCallback>} taskGroup */ get composing() { return this._composing(); } /** * Public API method used by the getter and also by Blueprints * @returns {Object} tasks */ _composing() { return {}; } /** * Priority API stub for blueprints. * * Loading should be used to load application configuration from jhipster configuration. * Before this priority the configuration should be considered dirty, while each generator configures itself at configuring priority, another generator composed at composing priority can still change it. * * @returns {Object.<string, YeomanTask>} taskGroup */ get loading() { return this._loading(); } /** * Public API method used by the getter and also by Blueprints * @returns {Object} tasks */ _loading() { return {}; } /** * Priority API stub for blueprints. * * Preparing should be used to generate derived properties. * * @returns {Object.<string, YeomanTask>} taskGroup */ get preparing() { return this._preparing(); } /** * Public API method used by the getter and also by Blueprints * @returns {Object} tasks */ _preparing() { return {}; } /** * Public API method used by the getter and also by Blueprints * @returns {Object} tasks */ _preparingFields() { return {}; } /** * Public API method used by the getter and also by Blueprints * @returns {Object} tasks */ _preparingRelationships() { return {}; } /** * @private * @deprecated * Execute custom priorities if they are not declared * Should be used by jhipster official generators only. * @returns {Object} tasks */ _missingPreDefault() { let tasks = {}; if (this.sbsBlueprint) return tasks; if (this._isPriorityMissing('composing', 'default')) { tasks = { ...tasks, ...this._composing() }; } if (this._isPriorityMissing('loading', 'default')) { tasks = { ...tasks, ...this._loading() }; } if (this._isPriorityMissing('preparing', 'default')) { tasks = { ...tasks, ...this._preparing() }; } if (this._isPriorityMissing('preparingFields', 'default')) { tasks = { ...tasks, ...this._preparingFields() }; } if (this._isPriorityMissing('preparingRelationships', 'default')) { tasks = { ...tasks, ...this._preparingRelationships() }; } return tasks; } /** * Priority API stub for blueprints. * * Default priority should used as misc customizations. * * @returns {Object.<string, YeomanTask>} generator tasks */ get default() { return this._default(); } /** * Public API method used by the getter and also by Blueprints * @returns {Object} tasks */ _default() { return {}; } /** * Priority API stub for blueprints. * * Writing priority should used to write files. * * @returns {Object.<string, YeomanTask>} generator tasks */ get writing() { return this._writing(); } /** * Public API method used by the getter and also by Blueprints * @returns {Object} tasks */ _writing() { return {}; } /** * @private * @deprecated * Execute custom priorities if they are not declared * Should be used by jhipster official generators only. * @returns {Object} tasks */ _missingPostWriting() { let tasks = {}; if (this.sbsBlueprint) return tasks; if (this._isPriorityMissing('postWriting', 'writing')) { tasks = { ...tasks, ...this._postWriting() }; } return tasks; } /** * Priority API stub for blueprints. * * PostWriting priority should used to customize files. * * @returns {Object.<string, YeomanTask>} generator tasks */ get postWriting() { return this._postWriting(); } /** * Public API method used by the getter and also by Blueprints */ _postWriting() { return {}; } /** * Priority API stub for blueprints. * * Install priority should used to prepare the project. * * @returns {Object.<string, YeomanTask>} generator tasks */ get install() { return this._install(); } /** * Public API method used by the getter and also by Blueprints * @returns {Object} tasks */ _install() { return {}; } /** * Priority API stub for blueprints. * * End priority should used to say good bye and print instructions. * * @returns {Object.<string, YeomanTask>} generator tasks */ get end() { return this._end(); } /** * Public API method used by the getter and also by Blueprints * @returns {Object} tasks */ _end() { return {}; } /** * @private * @deprecated * Detect if a priority is implemented in the super class but missing in current one. * That indicates the blueprint was not updated with the custom priorities. * @param {string} priorityName - Priority to be checked. * @param {sring} destPriority - Priority that the task is related to for logging purpose. * @return {boolean} true if the priority is missing. */ _isPriorityMissing(priorityName, destPriority = 'related') { const ownPrototype = Object.getPrototypeOf(this); const parentPrototype = Object.getPrototypeOf(ownPrototype); if ( parentPrototype !== JHipsterBaseBlueprintGenerator.prototype && !Object.getOwnPropertyDescriptor(ownPrototype, priorityName) && Object.getOwnPropertyDescriptor(parentPrototype, priorityName) ) { this.warning(`Priority ${priorityName} is missing for generator ${this.options.namespace}. Merging into ${destPriority} priority.`); return true; } return false; } /** * @private * Composes with blueprint generators, if any. * @param {String} subGen - sub generator * @param {Object} extraOptions - extra options to pass to blueprint generator */ async composeWithBlueprints(subGen, extraOptions) { this.delegateToBlueprint = false; if (!this.configOptions.blueprintConfigured) { this.configOptions.blueprintConfigured = true; this._configureBlueprints(); } const blueprints = this.jhipsterConfig.blueprints || []; for (const blueprint of blueprints) { const blueprintGenerator = await this._composeBlueprint(blueprint.name, subGen, extraOptions); if (blueprintGenerator) { if (blueprintGenerator.sbsBlueprint) { // If sbsBlueprint, add templatePath to the original generator templatesFolder. this.jhipsterTemplatesFolders.unshift(blueprintGenerator.templatePath()); } else { // If the blueprints does not sets sbsBlueprint property, ignore normal workflow. this.delegateToBlueprint = true; } } } } /** * @private * Configure blueprints. */ _configureBlueprints() { let argvBlueprints = this.options.blueprints || ''; // check for old single blueprint declaration const blueprint = this.options.blueprint; if (blueprint) { this.warning('--blueprint option is deprecated. Please use --blueprints instead'); if (!argvBlueprints.split(',').includes(blueprint)) { argvBlueprints = `${blueprint},${argvBlueprints}`; } } const blueprints = mergeBlueprints(parseBluePrints(argvBlueprints), loadBlueprintsFromConfiguration(this.config)); // EnvironmentBuilder already looks for blueprint when running from cli, this is required for tests. // Can be removed once the tests uses EnvironmentBuilder. const missingBlueprints = blueprints .filter(blueprint => !this.env.isPackageRegistered(packageNameToNamespace(blueprint.name))) .map(blueprint => blueprint.name); if (missingBlueprints.length > 0) { this.env.lookup({ filterPaths: true, packagePatterns: missingBlueprints }); } // OtherModules is used to create package.json dependencies. let otherModules = this.jhipsterConfig.otherModules || []; if (blueprints && blueprints.length > 0) { blueprints.forEach(blueprint => { blueprint.version = this._findBlueprintVersion(blueprint.name) || blueprint.version; }); // Remove potential previous value to avoid duplicates otherModules = otherModules.filter(module => blueprints.findIndex(blueprint => blueprint.name === module.name) === -1); otherModules.push(...blueprints); } if (blueprints.length > 0) { this.jhipsterConfig.blueprints = blueprints; } if (otherModules.length > 0) { this.jhipsterConfig.otherModules = otherModules; } if (!this.options.skipChecks) { const namespaces = blueprints.map(blueprint => packageNameToNamespace(blueprint.name)); // Verify if the blueprints hava been registered. const missing = namespaces.filter(namespace => !this.env.isPackageRegistered(namespace)); if (missing && missing.length > 0) { this.error(`Some blueprints were not found ${missing}, you should install them manually`); } } } /** * @private * Compose external blueprint module * @param {string} blueprint - name of the blueprint * @param {string} subGen - sub generator * @param {any} options - options to pass to blueprint generator * @return {Generator|undefined} */ async _composeBlueprint(blueprint, subGen, extraOptions = {}) { blueprint = normalizeBlueprintName(blueprint); if (!this.configOptions.skipChecks && !this.options.skipChecks) { this._checkBlueprint(blueprint); } const generatorName = packageNameToNamespace(blueprint); const generatorNamespace = `${generatorName}:${subGen}`; if (!this.env.isPackageRegistered(generatorName)) { await this.env.lookup({ filterPaths: true, packagePatterns: blueprint }); } if (!(await this.env.get(generatorNamespace))) { this.debug( `No blueprint found for blueprint ${chalk.yellow(blueprint)} and ${chalk.yellow(subGen)} with namespace ${chalk.yellow( generatorNamespace )} subgenerator: falling back to default generator` ); return undefined; } this.debug(`Found blueprint ${chalk.yellow(blueprint)} and ${chalk.yellow(subGen)} with namespace ${chalk.yellow(generatorNamespace)}`); const finalOptions = { ...this.options, configOptions: this.configOptions, ...extraOptions, jhipsterContext: this, }; const blueprintGenerator = await this.composeWith(generatorNamespace, finalOptions, true); if (blueprintGenerator instanceof Error) { throw blueprintGenerator; } this.info(`Using blueprint ${chalk.yellow(blueprint)} for ${chalk.yellow(subGen)} subgenerator`); return blueprintGenerator; } /** * @private * Try to retrieve the package.json of the blueprint used, as an object. * @param {string} blueprintPkgName - generator name * @return {object} packageJson - retrieved package.json as an object or undefined if not found */ _findBlueprintPackageJson(blueprintPkgName) { const blueprintGeneratorName = packageNameToNamespace(blueprintPkgName); const blueprintPackagePath = this.env.getPackagePath(blueprintGeneratorName); if (!blueprintPackagePath) { this.warning(`Could not retrieve packagePath of blueprint '${blueprintPkgName}'`); return undefined; } return JSON.parse(fs.readFileSync(path.join(blueprintPackagePath, 'package.json'))); } /** * @private * Try to retrieve the version of the blueprint used. * @param {string} blueprintPkgName - generator name * @return {string} version - retrieved version or empty string if not found */ _findBlueprintVersion(blueprintPkgName) { const blueprintPackageJson = this._findBlueprintPackageJson(blueprintPkgName); if (!blueprintPackageJson || !blueprintPackageJson.version) { this.warning(`Could not retrieve version of blueprint '${blueprintPkgName}'`); return undefined; } return blueprintPackageJson.version; } /** * @private * Check if the generator specified as blueprint is installed. * @param {string} blueprint - generator name */ _checkBlueprint(blueprint) { if (blueprint === 'generator-jhipster') { this.error(`You cannot use ${chalk.yellow(blueprint)} as the blueprint.`); } if (!this._findBlueprintPackageJson(blueprint)) { this.error( `The ${chalk.yellow(blueprint)} blueprint provided is not installed. Please install it using command ${chalk.yellow( `npm i -g ${blueprint}` )}.` ); } } /** * @private * Check if the generator specified as blueprint has a version compatible with current JHipster. * @param {string} blueprintPkgName - generator name */ _checkJHipsterBlueprintVersion(blueprintPkgName) { const blueprintPackageJson = this._findBlueprintPackageJson(blueprintPkgName); if (!blueprintPackageJson) { this.warning(`Could not retrieve version of JHipster declared by blueprint '${blueprintPkgName}'`); return; } const mainGeneratorJhipsterVersion = packagejs.version; const blueprintJhipsterVersion = blueprintPackageJson.dependencies && blueprintPackageJson.dependencies['generator-jhipster']; if (blueprintJhipsterVersion && !semver.valid(blueprintJhipsterVersion) && !semver.validRange(blueprintJhipsterVersion)) { this.info(`Blueprint ${blueprintPkgName} contains generator-jhipster dependency with non comparable version`); return; } if (blueprintJhipsterVersion) { if (mainGeneratorJhipsterVersion !== blueprintJhipsterVersion) { this.error( `The installed ${chalk.yellow( blueprintPkgName )} blueprint targets JHipster v${blueprintJhipsterVersion} and is not compatible with this JHipster version. Either update the blueprint or JHipster. You can also disable this check using --skip-checks at your own risk` ); } return; } const blueprintPeerJhipsterVersion = blueprintPackageJson.peerDependencies && blueprintPackageJson.peerDependencies['generator-jhipster']; if (blueprintPeerJhipsterVersion) { if (semver.satisfies(mainGeneratorJhipsterVersion, blueprintPeerJhipsterVersion)) { return; } this.error( `The installed ${chalk.yellow( blueprintPkgName )} blueprint targets JHipster ${blueprintPeerJhipsterVersion} and is not compatible with this JHipster version. Either update the blueprint or JHipster. You can also disable this check using --skip-checks at your own risk` ); } this.warning(`Could not retrieve version of JHipster declared by blueprint '${blueprintPkgName}'`); } };