@iobroker/create-adapter
Version:
Command line utility to create customized ioBroker adapters
435 lines • 13.8 kB
JavaScript
;
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