Repository navigation
Conversation
Why:
A binding printed as `Temporal` hides the global `Temporal` in its
scope. The bindings of the next commit are externals with dotted names
like "Temporal.PlainDate.from", which are printed as written: unlike
`@scope` externals (#8785), they are not read through `globalThis`
when a binding of the same name is in scope. A module named `Temporal`
next to a call of the bindings would then be called instead.
JS modified (module named Temporal):
Example:
type instant
@Val external now: unit => instant = "Temporal.Now.instant"
module Temporal = {
let zone = "UTC"
}
let stamp = () => now()
Before (js):
let Temporal = {
zone: "UTC"
};
function stamp() {
return Temporal.Now.instant(); // the module: a TypeError
}
export {
Temporal,
stamp,
}
After (js):
let $$Temporal = {
zone: "UTC"
};
function stamp() {
return Temporal.Now.instant(); // the global
}
export {
$$Temporal,
stamp,
}
Review: Why not read dotted externals through `globalThis` instead? That
changes every dotted external, not only Temporal's. A reserved name is
how `Intl` and the other built-ins are handled, and it also keeps raw
JavaScript next to the module reading the global. The next commit
tests it in `Stdlib_TemporalTests`.
Breaking: A nested module named `Temporal` is exported to JS as
`$$Temporal`, like one named `Intl`. JavaScript that imports it by name
imports `$$Temporal`.
Signed-off-by: mununki <woonki.moon@gmail.com>
Why:
JavaScript's Temporal API covers dates, times, time zones and
durations, which `Date` handles poorly, and Node.js 26 ships it. #9044
proposed stdlib bindings for it.
JS added (Temporal bindings):
Example:
let meeting = Temporal.ZonedDateTime.fromStringOrThrow(
"2024-03-09T09:30:00-05:00[America/New_York]",
)
let nextMeeting = meeting->Temporal.ZonedDateTime.addFields({days: 1.})
let length = meeting->Temporal.ZonedDateTime.until(nextMeeting)
let birthday = Temporal.PlainMonthDay.fromString("02-29")
let started = Date.make()->Date.toTemporalInstant
After (js):
import * as Stdlib_Temporal_PlainMonthDay from "@rescript/runtime/lib/es6/Stdlib_Temporal_PlainMonthDay.mjs";
let meeting = Temporal.ZonedDateTime.from("2024-03-09T09:30:00-05:00[America/New_York]");
let nextMeeting = meeting.add({
days: 1
});
let length = meeting.until(nextMeeting);
let birthday = Stdlib_Temporal_PlainMonthDay.fromString("02-29");
let started = new Date().toTemporalInstant();
Review: Why do `==` and `compare` treat any two Temporal values as equal?
Temporal objects keep their state in internal slots and have no
enumerable properties to compare. Each module's docs point to its
`equals` and `compare`; #9044 keeps `==` as it is.
Review: Why does `fromString` rethrow errors other than `RangeError`? Only
a `RangeError` means the string is invalid. Without the `Temporal`
global the call throws a `ReferenceError`, which `None` would hide.
Review: Why are the tests not in `Stdlib_TestSuite`? The installation
tests run that suite on Node.js 22, which lacks Temporal.
`Stdlib_TemporalTests` is its own entry point, which `scripts/test.js`
runs when `globalThis.Temporal` exists. On older Node.js versions,
`DocTest.res` now emits the doc examples it skips as `test.skip`, so
they are still type checked.
Breaking: The stdlib `Temporal` module shadows a project's own top-level
`Temporal` module, such as bindings to the Temporal.io SDK. Rename such
a module.
Signed-off-by: mununki <woonki.moon@gmail.com>
Why:
GenType emitted the Temporal types as types imported from `Stdlib.gen`
and `Stdlib_Temporal_Common.gen`, files that don't exist, so the
generated TypeScript didn't compile. TypeScript 6.0 declares the same
types in its `esnext.temporal` lib, which `ESNext` includes.
Representation modified (genType's TypeScript type of Temporal values):
Example:
@Gentype
let temporalDateOfDateTime = (x: Temporal.PlainDateTime.t) => x->Temporal.PlainDateTime.toPlainDate
@Gentype
let temporalNextDay = (x: Temporal.PlainDate.t) => x->Temporal.PlainDate.addFields({days: 1.})
Before (ts): tests/gentype_tests/stdlib-no-shims/src/StdlibNoShims.gen.tsx
import type {Temporal_PlainDateTime_t as Stdlib_Temporal_PlainDateTime_t} from './Stdlib.gen';
import type {Temporal_PlainDate_t as Stdlib_Temporal_PlainDate_t} from './Stdlib.gen';
import type {plainDate as Stdlib_Temporal_Common_plainDate} from './Stdlib_Temporal_Common.gen';
export const temporalDateOfDateTime: (x:Stdlib_Temporal_PlainDateTime_t) => Stdlib_Temporal_Common_plainDate = StdlibNoShimsJS.temporalDateOfDateTime as any;
export const temporalNextDay: (x:Stdlib_Temporal_PlainDate_t) => Stdlib_Temporal_PlainDate_t = StdlibNoShimsJS.temporalNextDay as any;
After (ts): tests/gentype_tests/stdlib-no-shims/src/StdlibNoShims.gen.tsx
export const temporalDateOfDateTime: (x:Temporal.PlainDateTime) => Temporal.PlainDate = StdlibNoShimsJS.temporalDateOfDateTime as any;
export const temporalNextDay: (x:Temporal.PlainDate) => Temporal.PlainDate = StdlibNoShimsJS.temporalNextDay as any;
Review: Why match the types of `Temporal.Common` too? Conversions like
`PlainDateTime.toPlainDate` return the shared types of
`Stdlib_Temporal_Common`, which the module types are defined as.
Signed-off-by: mununki <woonki.moon@gmail.com>
Changelog💥 Breaking Change
🚀 New Feature
Semantic Diff3 commits · Full page ✏️ Treat Temporal as a JS globalad770f4 · mununki · Oct 27, 2026, 23:27 UTC · edited
JS modified (module named Temporal) type instant
@val external now: unit => instant = "Temporal.Now.instant"
module Temporal = {
let zone = "UTC"
}
let stamp = () => now()
-let Temporal = {
+let $$Temporal = {
zone: "UTC"
};
function stamp() {
- return Temporal.Now.instant(); // the module: a TypeError
+ return Temporal.Now.instant(); // the global
}
export {
- Temporal,
+ $$Temporal,
stamp,
}
Review
✏️ Add Temporal bindings to the standard librarye2d13dd · mununki · Oct 27, 2026, 23:27 UTC · edited
JS added (Temporal bindings) let meeting = Temporal.ZonedDateTime.fromStringOrThrow(
"2024-03-09T09:30:00-05:00[America/New_York]",
)
let nextMeeting = meeting->Temporal.ZonedDateTime.addFields({days: 1.})
let length = meeting->Temporal.ZonedDateTime.until(nextMeeting)
let birthday = Temporal.PlainMonthDay.fromString("02-29")
let started = Date.make()->Date.toTemporalInstant
import * as Stdlib_Temporal_PlainMonthDay from "@rescript/runtime/lib/es6/Stdlib_Temporal_PlainMonthDay.mjs";
let meeting = Temporal.ZonedDateTime.from("2024-03-09T09:30:00-05:00[America/New_York]");
let nextMeeting = meeting.add({
days: 1
});
let length = meeting.until(nextMeeting);
let birthday = Stdlib_Temporal_PlainMonthDay.fromString("02-29");
let started = new Date().toTemporalInstant();
Review
✏️ Map Temporal types to TypeScript in GenTypea56765f · mununki · Oct 27, 2026, 23:27 UTC · edited
Representation modified (genType's TypeScript type of Temporal values) @genType
let temporalDateOfDateTime = (x: Temporal.PlainDateTime.t) => x->Temporal.PlainDateTime.toPlainDate
@genType
let temporalNextDay = (x: Temporal.PlainDate.t) => x->Temporal.PlainDate.addFields({days: 1.})
-import type {Temporal_PlainDateTime_t as Stdlib_Temporal_PlainDateTime_t} from './Stdlib.gen';
-import type {Temporal_PlainDate_t as Stdlib_Temporal_PlainDate_t} from './Stdlib.gen';
-import type {plainDate as Stdlib_Temporal_Common_plainDate} from './Stdlib_Temporal_Common.gen';
-
-export const temporalDateOfDateTime: (x:Stdlib_Temporal_PlainDateTime_t) => Stdlib_Temporal_Common_plainDate = StdlibNoShimsJS.temporalDateOfDateTime as any;
+export const temporalDateOfDateTime: (x:Temporal.PlainDateTime) => Temporal.PlainDate = StdlibNoShimsJS.temporalDateOfDateTime as any;
-export const temporalNextDay: (x:Stdlib_Temporal_PlainDate_t) => Stdlib_Temporal_PlainDate_t = StdlibNoShimsJS.temporalNextDay as any;
+export const temporalNextDay: (x:Temporal.PlainDate) => Temporal.PlainDate = StdlibNoShimsJS.temporalNextDay as any;Review
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #9094 +/- ##
==========================================
+ Coverage 81.42% 81.46% +0.03%
==========================================
Files 459 460 +1
Lines 64547 64675 +128
==========================================
+ Hits 52559 52689 +130
+ Misses 11988 11986 -2
🚀 New features to boost your workflow:
|
rescript
@rescript/belt
@rescript/darwin-arm64
@rescript/linux-arm64
@rescript/linux-x64
@rescript/runtime
@rescript/win32-x64
commit: |
|
Developer playground preview: https://rescript-lang.github.io/rescript/dev-playground/?version=pr-9094 |
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. Keep it up! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@mununki Looks great! Could you just ask you agent to fill out the commit records for semantic diff using the records skill as described in AGENTS.md? |
1c270f9 to
a56765f
Compare
|
@codex review |
Done, thanks! All three commits now have records, and the Semantic Diff shows them, with the two breaking changes as |
|
Codex Review: Didn't find any major issues. Bravo. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Great, thanks a lot! |
Implements the
Temporalbindings proposed in #9044. They are externals that compile to direct calls on the globalTemporal, so they need a runtime that provides it, such as Node.js 26, or a polyfill that definesglobalThis.Temporal.The PR has three commits:
Temporalis printed as$$Temporal, like one named after another standard built-in such asIntl. Without this, a module namedTemporalwould hide the global from the bindings: they are externals with dotted names like"Temporal.PlainDate.from", which, unlike@scopeexternals (Read JS globals through globalThis wherever a same-named binding is in scope #8785), aren't read throughglobalThis. Raw JavaScript next to such a binding reads the global as well.Temporal.*types, which need TypeScript 6.0 or later and itsesnext.temporallib (included inESNext).The design is the one in #9044:
Temporal.Duration,Instant,PlainDate,PlainTime,PlainDateTime,PlainYearMonth,PlainMonthDay,ZonedDateTimeandNow, one file each, aliased fromStdlib_Temporal.reslikeIntl. The value types are defined once inTemporal.Common, so the modules convert into each other without a dependency cycle, and@editor.completeFromkeeps pipe completion working on them.Date.toTemporalInstantconverts aDate.make,fromStringOrThrowandfromString,fromFields,addandaddFields,Duration.compareWithOptions.~optionsargument. Options that JS requires, like those ofroundandDuration.total, are a positional argument.float, date and time fieldsint, andepochNanosecondsisbigint. Nullable results are options.fromStringreturnsNonefor aRangeError.Changes since the RFC, following the review there:
==is unchanged. ReScript's==,compare,<and>treat any two Temporal values as equal, so each module's docs say to compare with itsequalsandcompare. JS has noDuration.prototype.equals: theDurationdocs point tocompareandcompareWithOptions, and note thatP1DandPT24Hcompare as equal. There is no field-wiseDuration.equal; adding one later wouldn't break anything.Stdlib_TemporalTestsis its own entry point, run byscripts/test.js, because the installation tests runStdlib_TestSuiteon Node.js 22. On a Node.js withoutTemporal,scripts/test.jsskips it with a warning, asDocTest.resdoes for doc examples.Breaking changes:
Temporalmodule shadows a project's own top-levelTemporalmodule, such as bindings to the Temporal.io SDK.Temporalis exported to JS as$$Temporal.Tests: 127 doc examples, 27 tests in
Stdlib_TemporalTests, the completion testCompletionTemporal.res, and a gentype test instdlib-no-shims.DocTest.resnow emits the doc examples it skips on older Node.js versions astest.skipinstead of dropping them, so they are still type checked there.Not included, as listed in #9044:
Duration.toLocaleStringoptions,largestUnit: "auto",relativeToas aPlainDateTimeor a string, string arguments toadd,subtractandcompare,fromwith another Temporal value, plural unit names,Intl.DateTimeFormat.formatwith Temporal values, andvalueOf.Closes #9044.
Checklist
changes/README.md), or no release notes are needed and a maintainer should applychangelog:skip.git commit -s).