@iobroker/create-adapter
Version:
Command line utility to create customized ioBroker adapters
315 lines • 11.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const typeguards_1 = require("alcalzone-shared/typeguards");
const ansi_colors_1 = require("ansi-colors");
const axios_1 = require("axios");
const child_process_1 = require("child_process");
const eslint_1 = require("eslint");
const fs = require("fs-extra");
const JSON5 = require("json5");
const os = require("os");
const path = require("path");
const prettier = require("prettier");
const nodeUrl = require("url");
function error(message) {
console.error(ansi_colors_1.bold.red(message));
console.error();
}
exports.error = error;
exports.isWindows = /^win/.test(os.platform());
const isTesting = !!process.env.TESTING;
function executeCommand(command, argsOrOptions, options) {
return new Promise(resolve => {
let args;
if (typeguards_1.isArray(argsOrOptions)) {
args = argsOrOptions;
}
else if (typeguards_1.isObject(argsOrOptions)) {
// no args were given
options = argsOrOptions;
}
if (options == null)
options = {};
if (args == null)
args = [];
const spawnOptions = {
stdio: [
options.stdin || process.stdin,
options.stdout || process.stdout,
options.stderr || process.stderr,
],
// @ts-ignore This option exists starting with NodeJS 8
windowsHide: true,
};
if (options.cwd != null)
spawnOptions.cwd = options.cwd;
if (options.logCommandExecution == null)
options.logCommandExecution = false;
if (options.logCommandExecution) {
console.log("executing: " + `${command} ${args.join(" ")}`);
}
// Now execute the npm process and avoid throwing errors
try {
let bufferedStdout;
let bufferedStderr;
const cmd = child_process_1.spawn(command, args, spawnOptions).on("close", (code, signal) => {
resolve({
exitCode: code,
signal,
stdout: bufferedStdout,
stderr: bufferedStderr,
});
});
// Capture stdout/stderr if requested
if (options.stdout === "pipe") {
bufferedStdout = "";
cmd.stdout.on("data", chunk => {
const buffer = Buffer.isBuffer(chunk)
? chunk
: Buffer.from(chunk, "utf8");
bufferedStdout += buffer;
});
}
if (options.stderr === "pipe") {
bufferedStderr = "";
cmd.stderr.on("data", chunk => {
const buffer = Buffer.isBuffer(chunk)
? chunk
: Buffer.from(chunk, "utf8");
bufferedStderr += buffer;
});
}
}
catch (e) {
// doesn't matter, we return the exit code in the "close" handler
}
});
}
exports.executeCommand = executeCommand;
/**
* Recursively enumerates all files in the given directory
* @param dir The directory to scan
* @param predicate An optional predicate to apply to every found file system entry
* @returns A list of all files found
*/
function enumFilesRecursiveSync(dir, predicate) {
const ret = [];
if (typeof predicate !== "function")
predicate = () => true;
// enumerate all files in this directory
const filesOrDirs = fs
.readdirSync(dir)
.filter(f => predicate(f, dir)) // exclude all files starting with "."
.map(f => path.join(dir, f)); // and prepend the full path
for (const entry of filesOrDirs) {
if (fs.statSync(entry).isDirectory()) {
// Continue recursing this directory and remember the files there
ret.push(...enumFilesRecursiveSync(entry, predicate));
}
else {
// remember this file
ret.push(entry);
}
}
return ret;
}
exports.enumFilesRecursiveSync = enumFilesRecursiveSync;
/**
* Recursively copies all files from the source to the target directory
* @param sourceDir The directory to scan
* @param targetDir The directory to copy to
* @param predicate An optional predicate to apply to every found file system entry
*/
function copyFilesRecursiveSync(sourceDir, targetDir, predicate) {
// Enumerate all files in this module that are supposed to be in the root directory
const filesToCopy = enumFilesRecursiveSync(sourceDir, predicate);
// Copy all of them to the corresponding target dir
for (const file of filesToCopy) {
// Find out where it's supposed to be
const targetFileName = path.join(targetDir, path.relative(sourceDir, file));
// Ensure the directory exists
fs.ensureDirSync(path.dirname(targetFileName));
// And copy the file
fs.copySync(file, targetFileName);
}
}
exports.copyFilesRecursiveSync = copyFilesRecursiveSync;
/**
* Adds https proxy options to an axios request if they were defined as an env variable
* @param options The options object passed to axios
*/
function applyHttpsProxy(options) {
const proxy = process.env.https_proxy || process.env.HTTPS_PROXY;
if (proxy) {
const proxyUrl = nodeUrl.parse(proxy);
if (proxyUrl.hostname) {
options.proxy = {
host: proxyUrl.hostname,
port: proxyUrl.port ? parseInt(proxyUrl.port, 10) : 443,
};
}
}
return options;
}
exports.applyHttpsProxy = applyHttpsProxy;
const translationCache = new Map();
async function translateText(text, targetLang) {
async function doTranslateText() {
if (isTesting)
return `Mock translation of '${text}' to '${targetLang}'`;
if (targetLang === "en")
return text;
try {
const url = `http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8`;
let options = {
url,
timeout: getRequestTimeout(),
};
// If an https-proxy is defined as an env variable, use it
options = applyHttpsProxy(options);
const response = await axios_1.default(options);
if (typeguards_1.isArray(response.data)) {
// we got a valid response
return response.data[0][0][0];
}
error(`Invalid response for translate request`);
}
catch (e) {
error(`Could not translate to "${targetLang}": ${e}`);
}
return text;
}
// Try to read the translation from the translation cache
if (!translationCache.has(targetLang))
translationCache.set(targetLang, new Map());
const langCache = translationCache.get(targetLang);
// or fall back to an online translation
if (!langCache.has(text))
langCache.set(text, await doTranslateText());
return langCache.get(text);
}
exports.translateText = translateText;
function formatLicense(licenseText, answers) {
return licenseText
.replace(/\[year\]/g, new Date().getFullYear().toString())
.replace(/\[fullname\]/g, answers.authorName)
.replace(/\[email\]/g, answers.authorEmail);
}
exports.formatLicense = formatLicense;
/** Replaces 4-space indentation with tabs */
function indentWithTabs(text) {
if (!text)
return text;
return text.replace(/^( {4})+/gm, match => "\t".repeat(match.length / 4));
}
exports.indentWithTabs = indentWithTabs;
/** Replaces tab indentation with 4 spaces */
function indentWithSpaces(text) {
if (!text)
return text;
return text.replace(/^(\t)+/gm, match => " ".repeat(match.length * 4));
}
exports.indentWithSpaces = indentWithSpaces;
/** Normalizes formatting of a JSON string */
function formatJsonString(json, indentation) {
return JSON.stringify(JSON5.parse(json), null, indentation === "Tab" ? "\t" : 4);
}
exports.formatJsonString = formatJsonString;
var Quotemark;
(function (Quotemark) {
Quotemark["single"] = "'";
Quotemark["double"] = "\"";
})(Quotemark = exports.Quotemark || (exports.Quotemark = {}));
function createESLintOptions(language, quotes) {
const baseOptions = {
env: {
es6: true,
node: true,
mocha: true,
},
parserOptions: {
ecmaVersion: 2018,
},
rules: {
quotes: [
"error",
quotes,
{
avoidEscape: true,
allowTemplateLiterals: true,
},
],
},
};
if (language === "TypeScript") {
baseOptions.parser = "@typescript-eslint/parser";
baseOptions.parserOptions = Object.assign(Object.assign({}, baseOptions.parserOptions), { sourceType: "module" });
}
return baseOptions;
}
/** Formats a JS source file to use single quotes */
function jsFixQuotes(sourceText, quotes) {
const linter = new eslint_1.Linter();
const result = linter.verifyAndFix(sourceText, createESLintOptions("JavaScript", quotes));
return result.output;
}
exports.jsFixQuotes = jsFixQuotes;
/** Formats a TS source file to use single quotes */
function tsFixQuotes(sourceText, quotes) {
const linter = new eslint_1.Linter();
const result = linter.verifyAndFix(sourceText, createESLintOptions("TypeScript", quotes));
return result.output;
}
exports.tsFixQuotes = tsFixQuotes;
function formatWithPrettier(sourceText, answers, extension) {
// Keep this in sync with templates/_prettierrc.js.ts
const prettierOptions = {
semi: true,
trailingComma: "all",
singleQuote: answers.quotes === "single",
printWidth: 120,
useTabs: answers.indentation === "Tab",
tabWidth: 4,
endOfLine: "lf",
// To infer the correct parser
filepath: `index.${extension}`,
};
return prettier.format(sourceText, prettierOptions);
}
exports.formatWithPrettier = formatWithPrettier;
function getOwnVersion() {
for (const jsonPath of ["../../package.json", "../../../package.json"]) {
try {
return require(jsonPath).version;
}
catch (e) {
/* OK */
}
}
/* istanbul ignore next */
return "unknown";
}
exports.getOwnVersion = getOwnVersion;
function capitalize(name) {
return name[0].toUpperCase() + name.slice(1);
}
exports.capitalize = capitalize;
function kebabCaseToUpperCamelCase(name) {
return name
.split(/[_\-]/)
.filter(part => part.length > 0)
.map(capitalize)
.join("");
}
exports.kebabCaseToUpperCamelCase = kebabCaseToUpperCamelCase;
function getRequestTimeout() {
let ret;
if (process.env.REQUEST_TIMEOUT) {
ret = parseInt(process.env.REQUEST_TIMEOUT, 10);
}
if (ret == undefined || Number.isNaN(ret))
return 5000;
return ret;
}
exports.getRequestTimeout = getRequestTimeout;
//# sourceMappingURL=tools.js.map