UNPKG

poku

Version:

🐷 Poku makes testing easy for Node.js, Bun, Deno, and you at the same time.

151 lines (149 loc) 7.83 kB
#!/usr/bin/env node import { env, exit } from "node:process"; import { GLOBAL, log$1 as log, format, isWindows, hasArg, VERSION, getArg, getPaths, argToArray, states, hr, escapeRegExp, kill, envFile, poku } from "../modules/_shared.js"; import { existsSync } from "node:fs"; import { readFile } from "node:fs/promises"; import { join } from "node:path"; import "node:os"; import "node:child_process"; import "node:assert"; import "node:assert/strict"; import "node:net"; const getConfigs = async (customPath) => { const expectedFiles = customPath ? [customPath] : ["poku.config.js", ".pokurc.json", ".pokurc.jsonc"]; for (const file of expectedFiles) { const filePath = join(GLOBAL.cwd, file), path = isWindows ? `file://${filePath}` : filePath; try { if (!existsSync(filePath)) continue; if (filePath.endsWith("js") || filePath.endsWith("ts")) { const mod = await import(path); return mod?.default ?? mod; } const [{ JSONC }, configsFile] = await Promise.all([ import("./jsonc.js"), readFile(filePath, "utf8") ]); return JSONC.parse(configsFile); } catch (error) { log( `${format("\u26A0").bold()} Failed to load config file ${format(file).bold()}:` ); const message = error instanceof Error ? error.message : String(error); log(`${format(message).fail()}`); } } return /* @__PURE__ */ Object.create(null); }; (async () => { if (hasArg("version") || hasArg("v", "-")) { log(VERSION); return; } if (hasArg("help") || hasArg("h", "-")) { (await import("./help.js")).help(); return; } const enforce = hasArg("enforce") || hasArg("x", "-"), configFile = getArg("config") || getArg("c", "-"); GLOBAL.configsFromFile = await getConfigs(configFile); const { configsFromFile } = GLOBAL, dirs = getPaths("-") ?? (configsFromFile?.include ? Array.prototype.concat(configsFromFile?.include) : ["."]), filter = getArg("filter") ?? configsFromFile?.filter, exclude = getArg("exclude") ?? configsFromFile?.exclude, killPort = getArg("killPort"), killRange = getArg("killRange"), killPID = getArg("killPid"), reporter = getArg("reporter") ?? getArg("r", "-") ?? GLOBAL.configsFromFile.reporter ?? "poku", denoAllow = argToArray("denoAllow") ?? configsFromFile?.deno?.allow, denoDeny = argToArray("denoDeny") ?? configsFromFile?.deno?.deny, quiet = hasArg("quiet") || hasArg("q", "-") || configsFromFile?.quiet, debug = hasArg("debug") || hasArg("d", "-") || configsFromFile?.debug, failFast = hasArg("failFast") || configsFromFile?.failFast, watchMode = hasArg("watch") || hasArg("w", "-"), hasEnvFile = hasArg("envFile"), concurrency = (() => { const value = Number(getArg("concurrency")); return Number.isNaN(value) ? configsFromFile?.concurrency : value; })(), timeout = (() => { const value = Number(getArg("timeout")); return Number.isNaN(value) ? configsFromFile?.timeout : value; })(), sequential = hasArg("sequential") || configsFromFile?.sequential, isolation = getArg("isolation") || configsFromFile?.isolation, testNamePattern = getArg("testNamePattern") ?? getArg("t", "-") ?? configsFromFile?.testNamePattern, testSkipPattern = getArg("testSkipPattern") ?? configsFromFile?.testSkipPattern; if (dirs.length === 1 && (states.isSinglePath = !0), hasArg("listFiles")) { const { listFiles } = await import("../modules/_shared.js").then(function(n) { return n.listFiles$1; }), files = []; hr(); for (const dir of dirs) files.push( ...await listFiles(dir, { filter: typeof filter == "string" ? new RegExp(escapeRegExp(filter)) : filter, exclude: typeof exclude == "string" ? new RegExp(escapeRegExp(exclude)) : exclude }) ); log( files.sort().map((file) => `${format("-").dim()} ${file}`).join(` `) ), hr(), log(`Total test files: ${format(String(files.length)).bold()}`), hr(); return; } if (GLOBAL.configFile = configFile, env.POKU_RUNTIME = GLOBAL.runtime, env.POKU_REPORTER = typeof reporter == "string" ? reporter : "poku", GLOBAL.configs = { filter: typeof filter == "string" ? new RegExp(escapeRegExp(filter)) : filter, exclude: typeof exclude == "string" ? new RegExp(escapeRegExp(exclude)) : exclude, concurrency, timeout, sequential, isolation, quiet, debug, failFast, deno: { allow: denoAllow, deny: denoDeny }, noExit: watchMode, reporter, testNamePattern: typeof testNamePattern == "string" ? new RegExp(escapeRegExp(testNamePattern)) : testNamePattern, testSkipPattern: typeof testSkipPattern == "string" ? new RegExp(escapeRegExp(testSkipPattern)) : testSkipPattern, beforeEach: "beforeEach" in configsFromFile ? configsFromFile.beforeEach : void 0, afterEach: "afterEach" in configsFromFile ? configsFromFile.afterEach : void 0, plugins: "plugins" in configsFromFile ? configsFromFile.plugins : void 0 }, hasArg("coverage")) { const customPkg = getArg("coverage"); customPkg && !/^(@[a-z0-9][a-z0-9-_.]*\/)?[a-z0-9][a-z0-9-_.]*$/i.test(customPkg) && (log("Coverage plugin must be a valid package name."), exit(1)); const coveragePackages = customPkg ? [customPkg] : [ "@pokujs/c8", "@pokujs/monocart", "@pokujs/one-double-zero", "@pokujs/istanbul", "@pokujs/coverage" ], existingPlugins = GLOBAL.configs.plugins ?? []; if (!existingPlugins.some( (plugin) => plugin.name !== void 0 && coveragePackages.includes(plugin.name) )) { let loaded = !1; for (const pkg of coveragePackages) try { const { coverage } = await import(pkg); GLOBAL.configs.plugins = existingPlugins, GLOBAL.configs.plugins.push(coverage()), loaded = !0; break; } catch { } loaded || (hr(), log( customPkg ? `Coverage plugin not found: ${format(customPkg).bold()}` : `To use ${format("--coverage").bold()}, install a coverage plugin, for example: ${coveragePackages.map((pkg) => ` ${format("npm i -D").dim()} ${format(pkg).underline()}`).join(` `)}` ), hr(), exit(1)); } } typeof testNamePattern == "string" && (env.POKU_TEST_NAME_PATTERN = testNamePattern), typeof testSkipPattern == "string" && (env.POKU_TEST_SKIP_PATTERN = testSkipPattern); const tasks = []; if ((hasEnvFile || configsFromFile?.envFile) && (GLOBAL.envFile = getArg("envFile") ?? configsFromFile?.envFile ?? ".env"), enforce && (await import("./enforce.js")).enforce(), killPort || configsFromFile?.kill?.port) { const ports = killPort?.split(",").map(Number) || configsFromFile?.kill?.port || []; tasks.push(kill.port(ports)); } if (killRange || configsFromFile?.kill?.range) { const ranges = killRange?.split(",") || configsFromFile?.kill?.range?.map((range) => `${range[0]}-${range[1]}`) || []; for (const range of ranges) { const ports = range.split("-").map(Number), startsAt = ports[0], endsAt = ports[1]; tasks.push(kill.range(startsAt, endsAt)); } } if (killPID || configsFromFile?.kill?.pid) { const PIDs = killPID?.split(",").map(Number) || configsFromFile?.kill?.pid || []; tasks.push(kill.pid(PIDs)); } GLOBAL.envFile && tasks.push(envFile(GLOBAL.envFile)), (debug || configsFromFile?.debug) && (hr(), log(`${format(" Debug Enabled ").bg("brightBlue")} `), log(`${format("\u2026").info().italic()} ${format("Options").bold()}`), console.dir(GLOBAL.configs, { depth: Number.POSITIVE_INFINITY, colors: !0 }), log( ` ${format("\u{1F4A1}")} To list all test files, run: ${format("poku --listFiles").bold()}` )), await Promise.all(tasks), await poku(dirs), watchMode ? import("./watch.js").then((mod) => { mod.startWatch(dirs); }) : GLOBAL.runtime === "deno" && setImmediate(exit); })();