hermesx
Version:
Run TypeScript files with Hermes JS engine
204 lines (188 loc) • 6.1 kB
JavaScript
// Hermes globals to provide Node.js-like environment
// Helper function to format console arguments
function formatConsoleArgs(...args) {
return args
.map((arg) => {
if (arg === null) return "null";
if (arg === undefined) return "undefined";
if (typeof arg === "string") return arg;
if (typeof arg === "number" || typeof arg === "boolean")
return String(arg);
if (typeof arg === "function")
return `[Function: ${arg.name || "anonymous"}]`;
if (typeof arg === "object") {
try {
// Handle circular references and format objects nicely
return JSON.stringify(arg, null, 2);
} catch (e) {
// Fallback for circular references
return "[object Object]";
}
}
return String(arg);
})
.join(" ");
}
globalThis.console = {
log: function (...args) {
print(formatConsoleArgs(...args));
},
error: function (...args) {
print("ERROR: " + formatConsoleArgs(...args));
},
warn: function (...args) {
print("WARN: " + formatConsoleArgs(...args));
},
time: function (label) {
if (!globalThis._timers) {
globalThis._timers = new Map();
}
globalThis._timers.set(label || "default", Date.now());
},
timeEnd: function (label) {
if (!globalThis._timers) {
globalThis._timers = new Map();
}
const key = label || "default";
const startTime = globalThis._timers.get(key);
if (startTime !== undefined) {
const duration = Date.now() - startTime;
print(`${key}: ${duration}ms`);
globalThis._timers.delete(key);
} else {
print(`Timer '${key}' does not exist`);
}
},
timeLog: function (label, ...data) {
if (!globalThis._timers) {
globalThis._timers = new Map();
}
const key = label || "default";
const startTime = globalThis._timers.get(key);
if (startTime !== undefined) {
const duration = Date.now() - startTime;
const message = data.length > 0 ? ` ${data.join(" ")}` : "";
print(`${key}: ${duration}ms${message}`);
} else {
print(`Timer '${key}' does not exist`);
}
},
};
// Basic process object
globalThis.process = {
argv: ["hermes", "script.js"], // Will be updated by the runner
env: {},
exit: function (code) {
throw new Error("Process exit: " + (code || 0));
},
};
// Polyfill for setInterval using setTimeout
globalThis._intervals = new Map();
globalThis._intervalId = 1;
globalThis.setInterval = function (callback, delay, ...args) {
const id = globalThis._intervalId++;
let active = true;
function repeat() {
if (active) {
try {
callback(...args);
} catch (error) {
console.error("Error in setInterval callback:", error);
}
// Schedule next execution
if (active) {
setTimeout(repeat, delay);
}
}
}
// Store the active flag so we can stop it
globalThis._intervals.set(id, {
stop: () => {
active = false;
},
});
// Start the first execution
setTimeout(repeat, delay);
return id;
};
globalThis.clearInterval = function (id) {
if (globalThis._intervals.has(id)) {
const interval = globalThis._intervals.get(id);
interval.stop();
globalThis._intervals.delete(id);
}
};
// Performance measurement utilities for Hermes
// Since performance.now() is not available, we use Date.now() as fallback
// WARNING: This provides millisecond precision only, not the microsecond precision of native performance.now()
globalThis.performance = {
// Native performance.now() returns milliseconds with microsecond precision (e.g., 1234.567890)
// Date.now() only provides whole millisecond precision (e.g., 1234)
// We return Date.now() as-is since we cannot add precision we don't have
now: function () {
// Note: This returns whole milliseconds, not fractional like native performance.now()
// Libraries expecting sub-millisecond precision may not work correctly
return Date.now();
},
mark: function (name) {
if (!globalThis._performanceMarks) {
globalThis._performanceMarks = new Map();
}
globalThis._performanceMarks.set(name, Date.now());
},
measure: function (name, startMark, endMark) {
if (!globalThis._performanceMarks) {
globalThis._performanceMarks = new Map();
}
const start = globalThis._performanceMarks.get(startMark);
const end = globalThis._performanceMarks.get(endMark);
if (start !== undefined && end !== undefined) {
const duration = end - start;
console.log(`${name}: ${duration}ms`);
// Return object compatible with PerformanceMeasure interface
return {
name: name,
duration: duration, // milliseconds as number
startTime: start,
entryType: "measure",
detail: null,
};
} else {
console.error(
`Performance marks '${startMark}' or '${endMark}' not found`
);
return null;
}
},
// Add getEntries method for compatibility with performance API
getEntries: function () {
return []; // Simple implementation - could be enhanced
},
getEntriesByType: function (type) {
return []; // Simple implementation - could be enhanced
},
getEntriesByName: function (name) {
return []; // Simple implementation - could be enhanced
},
};
// Simple benchmark utility function
globalThis.benchmark = function (name, fn, iterations = 1) {
const results = [];
for (let i = 0; i < iterations; i++) {
const start = Date.now();
fn();
const end = Date.now();
results.push(end - start);
}
const total = results.reduce((sum, time) => sum + time, 0);
const average = total / iterations;
const min = Math.min(...results);
const max = Math.max(...results);
console.log(`Benchmark: ${name}`);
console.log(` Iterations: ${iterations}`);
console.log(` Total: ${total}ms`);
console.log(` Average: ${average.toFixed(2)}ms`);
console.log(` Min: ${min}ms`);
console.log(` Max: ${max}ms`);
return { name, iterations, total, average, min, max, results };
};