UNPKG

@iobroker/create-adapter

Version:

Command line utility to create customized ioBroker adapters

315 lines 11.3 kB
"use strict"; 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