UNPKG

@h3ravel/shared

Version:
538 lines (528 loc) 16.7 kB
//#region rolldown:runtime var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); //#endregion let fs_promises = require("fs/promises"); let escalade_sync = require("escalade/sync"); escalade_sync = __toESM(escalade_sync); let path = require("path"); path = __toESM(path); let chalk = require("chalk"); chalk = __toESM(chalk); let inquirer_autocomplete_standalone = require("inquirer-autocomplete-standalone"); inquirer_autocomplete_standalone = __toESM(inquirer_autocomplete_standalone); let __inquirer_prompts = require("@inquirer/prompts"); let crypto = require("crypto"); crypto = __toESM(crypto); let preferred_pm = require("preferred-pm"); preferred_pm = __toESM(preferred_pm); //#region src/Utils/EnvParser.ts var EnvParser = class { static parse(initial) { const parsed = { ...initial }; for (const key in parsed) { const value = parsed[key]; parsed[key] = this.parseValue(value); } return parsed; } static parseValue(value) { /** * Null/undefined stay untouched */ if (value === null || value === void 0) return value; /** * Convert string "true"/"false" to boolean */ if (value === "true") return true; if (value === "false") return false; /** * Convert string numbers to number */ if (!isNaN(value) && value.trim() !== "") return Number(value); /** * Convert string "null" and "undefined" */ if (value === "null") return null; if (value === "undefined") return void 0; /** * Otherwise return as-is (string) */ return value; } }; //#endregion //#region src/Utils/FileSystem.ts var FileSystem = class { static findModulePkg(moduleId, cwd) { const parts = moduleId.replace(/\\/g, "/").split("/"); let packageName = ""; if (parts.length > 0 && parts[0][0] === "@") packageName += parts.shift() + "/"; packageName += parts.shift(); const packageJson = path.default.join(cwd ?? process.cwd(), "node_modules", packageName); const resolved = this.resolveFileUp("package", ["json"], packageJson); if (!resolved) return; return path.default.join(path.default.dirname(resolved), parts.join("/")); } /** * Check if file exists * * @param path * @returns */ static async fileExists(path$2) { try { await (0, fs_promises.access)(path$2); return true; } catch { return false; } } /** * Recursively find files starting from given cwd * * @param name * @param extensions * @param cwd * * @returns */ static resolveFileUp(name, extensions, cwd) { cwd ??= process.cwd(); return (0, escalade_sync.default)(cwd, (dir, filesNames) => { if (typeof extensions === "function") return extensions(dir, filesNames); const candidates = new Set(extensions.map((ext) => `${name}.${ext}`)); for (const filename of filesNames) if (candidates.has(filename)) return filename; return false; }) ?? void 0; } }; //#endregion //#region src/Utils/Logger.ts var Logger = class Logger { /** * Global verbosity configuration */ static verbosity = 0; static isQuiet = false; static isSilent = false; /** * Configure global verbosity levels */ static configure(options = {}) { this.verbosity = options.verbosity ?? 0; this.isQuiet = options.quiet ?? false; this.isSilent = options.silent ?? false; } /** * Check if output should be suppressed */ static shouldSuppressOutput(level) { if (this.isSilent) return true; if (this.isQuiet && (level === "info" || level === "success")) return true; if (level === "debug" && this.verbosity < 3) return true; return false; } static twoColumnDetail(name, value, log = true, spacer = ".") { const regex = /\x1b\[\d+m/g; const width = Math.max(process.stdout.columns, 100); const dots = Math.max(width - name.replace(regex, "").length - value.replace(regex, "").length - 10, 0); if (log) return console.log(name, chalk.default.gray(spacer.repeat(dots)), value); else return [ name, chalk.default.gray(spacer.repeat(dots)), value ]; } static describe(name, desc, width = 50, log = true) { width = Math.max(width, 30); const dots = Math.max(width - name.replace(/\x1b\[\d+m/g, "").length, 0); if (log) return console.log(name, " ".repeat(dots), desc); else return [ name, " ".repeat(dots), desc ]; } /** * Logs the message in two columns but allways passing status * * @param name * @param value * @param status * @param exit * @param preserveCol */ static split(name, value, status, exit = false, preserveCol = false) { status ??= "info"; const color = { success: chalk.default.bgGreen, info: chalk.default.bgBlue, error: chalk.default.bgRed }; const [_name, dots, val] = this.twoColumnDetail(name, value, false); console.log(this.textFormat(_name, color[status], preserveCol), dots, val); if (exit) process.exit(0); } /** * Wraps text with chalk * * @param txt * @param color * @param preserveCol * @returns */ static textFormat(txt, color, preserveCol = false) { const str = String(txt); if (preserveCol) return str; const [first, ...rest] = str.split(":"); if (rest.length === 0) return str; return color(` ${first} `) + rest.join(":"); } /** * Logs a success message * * @param msg * @param exit * @param preserveCol */ static success(msg, exit = false, preserveCol = false) { if (!this.shouldSuppressOutput("success")) console.log(chalk.default.green("✓"), this.textFormat(msg, chalk.default.bgGreen, preserveCol)); if (exit) process.exit(0); } /** * Logs an informational message * * @param msg * @param exit * @param preserveCol */ static info(msg, exit = false, preserveCol = false) { if (!this.shouldSuppressOutput("info")) console.log(chalk.default.blue("ℹ"), this.textFormat(msg, chalk.default.bgBlue, preserveCol)); if (exit) process.exit(0); } /** * Logs an error message * * @param msg * @param exit * @param preserveCol */ static error(msg, exit = true, preserveCol = false) { if (!this.shouldSuppressOutput("error")) if (msg instanceof Error) { if (msg.message) console.error(chalk.default.red("✖"), this.textFormat("ERROR:" + msg.message, chalk.default.bgRed, preserveCol)); console.error(chalk.default.red(`${msg.detail ? `${msg.detail}\n` : ""}${msg.stack}`)); } else console.error(chalk.default.red("✖"), this.textFormat(msg, chalk.default.bgRed, preserveCol)); if (exit) process.exit(1); } /** * Logs a warning message * * @param msg * @param exit * @param preserveCol */ static warn(msg, exit = false, preserveCol = false) { if (!this.shouldSuppressOutput("warn")) console.warn(chalk.default.yellow("⚠"), this.textFormat(msg, chalk.default.bgYellow, preserveCol)); if (exit) process.exit(0); } /** * Logs a debug message (only shown with verbosity >= 3) * * @param msg * @param exit * @param preserveCol */ static debug(msg, exit = false, preserveCol = false) { if (!this.shouldSuppressOutput("debug")) if (Array.isArray(msg)) for (let i = 0; i < msg.length; i++) console.log(chalk.default.gray("🐛"), chalk.default.bgGray(i + 1), this.textFormat(msg[i], chalk.default.bgGray, preserveCol)); else console.log(chalk.default.gray("🐛"), this.textFormat(msg, chalk.default.bgGray, preserveCol)); if (exit) process.exit(0); } /** * Terminates the process */ static quiet() { process.exit(0); } static chalker(styles) { return (input$1) => styles.reduce((acc, style) => { if (style in chalk.default) return (typeof style === "function" ? style : chalk.default[style])(acc); return acc; }, input$1); } static parse(config, joiner = " ", log = true, sc) { const string = config.map(([str, opt]) => { if (Array.isArray(opt)) opt = Logger.chalker(opt); const output = typeof opt === "string" && typeof chalk.default[opt] === "function" ? chalk.default[opt](str) : typeof opt === "function" ? opt(str) : str; if (!sc) return output; return this.textFormat(output, Logger.chalker(Array.isArray(sc) ? sc : [sc])); }).join(joiner); if (log && !this.shouldSuppressOutput("line")) console.log(string); else return string; } /** * Ouput formater object or format the output * * @returns */ static log = ((config, joiner, log = true, sc) => { if (typeof config === "string") { const conf = [[config, joiner]]; return this.parse(conf, "", log, sc); } else if (config) return this.parse(config, String(joiner), log, sc); return this; }); }; //#endregion //#region src/Utils/PathLoader.ts var PathLoader = class { paths = { base: "", views: "/src/resources/views", assets: "/public/assets", routes: "/src/routes", config: "/src/config", public: "/public", storage: "/storage", database: "/src/database" }; /** * Dynamically retrieves a path property from the class. * Any property ending with "Path" is accessible automatically. * * @param name - The base name of the path property * @param prefix - The base path to prefix to the path * @returns */ getPath(name, prefix) { let path$2; if (prefix && name !== "base") path$2 = path.default.join(prefix, this.paths[name]); else path$2 = this.paths[name]; if (name === "public") path$2 = path$2.replace("/public", path.default.join("/", process.env.DIST_DIR ?? ".h3ravel/serve")); else path$2 = path$2.replace("/src/", `/${process.env.DIST_DIR ?? ".h3ravel/serve"}/`.replace(/([^:]\/)\/+/g, "$1")); return path.default.normalize(path$2); } /** * Programatically set the paths. * * @param name - The base name of the path property * @param path - The new path * @param base - The base path to include to the path */ setPath(name, path$2, base) { if (base && name !== "base") this.paths[name] = path.default.join(base, path$2); this.paths[name] = path$2; } }; //#endregion //#region src/Utils/Prompts.ts var Prompts = class extends Logger { /** * Allows users to pick from a predefined set of choices when asked a question. */ static async choice(message, choices, defaultIndex) { return (0, __inquirer_prompts.select)({ message, choices, default: defaultIndex ? choices.at(defaultIndex) : void 0 }); } /** * Ask the user for a simple "yes or no" confirmation. * By default, this method returns `false`. However, if the user enters y or yes * in response to the prompt, the method would return `true`. */ static async confirm(message, def) { return (0, __inquirer_prompts.confirm)({ message, default: def }); } /** * Prompt the user with the given question, accept their input, * and then return the user's input back to your command. */ static async ask(message, def) { return (0, __inquirer_prompts.input)({ message, default: def }); } /** * Prompt the user with the given question, accept their input which * will not be visible to them as they type in the console, * and then return the user's input back to your command. */ static async secret(message, mask) { return (0, __inquirer_prompts.password)({ message, mask }); } /** * Provide auto-completion for possible choices. * The user can still provide any answer, regardless of the auto-completion hints. */ static async anticipate(message, source, def) { return (0, inquirer_autocomplete_standalone.default)({ message, source: Array.isArray(source) ? async (term) => { return (term ? source.filter((e) => e.includes(term)) : source).map((e) => ({ value: e })); } : source, suggestOnly: true, default: def }); } }; //#endregion //#region src/Utils/Resolver.ts var Resolver = class { static async getPakageInstallCommand(pkg) { const pm = (await (0, preferred_pm.default)(process.cwd()))?.name ?? "pnpm"; let cmd = "install "; if (!pkg) if (pm === "npm" || pm === "pnpm" || pm === "bun") cmd = "install"; else cmd = ""; else if (pm === "yarn" || pm === "pnpm" || pm === "bun") cmd = "add "; return `${pm} ${cmd}${pkg ?? ""}`; } static async getInstallCommand(pkg) { const pm = (await (0, preferred_pm.default)(process.cwd()))?.name ?? "pnpm"; let cmd = "install"; if (pm === "yarn" || pm === "pnpm") cmd = "add"; else if (pm === "bun") cmd = "create"; return `${pm} ${cmd} ${pkg}`; } /** * Create a hash for a function or an object * * @param provider * @returns */ static hashObjectOrFunction(provider) { return crypto.default.createHash("sha1").update(provider.toString()).digest("hex"); } /** * Checks if a function is asyncronous * * @param func * @returns */ static isAsyncFunction(func) { if (typeof func !== "function") return false; return Object.prototype.toString.call(func) === "[object AsyncFunction]"; } }; //#endregion //#region src/Utils/scripts.ts const mainTsconfig = { extends: "@h3ravel/shared/tsconfig.json", compilerOptions: { baseUrl: ".", outDir: "dist", paths: { "src/*": ["./../src/*"], "App/*": ["./../src/app/*"], "root/*": ["./../*"], "routes/*": ["./../src/routes/*"], "config/*": ["./../src/config/*"], "resources/*": ["./../src/resources/*"] }, target: "es2022", module: "es2022", moduleResolution: "Node", esModuleInterop: true, strict: true, allowJs: true, skipLibCheck: true, resolveJsonModule: true, noEmit: true, experimentalDecorators: true, emitDecoratorMetadata: true }, include: ["./**/*.d.ts", "./../**/*"], exclude: [ ".", "./../**/console/bin", "./../dist", "./../**/dist", "./../**/node_modules", "./../.node_modules", "./../**/node_modules/*", "./../**/public", "./../public", "./../**/storage", "./../storage", "./../**coverage**", "./../eslint.config.js", "./../jest.config.ts", "./../arquebus.config.js" ] }; const baseTsconfig = { extends: "./.h3ravel/tsconfig.json" }; const packageJsonScript = { build: "NODE_ENV=production tsdown --config-loader unconfig -c tsdown.default.config.ts", dev: "NODE_ENV=development pnpm tsdown --config-loader unconfig -c tsdown.default.config.ts", start: "DIST_DIR=dist node -r source-map-support/register dist/server.js", lint: "eslint . --ext .ts", test: "NODE_NO_WARNINGS=1 NODE_ENV=testing jest --passWithNoTests", postinstall: "pnpm prepare" }; //#endregion //#region src/Utils/TaskManager.ts var TaskManager = class { static async taskRunner(description, task) { const startTime = process.hrtime(); let result = false; try { result = await Promise.all([(task || (() => true))()].flat()); } finally { const endTime = process.hrtime(startTime); const duration = (endTime[0] * 1e9 + endTime[1]) / 1e6; Logger.twoColumnDetail(Logger.parse([[description, "green"]], "", false), [Logger.parse([[`${Math.floor(duration)}ms`, "gray"]], "", false), Logger.parse([[result !== false ? "✔" : "✘", result !== false ? "green" : "red"]], "", false)].join(" ")); } } static async advancedTaskRunner(info, task) { const startTime = process.hrtime(); const [startInfo, stopInfo] = info; if (stopInfo) Logger.twoColumnDetail(startInfo[0], Logger.log(startInfo[1], ["yellow", "bold"], false)); try { return await Promise.race([task()]); } catch (e) { Logger.error("ERROR: " + e.message); } finally { const endTime = process.hrtime(startTime); const duration = (endTime[0] * 1e9 + endTime[1]) / 1e6; Logger.twoColumnDetail(stopInfo?.[0] ?? startInfo[0], [Logger.parse([[`${Math.floor(duration)}ms`, "gray"]], "", false), Logger.parse([[`✔ ${stopInfo?.[1] ?? startInfo[1]}`, ["green", "bold"]]], "", false)].join(" ")); } } }; //#endregion exports.EnvParser = EnvParser; exports.FileSystem = FileSystem; exports.Logger = Logger; exports.PathLoader = PathLoader; exports.Prompts = Prompts; exports.Resolver = Resolver; exports.TaskManager = TaskManager; exports.baseTsconfig = baseTsconfig; exports.mainTsconfig = mainTsconfig; exports.packageJsonScript = packageJsonScript; //# sourceMappingURL=index.cjs.map