generator-luna
Version:
generate specific EHR application,it's built beyond the jhipster
1,313 lines (1,228 loc) • 49 kB
JavaScript
/**
* Copyright 2013-2019 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
*
* http://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 path = require('path');
const _ = require('lodash');
const fs = require('fs');
const Generator = require('yeoman-generator');
const chalk = require('chalk');
const shelljs = require('shelljs');
const semver = require('semver');
const exec = require('child_process').exec;
const https = require('https');
const jhiCore = require('jhipster-core');
const filter = require('gulp-filter');
const packagejs = require('../package.json');
const jhipsterUtils = require('./utils');
const constants = require('./generator-constants');
const { prettierTransform, prettierOptions } = require('./generator-transforms');
const CLIENT_MAIN_SRC_DIR = constants.CLIENT_MAIN_SRC_DIR;
/**
* This is the Generator base private class.
* This provides all the private API methods used internally.
* These methods should not be directly utilized using commonJS require,
* as these can have breaking changes without a major version bump
*
* The method signatures in private API can be changed without a major version change.
*/
module.exports = class extends Generator {
constructor(args, opts) {
super(args, opts);
this.env.options.appPath = this.config.get('appPath') || CLIENT_MAIN_SRC_DIR;
// expose lodash to templates
this._ = _;
}
/* ======================================================================== */
/* private methods use within generator (not exposed to modules) */
/* ======================================================================== */
/**
* Install I18N Client Files By Language
*
* @param {any} _this reference to generator
* @param {string} webappDir web app directory
* @param {string} lang language code
*/
installI18nClientFilesByLanguage(_this, webappDir, lang) {
const generator = _this || this;
const prefix = this.fetchFromInstalledJHipster('languages/templates');
if (generator.databaseType !== 'no' && generator.databaseType !== 'cassandra') {
generator.copyI18nFilesByName(generator, webappDir, 'audits.json', lang);
}
if (generator.applicationType === 'gateway' && generator.serviceDiscoveryType) {
generator.copyI18nFilesByName(generator, webappDir, 'gateway.json', lang);
}
generator.copyI18nFilesByName(generator, webappDir, 'configuration.json', lang);
generator.copyI18nFilesByName(generator, webappDir, 'error.json', lang);
generator.copyI18nFilesByName(generator, webappDir, 'login.json', lang);
generator.copyI18nFilesByName(generator, webappDir, 'home.json', lang);
generator.copyI18nFilesByName(generator, webappDir, 'metrics.json', lang);
generator.copyI18nFilesByName(generator, webappDir, 'logs.json', lang);
generator.copyI18nFilesByName(generator, webappDir, 'password.json', lang);
generator.copyI18nFilesByName(generator, webappDir, 'register.json', lang);
generator.copyI18nFilesByName(generator, webappDir, 'sessions.json', lang);
generator.copyI18nFilesByName(generator, webappDir, 'settings.json', lang);
generator.copyI18nFilesByName(generator, webappDir, 'user-management.json', lang);
// tracker.json for Websocket
if (this.websocket === 'spring-websocket') {
generator.copyI18nFilesByName(generator, webappDir, 'tracker.json', lang);
}
// Templates
generator.template(`${prefix}/${webappDir}i18n/${lang}/activate.json.ejs`, `${webappDir}i18n/${lang}/activate.json`);
generator.template(`${prefix}/${webappDir}i18n/${lang}/global.json.ejs`, `${webappDir}i18n/${lang}/global.json`);
generator.template(`${prefix}/${webappDir}i18n/${lang}/health.json.ejs`, `${webappDir}i18n/${lang}/health.json`);
generator.template(`${prefix}/${webappDir}i18n/${lang}/reset.json.ejs`, `${webappDir}i18n/${lang}/reset.json`);
}
/**
* Install I18N Server Files By Language
*
* @param {any} _this - reference to generator
* @param {string} resourceDir - resource directory
* @param {string} lang - language code
*/
installI18nServerFilesByLanguage(_this, resourceDir, lang) {
const generator = _this || this;
const prefix = this.fetchFromInstalledJHipster('languages/templates');
// Template the message server side properties
const langProp = lang.replace(/-/g, '_');
generator.template(
`${prefix}/${resourceDir}i18n/messages_${langProp}.properties.ejs`,
`${resourceDir}i18n/messages_${langProp}.properties`
);
}
/**
* Copy I18N
*
* @param language
* @param prefix
*/
copyI18n(language, prefix = '') {
try {
const fileName = this.entityTranslationKey;
this.template(
`${prefix ? `${prefix}/` : ''}i18n/entity_${language}.json.ejs`,
`${CLIENT_MAIN_SRC_DIR}i18n/${language}/${fileName}.json`
);
this.addEntityTranslationKey(this.entityTranslationKeyMenu, this.entityClass, language);
} catch (e) {
this.debug('Error:', e);
// An exception is thrown if the folder doesn't exist
// do nothing
}
}
/**
* Copy Enum I18N
*
* @param language
* @param enumInfo
* @param prefix
*/
copyEnumI18n(language, enumInfo, prefix = '') {
try {
this.template(
`${prefix ? `${prefix}/` : ''}i18n/enum.json.ejs`,
`${CLIENT_MAIN_SRC_DIR}i18n/${language}/${enumInfo.clientRootFolder}${enumInfo.enumInstance}.json`,
this,
{},
enumInfo
);
} catch (e) {
this.debug('Error:', e);
// An exception is thrown if the folder doesn't exist
// do nothing
}
}
/**
* Update Languages In Language Constant
*
* @param languages
*/
updateLanguagesInLanguageConstant(languages) {
const fullPath = `${CLIENT_MAIN_SRC_DIR}app/components/language/language.constants.js`;
try {
let content = ".constant('LANGUAGES', [\n";
languages.forEach((language, i) => {
content += ` '${language}'${i !== languages.length - 1 ? ',' : ''}\n`;
});
content +=
' // jhipster-needle-i18n-language-constant - JHipster will add/remove languages in this array\n ]';
jhipsterUtils.replaceContent(
{
file: fullPath,
pattern: /\.constant.*LANGUAGES.*\[([^\]]*jhipster-needle-i18n-language-constant[^\]]*)\]/g,
content
},
this
);
} catch (e) {
this.log(
chalk.yellow('\nUnable to find ') +
fullPath +
chalk.yellow(' or missing required jhipster-needle. LANGUAGE constant not updated with languages: ') +
languages +
chalk.yellow(' since block was not found. Check if you have enabled translation support.\n')
);
this.debug('Error:', e);
}
}
/**
* Update Languages In Language Constant NG2
*
* @param languages
*/
updateLanguagesInLanguageConstantNG2(languages) {
if (this.clientFramework !== 'angularX') {
return;
}
const fullPath = `${CLIENT_MAIN_SRC_DIR}app/core/language/language.constants.ts`;
try {
let content = 'export const LANGUAGES: string[] = [\n';
languages.forEach((language, i) => {
content += ` '${language}'${i !== languages.length - 1 ? ',' : ''}\n`;
});
content += ' // jhipster-needle-i18n-language-constant - JHipster will add/remove languages in this array\n];';
jhipsterUtils.replaceContent(
{
file: fullPath,
pattern: /export.*LANGUAGES.*\[([^\]]*jhipster-needle-i18n-language-constant[^\]]*)\];/g,
content
},
this
);
} catch (e) {
this.log(
chalk.yellow('\nUnable to find ') +
fullPath +
chalk.yellow(' or missing required jhipster-needle. LANGUAGE constant not updated with languages: ') +
languages +
chalk.yellow(' since block was not found. Check if you have enabled translation support.\n')
);
this.debug('Error:', e);
}
}
/**
* Update Languages In Language Pipe
*
* @param languages
*/
updateLanguagesInLanguagePipe(languages) {
const fullPath =
this.clientFramework === 'angularX'
? `${CLIENT_MAIN_SRC_DIR}app/shared/language/find-language-from-key.pipe.ts`
: `${CLIENT_MAIN_SRC_DIR}/app/config/translation.ts`;
try {
let content = '{\n';
this.generateLanguageOptions(languages, this.clientFramework).forEach((ln, i) => {
content += ` ${ln}${i !== languages.length - 1 ? ',' : ''}\n`;
});
content += ' // jhipster-needle-i18n-language-key-pipe - JHipster will add/remove languages in this object\n };';
jhipsterUtils.replaceContent(
{
file: fullPath,
pattern: /{\s*('[a-z-]*':)?([^=]*jhipster-needle-i18n-language-key-pipe[^;]*)\};/g,
content
},
this
);
} catch (e) {
this.log(
chalk.yellow('\nUnable to find ') +
fullPath +
chalk.yellow(' or missing required jhipster-needle. Language pipe not updated with languages: ') +
languages +
chalk.yellow(' since block was not found. Check if you have enabled translation support.\n')
);
this.debug('Error:', e);
}
}
/**
* Update Languages In Webpack
*
* @param languages
*/
updateLanguagesInWebpack(languages) {
const fullPath = 'webpack/webpack.common.js';
try {
let content = 'groupBy: [\n';
languages.forEach((language, i) => {
content += ` { pattern: "./src/main/webapp/i18n/${language}/*.json", fileName: "./i18n/${language}.json" }${
i !== languages.length - 1 ? ',' : ''
}\n`;
});
content +=
' // jhipster-needle-i18n-language-webpack - JHipster will add/remove languages in this array\n' +
' ]';
jhipsterUtils.replaceContent(
{
file: fullPath,
pattern: /groupBy:.*\[([^\]]*jhipster-needle-i18n-language-webpack[^\]]*)\]/g,
content
},
this
);
} catch (e) {
this.log(
chalk.yellow('\nUnable to find ') +
fullPath +
chalk.yellow(' or missing required jhipster-needle. Webpack language task not updated with languages: ') +
languages +
chalk.yellow(' since block was not found. Check if you have enabled translation support.\n')
);
this.debug('Error:', e);
}
}
/**
* Update Moment Locales to keep in webpack prod build
*
* @param languages
*/
updateLanguagesInMomentWebpackNgx(languages) {
const fullPath = 'webpack/webpack.prod.js';
try {
let content = 'localesToKeep: [\n';
languages.forEach((language, i) => {
content += ` '${this.getMomentLocaleId(language)}'${i !== languages.length - 1 ? ',' : ''}\n`;
});
content +=
' // jhipster-needle-i18n-language-moment-webpack - JHipster will add/remove languages in this array\n' +
' ]';
jhipsterUtils.replaceContent(
{
file: fullPath,
pattern: /localesToKeep:.*\[([^\]]*jhipster-needle-i18n-language-moment-webpack[^\]]*)\]/g,
content
},
this
);
} catch (e) {
this.log(
chalk.yellow('\nUnable to find ') +
fullPath +
chalk.yellow(' or missing required jhipster-needle. Webpack language task not updated with languages: ') +
languages +
chalk.yellow(' since block was not found. Check if you have enabled translation support.\n')
);
this.debug('Error:', e);
}
}
/**
* Update Moment Locales to keep in webpack prod build
*
* @param languages
*/
updateLanguagesInMomentWebpackReact(languages) {
const fullPath = 'webpack/webpack.prod.js';
try {
let content = 'localesToKeep: [\n';
languages.forEach((language, i) => {
content += ` '${this.getMomentLocaleId(language)}'${i !== languages.length - 1 ? ',' : ''}\n`;
});
content +=
' // jhipster-needle-i18n-language-moment-webpack - JHipster will add/remove languages in this array\n ]';
jhipsterUtils.replaceContent(
{
file: fullPath,
pattern: /localesToKeep:.*\[([^\]]*jhipster-needle-i18n-language-moment-webpack[^\]]*)\]/g,
content
},
this
);
} catch (e) {
this.log(
chalk.yellow('\nUnable to find ') +
fullPath +
chalk.yellow(' or missing required jhipster-needle. Webpack language task not updated with languages: ') +
languages +
chalk.yellow(' since block was not found. Check if you have enabled translation support.\n')
);
this.debug('Error:', e);
}
}
/**
* Remove File
*
* @param file
*/
removeFile(file) {
if (shelljs.test('-f', file)) {
this.log(`Removing the file - ${file}`);
shelljs.rm(file);
}
}
/**
* Remove Folder
*
* @param folder
*/
removeFolder(folder) {
if (shelljs.test('-d', folder)) {
this.log(`Removing the folder - ${folder}`);
shelljs.rm('-rf', folder);
}
}
/**
* @returns default app name
*/
getDefaultAppName() {
return /^[a-zA-Z0-9_]+$/.test(path.basename(process.cwd())) ? path.basename(process.cwd()) : 'jhipster';
}
/**
* Format As Class Javadoc
*
* @param {string} text - text to format
* @returns class javadoc
*/
formatAsClassJavadoc(text) {
return jhipsterUtils.getJavadoc(text, 0);
}
/**
* Format As Field Javadoc
*
* @param {string} text - text to format
* @returns field javadoc
*/
formatAsFieldJavadoc(text) {
return jhipsterUtils.getJavadoc(text, 4);
}
/**
* Format As Api Description
*
* @param {string} text - text to format
* @returns formatted api description
*/
formatAsApiDescription(text) {
if (!text) {
return text;
}
const rows = text.split('\n');
let description = this.formatLineForJavaStringUse(rows[0]);
for (let i = 1; i < rows.length; i++) {
// discard empty rows
if (rows[i].trim() !== '') {
// if simple text then put space between row strings
if (!description.endsWith('>') && !rows[i].startsWith('<')) {
description += ' ';
}
description += this.formatLineForJavaStringUse(rows[i]);
}
}
return description;
}
formatLineForJavaStringUse(text) {
if (!text) {
return text;
}
return text.replace(/"/g, '\\"');
}
/**
* Format As Liquibase Remarks
*
* @param {string} text - text to format
* @returns formatted liquibase remarks
*/
formatAsLiquibaseRemarks(text) {
if (!text) {
return text;
}
const rows = text.split('\n');
let description = rows[0];
for (let i = 1; i < rows.length; i++) {
// discard empty rows
if (rows[i].trim() !== '') {
// if simple text then put space between row strings
if (!description.endsWith('>') && !rows[i].startsWith('<')) {
description += ' ';
}
description += rows[i];
}
}
// escape & to &
description = description.replace(/&/g, '&');
// escape " to "
description = description.replace(/"/g, '"');
// escape ' to '
description = description.replace(/'/g, ''');
// escape < to <
description = description.replace(/</g, '<');
// escape > to >
description = description.replace(/>/g, '>');
return description;
}
/**
* @param {any} input input
* @returns {boolean} true if input is number; false otherwise
*/
isNumber(input) {
if (isNaN(this.filterNumber(input))) {
// eslint-disable-line
return false;
}
return true;
}
/**
* @param {any} input input
* @returns {boolean} true if input is a signed number; false otherwise
*/
isSignedNumber(input) {
if (isNaN(this.filterNumber(input, true))) {
// eslint-disable-line
return false;
}
return true;
}
/**
* @param {any} input input
* @returns {boolean} true if input is a signed decimal number; false otherwise
*/
isSignedDecimalNumber(input) {
if (isNaN(this.filterNumber(input, true, true))) {
// eslint-disable-line
return false;
}
return true;
}
/**
* Filter Number
*
* @param {string} input - input to filter
* @param isSigned - flag indicating whether to check for signed number or not
* @param isDecimal - flag indicating whether to check for decimal number or not
* @returns {number} parsed number if valid input; <code>NaN</code> otherwise
*/
filterNumber(input, isSigned, isDecimal) {
const signed = isSigned ? '(\\-|\\+)?' : '';
const decimal = isDecimal ? '(\\.[0-9]+)?' : '';
const regex = new RegExp(`^${signed}([0-9]+${decimal})$`);
if (regex.test(input)) return Number(input);
return NaN;
}
/**
* Execute callback if git is installed
*
* @param {function} callback - function to be called if git is installed
*/
isGitInstalled(callback) {
this.gitExec('--version', { trace: false }, code => {
if (code !== 0) {
this.warning('git is not found on your computer.\n', ` Install git: ${chalk.yellow('https://git-scm.com/')}`);
}
if (callback) callback(code);
});
}
/**
* Get Option From Array
*
* @param {Array} array - array
* @param {any} option - options
* @returns {boolean} true if option is in array and is set to 'true'
*/
getOptionFromArray(array, option) {
let optionValue = false;
array.forEach(value => {
if (_.includes(value, option)) {
optionValue = value.split(':')[1];
}
});
optionValue = optionValue === 'true' ? true : optionValue;
return optionValue;
}
/**
* get hibernate SnakeCase in JHipster preferred style.
*
* @param {string} value - table column name or table name string
* @see org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy
* @returns hibernate SnakeCase in JHipster preferred style
*/
hibernateSnakeCase(value) {
let res = '';
if (value) {
value = value.replace('.', '_');
res = value[0];
for (let i = 1, len = value.length - 1; i < len; i++) {
if (
value[i - 1] !== value[i - 1].toUpperCase() &&
value[i] !== value[i].toLowerCase() &&
value[i + 1] !== value[i + 1].toUpperCase()
) {
res += `_${value[i]}`;
} else {
res += value[i];
}
}
res += value[value.length - 1];
res = res.toLowerCase();
}
return res;
}
/**
* @param {Array} array - array to search in
* @param {any} item - item to search for
* @return {boolean} true if array contains item; false otherwise
*/
contains(array, item) {
return _.includes(array, item);
}
/**
* Function to issue a https get request, and process the result
*
* @param {string} url - the url to fetch
* @param {function} onSuccess - function, which gets called when the request succeeds, with the body of the response
* @param {function} onFail - callback when the get failed.
*/
httpsGet(url, onSuccess, onFail) {
https
.get(url, res => {
let body = '';
res.on('data', chunk => {
body += chunk;
});
res.on('end', () => {
onSuccess(body);
});
})
.on('error', onFail);
}
/**
* Function to print a proper array with simple quoted strings
*
* @param {array} array - the array to print
*/
toArrayString(array) {
return `['${array.join("', '")}']`;
}
/**
* Strip margin indicated by pipe `|` from a string literal
*
* @param {string} content - the string to process
*/
stripMargin(content) {
return content.replace(/^[ ]*\|/gm, '');
}
/**
* Utility function to copy and process templates.
*
* @param {string} source - source
* @param {string} destination - destination
* @param {*} generator - reference to the generator
* @param {*} options - options object
* @param {*} context - context
*/
template(source, destination, generator, options = {}, context) {
const _this = generator || this;
const _context = context || _this;
jhipsterUtils.renderContent(source, _this, _context, options, res => {
_this.fs.write(_this.destinationPath(destination), res);
});
}
/**
* Utility function to render a template into a string
*
* @param {string} source - source
* @param {function} callback - callback to take the rendered template as a string
* @param {*} generator - reference to the generator
* @param {*} options - options object
* @param {*} context - context
*/
render(source, callback, generator, options = {}, context) {
const _this = generator || this;
const _context = context || _this;
jhipsterUtils.renderContent(source, _this, _context, options, res => {
callback(res);
});
}
/**
* Utility function to copy files.
*
* @param {string} source - Original file.
* @param {string} destination - The resulting file.
*/
copy(source, destination) {
this.fs.copy(this.templatePath(source), this.destinationPath(destination));
}
/**
* Print a debug message.
*
* @param {string} msg - message to print
* @param {string[]} args - arguments to print
*/
debug(msg, ...args) {
if (this.isDebugEnabled || (this.options && this.options.debug)) {
this.log(`${chalk.yellow.bold('DEBUG!')} ${msg}`);
args.forEach(arg => this.log(arg));
}
}
/**
* Normalize blueprint name: prepend 'generator-jhipster-' if needed
* @param {string} blueprint - name of the blueprint
*/
normalizeBlueprintName(blueprint) {
if (blueprint && !blueprint.startsWith('generator-jhipster')) {
return `generator-jhipster-${blueprint}`;
}
return blueprint;
}
/**
* 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
*/
composeBlueprint(blueprint, subGen, options = {}) {
if (blueprint) {
blueprint = this.normalizeBlueprintName(blueprint);
this.checkBlueprint(blueprint, subGen);
try {
const finalOptions = {
...options,
jhipsterContext: this
};
this.useBlueprint = true;
this.composeExternalModule(blueprint, subGen, finalOptions);
this.info(`Using blueprint ${chalk.yellow(blueprint)} for ${chalk.yellow(subGen)} subgenerator`);
return true;
} catch (e) {
this.debug(`No blueprint found for ${chalk.yellow(subGen)} subgenerator: falling back to default generator`);
this.debug('Error', e);
return false;
}
}
return false;
}
/**
* 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) {
let packageJsonPath = path.join(process.cwd(), 'node_modules', blueprintPkgName, 'package.json');
try {
if (!fs.existsSync(packageJsonPath)) {
this.debug('using global module as local version could not be found in node_modules');
packageJsonPath = path.join(blueprintPkgName, 'package.json');
}
// eslint-disable-next-line global-require,import/no-dynamic-require
const packagejs = require(packageJsonPath);
return packagejs.version;
} catch (err) {
this.debug('ERROR:', err);
this.warning(`Could not retrieve version of blueprint '${blueprintPkgName}'`);
return '';
}
}
/**
* Check if the generator specified as blueprint is installed.
* @param {string} blueprint - generator name
*/
checkBlueprint(blueprint, subGen = '') {
if (blueprint === 'generator-jhipster') {
this.error(`You cannot use ${chalk.yellow(blueprint)} as the blueprint.`);
}
const done = this.async();
const localModule = path.join(process.cwd(), 'node_modules', blueprint);
if (fs.existsSync(localModule)) {
done();
return;
}
const generatorName = blueprint.replace('generator-', '');
if (this.env.get(`${generatorName}:${subGen}`)) {
done();
return;
}
shelljs.exec('yo --generators', { silent: true }, (err, stdout, stderr) => {
if (!stdout.includes(` ${blueprint}\n`) && !stdout.includes(` ${generatorName}\n`)) {
this.error(
`The ${chalk.yellow(blueprint)} blueprint provided is not installed. Please install it using command ${chalk.yellow(
`npm i -g ${blueprint}`
)}.`
);
}
done();
});
}
/**
* Check if Java is installed
*/
checkJava() {
if (this.skipChecks || this.skipServer) return;
const done = this.async();
exec('java -version', (err, stdout, stderr) => {
if (err) {
this.warning('Java 8 is not found on your computer.');
} else {
const javaVersion = stderr.match(/(?:java|openjdk) version "(.*)"/)[1];
if (!javaVersion.match(new RegExp(constants.JAVA_VERSION.replace('.', '\\.')))) {
this.warning(
`Java ${constants.JAVA_VERSION} is not found on your computer. Your Java version is: ${chalk.yellow(javaVersion)}`
);
}
}
done();
});
}
/**
* Check if Node is installed
*/
checkNode() {
if (this.skipChecks || this.skipServer) return;
const done = this.async();
exec('node -v', (err, stdout, stderr) => {
if (err) {
this.warning('NodeJS is not found on your system.');
} else {
const nodeVersion = semver.clean(stdout);
const nodeFromPackageJson = packagejs.engines.node;
if (!semver.satisfies(nodeVersion, nodeFromPackageJson)) {
this.warning(
`Your NodeJS version is too old (${nodeVersion}). You should use at least NodeJS ${chalk.bold(nodeFromPackageJson)}`
);
}
if (!(process.release || {}).lts) {
this.warning(
'Your Node version is not LTS (Long Term Support), use it at your own risk! JHipster does not support non-LTS releases, so if you encounter a bug, please use a LTS version first.'
);
}
}
done();
});
}
/**
* Check if Git is installed
*/
checkGit() {
if (this.skipChecks || this.skipClient) return;
const done = this.async();
this.isGitInstalled(code => {
this.gitInstalled = code === 0;
done();
});
}
/**
* Check if Yarn is installed
*/
checkYarn() {
if (this.skipChecks || !this.useYarn) return;
const done = this.async();
exec('yarn --version', err => {
if (err) {
this.warning('yarn is not found on your computer.\n', ' Using npm instead');
this.useYarn = false;
} else {
this.useYarn = true;
}
this.useNpm = !this.useYarn;
done();
});
}
/**
* Generate Entity Queries
*
* @param {Array|Object} relationships - array of relationships
* @param {string} entityInstance - entity instance
* @param {string} dto - dto
* @returns {{queries: Array, variables: Array, hasManyToMany: boolean}}
*/
generateEntityQueries(relationships, entityInstance, dto) {
const queries = [];
const variables = [];
let hasManyToMany = false;
relationships.forEach(relationship => {
let query;
let variableName;
let filter;
hasManyToMany = hasManyToMany || relationship.relationshipType === 'many-to-many';
if (
relationship.relationshipType === 'one-to-one' &&
relationship.ownerSide === true &&
relationship.otherEntityName !== 'user'
) {
variableName = relationship.relationshipFieldNamePlural.toLowerCase();
if (variableName === entityInstance) {
variableName += 'Collection';
}
const relationshipFieldName = `this.${entityInstance}.${relationship.relationshipFieldName}`;
const relationshipFieldNameIdCheck =
dto === 'no' ? `!${relationshipFieldName} || !${relationshipFieldName}.id` : `!${relationshipFieldName}Id`;
filter = `filter: '${relationship.otherEntityRelationshipName.toLowerCase()}-is-null'`;
if (this.jpaMetamodelFiltering) {
filter = `'${relationship.otherEntityRelationshipName}Id.specified': 'false'`;
}
query = `this.${relationship.otherEntityName}Service
.query({${filter}}).pipe(
filter((mayBeOk: HttpResponse<I${relationship.otherEntityAngularName}[]>) => mayBeOk.ok),
map((response: HttpResponse<I${relationship.otherEntityAngularName}[]>) => response.body),
)
.subscribe((res: I${relationship.otherEntityAngularName}[]) => {
if (${relationshipFieldNameIdCheck}) {
this.${variableName} = res;
} else {
this.${relationship.otherEntityName}Service
.find(${relationshipFieldName}${dto === 'no' ? '.id' : 'Id'}).pipe(
filter((subResMayBeOk: HttpResponse<I${relationship.otherEntityAngularName}>) => subResMayBeOk.ok),
map((subResponse: HttpResponse<I${relationship.otherEntityAngularName}>) => subResponse.body),
)
.subscribe((subRes: I${relationship.otherEntityAngularName}) =>
this.${variableName} = [subRes].concat(res)
, (subRes: HttpErrorResponse) => this.onError(subRes.message));
}
}, (res: HttpErrorResponse) => this.onError(res.message));`;
} else if (relationship.relationshipType !== 'one-to-many') {
variableName = relationship.otherEntityNameCapitalizedPlural.toLowerCase();
if (variableName === entityInstance) {
variableName += 'Collection';
}
query = `this.${relationship.otherEntityName}Service.query().pipe(
filter((mayBeOk: HttpResponse<I${relationship.otherEntityAngularName}[]>) => mayBeOk.ok),
map((response: HttpResponse<I${relationship.otherEntityAngularName}[]>) => response.body),
)
.subscribe(
(res: I${relationship.otherEntityAngularName}[]) => this.${variableName} = res,
(res: HttpErrorResponse) => this.onError(res.message));`;
}
if (variableName && !this.contains(queries, query)) {
queries.push(query);
variables.push(`${variableName}: I${relationship.otherEntityAngularName}[];`);
}
});
return {
queries,
variables,
hasManyToMany
};
}
/**
* Generate Entity Client Field Default Values
*
* @param {Array|Object} fields - array of fields
* @returns {Array} defaultVariablesValues
*/
generateEntityClientFieldDefaultValues(fields, clientFramework = 'angularX') {
const defaultVariablesValues = {};
fields.forEach(field => {
const fieldType = field.fieldType;
const fieldName = field.fieldName;
if (fieldType === 'Boolean') {
if (clientFramework === 'react') {
defaultVariablesValues[fieldName] = `${fieldName}: false,`;
} else {
defaultVariablesValues[fieldName] = `this.${fieldName} = this.${fieldName} || false;`;
}
}
});
return defaultVariablesValues;
}
/**
* Generate Entity Client Field Declarations
*
* @param {string} pkType - type of primary key
* @param {Array|Object} fields - array of fields
* @param {Array|Object} relationships - array of relationships
* @param {string} dto - dto
* @returns variablesWithTypes: Array
*/
generateEntityClientFields(pkType, fields, relationships, dto, customDateType = 'Moment') {
const variablesWithTypes = [];
let tsKeyType;
if (pkType === 'String') {
tsKeyType = 'string';
} else {
tsKeyType = 'number';
}
variablesWithTypes.push(`id?: ${tsKeyType}`);
fields.forEach(field => {
const fieldType = field.fieldType;
const fieldName = field.fieldName;
let tsType;
if (field.fieldIsEnum) {
tsType = fieldType;
} else if (fieldType === 'Boolean') {
tsType = 'boolean';
} else if (['Integer', 'Long', 'Float', 'Double', 'BigDecimal'].includes(fieldType)) {
tsType = 'number';
} else if (fieldType === 'String' || fieldType === 'UUID') {
tsType = 'string';
} else if (['LocalDate', 'Instant', 'ZonedDateTime'].includes(fieldType)) {
tsType = customDateType;
} else {
// (fieldType === 'byte[]' || fieldType === 'ByteBuffer') && fieldTypeBlobContent === 'any' || (fieldType === 'byte[]' || fieldType === 'ByteBuffer') && fieldTypeBlobContent === 'image' || fieldType === 'LocalDate'
tsType = 'any';
if (['byte[]', 'ByteBuffer'].includes(fieldType) && field.fieldTypeBlobContent !== 'text') {
variablesWithTypes.push(`${fieldName}ContentType?: string`);
}
}
variablesWithTypes.push(`${fieldName}?: ${tsType}`);
});
relationships.forEach(relationship => {
let fieldType;
let fieldName;
const relationshipType = relationship.relationshipType;
if (relationshipType === 'one-to-many' || relationshipType === 'many-to-many') {
fieldType = `I${relationship.otherEntityAngularName}[]`;
fieldName = relationship.relationshipFieldNamePlural;
} else if (dto === 'no') {
fieldType = `I${relationship.otherEntityAngularName}`;
fieldName = relationship.relationshipFieldName;
} else {
const relationshipFieldName = relationship.relationshipFieldName;
const relationshipFieldNamePlural = relationship.relationshipFieldNamePlural;
const relationshipType = relationship.relationshipType;
const otherEntityFieldCapitalized = relationship.otherEntityFieldCapitalized;
const ownerSide = relationship.ownerSide;
if (relationshipType === 'many-to-many' && ownerSide === true) {
fieldType = `I${otherEntityFieldCapitalized}[]`;
fieldName = relationshipFieldNamePlural;
} else if (relationshipType === 'many-to-one' || (relationshipType === 'one-to-one' && ownerSide === true)) {
if (otherEntityFieldCapitalized !== 'Id' && otherEntityFieldCapitalized !== '') {
fieldType = 'string';
fieldName = `${relationshipFieldName}${otherEntityFieldCapitalized}`;
variablesWithTypes.push(`${fieldName}?: ${fieldType}`);
}
fieldType = tsKeyType; // review: added for mongodb-with-relations
fieldName = `${relationshipFieldName}Id`;
} else {
fieldType = tsKeyType;
fieldName = `${relationship.relationshipFieldName}Id`;
}
}
variablesWithTypes.push(`${fieldName}?: ${fieldType}`);
});
return variablesWithTypes;
}
/**
* Generate Entity Client Imports
*
* @param {Array|Object} relationships - array of relationships
* @param {string} dto - dto
* @param {string} clientFramework the client framework, 'angularX' or 'react'.
* @returns typeImports: Map
*/
generateEntityClientImports(relationships, dto, clientFramework = this.clientFramework) {
const typeImports = new Map();
relationships.forEach(relationship => {
const relationshipType = relationship.relationshipType;
let toBeImported = false;
if (relationshipType === 'one-to-many' || relationshipType === 'many-to-many') {
toBeImported = true;
} else if (dto === 'no') {
toBeImported = true;
} else {
const ownerSide = relationship.ownerSide;
if (relationshipType === 'many-to-many' && ownerSide === true) {
toBeImported = true;
}
}
if (toBeImported) {
const otherEntityAngularName = relationship.otherEntityAngularName;
const importType = `I${otherEntityAngularName}`;
let importPath;
if (otherEntityAngularName === 'User') {
importPath = clientFramework === 'angularX' ? 'app/core/user/user.model' : 'app/shared/model/user.model';
} else {
importPath = `app/shared/model/${relationship.otherEntityClientRootFolder}${relationship.otherEntityFileName}.model`;
}
typeImports.set(importType, importPath);
}
});
return typeImports;
}
/**
* Get DB type from DB value
* @param {string} db - db
*/
getDBTypeFromDBValue(db) {
return jhipsterUtils.getDBTypeFromDBValue(db);
}
/**
* Get build directory used by buildTool
* @param {string} buildTool - buildTool
*/
getBuildDirectoryForBuildTool(buildTool) {
return buildTool === 'maven' ? 'target/' : 'build/';
}
/**
* @returns generated JDL from entities
*/
generateJDLFromEntities() {
const jdl = new jhiCore.JDLObject();
try {
const entities = {};
this.getExistingEntities().forEach(entity => {
entities[entity.name] = entity.definition;
});
jhiCore.convertJsonEntitiesToJDL(entities, jdl);
jhiCore.convertJsonServerOptionsToJDL({ 'generator-jhipster': this.config.getAll() }, jdl);
} catch (e) {
this.log(e.message || e);
this.error('\nError while parsing entities to JDL\n');
}
return jdl;
}
/**
* Generate language objects in array of "'en': { name: 'English' }" format
* @param {string[]} languages
* @returns generated language options
*/
generateLanguageOptions(languages, clientFramework) {
const selectedLangs = this.getAllSupportedLanguageOptions().filter(lang => languages.includes(lang.value));
if (clientFramework === 'react') {
return selectedLangs.map(lang => `'${lang.value}': { name: '${lang.dispName}'${lang.rtl ? ', rtl: true' : ''} }`);
}
return selectedLangs.map(lang => `'${lang.value}': { name: '${lang.dispName}'${lang.rtl ? ', rtl: true' : ''} }`);
}
/**
* Check if language should be skipped for locale setting
* @param {string} language
*/
skipLanguageForLocale(language) {
const out = this.getAllSupportedLanguageOptions().filter(lang => language === lang.value);
return out && out[0] && !!out[0].skipForLocale;
}
/**
* Get UAA app name from path provided.
* @param {string} input - path
*/
getUaaAppName(input) {
if (!input) return false;
input = input.trim();
let fromPath = '';
if (path.isAbsolute(input)) {
fromPath = `${input}/.yo-rc.json`;
} else {
fromPath = this.destinationPath(`${input}/.yo-rc.json`);
}
if (shelljs.test('-f', fromPath)) {
const fileData = this.fs.readJSON(fromPath);
if (fileData && fileData['generator-jhipster']) {
return fileData['generator-jhipster'];
}
return false;
}
return false;
}
/**
* Return the method name which converts the filter to specification
* @param {string} fieldType
*/
getSpecificationBuilder(fieldType) {
if (['Integer', 'Long', 'Float', 'Double', 'BigDecimal', 'LocalDate', 'ZonedDateTime', 'Instant'].includes(fieldType)) {
return 'buildRangeSpecification';
}
if (fieldType === 'String') {
return 'buildStringSpecification';
}
return 'buildSpecification';
}
/**
* @param {string} fieldType
* @returns {boolean} true if type is filterable; false otherwise.
*/
isFilterableType(fieldType) {
return !['byte[]', 'ByteBuffer'].includes(fieldType);
}
/**
* Copy Filtering Flag
*
* @param {any} from - from
* @param {any} to - to
* @param {any} context - generator context
*/
copyFilteringFlag(from, to, context = this) {
if (context.databaseType === 'sql' && context.service !== 'no') {
to.jpaMetamodelFiltering = from.jpaMetamodelFiltering;
} else {
to.jpaMetamodelFiltering = false;
}
}
/**
* Rebuild client for Angular
*/
rebuildClient() {
const done = this.async();
this.log(`\n${chalk.bold.green('Running `webpack:build` to update client app\n')}`);
this.spawnCommand(this.clientPackageManager, ['run', 'webpack:build']).on('close', () => {
done();
});
}
/**
* Generate a primary key, according to the type
*
* @param {any} pkType - the type of the primary key
* @param {any} prodDatabaseType - the database type
*/
generateTestEntityId(pkType, prodDatabaseType) {
if (pkType === 'String') {
if (prodDatabaseType === 'cassandra') {
return "'9fec3727-3421-4967-b213-ba36557ca194'";
}
return "'123'";
}
return 123;
}
/**
* Decide the primary key type based on DB
*
* @param {any} databaseType - the database type
*/
getPkType(databaseType) {
if (['cassandra', 'mongodb', 'couchbase'].includes(databaseType)) {
return 'String';
}
return 'Long';
}
/**
* Get a root folder name for entity
* @param {string} clientRootFolder
* @param {string} entityFileName
*/
getEntityFolderName(clientRootFolder, entityFileName) {
if (clientRootFolder) {
return `${clientRootFolder}/${entityFileName}`;
}
return entityFileName;
}
/**
* Get a parent folder path addition for entity
* @param {string} clientRootFolder
*/
getEntityParentPathAddition(clientRootFolder) {
if (clientRootFolder) {
return '../';
}
return '';
}
/**
* Register prettier as transform stream for prettifying files during generation
* @param {any} generator
*/
registerPrettierTransform(generator = this) {
// Prettier is clever, it uses correct rules and correct parser according to file extension.
const prettierFilter = filter(['{,src/**/}*.{md,json,ts,tsx,scss,css}'], { restore: true });
// this pipe will pass through (restore) anything that doesn't match typescriptFilter
generator.registerTransformStream([prettierFilter, prettierTransform(prettierOptions), prettierFilter.restore]);
}
/**
* Check if the subgenerator has been invoked from JHipster CLI or from Yeoman (yo jhipster:subgenerator)
*/
checkInvocationFromCLI() {
if (!this.options['from-cli']) {
this.warning(
`Deprecated: JHipster seems to be invoked using Yeoman command. Please use the JHipster CLI. Run ${chalk.red(
'jhipster <command>'
)} instead of ${chalk.red('yo jhipster:<command>')}`
);
}
}
};