@lbu/stdlib
Version:
All kinds of utility functions
143 lines (122 loc) • 3.24 kB
JavaScript
import { lstatSync, realpathSync } from "fs";
import path from "path";
import { fileURLToPath } from "url";
import { setFlagsFromString } from "v8";
import { runInNewContext } from "vm";
import { newLogger } from "@lbu/insight";
import dotenv from "dotenv";
import { isProduction, refreshEnvironmentCache } from "./env.js";
import { AppError } from "./error.js";
import { isNil } from "./lodash.js";
/**
* @returns {number}
*/
export function getSecondsSinceEpoch() {
return Math.floor(Date.now() / 1000);
}
/**
* @returns {undefined}
*/
export function noop() {
return undefined;
}
/**
* Internal gc function reference
* Note that this is undefined if the gc function is not called and Node is not running
* with --expose-gc on
*/
let internalGc = global.gc;
/**
* HACKY
*/
export function gc() {
if (isNil(internalGc)) {
setFlagsFromString("--expose_gc");
internalGc = runInNewContext("gc");
}
internalGc();
}
/**
* @param {ImportMeta} meta
* @param {MainFnCallback} cb
*/
export function mainFn(meta, cb) {
const { isMainFn, name } = isMainFnAndReturnName(meta);
if (!isMainFn) {
return;
}
dotenv.config();
refreshEnvironmentCache();
const logger = newLogger({
ctx: { type: name },
pretty: !isProduction(),
});
const unhandled = (error) => {
logger.error(error);
process.exit(1);
};
process.on("unhandledRejection", (reason, promise) =>
unhandled({
reason: AppError.format(reason),
promise: AppError.format(promise),
}),
);
process.on("uncaughtExceptionMonitor", (error, origin) =>
logger.error({
error: AppError.format(error),
origin,
}),
);
process.on("warning", (warn) => logger.error(AppError.format(warn)));
Promise.resolve(cb(logger)).catch((e) => {
unhandled(AppError.format(e));
});
}
/**
* @param {ImportMeta} meta
* @returns {string}
*/
export function filenameForModule(meta) {
return fileURLToPath(meta.url);
}
/**
* @param {ImportMeta} meta
* @returns {string}
*/
export function dirnameForModule(meta) {
return path.dirname(filenameForModule(meta));
}
/**
* Checks if the provided meta.url is the process entrypoint and also returns the name of
* the entrypoint file
* @param {ImportMeta} meta
* @returns {{ isMainFn: boolean, name?: string}}
*/
export function isMainFnAndReturnName(meta) {
const modulePath = fileURLToPath(meta.url);
let scriptPath = process.argv[1];
// Support following symbolic links for node_modules/.bin items
const scriptStat = lstatSync(scriptPath);
if (scriptStat.isSymbolicLink()) {
scriptPath = realpathSync(scriptPath);
}
const scriptPathExt = path.extname(scriptPath);
if (scriptPathExt) {
return {
isMainFn: modulePath === scriptPath,
name: scriptPath
.substring(0, scriptPath.length - scriptPathExt.length)
.split(path.sep)
.pop(),
};
}
let modulePathWithoutExt = modulePath;
const modulePathExt = path.extname(modulePath);
if (modulePathExt) {
modulePathWithoutExt = modulePathWithoutExt.slice(0, -modulePathExt.length);
}
return {
isMainFn: modulePathWithoutExt === scriptPath,
name: modulePathWithoutExt.split(path.sep).pop(),
};
}