import { core, primordials } from "ext:core/mod.js";
import {
op_fs_cwd,
op_fs_mkdir_sync,
op_fs_read_file_sync,
op_fs_remove_sync,
op_fs_write_file_sync,
op_require_path_resolve,
} from "ext:core/ops";
const {
MathImul,
ObjectFreeze,
SafeMap,
String,
StringPrototypeCharCodeAt,
StringPrototypeIncludes,
StringPrototypeReplaceAll,
StringPrototypeSlice,
StringPrototypeStartsWith,
StringPrototypeTrim,
TypeError,
} = primordials;
const compileCacheStatus = ObjectFreeze({
ENABLED: 0,
ALREADY_ENABLED: 1,
FAILED: 2,
DISABLED: 3,
});
const constants = ObjectFreeze({ compileCacheStatus });
let _enabled = false;
let _disabled = false;
let _directory = null;
let _bucket = null; let _portable = false;
let _baseDir = null;
let _debugEnabled = false;
let _envInitialized = false;
const _seen = new SafeMap();
function getProcessEnv(name) {
try {
const env = globalThis?.process?.env;
if (env === undefined) return undefined;
const v = env[name];
return v === undefined || v === null ? undefined : String(v);
} catch {
return undefined;
}
}
function fnv1a(str) {
let hLo = 0x811c9dc5 | 0;
let hHi = 0xcbf29ce4 | 0;
const len = str.length;
for (let i = 0; i < len; i++) {
const c = StringPrototypeCharCodeAt(str, i);
hLo = (hLo ^ (c & 0xff)) >>> 0;
hLo = MathImul(hLo, 0x01000193) >>> 0;
hHi = (hHi ^ ((c >>> 8) & 0xff)) >>> 0;
hHi = MathImul(hHi, 0x01000193) >>> 0;
}
const hex = (n) => {
let s = (n >>> 0).toString(16);
while (s.length < 8) s = "0" + s;
return s;
};
return hex(hHi) + hex(hLo);
}
function ensureBucket() {
if (_bucket !== null) return _bucket;
const tag = "deno-" + (globalThis?.Deno?.version?.deno ?? "0");
const name = fnv1a(tag);
_bucket = op_require_path_resolve([_directory, name]);
try {
op_fs_mkdir_sync(_directory, true, 0o777);
} catch { }
try {
op_fs_mkdir_sync(_bucket, true, 0o777);
} catch { }
return _bucket;
}
function debugWrite(msg) {
if (!_debugEnabled) return;
try {
const pid = globalThis?.Deno?.pid ?? 0;
core.print("[" + pid + ":Compile cache] " + msg + "\n", true);
} catch { }
}
function isDebugCompileCache() {
const flag = getProcessEnv("NODE_DEBUG_NATIVE");
if (!flag) return false;
return StringPrototypeIncludes(flag, "COMPILE_CACHE");
}
function cachePathFor(filename) {
let key = filename;
if (_portable && _directory) {
key = relativeTo(_directory, filename);
}
const name = fnv1a(key);
return op_require_path_resolve([ensureBucket(), name]);
}
function relativeTo(base, target) {
const normBase = StringPrototypeReplaceAll(String(base), "\\", "/");
const normTarget = StringPrototypeReplaceAll(String(target), "\\", "/");
const baseTrim = normBase.replace(/\/+$/, "");
const targetTrim = normTarget.replace(/\/+$/, "");
const baseSegs = baseTrim.split("/");
const targetSegs = targetTrim.split("/");
let common = 0;
while (
common < baseSegs.length &&
common < targetSegs.length &&
baseSegs[common] === targetSegs[common]
) {
common++;
}
const upSteps = baseSegs.length - common;
const downSegs = targetSegs.slice(common);
const parts = [];
for (let i = 0; i < upSteps; i++) parts.push("..");
for (const s of downSegs) parts.push(s);
return parts.length === 0 ? "." : parts.join("/");
}
function tryRead(path) {
try {
const data = op_fs_read_file_sync(path);
return data;
} catch {
return null;
}
}
function tryWrite(path, data) {
try {
op_fs_write_file_sync(path, undefined, false, true, false, data);
return true;
} catch {
return false;
}
}
function toUtf8(str) {
const enc = new globalThis.TextEncoder();
return enc.encode(str);
}
function fromUtf8(buf) {
const dec = new globalThis.TextDecoder();
return dec.decode(buf);
}
export function initFromEnv() {
if (_envInitialized) return;
_envInitialized = true;
_debugEnabled = isDebugCompileCache();
if (getProcessEnv("NODE_DISABLE_COMPILE_CACHE") === "1") {
_disabled = true;
debugWrite("Disabled by NODE_DISABLE_COMPILE_CACHE");
return;
}
const portable = getProcessEnv("NODE_COMPILE_CACHE_PORTABLE");
if (portable === "1") _portable = true;
const envDir = getProcessEnv("NODE_COMPILE_CACHE");
if (envDir && envDir.length > 0) {
doEnable(envDir);
}
}
function doEnable(dir) {
if (_enabled) return compileCacheStatus.ALREADY_ENABLED;
if (_disabled) return compileCacheStatus.DISABLED;
let resolved;
try {
resolved = op_require_path_resolve([op_fs_cwd(), dir]);
} catch {
resolved = dir;
}
try {
op_fs_mkdir_sync(resolved, true, 0o777);
} catch {
return compileCacheStatus.FAILED;
}
_enabled = true;
_directory = resolved;
_baseDir = op_fs_cwd();
ensureBucket();
return compileCacheStatus.ENABLED;
}
export function enableCompileCache(arg) {
initFromEnv();
let dir;
let portable = undefined;
if (arg === undefined) {
dir = undefined;
} else if (typeof arg === "string") {
dir = arg;
} else if (typeof arg === "object" && arg !== null) {
if ("directory" in arg) {
const d = arg.directory;
if (d !== undefined && typeof d !== "string") {
throw new TypeError(
'The "options.directory" property must be of type string. Received type ' +
typeof d,
);
}
dir = d;
}
if ("portable" in arg && arg.portable !== undefined) {
portable = !!arg.portable;
}
} else {
const err = new TypeError(
'The "cacheDir" argument must be of type string or undefined. Received type ' +
typeof arg,
);
err.code = "ERR_INVALID_ARG_TYPE";
throw err;
}
if (_disabled) {
return { status: compileCacheStatus.DISABLED };
}
if (_enabled) {
return {
status: compileCacheStatus.ALREADY_ENABLED,
directory: _directory,
};
}
let target = getProcessEnv("NODE_COMPILE_CACHE");
if (!target || target.length === 0) {
target = dir;
}
if (!target || target.length === 0) {
let tmp = getProcessEnv("TMPDIR") || getProcessEnv("TEMP") ||
getProcessEnv("TMP") || "/tmp";
target = op_require_path_resolve([tmp, "node-compile-cache"]);
}
try {
target = op_require_path_resolve([op_fs_cwd(), target]);
} catch { }
if (portable !== undefined) _portable = portable;
if (getProcessEnv("NODE_COMPILE_CACHE_PORTABLE") === "1") _portable = true;
const status = doEnable(target);
if (status === compileCacheStatus.ENABLED) {
return { status, directory: _directory };
}
if (status === compileCacheStatus.ALREADY_ENABLED) {
return { status, directory: _directory };
}
if (status === compileCacheStatus.DISABLED) {
return { status };
}
return {
status: compileCacheStatus.FAILED,
message: "Failed to create compile cache directory: " + target,
};
}
export function getCompileCacheDir() {
initFromEnv();
return _enabled ? _directory : undefined;
}
export function flushCompileCache() {
initFromEnv();
debugWrite("module.flushCompileCache() finished");
}
export function onCompile(filename, content, format) {
if (!_envInitialized) initFromEnv();
if (!_enabled || !_debugEnabled) return;
if (typeof filename !== "string" || typeof content !== "string") return;
if (StringPrototypeIncludes(filename, "ext:")) return;
if (
!StringPrototypeIncludes(filename, "/") &&
!StringPrototypeIncludes(filename, "\\")
) {
return;
}
const type = format === "module" ? "ESM" : "CommonJS";
const codeHash = fnv1a(content);
const cachePath = cachePathFor(filename);
const existing = tryRead(cachePath);
let cacheState;
if (existing === null) {
debugWrite(
"reading cache from " + cachePath + " for " + type + " " + filename +
", not_initialized",
);
debugWrite(
filename +
" was not initialized, initializing the in-memory entry",
);
cacheState = "missing";
} else {
const prev = StringPrototypeTrim(fromUtf8(existing));
if (prev === codeHash) {
debugWrite(
"reading cache from " + cachePath + " for " + type + " " + filename +
", success",
);
cacheState = "hit";
} else {
debugWrite(
"reading cache from " + cachePath + " for " + type + " " + filename +
", code hash mismatch: stored=" + prev + " current=" + codeHash,
);
cacheState = "mismatch";
}
}
_seen.set(filename, { codeHash, type, cachePath, cacheState });
}
export function onPersist(filename) {
if (!_enabled || !_debugEnabled) return;
const rec = _seen.get(filename);
if (!rec) return;
if (rec.cacheState === "hit") {
debugWrite(
"cache for " + filename + " was accepted, keeping the in-memory entry",
);
debugWrite(
"skip persisting " + rec.type + " " + filename +
" because cache was the same",
);
rec.persisted = false;
return;
}
const ok = tryWrite(rec.cachePath, toUtf8(rec.codeHash + "\n"));
rec.persisted = ok;
if (ok) {
debugWrite(
"writing cache for " + rec.type + " " + filename + ": success",
);
} else {
debugWrite(
"writing cache for " + rec.type + " " + filename + ": failed",
);
}
}
export function onCompileError(filename, format) {
if (!_enabled || !_debugEnabled) return;
if (typeof filename !== "string") return;
const rec = _seen.get(filename);
const type = rec?.type ?? (format === "module" ? "ESM" : "CommonJS");
if (rec?.persisted) {
try {
op_fs_remove_sync(rec.cachePath, false);
} catch { }
rec.persisted = false;
}
debugWrite(
"skip persisting " + type + " " + filename +
" because the cache was not initialized",
);
}
export { constants };