test(client): move the formatter benchmark onto the vitest 5 bench API

`bench` is no longer a top-level export; it is a test-context fixture whose
registrations `bench.compare` runs together. That reads better here than the
old shape did: every block in this file pairs a `formatters.ts` entry point
against a bare-`Intl` floor, which is the comparison `compare` exists to report.

This was the only real typecheck failure of the thirteen the vitest 5 bump
produced. `apps/client/tsconfig.app.json` includes `src/**/*.ts`, so this file
is part of the client app project, and a file that does not compile takes the
project's declaration emit down with it. Everything referencing those
declarations then degraded, which is where the other twelve came from:
`Parameters<typeof Component>[0]` collapsed to `unknown` in six specs,
`UniverLocaleSource` to `{}`, `contextMenu.show` and `executeBulkActions` lost
their signatures, and `Api` widened until build-docs' `@ts-expect-error` had
nothing left to suppress. All twelve go away with this one file fixed; none of
them needed a change of their own.

The imports are bound to locals because vitest 5 reports this file hitting
module-export getters per iteration. The overhead is asymmetric — the
`formatters.ts` side pays it and the floor, a local function, does not — so it
lands on the ratio the file exists to watch. Measured before and after, the
pairs stay at 1.24-1.28x, so the ~1.3x the header documents still holds. The
remaining warning is `options.ts`, read from inside `formatters.ts` itself.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Elian Doran
2026-09-07 23:19:53 +02:00
co-authored by Claude Opus 5
parent eed9684710
commit 1d94f21272
+54 -44
View File
@@ -26,10 +26,16 @@ vi.mock("../services/i18n", async () => {
};
});
import { bench, describe, vi } from "vitest";
import { test, vi } from "vitest";
import options from "../services/options";
import { formatDateNumeric, formatDateTime } from "./formatters";
import { formatDateNumeric as importedFormatDateNumeric, formatDateTime as importedFormatDateTime } from "./formatters";
// Vitest serves an import through a module-export getter, so naming one inside a benchmark pays for
// a getter on every iteration while the floor, a local function, pays nothing — which lands on the
// side of the pair being measured. Bind them once so the comparison is about formatting.
const formatDateTime = importedFormatDateTime;
const formatDateNumeric = importedFormatDateNumeric;
const LOCALE = "en-GB";
const DATE = new Date("2026-02-11T14:23:45Z");
@@ -41,50 +47,54 @@ options.set("formattingLocale", LOCALE);
const formatterCache = new Map<string, Intl.DateTimeFormat>();
describe("formatDateTime — date and time", () => {
bench("formatters.ts", () => {
formatDateTime(DATE, "short", "short");
});
bench("floor: bare cached Intl.DateTimeFormat", () => {
cachedFormatter(`${LOCALE}|short|short`, { dateStyle: "short", timeStyle: "short" }).format(DATE);
});
});
describe("formatDateTime — date only", () => {
bench("formatters.ts", () => {
formatDateTime(DATE, "short", "none");
});
bench("floor: bare cached Intl.DateTimeFormat", () => {
cachedFormatter(`${LOCALE}|short|none`, { dateStyle: "short" }).format(DATE);
});
});
describe("formatDateNumeric — with time (also probes the locale's hour cycle)", () => {
bench("formatters.ts", () => {
formatDateNumeric(DATE, true);
});
bench("floor: bare cached Intl.DateTimeFormat", () => {
cachedFormatter(`${LOCALE}|numeric|withTime`, {
year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit"
}).format(DATE);
});
});
describe(`board redraw — ${BOARD_CARDS} cards, one dated label each`, () => {
bench("formatters.ts", () => {
for (let i = 0; i < BOARD_CARDS; i++) {
test("formatDateTime — date and time", async ({ bench }) => {
await bench.compare(
bench("formatters.ts", () => {
formatDateTime(DATE, "short", "short");
}
});
bench("floor: bare cached Intl.DateTimeFormat", () => {
for (let i = 0; i < BOARD_CARDS; i++) {
}),
bench("floor: bare cached Intl.DateTimeFormat", () => {
cachedFormatter(`${LOCALE}|short|short`, { dateStyle: "short", timeStyle: "short" }).format(DATE);
}
});
})
);
});
test("formatDateTime — date only", async ({ bench }) => {
await bench.compare(
bench("formatters.ts", () => {
formatDateTime(DATE, "short", "none");
}),
bench("floor: bare cached Intl.DateTimeFormat", () => {
cachedFormatter(`${LOCALE}|short|none`, { dateStyle: "short" }).format(DATE);
})
);
});
test("formatDateNumeric — with time (also probes the locale's hour cycle)", async ({ bench }) => {
await bench.compare(
bench("formatters.ts", () => {
formatDateNumeric(DATE, true);
}),
bench("floor: bare cached Intl.DateTimeFormat", () => {
cachedFormatter(`${LOCALE}|numeric|withTime`, {
year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit"
}).format(DATE);
})
);
});
test(`board redraw — ${BOARD_CARDS} cards, one dated label each`, async ({ bench }) => {
await bench.compare(
bench("formatters.ts", () => {
for (let i = 0; i < BOARD_CARDS; i++) {
formatDateTime(DATE, "short", "short");
}
}),
bench("floor: bare cached Intl.DateTimeFormat", () => {
for (let i = 0; i < BOARD_CARDS; i++) {
cachedFormatter(`${LOCALE}|short|short`, { dateStyle: "short", timeStyle: "short" }).format(DATE);
}
})
);
});
/**