From 92e65808f8d77926a9b0db58e8bd382a1d5fd21f Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Mon, 11 Nov 2024 08:51:27 -0800 Subject: [PATCH 01/12] dark probe (#1802) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * dark probe * fix tests --------- Co-authored-by: Philippe Rivière --- src/client/stdlib/generators/dark.ts | 16 +++++++++++++--- .../_observablehq/stdlib/inputs.00000006.js | 0 .../{inputs.00000006.css => inputs.00000007.css} | 0 test/output/build/duckdb/index.html | 6 +++--- test/output/build/npm/_import/index.4bdc071f.js | 2 +- .../_observablehq/stdlib/inputs.00000005.js} | 0 .../_observablehq/stdlib/inputs.00000006.css | 0 test/output/build/npm/index.html | 6 +++--- .../_observablehq/stdlib/inputs.00000006.js} | 0 .../{inputs.00000007.js => inputs.00000007.css} | 0 test/output/build/sql/index.html | 6 +++--- 11 files changed, 23 insertions(+), 13 deletions(-) rename test/output/build/{npm => duckdb}/_observablehq/stdlib/inputs.00000006.js (100%) rename test/output/build/duckdb/_observablehq/stdlib/{inputs.00000006.css => inputs.00000007.css} (100%) rename test/output/build/{duckdb/_observablehq/stdlib/inputs.00000007.js => npm/_observablehq/stdlib/inputs.00000005.js} (100%) rename test/output/build/{sql => npm}/_observablehq/stdlib/inputs.00000006.css (100%) rename test/output/build/{npm/_observablehq/stdlib/inputs.00000005.css => sql/_observablehq/stdlib/inputs.00000006.js} (100%) rename test/output/build/sql/_observablehq/stdlib/{inputs.00000007.js => inputs.00000007.css} (100%) diff --git a/src/client/stdlib/generators/dark.ts b/src/client/stdlib/generators/dark.ts index 9996ec7d3..ae22c0e5a 100644 --- a/src/client/stdlib/generators/dark.ts +++ b/src/client/stdlib/generators/dark.ts @@ -1,18 +1,28 @@ import {observe} from "./observe.js"; // Watches dark mode based on theme and user preference. -// TODO: in preview, also watch for changes in the theme meta. export function dark() { return observe((notify: (dark: boolean) => void) => { let dark: boolean | undefined; const media = matchMedia("(prefers-color-scheme: dark)"); + const probe = document.createElement("div"); + probe.style.transitionProperty = "color, background-color"; + probe.style.transitionDuration = "1ms"; const changed = () => { - const d = getComputedStyle(document.body).getPropertyValue("color-scheme") === "dark"; + const s = getComputedStyle(document.body).getPropertyValue("color-scheme").split(/\s+/); + let d: boolean; + if (s.includes("light") && s.includes("dark")) d = media.matches; + else d = s.includes("dark"); if (dark === d) return; // only notify if changed notify((dark = d)); }; + document.body.appendChild(probe); changed(); + probe.addEventListener("transitionstart", changed); media.addEventListener("change", changed); - return () => media.removeEventListener("change", changed); + return () => { + probe.removeEventListener("transitionstart", changed); + media.removeEventListener("change", changed); + }; }); } diff --git a/test/output/build/npm/_observablehq/stdlib/inputs.00000006.js b/test/output/build/duckdb/_observablehq/stdlib/inputs.00000006.js similarity index 100% rename from test/output/build/npm/_observablehq/stdlib/inputs.00000006.js rename to test/output/build/duckdb/_observablehq/stdlib/inputs.00000006.js diff --git a/test/output/build/duckdb/_observablehq/stdlib/inputs.00000006.css b/test/output/build/duckdb/_observablehq/stdlib/inputs.00000007.css similarity index 100% rename from test/output/build/duckdb/_observablehq/stdlib/inputs.00000006.css rename to test/output/build/duckdb/_observablehq/stdlib/inputs.00000007.css diff --git a/test/output/build/duckdb/index.html b/test/output/build/duckdb/index.html index 9028d12f2..b0ceaf67b 100644 --- a/test/output/build/duckdb/index.html +++ b/test/output/build/duckdb/index.html @@ -8,15 +8,15 @@ - + - + - + diff --git a/test/output/build/npm/_import/index.4bdc071f.js b/test/output/build/npm/_import/index.4bdc071f.js index 9b6843f78..108b15a25 100644 --- a/test/output/build/npm/_import/index.4bdc071f.js +++ b/test/output/build/npm/_import/index.4bdc071f.js @@ -1 +1 @@ -import "../_observablehq/stdlib/inputs.00000006.js"; +import "../_observablehq/stdlib/inputs.00000005.js"; diff --git a/test/output/build/duckdb/_observablehq/stdlib/inputs.00000007.js b/test/output/build/npm/_observablehq/stdlib/inputs.00000005.js similarity index 100% rename from test/output/build/duckdb/_observablehq/stdlib/inputs.00000007.js rename to test/output/build/npm/_observablehq/stdlib/inputs.00000005.js diff --git a/test/output/build/sql/_observablehq/stdlib/inputs.00000006.css b/test/output/build/npm/_observablehq/stdlib/inputs.00000006.css similarity index 100% rename from test/output/build/sql/_observablehq/stdlib/inputs.00000006.css rename to test/output/build/npm/_observablehq/stdlib/inputs.00000006.css diff --git a/test/output/build/npm/index.html b/test/output/build/npm/index.html index 24dcc3f28..64771f980 100644 --- a/test/output/build/npm/index.html +++ b/test/output/build/npm/index.html @@ -7,15 +7,15 @@ - + - + - + ``` -
+
-Observable Cloud support for cross-origin resource sharing (CORS) is not yet generally available and is needed for exported modules. If you are interested in beta-testing this feature, please [email us](mailto:support@observablehq.com). For public apps, you can use a third-party host supporting CORS such as GitHub Pages. +Observable Cloud supports [cross-origin resource sharing](https://observablehq.com/documentation/data-apps/embeds#cors) (CORS), which is needed for exported modules.
From 30be23fec3a99d76f9c61be6875adabf98a926b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 22 Nov 2024 17:48:23 +0100 Subject: [PATCH 07/12] =?UTF-8?q?add=20lastModified=20to=20a=20module?= =?UTF-8?q?=E2=80=99s=20hash=20when=20it=20references=20a=20file=20attachm?= =?UTF-8?q?ent=20(#1837)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * when a module references a file attachment, include the file's lastModified timestamp in the module's hash. closes #1836 Note that this allows us to remove from the tests the ts rewrite that we used to do on modules, because their payload’s lastModified is now always set to the fake currentDate injected by the tests. Some tests don't want to use the fake date though! (I hope they never run in parallel?) * clear currentDate in after * prettier --------- Co-authored-by: Mike Bostock --- src/config.ts | 2 +- src/javascript/module.ts | 3 +- src/loader.ts | 2 +- test/build-test.ts | 4 +- test/config-test.ts | 1 + test/deploy-test.ts | 1 + test/input/build/params2/[code]/analytics.js | 3 ++ test/input/build/params2/[code]/data.json.js | 1 + test/input/build/params2/index.md | 5 ++ .../_import/import-test.3349a02d.js | 1 + .../_import/import-test.e7269c4e.js | 1 - .../{test.86a60bc6.js => test.a9a4ef0e.js} | 2 +- test/output/build/data-loaders/index.html | 6 +-- .../{chart.2ce91e05.js => chart.4140747c.js} | 2 +- test/output/build/embed/chart.js | 2 +- .../foo/{foo.666599bc.js => foo.0cc12e18.js} | 4 +- .../{top.c85e149a.js => top.bacf54cc.js} | 6 +-- test/output/build/fetches/foo.html | 4 +- test/output/build/fetches/top.html | 6 +-- .../params2/_file/code/data.015abd7f.json | 1 + .../_import/code/analytics.0d826e00.js | 3 ++ .../params2/_observablehq/client.00000001.js | 0 .../params2/_observablehq/runtime.00000002.js | 0 .../params2/_observablehq/stdlib.00000003.js | 0 .../theme-air,near-midnight.00000004.css | 0 test/output/build/params2/index.html | 46 +++++++++++++++++++ 26 files changed, 83 insertions(+), 23 deletions(-) create mode 100644 test/input/build/params2/[code]/analytics.js create mode 100644 test/input/build/params2/[code]/data.json.js create mode 100644 test/input/build/params2/index.md create mode 100644 test/output/build/data-loaders/_import/import-test.3349a02d.js delete mode 100644 test/output/build/data-loaders/_import/import-test.e7269c4e.js rename test/output/build/data-loaders/_import/{test.86a60bc6.js => test.a9a4ef0e.js} (76%) rename test/output/build/embed/_import/{chart.2ce91e05.js => chart.4140747c.js} (84%) rename test/output/build/fetches/_import/foo/{foo.666599bc.js => foo.0cc12e18.js} (61%) rename test/output/build/fetches/_import/{top.c85e149a.js => top.bacf54cc.js} (57%) create mode 100644 test/output/build/params2/_file/code/data.015abd7f.json create mode 100644 test/output/build/params2/_import/code/analytics.0d826e00.js create mode 100644 test/output/build/params2/_observablehq/client.00000001.js create mode 100644 test/output/build/params2/_observablehq/runtime.00000002.js create mode 100644 test/output/build/params2/_observablehq/stdlib.00000003.js create mode 100644 test/output/build/params2/_observablehq/theme-air,near-midnight.00000004.css create mode 100644 test/output/build/params2/index.html diff --git a/src/config.ts b/src/config.ts index bf063bc46..8e53688ce 100644 --- a/src/config.ts +++ b/src/config.ts @@ -233,7 +233,7 @@ function readPages(root: string, md: MarkdownIt): Page[] { return pages; } -let currentDate: Date | null = null; +export let currentDate: Date | null = null; /** For testing only! */ export function setCurrentDate(date: Date | null): void { diff --git a/src/javascript/module.ts b/src/javascript/module.ts index 46daa8f47..03f5e1f5a 100644 --- a/src/javascript/module.ts +++ b/src/javascript/module.ts @@ -6,6 +6,7 @@ import {extname, join} from "node:path/posix"; import type {Program} from "acorn"; import type {TransformOptions} from "esbuild"; import {transform, transformSync} from "esbuild"; +import {currentDate} from "../config.js"; import {resolveJsrImport} from "../jsr.js"; import {resolveNodeImport} from "../node.js"; import {resolveNpmImport} from "../npm.js"; @@ -199,7 +200,7 @@ export function getFileInfo(root: string, path: string): FileInfo | undefined { const stat = statSync(key); if (!stat.isFile()) return; // ignore non-files accessSync(key, constants.R_OK); // verify that file is readable - mtimeMs = Math.floor(stat.mtimeMs); + mtimeMs = Math.floor((currentDate ?? stat.mtimeMs) as number); size = stat.size; } catch { fileInfoCache.delete(key); // delete stale entry diff --git a/src/loader.ts b/src/loader.ts index 324f1a8e1..8e28ace79 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -300,7 +300,7 @@ export class LoaderResolver { getOutputFileHash(name: string): string { const info = this.getOutputInfo(name); if (!info) throw new Error(`output file not found: ${name}`); - return info.hash; + return createHash("sha256").update(info.hash).update(String(info.mtimeMs)).digest("hex"); } getSourceInfo(name: string): FileInfo | undefined { diff --git a/test/build-test.ts b/test/build-test.ts index da7726f6c..b477d58fd 100644 --- a/test/build-test.ts +++ b/test/build-test.ts @@ -32,6 +32,7 @@ const failureTests = ["missing-file", "missing-import"]; describe("build", () => { before(() => setCurrentDate(new Date("2024-01-10T16:00:00"))); + after(() => setCurrentDate(null)); mockJsDelivr(); mockJsr(); mockDuckDB(); @@ -233,9 +234,6 @@ class TestEffects extends FileBuildEffects { contents = contents.replace(/^(\s* + + +
+ +
+

test

+
+
+ +
+ + From 8efdcf45c00f7cb471deab01f65fe93e1b539d44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 22 Nov 2024 18:25:17 +0100 Subject: [PATCH 08/12] in build tests, sort by extension then path (#1809) * sort by extension then path this fixes the wobbling tests (duckdb, sql, npm) that change when a commit modifies the content hash of inputs.js vs inputs.css * win32 * adjust sort order to minimize churn * remove unused import --------- Co-authored-by: Mike Bostock --- test/build-test.ts | 8 ++++++-- .../{duckdb.00000005.js => duckdb.00000004.js} | 0 .../_observablehq/stdlib/inputs.00000005.js | 0 .../_observablehq/stdlib/inputs.00000006.css | 0 ...006.js => theme-air,near-midnight.00000007.css} | 0 test/output/build/duckdb/index.html | 12 ++++++------ test/output/build/npm/_import/index.4bdc071f.js | 2 +- .../_observablehq/stdlib/inputs.00000004.js} | 0 .../_observablehq/stdlib/inputs.00000005.css} | 0 ...04.css => theme-air,near-midnight.00000006.css} | 0 test/output/build/npm/index.html | 10 +++++----- .../{duckdb.00000005.js => duckdb.00000004.js} | 0 .../{inputs.00000006.js => inputs.00000005.js} | 0 .../{inputs.00000007.css => inputs.00000006.css} | 0 ...04.css => theme-air,near-midnight.00000007.css} | 0 test/output/build/sql/index.html | 14 +++++++------- 16 files changed, 25 insertions(+), 21 deletions(-) rename test/output/build/duckdb/_observablehq/stdlib/{duckdb.00000005.js => duckdb.00000004.js} (100%) rename test/output/build/{npm => duckdb}/_observablehq/stdlib/inputs.00000005.js (100%) rename test/output/build/{npm => duckdb}/_observablehq/stdlib/inputs.00000006.css (100%) rename test/output/build/duckdb/_observablehq/{stdlib/inputs.00000006.js => theme-air,near-midnight.00000007.css} (100%) rename test/output/build/{duckdb/_observablehq/stdlib/inputs.00000007.css => npm/_observablehq/stdlib/inputs.00000004.js} (100%) rename test/output/build/{duckdb/_observablehq/theme-air,near-midnight.00000004.css => npm/_observablehq/stdlib/inputs.00000005.css} (100%) rename test/output/build/npm/_observablehq/{theme-air,near-midnight.00000004.css => theme-air,near-midnight.00000006.css} (100%) rename test/output/build/sql/_observablehq/stdlib/{duckdb.00000005.js => duckdb.00000004.js} (100%) rename test/output/build/sql/_observablehq/stdlib/{inputs.00000006.js => inputs.00000005.js} (100%) rename test/output/build/sql/_observablehq/stdlib/{inputs.00000007.css => inputs.00000006.css} (100%) rename test/output/build/sql/_observablehq/{theme-air,near-midnight.00000004.css => theme-air,near-midnight.00000007.css} (100%) diff --git a/test/build-test.ts b/test/build-test.ts index b477d58fd..aade58fd6 100644 --- a/test/build-test.ts +++ b/test/build-test.ts @@ -2,9 +2,10 @@ import assert from "node:assert"; import {existsSync, readdirSync, statSync} from "node:fs"; import {mkdir, mkdtemp, open, readFile, rename, rm, unlink, writeFile} from "node:fs/promises"; import os from "node:os"; +import {extname} from "node:path/posix"; import {join, normalize, relative} from "node:path/posix"; import {PassThrough} from "node:stream"; -import {ascending, difference} from "d3-array"; +import {ascending, difference, sort} from "d3-array"; import type {BuildManifest} from "../src/build.js"; import {FileBuildEffects, build} from "../src/build.js"; import {normalizeConfig, readConfig, setCurrentDate} from "../src/config.js"; @@ -76,7 +77,10 @@ describe("build", () => { // renumber the hashes so they are sequential. This way we don’t have to // update the test snapshots whenever Framework’s client code changes. We // make an exception for minisearch.json because to test the content. - for (const path of findFiles(join(outputDir, "_observablehq"))) { + for (const path of sort( + findFiles(join(outputDir, "_observablehq")), + (a, b) => ascending(extname(a) === ".css", extname(b) === ".css") || ascending(a, b) + )) { const match = /^((.+)\.[0-9a-f]{8})\.(\w+)$/.exec(path); if (!match) throw new Error(`no hash found: ${path}`); const [, key, name, ext] = match; diff --git a/test/output/build/duckdb/_observablehq/stdlib/duckdb.00000005.js b/test/output/build/duckdb/_observablehq/stdlib/duckdb.00000004.js similarity index 100% rename from test/output/build/duckdb/_observablehq/stdlib/duckdb.00000005.js rename to test/output/build/duckdb/_observablehq/stdlib/duckdb.00000004.js diff --git a/test/output/build/npm/_observablehq/stdlib/inputs.00000005.js b/test/output/build/duckdb/_observablehq/stdlib/inputs.00000005.js similarity index 100% rename from test/output/build/npm/_observablehq/stdlib/inputs.00000005.js rename to test/output/build/duckdb/_observablehq/stdlib/inputs.00000005.js diff --git a/test/output/build/npm/_observablehq/stdlib/inputs.00000006.css b/test/output/build/duckdb/_observablehq/stdlib/inputs.00000006.css similarity index 100% rename from test/output/build/npm/_observablehq/stdlib/inputs.00000006.css rename to test/output/build/duckdb/_observablehq/stdlib/inputs.00000006.css diff --git a/test/output/build/duckdb/_observablehq/stdlib/inputs.00000006.js b/test/output/build/duckdb/_observablehq/theme-air,near-midnight.00000007.css similarity index 100% rename from test/output/build/duckdb/_observablehq/stdlib/inputs.00000006.js rename to test/output/build/duckdb/_observablehq/theme-air,near-midnight.00000007.css diff --git a/test/output/build/duckdb/index.html b/test/output/build/duckdb/index.html index b0ceaf67b..d470ee724 100644 --- a/test/output/build/duckdb/index.html +++ b/test/output/build/duckdb/index.html @@ -7,16 +7,16 @@ test DuckDB - - + + - - + + - - + + diff --git a/test/output/build/npm/_import/index.4bdc071f.js b/test/output/build/npm/_import/index.4bdc071f.js index 108b15a25..bddccaad0 100644 --- a/test/output/build/npm/_import/index.4bdc071f.js +++ b/test/output/build/npm/_import/index.4bdc071f.js @@ -1 +1 @@ -import "../_observablehq/stdlib/inputs.00000005.js"; +import "../_observablehq/stdlib/inputs.00000004.js"; diff --git a/test/output/build/duckdb/_observablehq/stdlib/inputs.00000007.css b/test/output/build/npm/_observablehq/stdlib/inputs.00000004.js similarity index 100% rename from test/output/build/duckdb/_observablehq/stdlib/inputs.00000007.css rename to test/output/build/npm/_observablehq/stdlib/inputs.00000004.js diff --git a/test/output/build/duckdb/_observablehq/theme-air,near-midnight.00000004.css b/test/output/build/npm/_observablehq/stdlib/inputs.00000005.css similarity index 100% rename from test/output/build/duckdb/_observablehq/theme-air,near-midnight.00000004.css rename to test/output/build/npm/_observablehq/stdlib/inputs.00000005.css diff --git a/test/output/build/npm/_observablehq/theme-air,near-midnight.00000004.css b/test/output/build/npm/_observablehq/theme-air,near-midnight.00000006.css similarity index 100% rename from test/output/build/npm/_observablehq/theme-air,near-midnight.00000004.css rename to test/output/build/npm/_observablehq/theme-air,near-midnight.00000006.css diff --git a/test/output/build/npm/index.html b/test/output/build/npm/index.html index 64771f980..a14a21894 100644 --- a/test/output/build/npm/index.html +++ b/test/output/build/npm/index.html @@ -6,16 +6,16 @@ - - + + - - + + - +