Skip to content

Commit dc57ba9

Browse files
committed
inspect: add Temporal support
Signed-off-by: Renegade334 <contact.9a5d6388@renegade334.me.uk>
1 parent e690df3 commit dc57ba9

1 file changed

Lines changed: 50 additions & 0 deletions

File tree

lib/internal/util/inspect.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,47 @@ const builtInObjects = new SafeSet(
218218
// https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot
219219
const isUndetectableObject = (v) => typeof v === 'undefined' && v !== undefined;
220220

221+
const temporalConstructors = [
222+
{ name: 'Duration', fullName: 'Temporal.Duration' },
223+
{ name: 'Instant', fullName: 'Temporal.Instant' },
224+
{ name: 'PlainDate', fullName: 'Temporal.PlainDate' },
225+
{ name: 'PlainDateTime', fullName: 'Temporal.PlainDateTime' },
226+
{ name: 'PlainMonthDay', fullName: 'Temporal.PlainMonthDay' },
227+
{ name: 'PlainTime', fullName: 'Temporal.PlainTime' },
228+
{ name: 'PlainYearMonth', fullName: 'Temporal.PlainYearMonth' },
229+
{ name: 'ZonedDateTime', fullName: 'Temporal.ZonedDateTime' },
230+
];
231+
232+
// Best-effort, using instanceof. Polyfills will likely pass these checks.
233+
// Returns void if either the type checks fail or the attempt at calling
234+
// .toString() fails, so that format() can fall back to standard object
235+
// formatting in either case.
236+
// TODO: Hopefully the V8 API will expose typechecks at some point.
237+
function getTemporalInfo(value) {
238+
try {
239+
const { Temporal } = globalThis;
240+
if (Temporal === undefined) {
241+
return;
242+
}
243+
for (let i = 0; i < temporalConstructors.length; i++) {
244+
const entry = temporalConstructors[i];
245+
const constructor = Temporal[entry.name];
246+
if (typeof constructor !== 'function') {
247+
return;
248+
}
249+
if (!FunctionPrototypeSymbolHasInstance(constructor, value)) {
250+
continue;
251+
}
252+
const { prototype } = constructor;
253+
if (!ObjectPrototypeHasOwnProperty(prototype, 'toString')) {
254+
return;
255+
}
256+
const result = FunctionPrototypeCall(prototype.toString, value);
257+
return { ...entry, result };
258+
}
259+
} catch { /* void */ }
260+
}
261+
221262
// These options must stay in sync with `getUserOptions`. So if any option will
222263
// be added or removed, `getUserOptions` must also be updated accordingly.
223264
const inspectDefaultOptions = ObjectSeal({
@@ -1321,6 +1362,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
13211362
if (noIterator) {
13221363
keys = getKeys(value, ctx.showHidden);
13231364
braces = ['{', '}'];
1365+
let temporalInfo;
13241366
if (typeof value === 'function') {
13251367
base = getFunctionBase(ctx, value, constructor, tag);
13261368
if (keys.length === 0 && protoProps === undefined)
@@ -1404,6 +1446,14 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
14041446
if (keys.length === 0 && protoProps === undefined) {
14051447
return base;
14061448
}
1449+
} else if ((temporalInfo = getTemporalInfo(value))) {
1450+
const prefix = constructor === temporalInfo.name && tag === temporalInfo.fullName ?
1451+
(temporalInfo.fullName + ' ') :
1452+
getPrefix(constructor, tag, temporalInfo.fullName);
1453+
base = `${prefix}${temporalInfo.result}`;
1454+
if (keys.length === 0 && protoProps === undefined) {
1455+
return ctx.stylize(base, 'date');
1456+
}
14071457
} else {
14081458
if (keys.length === 0 && protoProps === undefined) {
14091459
if (isExternal(value)) {

0 commit comments

Comments
 (0)