From 765082af574299a5a5467d485fd492cebf1be9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20J=C3=A4genstedt?= Date: Mon, 8 Jul 2024 14:17:12 +0200 Subject: [PATCH] Use a cutoff date from BCD for Baseline high Fixes https://github.com/web-platform-dx/web-features/issues/1331. --- .../src/baseline/date-utils.test.ts | 26 +++---- .../src/baseline/date-utils.ts | 6 -- .../src/baseline/index.test.ts | 27 +++---- .../compute-baseline/src/baseline/index.ts | 77 ++++++++----------- 4 files changed, 55 insertions(+), 81 deletions(-) diff --git a/packages/compute-baseline/src/baseline/date-utils.test.ts b/packages/compute-baseline/src/baseline/date-utils.test.ts index 7df14cad841..df417eaebdc 100644 --- a/packages/compute-baseline/src/baseline/date-utils.test.ts +++ b/packages/compute-baseline/src/baseline/date-utils.test.ts @@ -1,21 +1,19 @@ import assert from "node:assert/strict"; -import { Temporal } from "@js-temporal/polyfill"; -import { isFuture } from "./date-utils.js"; +import { toHighDate, toDateString } from "./date-utils.js"; -describe("isFuture", function () { - it("returns true for tomorrow", function () { - const tomorrow = Temporal.Now.plainDateISO().add({ days: 1 }); - assert(isFuture(tomorrow)); +describe("toHighDate", function () { + it("Jan 1", function () { + assert.equal(toDateString(toHighDate("2020-01-01")), "2022-07-01"); }); - - it("returns false for yesterday", function () { - const yesterday = Temporal.Now.plainDateISO().subtract({ days: 1 }); - assert(isFuture(yesterday) === false); + it("Feb 28", function () { + assert.equal(toDateString(toHighDate("2020-02-28")), "2022-08-28"); }); - - it("returns false for today", function () { - const now = Temporal.Now.plainDateISO(); - assert(isFuture(now) === false); + // Last date for a feature to become Baseline high in YYYY+2 instead of +3. + it("Jun 30", function () { + assert.equal(toDateString(toHighDate("2020-06-30")), "2022-12-30"); + }); + it("Dec 31", function () { + assert.equal(toDateString(toHighDate("2020-12-31")), "2023-06-30"); }); }); diff --git a/packages/compute-baseline/src/baseline/date-utils.ts b/packages/compute-baseline/src/baseline/date-utils.ts index a7c4788f1d8..f09e784e8b4 100644 --- a/packages/compute-baseline/src/baseline/date-utils.ts +++ b/packages/compute-baseline/src/baseline/date-utils.ts @@ -1,12 +1,6 @@ import { Temporal } from "@js-temporal/polyfill"; import { BASELINE_LOW_TO_HIGH_DURATION } from "./index.js"; -type LowDate = Temporal.PlainDate | string; - -export function isFuture(date: Temporal.PlainDate): boolean { - return Temporal.PlainDate.compare(Temporal.Now.plainDateISO(), date) < 0; -} - export function toHighDate( lowDate: Parameters[0], ): Temporal.PlainDate { diff --git a/packages/compute-baseline/src/baseline/index.test.ts b/packages/compute-baseline/src/baseline/index.test.ts index 5cbe64e6103..bb2a9433147 100644 --- a/packages/compute-baseline/src/baseline/index.test.ts +++ b/packages/compute-baseline/src/baseline/index.test.ts @@ -138,29 +138,32 @@ describe("computeBaseline", function () { }); describe("keystoneDateToStatus()", function () { - it('returns "low" for recent dates', function () { + it('returns "low" for date 1 year before cutoff date', function () { const status = keystoneDateToStatus( - Temporal.Now.plainDateISO().subtract({ days: 7 }), + Temporal.PlainDate.from("2020-01-01"), + Temporal.PlainDate.from("2021-01-01"), false, ); assert.equal(status.baseline, "low"); - assert.equal(typeof status.baseline_low_date, "string"); + assert.equal(status.baseline_low_date, "2020-01-01"); assert.equal(status.baseline_high_date, null); }); - it('returns "high" for long past dates', function () { + it('returns "high" for date 3 years before cutoff date', function () { const status = keystoneDateToStatus( Temporal.PlainDate.from("2020-01-01"), + Temporal.PlainDate.from("2024-01-01"), false, ); assert.equal(status.baseline, "high"); - assert.equal(typeof status.baseline_low_date, "string"); - assert.equal(typeof status.baseline_high_date, "string"); + assert.equal(status.baseline_low_date, "2020-01-01"); + assert.equal(status.baseline_high_date, "2022-07-01"); }); - it("returns false for future dates", function () { + it("returns false for null dates", function () { const status = keystoneDateToStatus( - Temporal.Now.plainDateISO().add({ days: 90 }), + null, + Temporal.PlainDate.from("2020-01-01"), false, ); assert.equal(status.baseline, false); @@ -168,15 +171,9 @@ describe("keystoneDateToStatus()", function () { assert.equal(status.baseline_high_date, null); }); - it("returns false for null dates", function () { - const status = keystoneDateToStatus(null, false); - assert.equal(status.baseline, false); - assert.equal(status.baseline_low_date, null); - assert.equal(status.baseline_high_date, null); - }); - it("returns false for discouraged (deprecated, obsolete, etc.) features", function () { const status = keystoneDateToStatus( + Temporal.PlainDate.from("2020-01-01"), Temporal.PlainDate.from("2020-01-01"), true, ); diff --git a/packages/compute-baseline/src/baseline/index.ts b/packages/compute-baseline/src/baseline/index.ts index 829dd9b3c39..98778de05a3 100644 --- a/packages/compute-baseline/src/baseline/index.ts +++ b/packages/compute-baseline/src/baseline/index.ts @@ -7,7 +7,7 @@ import { Compat, defaultCompat } from "../browser-compat-data/compat.js"; import { feature } from "../browser-compat-data/feature.js"; import { Release } from "../browser-compat-data/release.js"; import { browsers } from "./core-browser-set.js"; -import { isFuture, toDateString, toHighDate } from "./date-utils.js"; +import { toDateString, toHighDate } from "./date-utils.js"; import { support } from "./support.js"; interface Logger { @@ -90,6 +90,14 @@ export function computeBaseline( }, compat: Compat = defaultCompat, ): SupportDetails { + // A cutoff date approximating "now" is needed to determine when a feature has + // entered Baseline high. We use BCD's __meta.timestamp for this, but any + // "clock" based on the state of the tree that ticks frequently would work. + const timestamp: string = (compat.data as any).__meta.timestamp; + const cutoffDate = Temporal.Instant.from(timestamp) + .toZonedDateTimeISO("UTC") + .toPlainDate(); + const { compatKeys } = featureSelector; const keys = featureSelector.checkAncestors ? compatKeys.flatMap((key) => withAncestors(key, compat)) @@ -101,11 +109,9 @@ export function computeBaseline( const keystoneDate = findKeystoneDate( statuses.flatMap((s) => [...s.support.values()]), ); - const { baseline, baseline_low_date, baseline_high_date, discouraged } = - keystoneDateToStatus( - keystoneDate, - statuses.some((s) => s.discouraged), - ); + const discouraged = statuses.some((s) => s.discouraged); + const { baseline, baseline_low_date, baseline_high_date } = + keystoneDateToStatus(keystoneDate, cutoffDate, discouraged); return { baseline, @@ -123,24 +129,12 @@ export function computeBaseline( * Compute the Baseline support ("high", "low" or false, dates, and releases) * for a single compat key. */ -function calculate(compatKey: string, compat: Compat): SupportDetails { +function calculate(compatKey: string, compat: Compat) { const f = feature(compatKey); - const s = support(f, browsers(compat)); - const keystoneDate = findKeystoneDate([...s.values()]); - - const { baseline, baseline_low_date, baseline_high_date, discouraged } = - keystoneDateToStatus(keystoneDate, f.deprecated ?? false); return { - compatKey, - baseline, - baseline_low_date, - baseline_high_date, - discouraged, - support: s, - toJSON: function () { - return jsonify(this); - }, + discouraged: f.deprecated ?? false, + support: support(f, browsers(compat)), }; } @@ -202,41 +196,32 @@ function collateSupport( */ export function keystoneDateToStatus( date: Temporal.PlainDate | null, + cutoffDate: Temporal.PlainDate, discouraged: boolean, ): { baseline: BaselineStatus; baseline_low_date: BaselineDate; baseline_high_date: BaselineDate; - discouraged: boolean; } { - let baseline: BaselineStatus; - let baseline_low_date; - let baseline_high_date; - - if (discouraged || date === null || isFuture(date)) { - baseline = false; - baseline_low_date = null; - baseline_high_date = null; - discouraged = discouraged; - } else { - baseline = "low"; - baseline_low_date = toDateString(date); - baseline_high_date = null; - discouraged = false; + if (date == null || discouraged) { + return { + baseline: false, + baseline_low_date: null, + baseline_high_date: null, + }; } - if (baseline === "low") { - assert(date !== null); - const possibleHighDate = toHighDate(date); - if (isFuture(possibleHighDate)) { - baseline_high_date = null; - } else { - baseline = "high"; - baseline_high_date = toDateString(possibleHighDate); - } + let baseline: BaselineStatus = "low"; + let baseline_low_date: BaselineDate = toDateString(date); + let baseline_high_date: BaselineDate = null; + + const possibleHighDate = toHighDate(date); + if (Temporal.PlainDate.compare(possibleHighDate, cutoffDate) <= 0) { + baseline = "high"; + baseline_high_date = toDateString(possibleHighDate); } - return { baseline, baseline_low_date, baseline_high_date, discouraged }; + return { baseline, baseline_low_date, baseline_high_date }; } /**