@js-temporal/polyfill
Version:
Polyfill for Temporal (https://github.com/tc39/proposal-temporal), an ECMA TC39 Stage 3 proposal
163 lines (151 loc) • 6.52 kB
text/typescript
import type JSBI from 'jsbi';
import type { Temporal } from '..';
import type { CalendarImpl } from './calendar';
import type { BuiltinCalendarId } from './internaltypes';
import type { DateTimeFormatImpl } from './intl';
import { DEBUG } from './debug';
import { GetSlot, ORIGINAL } from './slots';
type OmitConstructor<T> = { [P in keyof T as T[P] extends new (...args: any[]) => any ? P : never]: T[P] };
type TemporalIntrinsics = {
['Intl.DateTimeFormat']: typeof globalThis.Intl.DateTimeFormat;
['Temporal.Duration']: typeof Temporal.Duration;
['Temporal.Instant']: OmitConstructor<Temporal.Instant> &
(new (epochNanoseconds: JSBI) => Temporal.Instant) & { prototype: typeof Temporal.Instant.prototype };
['Temporal.PlainDate']: typeof Temporal.PlainDate;
['Temporal.PlainDateTime']: typeof Temporal.PlainDateTime;
['Temporal.PlainMonthDay']: typeof Temporal.PlainMonthDay;
['Temporal.PlainTime']: typeof Temporal.PlainTime;
['Temporal.PlainYearMonth']: typeof Temporal.PlainYearMonth;
['Temporal.ZonedDateTime']: OmitConstructor<Temporal.ZonedDateTime> &
(new (epochNanoseconds: JSBI, timeZone: string, calendar?: string) => Temporal.ZonedDateTime) & {
prototype: typeof Temporal.ZonedDateTime.prototype;
from: typeof Temporal.ZonedDateTime.from;
compare: typeof Temporal.ZonedDateTime.compare;
};
};
type TemporalIntrinsicRegistrations = {
[key in keyof TemporalIntrinsics]: TemporalIntrinsics[key];
};
type TemporalIntrinsicPrototypeRegistrations = {
[key in keyof TemporalIntrinsics as `${key}.prototype`]: TemporalIntrinsics[key]['prototype'];
};
type TemporalIntrinsicRegisteredKeys = {
[key in keyof TemporalIntrinsicRegistrations as `%${key}%`]: TemporalIntrinsicRegistrations[key];
};
type TemporalIntrinsicPrototypeRegisteredKeys = {
[key in keyof TemporalIntrinsicPrototypeRegistrations as `%${key}%`]: TemporalIntrinsicPrototypeRegistrations[key];
};
type OtherIntrinsics = {
calendarImpl: (id: BuiltinCalendarId) => CalendarImpl;
};
type OtherIntrinsicKeys = { [key in keyof OtherIntrinsics as `%${key}%`]: OtherIntrinsics[key] };
const INTRINSICS = {} as TemporalIntrinsicRegisteredKeys &
TemporalIntrinsicPrototypeRegisteredKeys &
OtherIntrinsicKeys;
type StylizeOption = (value: unknown, type: 'number' | 'special') => string;
type customFormatFunction<T> = (
this: T & { _repr_: string }, // _repr_ is present if DEBUG
depth: number,
options: { stylize: StylizeOption },
inspect: (object: T, options?: { depth: number; stylize: StylizeOption }) => string
) => string;
const customUtilInspectFormatters: Partial<{
[key in keyof TemporalIntrinsicRegistrations]: customFormatFunction<
InstanceType<TemporalIntrinsicRegistrations[key]>
>;
}> = {
['Intl.DateTimeFormat'](depth, options, inspect) {
return inspect(GetSlot(this as DateTimeFormatImpl, ORIGINAL), { depth, ...options });
},
['Temporal.Duration'](depth, options) {
const descr = options.stylize(this._repr_, 'special');
if (depth < 1) return descr;
const entries: string[] = [];
const props = [
'years',
'months',
'weeks',
'days',
'hours',
'minutes',
'seconds',
'milliseconds',
'microseconds',
'nanoseconds'
] as const;
for (let i = 0; i < props.length; i++) {
const prop = props[i];
if (this[prop] !== 0) {
entries.push(` ${prop}: ${options.stylize(this[prop], 'number')}`);
}
}
return descr + ' {\n' + entries.join(',\n') + '\n}';
}
};
type InspectFormatterOptions = { stylize: (str: string, styleType: string) => string };
function defaultUtilInspectFormatter(this: any, depth: number, options: InspectFormatterOptions) {
return options.stylize(this._repr_, 'special');
}
export function MakeIntrinsicClass(
Class: TemporalIntrinsicRegistrations[typeof name],
name: keyof TemporalIntrinsicRegistrations
) {
Object.defineProperty(Class.prototype, Symbol.toStringTag, {
value: name,
writable: false,
enumerable: false,
configurable: true
});
if (DEBUG) {
Object.defineProperty(Class.prototype, Symbol.for('nodejs.util.inspect.custom'), {
value: customUtilInspectFormatters[name] || defaultUtilInspectFormatter,
writable: false,
enumerable: false,
configurable: true
});
}
const staticNames = Object.getOwnPropertyNames(Class);
for (let i = 0; i < staticNames.length; i++) {
const prop = staticNames[i];
// we know that `prop` is present, so the descriptor is never undefined
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const desc = Object.getOwnPropertyDescriptor(Class, prop)!;
if (!desc.configurable || !desc.enumerable) continue;
desc.enumerable = false;
Object.defineProperty(Class, prop, desc);
}
const protoNames = Object.getOwnPropertyNames(Class.prototype);
for (let i = 0; i < protoNames.length; i++) {
const prop = protoNames[i];
// we know that `prop` is present, so the descriptor is never undefined
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const desc = Object.getOwnPropertyDescriptor(Class.prototype, prop)!;
if (!desc.configurable || !desc.enumerable) continue;
desc.enumerable = false;
Object.defineProperty(Class.prototype, prop, desc);
}
DefineIntrinsic(name, Class);
DefineIntrinsic(`${name}.prototype`, Class.prototype);
}
type IntrinsicDefinitionKeys =
| keyof TemporalIntrinsicRegistrations
| keyof TemporalIntrinsicPrototypeRegistrations
| keyof OtherIntrinsics;
export function DefineIntrinsic<KeyT extends keyof TemporalIntrinsicRegistrations>(
name: KeyT,
value: TemporalIntrinsicRegistrations[KeyT]
): void;
export function DefineIntrinsic<KeyT extends keyof TemporalIntrinsicPrototypeRegistrations>(
name: KeyT,
value: TemporalIntrinsicPrototypeRegistrations[KeyT]
): void;
export function DefineIntrinsic<KeyT extends keyof OtherIntrinsics>(name: KeyT, value: OtherIntrinsics[KeyT]): void;
export function DefineIntrinsic<KeyT>(name: KeyT, value: never): void;
export function DefineIntrinsic<KeyT extends IntrinsicDefinitionKeys>(name: KeyT, value: unknown): void {
const key: `%${IntrinsicDefinitionKeys}%` = `%${name}%`;
if (INTRINSICS[key] !== undefined) throw new Error(`intrinsic ${name} already exists`);
INTRINSICS[key] = value;
}
export function GetIntrinsic<KeyT extends keyof typeof INTRINSICS>(intrinsic: KeyT): (typeof INTRINSICS)[KeyT] {
return INTRINSICS[intrinsic];
}