generator-pyhipster
Version:
Python (Flask) + Angular/React/Vue in one handy generator
1,341 lines (1,237 loc) • 128 kB
JavaScript
/**
* 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