UNPKG

@iobroker/create-adapter

Version:

Command line utility to create customized ioBroker adapters

435 lines 13.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.Quotemark = exports.isWindows = void 0; exports.error = error; exports.executeNpmCommand = executeNpmCommand; exports.executeCommand = executeCommand; exports.enumFilesRecursiveSync = enumFilesRecursiveSync; exports.copyFilesRecursiveSync = copyFilesRecursiveSync; exports.applyHttpsProxy = applyHttpsProxy; exports.formatLicense = formatLicense; exports.getFormattedLicense = getFormattedLicense; exports.indentWithTabs = indentWithTabs; exports.indentWithSpaces = indentWithSpaces; exports.formatJsonString = formatJsonString; exports.jsFixQuotes = jsFixQuotes; exports.tsFixQuotes = tsFixQuotes; exports.formatWithPrettier = formatWithPrettier; exports.getOwnVersion = getOwnVersion; exports.capitalize = capitalize; exports.kebabCaseToUpperCamelCase = kebabCaseToUpperCamelCase; exports.getRequestTimeout = getRequestTimeout; const typeguards_1 = require("alcalzone-shared/typeguards"); const ansi_colors_1 = require("ansi-colors"); const child_process_1 = require("child_process"); const eslint_1 = require("eslint"); const fs = __importStar(require("fs-extra")); const JSON5 = __importStar(require("json5")); const os = __importStar(require("os")); const path = __importStar(require("path")); const prettier = __importStar(require("prettier")); const url_1 = require("url"); const licenses_1 = require("./core/licenses"); /** * * @param message */ function error(message) { console.error(ansi_colors_1.bold.red(message)); console.error(); } exports.isWindows = /^win/.test(os.platform()); /** * Executes an npm command with update-notifier disabled and common parameters * * @param args The npm command arguments * @param options (optional) Some options for the command execution */ function executeNpmCommand(args, options) { // Add common npm parameters that reduce noise and improve reliability const enhancedArgs = [...args]; // Add common parameters for install commands if (args[0] === "install") { // Add logging and audit/fund parameters if not already present if (!args.includes("--loglevel")) { enhancedArgs.push("--loglevel", "error"); } if (!args.includes("--audit=false") && !args.includes("--audit")) { enhancedArgs.push("--audit=false"); } if (!args.includes("--fund=false") && !args.includes("--fund")) { enhancedArgs.push("--fund=false"); } } const npmOptions = { ...options, env: { ...options?.env, npm_config_update_notifier: "false", }, }; return executeCommand(exports.isWindows ? "npm.cmd" : "npm", enhancedArgs, npmOptions); } function executeCommand(command, argsOrOptions, options) { return new Promise(resolve => { let args; if (Array.isArray(argsOrOptions)) { args = argsOrOptions; } else if ((0, 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], windowsHide: true, env: options.env ? { ...process.env, ...options.env } : process.env, }; 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 = (0, child_process_1.spawn)(command, args, spawnOptions).on("close", (code, signal) => { resolve({ exitCode: code ?? undefined, signal: signal ?? undefined, stdout: bufferedStdout, stderr: bufferedStderr, }); }); // Capture stdout/stderr if requested if (options.stdout === "pipe") { bufferedStdout = ""; cmd.stdout.on("data", chunk => { bufferedStdout += Buffer.isBuffer(chunk) ? chunk.toString("utf8") : chunk; }); } if (options.stderr === "pipe") { bufferedStderr = ""; cmd.stderr.on("data", chunk => { bufferedStderr += Buffer.isBuffer(chunk) ? chunk.toString("utf8") : chunk; }); } } catch { // doesn't matter, we return the exit code in the "close" handler } }); } /** * 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; } /** * 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); } } /** * 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) { try { const proxyUrl = new url_1.URL(proxy); if (proxyUrl.hostname) { options.proxy = { host: proxyUrl.hostname, port: proxyUrl.port ? parseInt(proxyUrl.port, 10) : 443, }; } } catch { // Invalid URL, don't use proxy } } return options; } /** * * @param licenseText * @param answers */ function formatLicense(licenseText, answers) { return licenseText .replace(/\[(year|yyyy)\]/g, new Date().getFullYear().toString()) .replace(/\[(fullname|name of copyright owner)\]/g, answers.authorName) .replace(/\[email\]/g, answers.authorEmail); } /** * * @param answers */ function getFormattedLicense(answers) { if (answers.license) { const license = licenses_1.licenses[answers.license]; if (license) { return formatLicense(license.text, answers); } } return "TODO: enter license text here"; } /** * Replaces 4-space indentation with tabs * * @param text */ function indentWithTabs(text) { if (!text) { return text; } return text.replace(/^( {4})+/gm, match => "\t".repeat(match.length / 4)); } /** * Replaces tab indentation with 4 spaces * * @param text */ function indentWithSpaces(text) { if (!text) { return text; } return text.replace(/^(\t)+/gm, match => " ".repeat(match.length * 4)); } /** * Normalizes formatting of a JSON string * * @param json * @param indentation */ function formatJsonString(json, indentation) { return JSON.stringify(JSON5.parse(json), null, indentation === "Tab" ? "\t" : 4); } var Quotemark; (function (Quotemark) { Quotemark["single"] = "'"; Quotemark["double"] = "\""; })(Quotemark || (exports.Quotemark = Quotemark = {})); function createESLintOptions(language, quotes) { // ESLint 9 flat config format const baseConfig = { languageOptions: { ecmaVersion: "latest", sourceType: "module", parserOptions: { ecmaFeatures: { jsx: true, }, }, globals: { // Node.js process: "readonly", Buffer: "readonly", __dirname: "readonly", __filename: "readonly", module: "readonly", require: "readonly", exports: "readonly", global: "readonly", console: "readonly", // Mocha describe: "readonly", it: "readonly", before: "readonly", after: "readonly", beforeEach: "readonly", afterEach: "readonly", }, }, rules: { quotes: [ "error", quotes, { avoidEscape: true, allowTemplateLiterals: true, }, ], }, }; if (language === "TypeScript") { baseConfig.languageOptions.parser = require("@typescript-eslint/parser"); } return [baseConfig]; } /** * Formats a JS source file to use single quotes * * @param sourceText * @param quotes */ function jsFixQuotes(sourceText, quotes) { const linter = new eslint_1.Linter(); const result = linter.verifyAndFix(sourceText, createESLintOptions("JavaScript", quotes)); return result.output; } /** * Formats a TS source file to use single quotes * * @param sourceText * @param quotes */ function tsFixQuotes(sourceText, quotes) { const linter = new eslint_1.Linter(); const result = linter.verifyAndFix(sourceText, createESLintOptions("TypeScript", quotes)); return result.output; } /** * * @param sourceText * @param answers * @param extension */ 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); } /** * */ function getOwnVersion() { for (const jsonPath of ["../../package.json", "../../../package.json"]) { try { return require(jsonPath).version; } catch { /* OK */ } } /* istanbul ignore next */ return "unknown"; } /** * * @param name */ function capitalize(name) { return name[0].toUpperCase() + name.slice(1); } /** * * @param name */ function kebabCaseToUpperCamelCase(name) { return name .split(/[_-]/) .filter(part => part.length > 0) .map(capitalize) .join(""); } /** * */ 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; } //# sourceMappingURL=tools.js.map