Summary of the July-August 2024 TC39 plenary
The TC39 committee met again at the end of July, this time remotely, to discuss updated to various proposals at different stages of the standardization process. Let's go together through some of the most significant updates!
You can also read the full agenda and the meeting minutes on GitHub.
Day 1 #
Test262 Updates #
On the compilers team at Igalia we have several reviewers for Test262, which is the conformance test suite that ensures all JS implementations conform to the ECMA-262 specification. As of this TC39 meeting, RegExp.escape
, Atomics.pause
, Math.sumPrecise
, Base64, and source phase imports now have full or almost-full test coverage. Additionally, our team is working towards the goal of having a testing plan for each new proposal, to make it easier for people to write tests.
Intl.DurationFormat #
DurationFormat continues its approach to Stage 4 with a fix to an edge case involving the formatting of certain negative durations when using the "numeric"
or "two-digit"
styles. Previously if a zero-valued "numeric"
style unit was the first formatted unit in a negative duration, the negative sign would get dropped.
Drop assert from import attributes #
The import attributes proposal was originally called "import assertions" and had the following syntax:
import data from "./data.json" assert { type: "json" }
The semantics of this example is that "./data.json"
is fetched/interpreted ignoring the assertions (for example, web browsers would have known to parse the file as JSON because the server would use the application/json
MIME type). Then the assertions were applied, potentially throwing an error.
The proposal had then been updated from "assertions" to "attributes", and the keyword was changed to reflect the new semantics:
import data from "./data.json" with { type: "json" }
Attributes can affect how a module is loaded and interpreted, rather than just causing an error if it's the "wrong" module. Web browsers can send the proper HTTP headers to servers indicating that they expect a JSON response, and other non-HTTP-based platform can use attributes to decide how they want to parse the file.
Chrome, Node.js and Deno shipped the assert
keyword a while ago, so TC39 was not sure that removing it would have been web-compatible. Chrome succesfully unshipped support for assert
in Chrome 126, released in June, so now the proposal has been updated to only support with
.
AsyncContext Update #
The AsyncContext
allows defining variables that are implicitly propagated
following the "async flow" of your code:
const clickX = new AsyncContext.Variable();
document.addEventListener("click", e => {
clickX.run(e.clientX, async () => {
await timeout("30 seconds");
await doSomething();
})
});
async function doSomething() {
console.log("Doing something due to the click at x=" + clickX.get());
}
In the above example, doSomething
will log 30 seconds after each click, logging the x
coordinate of the mouse corresponding to the click that scheduled that doSomething
call.
The proposal, currently at stage 2, is proceeding steadily and the champions are currently figuring out how it should be integrated with other web APIs.
Deferred imports #
The import defer
proposal allows importing modules while deferring their execution until when it's needed, to reduce their impact on application startup time:
import defer * as mod from "./dep.js";
$button.addEventListener("click", () => {
console.log(mod.number); // ./dep.js is only evaluated at this point, when we need it.
})
The main difference between using import defer
and dynamic import()
is that the former can be used synchronously (without having to introduce promises or async
/await
), at the expense of only deferring code execution and not code loading.
During this meeting proposal reached Stage 2.7, which means that it's almost ready to be implemented (it's only missing test262 tests). The committee confirmed the semantics around top-level await: given that modules using top-level await cannot be executed asynchronously, they will be executed at startup even if they are part of a set of modules pulled in by an import defer
declaration.
Day 2 #
Intl Locale Info #
Frank Yung-Fong Tang of Google provided a minor update to the stage 3 Intl.LocaleInfo proposal, and received consensus for a PR fixing a minor bug with the handling of requests for the week of day and clarifying the result of using the "-u-ca-iso8601"
Unicode locale extension. He also gave an update on the implementation status of Intl.LocaleInfo
, noting that it is currently implemented in Chrome and Safari, with a Mozilla implementation pending.
Temporal Update #
Our colleague Philip Chimento presented another minor update on Temporal. There were two bug fixes to approve: one to correctly handle a corner case in the time zone database where the day started at half past midnight on March 31, 1919 in Toronto due to a time zone shift, and another to disallow a particular combination of options in Temporal.Duration.prototype.round()
where there were multiple interpretations possible for what the outcome should be.
Temporal is nearing completion; JS engines continue to work on implementing it. Firefox is the closest to having a complete implementation. There is a checklist that you can follow if you are interested in further updates on the TC39 side.
Fingerprinting discussion #
Ben Allen led a fruitful discussion on the potential for fingerprinting risk should browser implementations allow users to dynamically update locale-related information, either through adding new locales beyond the ones shipped with the browser or adding additional locale-related components, such as additional scripts or additional calendars. Including this feature would greatly improve the usability of the Web for users who speak minority languages -- but letting hosts know whether users have those non-base locales installed could result in those users having their privacy compromised in a way that in some cases could be genuinely dangerous for them.
Although no browsers currently allow for dynamic locale-related updates, it is a feature under consideration among multiple browser-makers. The conversation concluded with the decision for the relevant privacy teams to consider the matter and return with feedback.
RegExp.escape
for Stage 3 #
RegExp.escape
is a new utility for escaping strings so that they can be safely used in regular expression.
Consider the case where you have a string ".js"
, and you want to check wether another strings ends with that suffix using a regular expression. You might be tempted to write something like this:
const suffix = ".js";
const matches = new RegExp(`${suffix}$`).test(filename);
However, that code has a problem: .
has a special meaning in regular expressions, so matches
would also be true for something.notjs
! To fix this, you would need to escape the suffix
:
const suffix = ".js";
const matches = new RegExp(`${RegExp.escape(suffix)}$`).test(filename);
Thanks to the proposal's champion Jordan Harband the committee approved RegExp.escape
to advance to Stage 3, which means that browsers can now implement and ship it.
Day 3 #
Decimal #
Decimal champion Jesse Alama gave an update about the current status of the proposal. There were minor changes to the API; the main update was to make the spec text considerable sharper, thanks to close cooperation with new co-author Waldemar Horwat. Jesse asked for decimal to advance to stage 2 in the TC39 process, but it did not, because the argument that decimal, in its current form, is too close to just being a library. In the discussion, some concerns were raised that if we were to go ahead with the current Decimal128-based data model, in which trailing zeroes are preserved, we close the door to a potential future where decimal exists JavaScript as a primitive type. Our work in decimal continues -- stay tuned!