UNPKG

generator-fedhipster

Version:

Spring Boot + Angular/React in one handy generator

471 lines (425 loc) 14.9 kB
/** * 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 shelljs = require('shelljs'); const ejs = require('ejs'); const _ = require('lodash'); const jhiCore = require('jhipster-core'); const fs = require('fs'); const crypto = require('crypto'); const constants = require('./generator-constants'); const LANGUAGES_MAIN_SRC_DIR = `${__dirname}/languages/templates/${constants.CLIENT_MAIN_SRC_DIR}`; module.exports = { rewrite, rewriteFile, replaceContent, classify, rewriteJSONFile, copyWebResource, renderContent, deepFind, getJavadoc, buildEnumInfo, copyObjectProps, decodeBase64, getAllJhipsterConfig, getDBTypeFromDBValue, getBase64Secret, getRandomHex, checkStringInFile }; /** * Rewrite file with passed arguments * @param {object} args argument object (containing path, file, haystack, etc properties) * @param {object} generator reference to the generator */ function rewriteFile(args, generator) { args.path = args.path || process.cwd(); const fullPath = path.join(args.path, args.file); args.haystack = generator.fs.read(fullPath); const body = rewrite(args); generator.fs.write(fullPath, body); } /** * Replace content * @param {object} args argument object * @param {object} generator reference to the generator */ function replaceContent(args, generator) { args.path = args.path || process.cwd(); const fullPath = path.join(args.path, args.file); const re = args.regex ? new RegExp(args.pattern, 'g') : args.pattern; let body = generator.fs.read(fullPath); body = body.replace(re, args.content); generator.fs.write(fullPath, body); } /** * Escape regular expressions. * * @param {string} str string * @returns {string} string with regular expressions escaped */ function escapeRegExp(str) { return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); // eslint-disable-line } /** * Rewrite using the passed argument object. * * @param {object} args arguments object (containing splicable, haystack, needle properties) to be used * @returns {*} re-written file */ function rewrite(args) { // check if splicable is already in the body text const re = new RegExp(args.splicable.map(line => `\\s*${escapeRegExp(line)}`).join('\n')); if (re.test(args.haystack)) { return args.haystack; } const lines = args.haystack.split('\n'); let otherwiseLineIndex = -1; lines.forEach((line, i) => { if (line.includes(args.needle)) { otherwiseLineIndex = i; } }); let spaces = 0; while (lines[otherwiseLineIndex].charAt(spaces) === ' ') { spaces += 1; } let spaceStr = ''; // eslint-disable-next-line no-cond-assign while ((spaces -= 1) >= 0) { spaceStr += ' '; } lines.splice(otherwiseLineIndex, 0, args.splicable.map(line => spaceStr + line).join('\n')); return lines.join('\n'); } /** * Convenient function to convert string into valid java class name * Note: _.classify uses _.titleize which lowercase the string, * so if the user chooses a proper ClassName it will not rename properly * * @param string string to 'class'-ify * @returns {string} 'class'-ified string */ function classify(string) { string = string.replace(/[\W_](\w)/g, match => ` ${match[1].toUpperCase()}`).replace(/\s/g, ''); return string.charAt(0).toUpperCase() + string.slice(1); } /** * Rewrite JSON file * * @param {string} filePath file path * @param {function} rewriteFile rewriteFile function * @param {object} generator reference to the generator */ function rewriteJSONFile(filePath, rewriteFile, generator) { const jsonObj = generator.fs.readJSON(filePath); rewriteFile(jsonObj, generator); generator.fs.writeJSON(filePath, jsonObj, null, 2); } /** * Copy web resources * * @param {string} source source * @param {string} dest destination * @param {regex} regex regex * @param {string} type type of resource (html, js, etc) * @param {object} generator reference to the generator * @param {object} opt options * @param {any} template template */ function copyWebResource(source, dest, regex, type, generator, opt = {}, template) { if (generator.enableTranslation) { generator.template(source, dest, generator, opt); } else { renderContent(source, generator, generator, opt, body => { body = body.replace(regex, ''); switch (type) { case 'html': body = replacePlaceholders(body, generator); break; case 'js': body = replaceTitle(body, generator); break; case 'jsx': body = replaceTranslation(body, generator); break; default: break; } generator.fs.write(dest, body); }); } } /** * Render content * * @param {string} source source * @param {object} generator reference to the generator * @param {any} context context * @param {object} options options * @param {function} cb callback function */ function renderContent(source, generator, context, options, cb) { ejs.renderFile(generator.templatePath(source), context, options, (err, res) => { if (!err) { cb(res); } else { generator.error(`Copying template ${source} failed. [${err}]`); } }); } /** * * @param {string} body html body * @param {object} generator reference to the generator * @returns string with pageTitle replaced */ function replaceTitle(body, generator) { const re = /pageTitle[\s]*:[\s]*['|"]([a-zA-Z0-9.\-_]+)['|"]/g; let match; // eslint-disable-next-line no-cond-assign while ((match = re.exec(body)) !== null) { // match is now the next match, in array form and our key is at index 1, index 1 is replace target. const key = match[1]; const target = key; const jsonData = geti18nJson(key, generator); const keyValue = jsonData !== undefined ? deepFind(jsonData, key) : undefined; body = body.replace(target, keyValue !== undefined ? keyValue : generator.baseName); } return body; } /** * * @param {string} body html body * @param {object} generator reference to the generator * @returns string with placeholders replaced */ function replacePlaceholders(body, generator) { const re = /placeholder=['|"]([{]{2}['|"]([a-zA-Z0-9.\-_]+)['|"][\s][|][\s](translate)[}]{2})['|"]/g; let match; // eslint-disable-next-line no-cond-assign while ((match = re.exec(body)) !== null) { // match is now the next match, in array form and our key is at index 2, index 1 is replace target. const key = match[2]; const target = match[1]; const jsonData = geti18nJson(key, generator); const keyValue = jsonData !== undefined ? deepFind(jsonData, key, true) : undefined; // dirty fix to get placeholder as it is not in proper json format, name has a dot in it. Assuming that all placeholders are in similar format body = body.replace(target, keyValue !== undefined ? keyValue : ''); } return body; } /** * * @param {string} body html body * @param {object} generator reference to the generator * @returns string with Translate components replaced */ function replaceTranslation(body, generator) { const replaceRegex = (re, defultReplaceText) => { let match; // eslint-disable-next-line no-cond-assign while ((match = re.exec(body)) !== null) { // match is now the next match, in array form and our key is at index 2, index 1 is replace target. const key = match[2]; const target = match[1]; const limit = match[4]; // string indicating validation limit (e.g. "{ max: 4 }") const jsonData = geti18nJson(key, generator); let keyValue = jsonData !== undefined ? deepFind(jsonData, key) : undefined; if (!keyValue) { keyValue = deepFind(jsonData, key, true); // dirty fix to get placeholder as it is not in proper json format, name has a dot in it. Assuming that all placeholders are in similar format } if (limit) { // Replace "{{ placeholder }}" with numeric limit keyValue = keyValue.replace(/{{.+}}/, /{.+:\s(.+)\s}/.exec(limit)[1]); } body = body.replace(target, keyValue !== undefined ? `"${keyValue}"` : defultReplaceText); } }; replaceRegex(/(\{translate\('([a-zA-Z0-9.\-_]+)'(, ?null, ?'.*')?\)\})/g, '""'); replaceRegex(/(translate\(\s*'([a-zA-Z0-9.\-_]+)'(,\s*(null|\{.*\}),?\s*('.*')?\s*)?\))/g, "''"); return body; } /** * * @param key i18n key * @param {object} generator reference to the generator * @returns parsed json file */ function geti18nJson(key, generator) { const i18nDirectory = `${LANGUAGES_MAIN_SRC_DIR}i18n/en/`; let name = _.kebabCase(key.split('.')[0]); if (['entity', 'error', 'footer'].includes(name)) { name = 'global'; } let filename = `${i18nDirectory + name}.json`; let render; if (!shelljs.test('-f', filename)) { filename = `${filename}.ejs`; render = true; } try { let file = generator.fs.read(filename); file = render ? ejs.render(file, generator, {}) : file; file = JSON.parse(file); return file; } catch (err) { generator.log(err); generator.log(`Error in file: ${filename}`); // 'Error reading translation file!' return undefined; } } /** * * @param obj object to find in * @param path path to traverse * @param placeholder placeholder */ function deepFind(obj, path, placeholder) { const paths = path.split('.'); let current = obj; if (placeholder) { // dirty fix for placeholders, the json files needs to be corrected paths[paths.length - 2] = `${paths[paths.length - 2]}.${paths[paths.length - 1]}`; paths.pop(); } for (let i = 0; i < paths.length; ++i) { if (current[paths[i]] === undefined) { return undefined; } current = current[paths[i]]; } return current; } /** * Convert passed block of string to javadoc formatted string. * * @param {string} text text to convert to javadoc format * @param {number} indentSize indent size * @returns javadoc formatted string */ function getJavadoc(text, indentSize) { if (!text) { text = ''; } if (text.includes('"')) { text = text.replace(/"/g, '\\"'); } let javadoc = `${_.repeat(' ', indentSize)}/**`; const rows = text.split('\n'); for (let i = 0; i < rows.length; i++) { javadoc = `${javadoc}\n${_.repeat(' ', indentSize)} * ${rows[i]}`; } javadoc = `${javadoc}\n${_.repeat(' ', indentSize)} */`; return javadoc; } /** * Build an enum object * @param {any} field : entity field * @param {string} angularAppName * @param {string} packageName */ function buildEnumInfo(field, angularAppName, packageName, clientRootFolder) { const fieldType = field.fieldType; field.enumInstance = _.lowerFirst(fieldType); const enumInfo = { enumName: fieldType, enumValues: field.fieldValues.split(',').join(', '), enumInstance: field.enumInstance, enums: field.fieldValues.replace(/\s/g, '').split(','), angularAppName, packageName, clientRootFolder: clientRootFolder ? `${clientRootFolder}-` : '' }; return enumInfo; } /** * Copy object props from source to destination * @param {*} toObj * @param {*} fromObj */ function copyObjectProps(toObj, fromObj) { // we use Object.assign instead of spread as we want to mutilate the object. Object.assign(toObj, fromObj); } /** * Decode the given string from base64 to said encoding. * @param string the base64 string to decode * @param encoding the encoding to decode into. default to 'utf-8' */ function decodeBase64(string, encoding = 'utf-8') { return Buffer.from(string, 'base64').toString(encoding); } /** * Get all the generator configuration from the .yo-rc.json file * @param {Generator} generator the generator instance to use * @param {boolean} force force getting direct from file */ function getAllJhipsterConfig(generator, force) { let configuration = generator && generator.config ? generator.config.getAll() || {} : {}; if ((force || !configuration.baseName) && jhiCore.FileUtils.doesFileExist('.yo-rc.json')) { const yoRc = JSON.parse(fs.readFileSync('.yo-rc.json', { encoding: 'utf-8' })); configuration = yoRc['generator-fedhipster']; // merge the blueprint config if available if (configuration.blueprint) { configuration = { ...configuration, ...yoRc[configuration.blueprint] }; } } if (!configuration.get || typeof configuration.get !== 'function') { configuration = { ...configuration, getAll: () => configuration, get: key => configuration[key], set: (key, value) => { configuration[key] = value; } }; } return configuration; } /** * Get DB type from DB value * @param {string} db - db */ function getDBTypeFromDBValue(db) { if (constants.SQL_DB_OPTIONS.map(db => db.value).includes(db)) { return 'sql'; } return db; } /** * Get a random hex string * @param {int} len the length to use, defaults to 50 */ function getRandomHex(len = 50) { return crypto.randomBytes(len).toString('hex'); } /** * Generates a base64 secret from given string or random hex * @param {string} value the value used to get base64 secret * @param {int} len the length to use for random hex, defaults to 50 */ function getBase64Secret(value, len = 50) { return Buffer.from(value || getRandomHex(len)).toString('base64'); } function checkStringInFile(path, search, generator) { const fileContent = generator.fs.read(path); return fileContent.includes(search); }