UNPKG

generator-pyhipster

Version:

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

1,341 lines (1,237 loc) 128 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 assert = require('assert'); const path = require('path'); const _ = require('lodash'); const { kebabCase } = require('lodash'); const chalk = require('chalk'); const fs = require('fs'); const shelljs = require('shelljs'); const semver = require('semver'); const exec = require('child_process').exec; const os = require('os'); const normalize = require('normalize-path'); const simpleGit = require('simple-git'); const SharedData = require('../lib/support/shared-data.cjs'); const packagejs = require('../package.json'); const jhipsterUtils = require('./utils'); const constants = require('./generator-constants'); const PrivateBase = require('./generator-base-private'); const NeedleApi = require('./needle-api'); const { defaultConfig, defaultConfigMicroservice } = require('./generator-defaults'); const { commonOptions } = require('./options'); const { detectLanguage } = require('../utils/language'); const { formatDateForChangelog } = require('../utils/liquibase'); const { calculateDbNameWithLimit, hibernateSnakeCase } = require('../utils/db'); const defaultApplicationOptions = require('../jdl/jhipster/default-application-options'); const databaseTypes = require('../jdl/jhipster/database-types'); const { ANGULAR_X: ANGULAR, REACT, VUE, NO: CLIENT_FRAMEWORK_NO } = require('../jdl/jhipster/client-framework-types'); const { PRIORITY_NAMES: { LOADING, PREPARING, CONFIGURING_EACH_ENTITY, LOADING_EACH_ENTITY, PREPARING_EACH_ENTITY, PREPARING_FIELDS, PREPARING_EACH_ENTITY_FIELD, PREPARING_RELATIONSHIPS, PREPARING_EACH_ENTITY_RELATIONSHIP, POST_PREPARING_EACH_ENTITY, DEFAULT, WRITING, WRITING_ENTITIES, POST_WRITING, POST_WRITING_ENTITIES, PRE_CONFLICTS, INSTALL, END, }, } = require('../lib/constants/priorities.cjs'); const JHIPSTER_CONFIG_DIR = constants.JHIPSTER_CONFIG_DIR; const MODULES_HOOK_FILE = `${JHIPSTER_CONFIG_DIR}/modules/jhi-hooks.json`; const GENERATOR_JHIPSTER = 'generator-pyhipster'; const SERVER_MAIN_RES_DIR = constants.SERVER_MAIN_RES_DIR; const { ORACLE, MYSQL, POSTGRESQL, COCKROACHDB, MARIADB, MSSQL, SQL, MONGODB, COUCHBASE, NEO4J, CASSANDRA, H2_MEMORY, H2_DISK, SQLITE_DISK, SQLITE_MEMORY } = databaseTypes; const NO_DATABASE = databaseTypes.NO; const { GENERATOR_BOOTSTRAP } = require('./generator-list'); const { PROMETHEUS, ELK } = require('../jdl/jhipster/monitoring-types'); const { JWT, OAUTH2, SESSION } = require('../jdl/jhipster/authentication-types'); const { CAFFEINE, EHCACHE, REDIS, HAZELCAST, INFINISPAN, MEMCACHED, SIMPLE_CACHE, FILESYSTEM_CACHE } = require('../jdl/jhipster/cache-types'); const { GRADLE, MAVEN } = require('../jdl/jhipster/build-tool-types'); const { SPRING_WEBSOCKET } = require('../jdl/jhipster/websocket-types'); const { KAFKA } = require('../jdl/jhipster/message-broker-types'); const { CONSUL, EUREKA } = require('../jdl/jhipster/service-discovery-types'); const { GATLING, CUCUMBER, PROTRACTOR, CYPRESS } = require('../jdl/jhipster/test-framework-types'); const { GATEWAY, MICROSERVICE, MONOLITH } = require('../jdl/jhipster/application-types'); const { ELASTICSEARCH } = require('../jdl/jhipster/search-engine-types'); const { CUSTOM_PRIORITIES } = require('../lib/constants/priorities.cjs'); const cacheTypes = require('../jdl/jhipster/cache-types'); const serviceDiscoveryTypes = require('../jdl/jhipster/service-discovery-types'); const searchEngineTypes = require('../jdl/jhipster/search-engine-types'); const messageBrokerTypes = require('../jdl/jhipster/message-broker-types'); const websocketTypes = require('../jdl/jhipster/websocket-types'); const NO_CACHE = cacheTypes.NO; const NO_SERVICE_DISCOVERY = serviceDiscoveryTypes.NO; const NO_SEARCH_ENGINE = searchEngineTypes.FALSE; const NO_MESSAGE_BROKER = messageBrokerTypes.NO; const NO_WEBSOCKET = websocketTypes.FALSE; /** * This is the Generator base class. * This provides all the public API methods exposed via the module system. * The public API methods can be directly utilized as well using commonJS require. * * The method signatures in public API should not be changed without a major version change */ module.exports = class JHipsterBaseGenerator extends PrivateBase { constructor(args, options, features) { super(args, options, features); if (!this.features.jhipsterModular) { // This adds support for a `--from-cli` flag this.option('from-cli', { desc: 'Indicates the command is run from JHipster CLI', type: Boolean, hide: true, }); this.option('with-generated-flag', { desc: 'Add a GeneratedByJHipster annotation to all generated java classes and interfaces', type: Boolean, }); this.option('skip-prompts', { desc: 'Skip prompts', type: Boolean, }); this.option('skip-prettier', { desc: 'Skip prettier', type: Boolean, hide: true, }); } if (this.options.help) { return; } this.registerPriorities(CUSTOM_PRIORITIES); // JHipster runtime config that should not be stored to .yo-rc.json. this.configOptions = this.options.configOptions || { sharedEntities: {} }; this.configOptions.sharedEntities = this.configOptions.sharedEntities || {}; /* Force config to use 'generator-jhipster' namespace. */ this._config = this._getStorage('generator-jhipster', { sorted: true }); /* JHipster config using proxy mode used as a plain object instead of using get/set. */ this.jhipsterConfig = this.config.createProxy(); this.parseTestOptions(); if (this.configOptions.existingProject === undefined) { this.configOptions.existingProject = Boolean(this.jhipsterConfig.baseName); } // TODO v8 rename to existingProject. this.existingModularProject = this.configOptions.existingProject; /* Register generator for compose once */ this.registerComposedGenerator(this.options.namespace); if (this.options.namespace !== 'jhipster:bootstrap') { /* // eslint-disable-next-line global-require const boostrapGen = require('./bootstrap'); boostrapGen.namespace = 'jhipster:bootstrap'; const generator = this.env.instantiate(boostrapGen, { ...this.options, configOptions: this.configOptions }); if (this.env.queueGenerator) { this.env.queueGenerator(generator, true); } */ this.composeWithJHipster(GENERATOR_BOOTSTRAP, { ...this.options, configOptions: this.configOptions }, true); } } /** * Alternative templatePath that fetches from the blueprinted generator, instead of the blueprint. */ jhipsterTemplatePath(...args) { try { this._jhipsterGenerator = this._jhipsterGenerator || this.env.requireNamespace(this.options.namespace).generator; } catch (error) { throw new Error( `The Namespace ${this.options.namespace} may not be correct. Please check your configuration and ensure your blueprint folder start with "generator-". Detail: ${error}` ); } return this.fetchFromInstalledJHipster(this._jhipsterGenerator, 'templates', ...args); } /** * Get generator dependencies for building help * This is a stub and should be overwritten by the generator. * * @returns {string[]} */ getPossibleDependencies() { return []; } /** * Shared Data */ get sharedData() { if (!this._sharedData) { const { baseName } = this.jhipsterConfig; if (!baseName) { throw new Error('baseName is required'); } if (!this.options.sharedData[baseName]) { this.options.sharedData[baseName] = {}; } this._sharedData = new SharedData(this.options.sharedData[baseName]); } return this._sharedData; } /** * expose custom CLIENT_MAIN_SRC_DIR to templates and needles */ get CLIENT_MAIN_SRC_DIR() { this._CLIENT_MAIN_SRC_DIR = this._CLIENT_MAIN_SRC_DIR || this.applyOutputPathCustomizer(constants.CLIENT_MAIN_SRC_DIR) || constants.CLIENT_MAIN_SRC_DIR; return this._CLIENT_MAIN_SRC_DIR; } /** * expose custom CLIENT_MAIN_SRC_DIR to templates and needles */ get CLIENT_TEST_SRC_DIR() { this._CLIENT_TEST_SRC_DIR = this._CLIENT_TEST_SRC_DIR || this.applyOutputPathCustomizer(constants.CLIENT_TEST_SRC_DIR) || constants.CLIENT_TEST_SRC_DIR; return this._CLIENT_TEST_SRC_DIR; } /** * Verify if the entity is a built-in Entity. * @param {String} entityName - Entity name to verify. * @return {boolean} true if the entity is built-in. */ isBuiltInEntity(entityName) { return this.isBuiltInUser(entityName) || this.isBuiltInAuthority(entityName); } /** * Verify if the application is using built-in User. * @return {boolean} true if the User is built-in. */ isUsingBuiltInUser() { return ( !this.jhipsterConfig || (!this.jhipsterConfig.skipUserManagement && this.jhipsterConfig.databaseType !== NO_DATABASE) || (this.jhipsterConfig.authenticationType === OAUTH2 && this.jhipsterConfig.databaseType !== NO_DATABASE) ); } /** * Verify if the entity is a User entity. * @param {String} entityName - Entity name to verify. * @return {boolean} true if the entity is User. */ isUserEntity(entityName) { return _.upperFirst(entityName) === 'User'; } /** * Verify if the entity is a built-in User. * @param {String} entityName - Entity name to verify. * @return {boolean} true if the entity is User and is built-in. */ isBuiltInUser(entityName) { return this.isUsingBuiltInUser() && this.isUserEntity(entityName); } /** * Verify if the application is using built-in Authority. * @return {boolean} true if the Authority is built-in. */ isUsingBuiltInAuthority() { return ( !this.jhipsterConfig || (!this.jhipsterConfig.skipUserManagement && [SQL, MONGODB, COUCHBASE, NEO4J].includes(this.jhipsterConfig.databaseType)) || (this.jhipsterConfig.authenticationType === OAUTH2 && this.jhipsterConfig.databaseType !== NO_DATABASE) ); } /** * Verify if the entity is a Authority entity. * @param {String} entityName - Entity name to verify. * @return {boolean} true if the entity is Authority. */ isAuthorityEntity(entityName) { return _.upperFirst(entityName) === 'Authority'; } /** * Verify if the entity is a built-in Authority. * @param {String} entityName - Entity name to verify. * @return {boolean} true if the entity is Authority and is built-in. */ isBuiltInAuthority(entityName) { return this.isUsingBuiltInAuthority() && this.isAuthorityEntity(entityName); } /** * Apply output customizer. * * @param {string} outputPath - Path to customize. */ applyOutputPathCustomizer(outputPath) { let outputPathCustomizer = this.options.outputPathCustomizer; if (!outputPathCustomizer && this.configOptions) { outputPathCustomizer = this.configOptions.outputPathCustomizer; } if (!outputPathCustomizer) { return outputPath; } outputPath = outputPath ? normalize(outputPath) : outputPath; if (Array.isArray(outputPathCustomizer)) { outputPathCustomizer.forEach(customizer => { outputPath = customizer.call(this, outputPath); }); return outputPath; } return outputPathCustomizer.call(this, outputPath); } getPrettierExtensions() { let prettierExtensions = 'md,json,yml,html'; if (!this.skipClient && !this.jhipsterConfig.skipClient) { prettierExtensions = `${prettierExtensions},cjs,mjs,js,ts,tsx,css,scss`; if (this.jhipsterConfig.clientFramework === VUE) { prettierExtensions = `${prettierExtensions},vue`; } } if (!this.skipServer && !this.jhipsterConfig.skipServer) { prettierExtensions = `${prettierExtensions},java`; } return prettierExtensions; } /** * Replace placeholders with versions from packageJsonSourceFile. * @param {string} keyToReplace - PlaceHolder name. * @param {string} packageJsonSourceFile - Package json filepath with actual versions. */ replacePackageJsonVersions(keyToReplace, packageJsonSourceFile) { const packageJsonSource = JSON.parse(fs.readFileSync(packageJsonSourceFile, 'utf-8')); const packageJsonTargetFile = this.destinationPath('package.json'); const packageJsonTarget = this.fs.readJSON(packageJsonTargetFile); const replace = section => { if (packageJsonTarget[section]) { Object.entries(packageJsonTarget[section]).forEach(([dependency, dependencyReference]) => { if (dependencyReference.startsWith(keyToReplace)) { const [keyToReplaceAtSource, sectionAtSource = section, dependencyAtSource = dependency] = dependencyReference.split('#'); if (keyToReplaceAtSource !== keyToReplace) return; if (!packageJsonSource[sectionAtSource] || !packageJsonSource[sectionAtSource][dependencyAtSource]) { throw new Error(`Error setting ${dependencyAtSource} version, not found at ${sectionAtSource}.${dependencyAtSource}`); } packageJsonTarget[section][dependency] = packageJsonSource[sectionAtSource][dependencyAtSource]; } }); } }; replace('dependencies'); replace('devDependencies'); this.fs.writeJSON(packageJsonTargetFile, packageJsonTarget); } /** * Add a new icon to icon imports. * * @param {string} iconName - The name of the Font Awesome icon. * @param {string} clientFramework - The name of the client framework */ addIcon(iconName, clientFramework) { if (clientFramework === ANGULAR) { this.needleApi.clientAngular.addIcon(iconName); } else if (clientFramework === REACT) { // React // TODO: } } /** * Add a new menu element, at the root of the menu. * * @param {string} routerName - The name of the Angular router that is added to the menu. * @param {string} iconName - The name of the Font Awesome icon that will be displayed. * @param {boolean} enableTranslation - If translations are enabled or not * @param {string} clientFramework - The name of the client framework * @param {string} translationKeyMenu - i18n key for entry in the menu */ addElementToMenu(routerName, iconName, enableTranslation, clientFramework, translationKeyMenu = _.camelCase(routerName)) { if (clientFramework === ANGULAR) { this.needleApi.clientAngular.addElementToMenu(routerName, iconName, enableTranslation, translationKeyMenu, this.jhiPrefix); } else if (clientFramework === REACT) { // React // TODO: } } /** * Add external resources to root file(index.html). * * @param {string} resources - Resources added to root file. * @param {string} comment - comment to add before resources content. */ addExternalResourcesToRoot(resources, comment) { this.needleApi.client.addExternalResourcesToRoot(resources, comment); } /** * Add a new menu element to the admin menu. * * @param {string} routerName - The name of the Angular router that is added to the admin menu. * @param {string} iconName - The name of the Font Awesome icon that will be displayed. * @param {boolean} enableTranslation - If translations are enabled or not * @param {string} clientFramework - The name of the client framework * @param {string} translationKeyMenu - i18n key for entry in the admin menu */ addElementToAdminMenu(routerName, iconName, enableTranslation, clientFramework, translationKeyMenu = _.camelCase(routerName)) { if (clientFramework === ANGULAR) { this.needleApi.clientAngular.addElementToAdminMenu(routerName, iconName, enableTranslation, translationKeyMenu, this.jhiPrefix); } else if (clientFramework === REACT) { // React // TODO: } } /** * Add a new entity in the "entities" menu. * * @param {string} routerName - The name of the Angular router (which by default is the name of the entity). * @param {boolean} enableTranslation - If translations are enabled or not * @param {string} clientFramework - The name of the client framework * @param {string} entityTranslationKeyMenu - i18n key for entity entry in menu * @param {string} entityTranslationValue - i18n value for entity entry in menu */ addEntityToMenu( routerName, enableTranslation, clientFramework = this.clientFramework, entityTranslationKeyMenu = _.camelCase(routerName), entityTranslationValue = _.startCase(routerName) ) { if (clientFramework === ANGULAR) { this.needleApi.clientAngular.addEntityToMenu( routerName, enableTranslation, entityTranslationKeyMenu, entityTranslationValue, this.jhiPrefix ); } else if (clientFramework === REACT) { this.needleApi.clientReact.addEntityToMenu(routerName, enableTranslation, entityTranslationKeyMenu, entityTranslationValue); } else if (clientFramework === VUE) { this.needleApi.clientVue.addEntityToMenu(routerName, enableTranslation, entityTranslationKeyMenu, entityTranslationValue); } } /** * Add a new entity in the TS modules file. * * @param {string} entityInstance - Entity Instance * @param {string} entityClass - Entity Class * @param {string} entityName - Entity Name * @param {string} entityFolderName - Entity Folder Name * @param {string} entityFileName - Entity File Name * @param {string} entityUrl - Entity router URL * @param {string} clientFramework - The name of the client framework * @param {string} microserviceName - Microservice Name * @param {boolean} readOnly - If the entity is read-only or not * @param {string} pageTitle - The translation key or the text for the page title in the browser */ addEntityToModule( entityInstance = this.entityInstance, entityClass = this.entityClass, entityName = this.entityAngularName, entityFolderName = this.entityFolderName, entityFileName = this.entityFileName, entityUrl = this.entityUrl, clientFramework = this.clientFramework, microserviceName = this.microserviceName, readOnly = this.readOnly, pageTitle = this.enableTranslation ? `${this.i18nKeyPrefix}.home.title` : this.entityClassPlural ) { if (clientFramework === ANGULAR) { this.needleApi.clientAngular.addEntityToModule(entityName, entityFolderName, entityFileName, entityUrl, microserviceName, pageTitle); } else if (clientFramework === REACT) { this.needleApi.clientReact.addEntityToModule(entityInstance, entityClass, entityName, entityFolderName, entityFileName); } else if (clientFramework === VUE) { this.needleApi.clientVue.addEntityToRouterImport(entityName, entityFileName, entityFolderName, readOnly); this.needleApi.clientVue.addEntityToRouter(entityInstance, entityName, entityFileName, readOnly); this.needleApi.clientVue.addEntityServiceToEntitiesComponentImport(entityName, entityClass, entityFileName, entityFolderName); this.needleApi.clientVue.addEntityServiceToEntitiesComponent(entityInstance, entityName); } } /** * Add a new admin in the TS modules file. * * @param {string} appName - Angular2 application name. * @param {string} adminAngularName - The name of the new admin item. * @param {string} adminFolderName - The name of the folder. * @param {string} adminFileName - The name of the file. * @param {boolean} enableTranslation - If translations are enabled or not. * @param {string} clientFramework - The name of the client framework. */ addAdminToModule(appName, adminAngularName, adminFolderName, adminFileName, enableTranslation, clientFramework) { this.needleApi.clientAngular.addToAdminModule( appName, adminAngularName, adminFolderName, adminFileName, enableTranslation, clientFramework ); } /** * Add a new lazy loaded module to admin routing file. * * @param {string} route - The route for the module. For example 'entity-audit'. * @param {string} modulePath - The path to the module file. For example './entity-audit/entity-audit.module'. * @param {string} moduleName - The name of the module. For example 'EntityAuditModule'. * @param {string} pageTitle - The translation key if i18n is enabled or the text if i18n is disabled for the page title in the browser. * For example 'entityAudit.home.title' for i18n enabled or 'Entity audit' for i18n disabled. * If undefined then application global page title is used in the browser title bar. */ addAdminRoute(route, modulePath, moduleName, pageTitle) { this.needleApi.clientAngular.addAdminRoute(route, modulePath, moduleName, pageTitle); } /** * Add a new element in the "global.json" translations. * * @param {string} key - Key for the menu entry * @param {string} value - Default translated value * @param {string} language - The language to which this translation should be added */ addElementTranslationKey(key, value, language) { this.needleApi.clientI18n.addElementTranslationKey(key, value, language); } /** * Add a new element in the admin section of "global.json" translations. * * @param {string} key - Key for the menu entry * @param {string} value - Default translated value * @param {string} language - The language to which this translation should be added */ addAdminElementTranslationKey(key, value, language) { this.needleApi.clientI18n.addAdminElementTranslationKey(key, value, language); } /** * Add a new entity in the "global.json" translations. * * @param {string} key - Key for the entity name * @param {string} value - Default translated value * @param {string} language - The language to which this translation should be added */ addEntityTranslationKey(key, value, language) { this.needleApi.clientI18n.addEntityTranslationKey(key, value, language); } /** * Add a new entry as a root param in "global.json" translations. * * @param {string} key - Key for the entry * @param {string} value - Default translated value or object with multiple key and translated value * @param {string} language - The language to which this translation should be added */ addGlobalTranslationKey(key, value, language) { const fullPath = `${this.CLIENT_MAIN_SRC_DIR}i18n/${language}/global.json`; try { jhipsterUtils.rewriteJSONFile( fullPath, jsonObj => { jsonObj[key] = value; }, this ); } catch (e) { this.log( `${chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ')}(key: ${key}, value:${value})${chalk.yellow( ' not added to global translations.\n' )}` ); this.debug('Error:', e); } } /** * Add a translation key to all installed languages * * @param {string} key - Key for the entity name * @param {string} value - Default translated value * @param {string} method - The method to be run with provided key and value from above * @param {string} enableTranslation - specify if i18n is enabled */ addTranslationKeyToAllLanguages(key, value, method, enableTranslation) { if (enableTranslation) { this.getAllInstalledLanguages().forEach(language => { this[method](key, value, language); }); } } /** * get all the languages installed currently */ getAllInstalledLanguages() { const languages = []; this.getAllSupportedLanguages().forEach(language => { try { const stats = fs.lstatSync(`${this.CLIENT_MAIN_SRC_DIR}i18n/${language}`); if (stats.isDirectory()) { languages.push(language); } } catch (e) { this.debug('Error:', e); // An exception is thrown if the folder doesn't exist // do nothing as the language might not be installed } }); return languages; } /** * get all the languages supported by JHipster */ getAllSupportedLanguages() { return _.map(this.getAllSupportedLanguageOptions(), 'value'); } /** * check if a language is supported by JHipster * @param {string} language - Key for the language */ isSupportedLanguage(language) { return _.includes(this.getAllSupportedLanguages(), language); } /** * check if Right-to-Left support is necessary for i18n * @param {string[]} languages - languages array */ isI18nRTLSupportNecessary(languages) { if (!languages) { return false; } const rtlLanguages = this.getAllSupportedLanguageOptions().filter(langObj => langObj.rtl); return languages.some(lang => !!rtlLanguages.find(langObj => langObj.value === lang)); } /** * return the localeId from the given language key (from constants.LANGUAGES) * if no localeId is defined, return the language key (which is a localeId itself) * @param {string} language - language key */ getLocaleId(language) { const langObj = this.getAllSupportedLanguageOptions().find(langObj => langObj.value === language); return langObj.localeId || language; } /** * return the dayjsLocaleId from the given language key (from constants.LANGUAGES) * if no dayjsLocaleId is defined, return the language key (which is a localeId itself) * @param {string} language - language key */ getDayjsLocaleId(language) { const langObj = this.getAllSupportedLanguageOptions().find(langObj => langObj.value === language); return langObj.dayjsLocaleId || language; } /** * get all the languages options supported by JHipster */ getAllSupportedLanguageOptions() { return constants.LANGUAGES; } /** * Add a new dependency in the "package.json". * * @param {string} name - dependency name * @param {string} version - dependency version */ addNpmDependency(name, version) { const fullPath = 'package.json'; try { jhipsterUtils.rewriteJSONFile( fullPath, jsonObj => { if (jsonObj.dependencies === undefined) { jsonObj.dependencies = {}; } jsonObj.dependencies[name] = version; }, this ); } catch (e) { this.log( `${ chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') }npm dependency (name: ${name}, version:${version})${chalk.yellow(' not added.\n')}` ); this.debug('Error:', e); } } /** * Add a new devDependency in the "package.json". * * @param {string} name - devDependency name * @param {string} version - devDependency version */ addNpmDevDependency(name, version) { const fullPath = 'package.json'; try { jhipsterUtils.rewriteJSONFile( fullPath, jsonObj => { if (jsonObj.devDependencies === undefined) { jsonObj.devDependencies = {}; } jsonObj.devDependencies[name] = version; }, this ); } catch (e) { this.log( `${ chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') }npm devDependency (name: ${name}, version:${version})${chalk.yellow(' not added.\n')}` ); this.debug('Error:', e); } } /** * Add a new script in the "package.json". * * @param {string} name - script name * @param {string} data - script version */ addNpmScript(name, data) { const fullPath = 'package.json'; try { jhipsterUtils.rewriteJSONFile( fullPath, jsonObj => { if (jsonObj.scripts === undefined) { jsonObj.scripts = {}; } jsonObj.scripts[name] = data; }, this ); } catch (e) { this.log( `${ chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') }npm script (name: ${name}, data:${data})${chalk.yellow(' not added.\n')}` ); this.debug('Error:', e); } } /** * Add a new module in the TS modules file. * * @param {string} appName - Angular2 application name. * @param {string} angularName - The name of the new admin item. * @param {string} folderName - The name of the folder. * @param {string} fileName - The name of the file. * @param {boolean} enableTranslation - If translations are enabled or not. * @param {string} clientFramework - The name of the client framework. */ addAngularModule(appName, angularName, folderName, fileName, enableTranslation, clientFramework) { this.needleApi.clientAngular.addModule(appName, angularName, folderName, fileName, enableTranslation, clientFramework); } /** * Add a new entity to Ehcache, for the 2nd level cache of an entity and its relationships. * * @param {string} entityClass - the entity to cache * @param {array} relationships - the relationships of this entity * @param {string} packageName - the Java package name * @param {string} packageFolder - the Java package folder */ addEntityToEhcache(entityClass, relationships, packageName, packageFolder) { this.addEntityToCache(entityClass, relationships, packageName, packageFolder, EHCACHE); } /** * Add a new entry to Ehcache in CacheConfiguration.java * * @param {string} entry - the entry (including package name) to cache * @param {string} packageFolder - the Java package folder */ addEntryToEhcache(entry, packageFolder) { this.addEntryToCache(entry, packageFolder, EHCACHE); } /** * Add a new entity to the chosen cache provider, for the 2nd level cache of an entity and its relationships. * * @param {string} entityClass - the entity to cache * @param {array} relationships - the relationships of this entity * @param {string} packageName - the Java package name * @param {string} packageFolder - the Java package folder * @param {string} cacheProvider - the cache provider */ addEntityToCache(entityClass, relationships, packageName, packageFolder, cacheProvider) { this.needleApi.serverCache.addEntityToCache(entityClass, relationships, packageName, packageFolder, cacheProvider); } addEntitiesToAPIList(entityClass) { this.needleApi.serverEntities.addEntitiesToAPIList(entityClass); } /** * Add a new entry to the chosen cache provider in CacheConfiguration.java * * @param {string} entry - the entry (including package name) to cache * @param {string} packageFolder - the Java package folder * @param {string} cacheProvider - the cache provider */ addEntryToCache(entry, packageFolder, cacheProvider) { this.needleApi.serverCache.addEntryToCache(entry, packageFolder, cacheProvider); } /** * Add a new changelog to the Liquibase master.xml file. * * @param {string} changelogName - The name of the changelog (name of the file without .xml at the end). */ addChangelogToLiquibase(changelogName) { this.needleApi.serverLiquibase.addChangelog(changelogName); } /** * Add a incremental changelog to the Liquibase master.xml file. * * @param {string} changelogName - The name of the changelog (name of the file without .xml at the end). */ addIncrementalChangelogToLiquibase(changelogName) { this.needleApi.serverLiquibase.addIncrementalChangelog(changelogName); } /** * Add a new constraints changelog to the Liquibase master.xml file. * * @param {string} changelogName - The name of the changelog (name of the file without .xml at the end). */ addConstraintsChangelogToLiquibase(changelogName) { this.needleApi.serverLiquibase.addConstraintsChangelog(changelogName); } /** * Add a new changelog to the Liquibase master.xml file. * * @param {string} changelogName - The name of the changelog (name of the file without .xml at the end). * @param {string} needle - The needle at where it has to be added. */ addLiquibaseChangelogToMaster(changelogName, needle) { this.needleApi.serverLiquibase.addChangelogToMaster(changelogName, needle); } /** * Add a new column to a Liquibase changelog file for entity. * * @param {string} filePath - The full path of the changelog file. * @param {string} content - The content to be added as column, can have multiple columns as well */ addColumnToLiquibaseEntityChangeset(filePath, content) { this.needleApi.serverLiquibase.addColumnToEntityChangeset(filePath, content); } /** * Add a new load column to a Liquibase changelog file for entity. * * @param {string} filePath - The full path of the changelog file. * @param {string} content - The content to be added as column, can have multiple columns as well */ addLoadColumnToLiquibaseEntityChangeSet(filePath, content) { this.needleApi.serverLiquibase.addLoadColumnToEntityChangeSet(filePath, content); } /** * Add a new changeset to a Liquibase changelog file for entity. * * @param {string} filePath - The full path of the changelog file. * @param {string} content - The content to be added as changeset */ addChangesetToLiquibaseEntityChangelog(filePath, content) { this.needleApi.serverLiquibase.addChangesetToEntityChangelog(filePath, content); } /** * Add new scss style to the angular application in "global.scss * * @param {string} style - css to add in the file * @param {string} comment - comment to add before css code * * example: * * style = '.jhipster {\n color: #baa186;\n}' * comment = 'New JHipster color' * * * ========================================================================== * New JHipster color * ========================================================================== * * .jhipster { * color: #baa186; * } * */ addMainSCSSStyle(style, comment) { this.needleApi.clientAngular.addGlobalSCSSStyle(style, comment); } /** * Add new scss style to the angular application in "vendor.scss". * * @param {string} style - scss to add in the file * @param {string} comment - comment to add before css code * * example: * * style = '.success {\n @extend .message;\n border-color: green;\n}' * comment = 'Message' * * * ========================================================================== * Message * ========================================================================== * * .success { * @extend .message; * border-color: green; * } * */ addVendorSCSSStyle(style, comment) { this.needleApi.clientAngular.addVendorSCSSStyle(style, comment); } /** * Add new scss style to the react application in "app.scss". * * @param {string} style - css to add in the file * @param {string} comment - comment to add before css code * * example: * * style = '.jhipster {\n color: #baa186;\n}' * comment = 'New JHipster color' * * * ========================================================================== * New JHipster color * ========================================================================== * * .jhipster { * color: #baa186; * } * */ addAppSCSSStyle(style, comment) { this.needleApi.clientReact.addAppSCSSStyle(style, comment); } /** * Copy third-party library resources path. * * @param {string} sourceFolder - third-party library resources source path * @param {string} targetFolder - third-party library resources destination path */ copyExternalAssetsInWebpack(sourceFolder, targetFolder) { this.needleApi.clientWebpack.copyExternalAssets(sourceFolder, targetFolder); } /** * Add webpack config. * * @param {string} config - webpack config to be merged */ addWebpackConfig(config) { this.needleApi.clientWebpack.addWebpackConfig(config); } /** * Add a Maven dependency Management. * * @param {string} groupId - dependency groupId * @param {string} artifactId - dependency artifactId * @param {string} version - (optional) explicit dependency version number * @param {string} type - (optional) explicit type * @param {string} scope - (optional) explicit scope * @param {string} other - (optional) explicit other thing: exclusions... */ addMavenDependencyManagement(groupId, artifactId, version, type, scope, other) { this.needleApi.serverMaven.addDependencyManagement(groupId, artifactId, version, type, scope, other); } /** * Add a remote Maven Repository to the Maven build. * * @param {string} id - id of the repository * @param {string} url - url of the repository * @param {string} other - (optional) explicit other thing: name, releases, snapshots, ... */ addMavenRepository(id, url, other = '') { this.needleApi.serverMaven.addRepository(id, url, other); } /** * Add a remote Maven Plugin Repository to the Maven build. * * @param {string} id - id of the repository * @param {string} url - url of the repository */ addMavenPluginRepository(id, url) { this.needleApi.serverMaven.addPluginRepository(id, url); } /** * Add a distributionManagement to the Maven build. * * @param {string} snapshotsId Snapshots Repository Id * @param {string} snapshotsUrl Snapshots Repository Url * @param {string} releasesId Repository Id * @param {string} releasesUrl Repository Url */ addMavenDistributionManagement(snapshotsId, snapshotsUrl, releasesId, releasesUrl) { this.needleApi.serverMaven.addDistributionManagement(snapshotsId, snapshotsUrl, releasesId, releasesUrl); } /** * Add a new Maven property. * * @param {string} name - property name * @param {string} value - property value */ addMavenProperty(name, value) { this.needleApi.serverMaven.addProperty(name, value); } /** * Add a new Maven dependency. * * @param {string} groupId - dependency groupId * @param {string} artifactId - dependency artifactId * @param {string} version - (optional) explicit dependency version number * @param {string} other - (optional) explicit other thing: scope, exclusions... */ addMavenDependency(groupId, artifactId, version, other) { this.addMavenDependencyInDirectory('.', groupId, artifactId, version, other); } /** * Add a new Maven dependency in a specific folder.. * * @param {string} directory - the folder to add the dependency in * @param {string} groupId - dependency groupId * @param {string} artifactId - dependency artifactId * @param {string} version - (optional) explicit dependency version number * @param {string} other - (optional) explicit other thing: scope, exclusions... */ addMavenDependencyInDirectory(directory, groupId, artifactId, version, other) { this.needleApi.serverMaven.addDependencyInDirectory(directory, groupId, artifactId, version, other); } /** * Add a new Maven plugin. * * @param {string} groupId - plugin groupId * @param {string} artifactId - plugin artifactId * @param {string} version - explicit plugin version number * @param {string} other - explicit other thing: executions, configuration... */ addMavenPlugin(groupId, artifactId, version, other) { this.needleApi.serverMaven.addPlugin(groupId, artifactId, version, other); } /** * Add a new Maven plugin management. * * @param {string} groupId - plugin groupId * @param {string} artifactId - plugin artifactId * @param {string} version - explicit plugin version number * @param {string} other - explicit other thing: executions, configuration... */ addMavenPluginManagement(groupId, artifactId, version, other) { this.needleApi.serverMaven.addPluginManagement(groupId, artifactId, version, other); } /** * Add a new annotation processor path to Maven compiler configuration. * * @param {string} groupId - plugin groupId * @param {string} artifactId - plugin artifactId * @param {string} version - explicit plugin version number */ addMavenAnnotationProcessor(groupId, artifactId, version) { this.needleApi.serverMaven.addAnnotationProcessor(groupId, artifactId, version); } /** * Add a new Maven profile. * * @param {string} profileId - profile ID * @param {string} other - explicit other thing: build, dependencies... */ addMavenProfile(profileId, other) { this.needleApi.serverMaven.addProfile(profileId, other); } /** * A new Gradle property. * * @param {string} name - property name * @param {string} value - property value */ addGradleProperty(name, value) { this.needleApi.serverGradle.addProperty(name, value); } /** * A new Gradle plugin. * * @param {string} group - plugin GroupId * @param {string} name - plugin name * @param {string} version - explicit plugin version number */ addGradlePlugin(group, name, version) { this.needleApi.serverGradle.addPlugin(group, name, version); } /** * A new Gradle plugin to plugin management block in settings.gradle * * @param {string} id - plugin id * @param {string} version - explicit plugin version number */ addGradlePluginToPluginManagement(id, version) { this.needleApi.serverGradle.addPluginToPluginManagement(id, version); } /** * Add Gradle plugin to the plugins block * * @param {string} id - plugin id * @param {string} version - explicit plugin version number */ addGradlePluginToPluginsBlock(id, version) { this.needleApi.serverGradle.addPluginToPluginsBlock(id, version); } /** * A new dependency to build.gradle file. * * @param {string} scope - scope of the new dependency, e.g. compile * @param {string} group - maven GroupId * @param {string} name - maven ArtifactId * @param {string} version - (optional) explicit dependency version number */ addGradleDependency(scope, group, name, version) { this.addGradleDependencyInDirectory('.', scope, group, name, version); } /** * A new dependency to build.gradle file in a specific folder. * * @param {string} directory - directory * @param {string} scope - scope of the new dependency, e.g. compile * @param {string} group - maven GroupId * @param {string} name - maven ArtifactId * @param {string} version - (optional) explicit dependency version number */ addGradleDependencyInDirectory(directory, scope, group, name, version) { this.needleApi.serverGradle.addDependencyInDirectory(directory, scope, group, name, version); } /** * Apply from an external Gradle build script. * * @param {string} name - name of the file to apply from, must be 'fileName.gradle' */ applyFromGradleScript(name) { this.needleApi.serverGradle.applyFromScript(name); } /** * Add a logger to the logback-spring.xml * * @param {string} logName - name of the log we want to track * @param {string} level - tracking level */ addLoggerForLogbackSpring(logName, level) { this.needleApi.serverLog.addlog(logName, level); } /** * Add a remote Maven Repository to the Gradle build. * * @param {string} url - url of the repository * @param {string} username - (optional) username of the repository credentials * @param {string} password - (optional) password of the repository credentials */ addGradleMavenRepository(url, username, password) { this.needleApi.serverGradle.addMavenRepository(url, username, password); } /** * Add a remote Maven repository to the Gradle plugin management block in settings.gradle * * @param {string} url - url of the repository * @param {string} username - (optional) username of the repository credentials * @param {string} password - (optional) password of the repository credentials */ addGradlePluginManagementRepository(url, username, password) { this.needleApi.serverGradle.addPluginManagementRepository(url, username, password); } /** * Generate a date to be used by Liquibase changelogs. * * @param {Boolean} [reproducible=true] - Set true if the changelog date can be reproducible. * Set false to create a changelog date incrementing the last one. * @return {String} Changelog date. */ dateFormatForLiquibase(reproducible = this.configOptions.reproducible) { let now = new Date(); // Miliseconds is ignored for changelogDate. now.setMilliseconds(0); // Run reproducible timestamp when regenerating the project with with-entities option. if (reproducible || this.configOptions.creationTimestamp) { if (this.configOptions.reproducibleLiquibaseTimestamp) { // Counter already started. now = this.configOptions.reproducibleLiquibaseTimestamp; } else { // Create a new counter const creationTimestamp = this.configOptions.creationTimestamp || this.config.get('creationTimestamp'); now = creationTimestamp ? new Date(creationTimestamp) : now; now.setMilliseconds(0); } now.setMinutes(now.getMinutes() + 1); this.configOptions.reproducibleLiquibaseTimestamp = now; // Reproducible build can create future timestamp, save it. const lastLiquibaseTimestamp = this.config.get('lastLiquibaseTimestamp'); if (!lastLiquibaseTimestamp || now.getTime() > lastLiquibaseTimestamp) { this.config.set('lastLiquibaseTimestamp', now.getTime()); } } else { // Get and store lastLiquibaseTimestamp, a future timestamp can be used let lastLiquibaseTimestamp = this.config.get('lastLiquibaseTimestamp'); if (lastLiquibaseTimestamp) { lastLiquibaseTimestamp = new Date(lastLiquibaseTimestamp); if (lastLiquibaseTimestamp >= now) { now = lastLiquibaseTimestamp; now.setSeconds(now.getSeconds() + 1); now.setMilliseconds(0); } } this.config.set('lastLiquibaseTimestamp', now.getTime()); } return formatDateForChangelog(now); } /** * Copy templates with all the custom logic applied according to the type. * * @param {string} source - path of the source file to copy from * @param {string} dest - path of the destination file to copy to * @param {string} action - type of the action to be performed on the template file, i.e: stripHtml | stripJs | template | copy * @param {object} generator - context that can be used as the generator instance or data to process template * @param {object} opt - options that can be passed to template method * @param {boolean} template - flag to use template method instead of copy method */ copyTemplate(source, dest, action, generator, opt = {}, template) { const _this = generator || this; let regex; switch (action) { case 'stripHtml': regex = new RegExp( [ /([\s\n\r]+[a-z][a-zA-Z]*Translate="[a-zA-Z0-9 +{}'_!?.]+")/, // jhiTranslate /([\s\n\r]+\[translate(-v|V)alues\]="\{([a-zA-Z]|\d|:|\{|\}|\[|\]|-|'|\s|\.|_)*?\}")/, // translate-values or translateValues /([\s\n\r]+translate-compile)/, // translate-compile /([\s\n\r]+translate-value-max="[0-9{}()|]*")/, // translate-value-max ] .map(r => r.source) .join('|'), 'g' ); jhipsterUtils.copyWebResource(source, dest, regex, 'html', _this, opt, template); break; case 'stripJs': jhipsterUtils.copyWebResource(source, dest, null, 'js', _this, opt, template); break; case 'stripJsx': regex = new RegExp( [ /(import { ?Translate, ?translate ?} from 'react-jhipster';?)/, // Translate imports /(import { ?translate, ?Translate ?} from 'react-jhipster';?)/, // translate imports /( Translate,|, ?Translate|import { ?Translate ?} from 'react-jhipster';?)/, // Translate import /( translate,|, ?translate|import { ?translate ?} from 'react-jhipster';?)/, // translate import /<Translate(\s*)?((component="[a-z]+")(\s*)|(contentKey=("[a-zA-Z0-9.\-_]+"|\{.*\}))(\s*)|(interpolate=\{.*\})(\s*))*(\s*)\/?>|<\/Translate>/, // Translate component tag ] .map(r => r.source) .join('|'), 'g' ); jhipsterUtils.copyWebResource(source, dest, regex, 'jsx', _this, opt, template); break; case 'copy': _this.copy(source, dest); break; default: _this.template(source, dest, _this, opt); } } /** * Copy html templates after stripping translation keys when translation is disabled. * * @param {string} source - path of the source file to copy from * @param {string} dest - path of the destination file to copy to * @param {object} generator - context that can be used as the generator instance or data to process template * @param {object} opt - options that can be passed to template method * @param {boolean} template - flag to use template method instead of copy */ processHtml(source, dest, generator, opt, template) { this.copyTempla