cfg-test
Version:
In-source testing using Node.js Test Runner
189 lines (188 loc) • 5.63 kB
JavaScript
// src/core/api.ts
import { existsSync, readFileSync } from "node:fs";
import { createRequire, register as load } from "node:module";
import { resolve } from "node:path";
import { sep } from "node:path";
import process from "node:process";
import { pathToFileURL } from "node:url";
import { testEnv } from "./define.mjs";
import * as log from "./log.mjs";
var ARGV = ["/path/to/node", "/path/to/file"];
var fileIndex = ARGV.indexOf("/path/to/file");
var cwd = process.cwd();
var cwdUrl = pathToFileURL(
cwd.endsWith(sep) ? sep : cwd + sep
);
var require2 = createRequire(cwdUrl);
var parentUrl = cwdUrl.toString();
function register(options = {}) {
const argv = options.argv || process.argv;
if (!(fileIndex in argv)) {
return;
}
const file = resolve(argv[fileIndex]);
const execArgv = options.execArgv || process.execArgv;
const nodeOptions = `,${process.env["NODE_OPTIONS"] ? execArgv.concat(process.
env["NODE_OPTIONS"].split(/\s/g)) : execArgv},`;
const isEsmMode = /,--import,cfg-test[,/]/.test(nodeOptions);
const isWatchMode = /,--watch,/.test(nodeOptions);
const isDTsFile = file.endsWith(".d.ts");
const isTypeScript = /\.[cm]?tsx?$/i.test(file);
log.debug(() => [
`esm mode -> ${isEsmMode}`,
`watch mode -> ${isWatchMode}`,
`typescript file -> ${isTypeScript}`,
`declare file -> ${isDTsFile}`,
`argv -> ${argv.map((a) => JSON.stringify(a)).join(" ")}`,
`execArgv -> ${execArgv.map((a) => JSON.stringify(a)).join(" ")}`,
`cwd -> ${JSON.stringify(cwd)}`,
`parentUrl -> ${JSON.stringify(parentUrl)}`,
`target file -> ${JSON.stringify(file)}`
]);
if (isEsmMode && false) {
log.error(() => ["Cannot import `cfg-test` in CommonJS"]);
process.exit(1);
}
const env = {
...testEnv,
CFG_TEST_CFG: process.env.CFG_TEST_CFG ?? `${[
".config/cfg-test",
".config/cfg-test/config",
"config/cfg-test",
"config/cfg-test/config",
"cfg-test"
]}`,
CFG_TEST_FILE: file
};
if (isEsmMode) {
Object.assign(env, {
CFG_TEST_URL: pathToFileURL(file)
});
}
Object.assign(env, {
CFG_TEST_WATCH: `${isWatchMode}`
});
const originalEnv = { ...process.env };
log.debug(
() => Object.entries(env).filter(([k, v]) => [void 0, v].includes(originalEnv[k])).
map(([k, v]) => `Added env.${k}=${JSON.stringify(v)} by cfg-test.`)
);
log.warn(
() => Object.entries(env).filter(([k, v]) => [void 0, v].every((v2) => v2 !==
originalEnv[k])).map(([k, v]) => `Updated env.${k}=${JSON.stringify(v)} by c\
fg-test.`)
);
Object.assign(process.env, env);
const cfgTest = new Proxy(require2("node:test"), {
get(target, p, receiver) {
switch (p) {
case "url":
return process.env.CFG_TEST_URL;
case "file":
return process.env.CFG_TEST_FILE;
case "watch":
return process.env.CFG_TEST_WATCH === "true";
case "assert":
return require2("node:assert/strict");
default:
return Reflect.get(target, p, receiver);
}
}
});
global.cfgTest = cfgTest;
let cfg;
for (const id of process.env.CFG_TEST_CFG.split(",")) {
const cfgPath = id.endsWith(".json") ? id : `${id}.json`;
if (existsSync(cfgPath)) {
cfg = JSON.parse(readFileSync(cfgPath, "utf8"));
break;
}
}
if (cfg && cfg.env) {
for (const [key, value] of Object.entries(cfg.env)) {
if (typeof value !== "string") {
continue;
}
if (process.env[key] === void 0) {
log.debug(() => [`Added env.${key} by config file.`]);
} else {
log.warn(() => [`Updated env.${key} by config file.`]);
}
process.env[key] = value;
}
}
if (cfg && cfg.globals) {
for (const [key, value] of Object.entries(cfg.globals)) {
if (key in global) {
log.warn(() => [`Updated global.${key} by config file.`]);
} else {
log.debug(() => [`Added global.${key} by config file.`]);
global[key] = value;
}
}
}
if (cfg && cfg.import) {
if (!Array.isArray(cfg.import)) {
cfg.import = [cfg.import];
}
for (const id of cfg.import) {
log.debug(() => [`Imported module ${id} by config file.`]);
load(id, parentUrl);
}
}
if (cfg && cfg.require) {
if (!Array.isArray(cfg.require)) {
cfg.require = [cfg.require];
}
for (const id of cfg.require) {
log.debug(() => [`Required module ${id} by config file.`]);
require2(id);
}
}
const ctx = {
log,
argv,
file,
execArgv,
isEsmMode,
parentUrl,
isWatchMode,
isTypeScript,
import(id) {
try {
log.debug(() => [`Register ESM module ${id}.`]);
load(id, parentUrl);
log.debug(() => [`Registered ESM module ${id}.`]);
} catch (e) {
log.error(() => [`Cannot register ESM module ${id}.`]);
throw e;
}
},
require(id, onLoad) {
try {
log.debug(() => [`Register CJS module ${id}`]);
const mod = require2(id);
log.debug(() => [`Loaded CJS module ${id}`]);
onLoad(mod);
log.debug(() => [`Registered CJS module ${id}`]);
} catch (e) {
log.error(() => [`Cannot register CJS module ${id}.`]);
throw e;
}
}
};
if (isDTsFile) {
if (ctx.isEsmMode) {
ctx.import("cfg-test/dts-loader");
} else {
require2("node:module")._extensions[".ts"] = () => "";
ctx.log.debug(() => ["Registered CJS module cfg-test/dts-loader."]);
}
return;
}
return ctx;
}
export {
register
};
//# sourceMappingURL=api.mjs.map