nstdlib-nightly
Version:
Node.js standard library converted to runtime-agnostic ES modules.
254 lines (224 loc) • 8.78 kB
JavaScript
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/process/execution.js
import * as __hoisted_internal_modules_cjs_loader__ from "nstdlib/lib/internal/modules/cjs/loader";
import * as __hoisted_internal_modules_esm_loader__ from "nstdlib/lib/internal/modules/esm/loader";
import * as __hoisted_internal_console_global__ from "nstdlib/lib/internal/console/global";
import * as __hoisted_timers__ from "nstdlib/lib/timers";
import * as path from "nstdlib/lib/path";
import { codes as __codes__ } from "nstdlib/lib/internal/errors";
import { pathToFileURL } from "nstdlib/lib/internal/url";
import { exitCodes as __exitCodes__ } from "nstdlib/stub/binding/errors";
import {
executionAsyncId,
clearDefaultTriggerAsyncId,
clearAsyncIdStack,
hasAsyncIdStack,
afterHooksExist,
emitAfter,
popAsyncContext,
} from "nstdlib/lib/internal/async_hooks";
import { containsModuleSyntax } from "nstdlib/stub/binding/contextify";
import { getOptionValue } from "nstdlib/lib/internal/options";
import {
makeContextifyScript,
runScriptInThisContext,
} from "nstdlib/lib/internal/vm";
import { shouldAbortOnUncaughtToggle } from "nstdlib/stub/binding/util";
import * as __hoisted_internal_modules_run_main__ from "nstdlib/lib/internal/modules/run_main";
const {
ERR_EVAL_ESM_CANNOT_PRINT,
ERR_INVALID_ARG_TYPE,
ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET,
} = __codes__;
const { kGenericUserError } = __exitCodes__;
// shouldAbortOnUncaughtToggle is a typed array for faster
// communication with JS.
function tryGetCwd() {
try {
return process.cwd();
} catch {
// getcwd(3) can fail if the current working directory has been deleted.
// Fall back to the directory name of the (absolute) executable path.
// It's not really correct but what are the alternatives?
return path.dirname(process.execPath);
}
}
let evalIndex = 0;
function getEvalModuleUrl() {
return pathToFileURL(`${process.cwd()}/[eval${++evalIndex}]`).href;
}
/**
* Evaluate an ESM entry point and return the promise that gets fulfilled after
* it finishes evaluation.
* @param {string} source Source code the ESM
* @param {boolean} print Whether the result should be printed.
* @returns {Promise}
*/
function evalModuleEntryPoint(source, print) {
if (print) {
throw new ERR_EVAL_ESM_CANNOT_PRINT();
}
RegExp.prototype.exec.call(/^/, ""); // Necessary to reset RegExp statics before user code runs.
return __hoisted_internal_modules_run_main__.runEntryPointWithESMLoader(
(loader) => loader.eval(source, getEvalModuleUrl(), true),
);
}
function evalScript(name, body, breakFirstLine, print, shouldLoadESM = false) {
const { Module: CJSModule } = __hoisted_internal_modules_cjs_loader__;
const cwd = tryGetCwd();
const origModule = globalThis.module; // Set e.g. when called from the REPL.
const module = new CJSModule(name);
module.filename = path.join(cwd, name);
module.paths = CJSModule._nodeModulePaths(cwd);
const baseUrl = pathToFileURL(module.filename).href;
if (
getOptionValue("--experimental-detect-module") &&
getOptionValue("--input-type") === "" &&
getOptionValue("--experimental-default-type") === "" &&
containsModuleSyntax(body, name, null, "no CJS variables")
) {
return ((...args) => globalThis.evalModuleEntryPoint(...args))(body, print);
}
const runScript = () => {
// Create wrapper for cache entry
const script = `
globalThis.module = module;
globalThis.exports = exports;
globalThis.__dirname = __dirname;
globalThis.require = require;
return (main) => main();
`;
globalThis.__filename = name;
RegExp.prototype.exec.call(/^/, ""); // Necessary to reset RegExp statics before user code runs.
const result = module._compile(
script,
`${name}-wrapper`,
)(() => {
const hostDefinedOptionId = Symbol(name);
async function importModuleDynamically(specifier, _, importAttributes) {
const cascadedLoader =
__hoisted_internal_modules_esm_loader__.getOrInitializeCascadedLoader();
return cascadedLoader.import(specifier, baseUrl, importAttributes);
}
const script = makeContextifyScript(
body, // code
name, // filename,
0, // lineOffset
0, // columnOffset,
undefined, // cachedData
false, // produceCachedData
undefined, // parsingContext
hostDefinedOptionId, // hostDefinedOptionId
importModuleDynamically, // importModuleDynamically
);
return runScriptInThisContext(script, true, !!breakFirstLine);
});
if (print) {
const { log } = __hoisted_internal_console_global__;
process.on("exit", () => {
log(result);
});
}
if (origModule !== undefined) globalThis.module = origModule;
};
if (shouldLoadESM) {
__hoisted_internal_modules_run_main__.runEntryPointWithESMLoader(runScript);
return;
}
runScript();
}
const exceptionHandlerState = {
captureFn: null,
reportFlag: false,
};
function setUncaughtExceptionCaptureCallback(fn) {
if (fn === null) {
exceptionHandlerState.captureFn = fn;
shouldAbortOnUncaughtToggle[0] = 1;
process.report.reportOnUncaughtException = exceptionHandlerState.reportFlag;
return;
}
if (typeof fn !== "function") {
throw new ERR_INVALID_ARG_TYPE("fn", ["Function", "null"], fn);
}
if (exceptionHandlerState.captureFn !== null) {
throw new ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET();
}
exceptionHandlerState.captureFn = fn;
shouldAbortOnUncaughtToggle[0] = 0;
exceptionHandlerState.reportFlag =
process.report.reportOnUncaughtException === true;
process.report.reportOnUncaughtException = false;
}
function hasUncaughtExceptionCaptureCallback() {
return exceptionHandlerState.captureFn !== null;
}
function noop() {}
// XXX(joyeecheung): for some reason this cannot be defined at the top-level
// and exported to be written to process._fatalException, it has to be
// returned as an *anonymous function* wrapped inside a factory function,
// otherwise it breaks the test-timers.setInterval async hooks test -
// this may indicate that node::errors::TriggerUncaughtException() should
// fix up the callback scope before calling into process._fatalException,
// or this function should take extra care of the async hooks before it
// schedules a setImmediate.
function createOnGlobalUncaughtException() {
// The C++ land node::errors::TriggerUncaughtException() will
// exit the process if it returns false, and continue execution if it
// returns true (which indicates that the exception is handled by the user).
return (er, fromPromise) => {
// It's possible that defaultTriggerAsyncId was set for a constructor
// call that threw and was never cleared. So clear it now.
clearDefaultTriggerAsyncId();
const type = fromPromise ? "unhandledRejection" : "uncaughtException";
process.emit("uncaughtExceptionMonitor", er, type);
if (exceptionHandlerState.captureFn !== null) {
exceptionHandlerState.captureFn(er);
} else if (!process.emit("uncaughtException", er, type)) {
// If someone handled it, then great. Otherwise, die in C++ land
// since that means that we'll exit the process, emit the 'exit' event.
try {
if (!process._exiting) {
process._exiting = true;
process.exitCode = kGenericUserError;
process.emit("exit", kGenericUserError);
}
} catch {
// Nothing to be done about it at this point.
}
return false;
}
// If we handled an error, then make sure any ticks get processed
// by ensuring that the next Immediate cycle isn't empty.
__hoisted_timers__.setImmediate(noop);
// Emit the after() hooks now that the exception has been handled.
if (afterHooksExist()) {
do {
const asyncId = executionAsyncId();
if (asyncId === 0) popAsyncContext(0);
else emitAfter(asyncId);
} while (hasAsyncIdStack());
}
// And completely empty the id stack, including anything that may be
// cached on the native side.
clearAsyncIdStack();
return true;
};
}
function readStdin(callback) {
process.stdin.setEncoding("utf8");
let code = "";
process.stdin.on("data", (d) => {
code += d;
});
process.stdin.on("end", () => {
callback(code);
});
}
export { readStdin };
export { tryGetCwd };
export { evalModuleEntryPoint };
export { evalScript };
const _export_onGlobalUncaughtException_ = createOnGlobalUncaughtException();
export { _export_onGlobalUncaughtException_ as onGlobalUncaughtException };
export { setUncaughtExceptionCaptureCallback };
export { hasUncaughtExceptionCaptureCallback };