Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use observablehq:stdlib instead of npm:@observablehq/stdlib #1670

Merged
merged 4 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/chart.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {FileAttachment} from "npm:@observablehq/stdlib";
import * as Plot from "npm:@observablehq/plot";
import {FileAttachment} from "observablehq:stdlib";

export async function Chart() {
const gistemp = await FileAttachment("./lib/gistemp.csv").csv({typed: true});
Expand Down
2 changes: 1 addition & 1 deletion docs/embeds.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ Embedded modules are vanilla JavaScript, and behave identically when embedded in
Embedded modules are often written as component functions that return DOM elements. These functions can take options (or “props”), and typically load their own data. For example, below is a simple `chart.js` module that exports a `Chart` function that renders a scatterplot of global surface temperature data.

```js run=false
import {FileAttachment} from "npm:@observablehq/stdlib";
import * as Plot from "npm:@observablehq/plot";
import {FileAttachment} from "observablehq:stdlib";

export async function Chart() {
const gistemp = await FileAttachment("./lib/gistemp.csv").csv({typed: true});
Expand Down
2 changes: 1 addition & 1 deletion docs/files.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ keywords: file, fileattachment, attachment
Load files — whether static or generated dynamically by a [data loader](./data-loaders) — using the built-in `FileAttachment` function. This is available by default in Markdown, but you can import it explicitly like so:

```js echo
import {FileAttachment} from "npm:@observablehq/stdlib";
import {FileAttachment} from "observablehq:stdlib";
```

The `FileAttachment` function takes a path and returns a file handle. This handle exposes:
Expand Down
21 changes: 16 additions & 5 deletions docs/imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ Imported symbols can be referenced in any code block or inline expression — no
Inputs.button("Throw confetti! 🎉", {reduce: () => confetti()})
```


<div class="tip">While imports can live in code blocks anywhere on the page, by convention imports are placed at the top of pages for readability.</div>

Framework provides a variety of ways to import. When you reference `d3`, `Inputs`, `Plot` or some other built-in, you’re [implicitly importing](#implicit-imports) from npm. In addition, you can import modules explicitly from:
Expand Down Expand Up @@ -130,7 +129,7 @@ import {foo} from "./foo.js";
Within a local module, you can import other local modules, as well as `npm:`, Node, and remote imports. You can also reference local files within a local module by importing [`FileAttachment`](./files) from the Observable standard library like so:

```js run=false
import {FileAttachment} from "npm:@observablehq/stdlib";
import {FileAttachment} from "observablehq:stdlib";

export const sales = await FileAttachment("sales.csv").csv({typed: true});
```
Expand Down Expand Up @@ -206,15 +205,23 @@ Module preloading does not apply to [dynamic imports](#dynamic-imports) and [`im
<link rel="modulepreload" href="npm:d3-array">
```

## Observable imports

Framework includes a few built-in libraries implemented by Framework itself that can be imported via the `observablehq:` protocol. This currently includes:

* `observablehq:runtime` - the [Observable Runtime](https://github.com/observablehq/runtime)
* `observablehq:stdlib` - [Framework’s standard library](https://github.com/observablehq/framework/blob/main/src/client/stdlib.js)

## Implicit imports

For convenience, Framework provides recommended libraries by default in Markdown. These implicit imports are only evaluated if you reference the corresponding symbol and hence don’t add overhead if you don’t use them; for example, D3 won’t be loaded unless you reference `d3`.

Click on any of the imported symbols below to learn more.

<pre><code class="language-js">import {<a href="./files">FileAttachment</a>} from "npm:@observablehq/stdlib";</code></pre>
<pre><code class="language-js">import {<a href="./reactivity#generators">Generators</a>} from "npm:@observablehq/stdlib";</code></pre>
<pre><code class="language-js">import {<a href="./reactivity#mutables">Mutable</a>} from "npm:@observablehq/stdlib";</code></pre>
<pre><code class="language-js">import {<a href="./files">FileAttachment</a>} from "observablehq:stdlib";</code></pre>
<pre><code class="language-js">import {<a href="./reactivity#generators">Generators</a>} from "observablehq:stdlib";</code></pre>
<pre><code class="language-js">import {<a href="./reactivity#mutables">Mutable</a>} from "observablehq:stdlib";</code></pre>
<pre><code class="language-js">import {<a href="./javascript#resize-render">resize</a>} from "observablehq:stdlib";</code></pre>
<pre><code class="language-js">import <a href="./lib/dot">dot</a> from "npm:@observablehq/dot";</code></pre>
<pre><code class="language-js">import * as <a href="./lib/duckdb">duckdb</a> from "npm:@duckdb/duckdb-wasm";</code></pre>
<pre><code class="language-js">import {<a href="./lib/duckdb">DuckDBClient</a>} from "npm:@observablehq/duckdb";</code></pre>
Expand All @@ -235,8 +242,12 @@ Click on any of the imported symbols below to learn more.
<pre><code class="language-js">import {<a href="./lib/htl">svg</a>} from "npm:htl";</code></pre>
<pre><code class="language-js">import * as <a href="./lib/leaflet">L</a> from "npm:leaflet";</code></pre>
<pre><code class="language-js">import <a href="../lib/lodash">_</a> from "npm:lodash";</code></pre>
<pre><code class="language-js">import * as <a href="./jsx">React</a> from "npm:react";</code></pre>
<pre><code class="language-js">import * as <a href="./jsx">ReactDOM</a> from "npm:react-dom";</code></pre>
<pre><code class="language-js">import * as <a href="../lib/topojson">topojson</a> from "npm:topojson-client";</code></pre>

In addition to the above, several implicit imports have slightly more involved definitions: [`now`](./lib/generators#now), [`width`](./lib/generators#width-element), [`dark`](./lib/generators#dark), [`vg`](./lib/mosaic), and [`vl`](./lib/vega-lite).

## Require

If you’re familiar with Observable notebooks, you may be familiar with `require`. We recommend that you avoid `require` as the underlying Asynchronous Module Definition (AMD) convention has been made obsolete by standard imports in JavaScript, and AMD tends to be implemented inconsistently.
Expand Down
6 changes: 5 additions & 1 deletion docs/lib/generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ keywords: [dark, width]
The Observable standard library includes several generator utilities. These are available by default in Markdown as `Generators`, but you can import them explicitly:

```js echo
import {Generators} from "npm:@observablehq/stdlib";
import {Generators} from "observablehq:stdlib";
```

## input(*element*)
Expand Down Expand Up @@ -85,6 +85,10 @@ width

[Source](https://github.com/observablehq/framework/blob/main/src/client/stdlib/generators/dark.ts) · Returns an async generator that yields a boolean indicating whether the page is currently displayed with a dark [color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme).

```js run=false
const dark = Generators.dark();
```

If the page supports both light and dark mode (as with the [default theme](../themes)), the value reflects the user’s [preferred color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). The generator will yield a new value if the preferred color changes — as when the user changes their system settings, or if the user’s system adapts automatically to the diurnal cycle — allowing you to update the display as needed without requiring a page reload.

If the page only supports light mode, the value is always false; likewise it is always true if the page only has a dark theme.
Expand Down
12 changes: 12 additions & 0 deletions docs/lib/mosaic.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ ${histogram}

The views above are coordinated: brushing a time window in the histogram, or a region in either map, will filter both maps. _What spatial patterns can you find?_

Mosaic vgplot is available by default in Markdown as `vg` and is backed by the default DuckDB client that is configured using [SQL front matter](../sql). If you would prefer to initialize Mosaic yourself, you can do something like:

```js run=false
import {DuckDBClient} from "npm:@observablehq/duckdb";
import * as vgplot from "npm:@uwdata/vgplot";

const db = await DuckDBClient.of({trips: FileAttachment("nyc-taxi.parquet")});
const coordinator = new vgplot.Coordinator();
coordinator.databaseConnector(vgplot.wasmConnector({duckdb: db._db}));
const vg = vgplot.createAPIContext({coordinator});
```

The code below creates three views, coordinated by Mosaic’s [crossfilter](https://uwdata.github.io/mosaic/api/core/selection.html#selection-crossfilter) helper.

```js echo
Expand Down
2 changes: 1 addition & 1 deletion docs/reactivity.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ Normally, only the code block that declares a top-level variable can define it o
`Mutable` is available by default in Markdown but you can import it explicitly like so:

```js echo
import {Mutable} from "npm:@observablehq/stdlib";
import {Mutable} from "observablehq:stdlib";
```

Then to use it:
Expand Down
2 changes: 1 addition & 1 deletion src/client/preview.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {FileAttachment, registerFile} from "npm:@observablehq/stdlib";
import {FileAttachment, registerFile} from "observablehq:stdlib";
import {main, runtime, undefine} from "./main.js";
import {findLoading, findRoots, registerRoot} from "./main.js";
import {enableCopyButtons} from "./pre.js";
Expand Down
2 changes: 1 addition & 1 deletion src/client/stdlib/inputs.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {file as _file} from "@observablehq/inputs";
import {AbstractFile} from "npm:@observablehq/stdlib";
import {AbstractFile} from "observablehq:stdlib";

export {
button,
Expand Down
2 changes: 1 addition & 1 deletion src/client/stdlib/zip.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {AbstractFile} from "npm:@observablehq/stdlib";
import JSZip from "npm:jszip";
import {AbstractFile} from "observablehq:stdlib";

export class ZipArchive {
constructor(archive) {
Expand Down
2 changes: 1 addition & 1 deletion src/javascript/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export function findFiles(
// Support namespace imports? Error if stdlib is expressed with a version?
simple(body, {
ImportDeclaration(node) {
if (node.source.value === "npm:@observablehq/stdlib") {
if (node.source.value === "observablehq:stdlib" || node.source.value === "npm:@observablehq/stdlib") {
for (const specifier of node.specifiers) {
if (
specifier.type === "ImportSpecifier" &&
Expand Down
8 changes: 4 additions & 4 deletions src/libraries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export function getImplicitFileImports(methods: Iterable<string>): Set<string> {
if (set.has("csv") || set.has("tsv")) implicits.add("npm:d3-dsv");
if (set.has("parquet")) implicits.add("npm:apache-arrow").add("npm:parquet-wasm");
if (set.has("sqlite")) implicits.add("npm:@observablehq/sqlite");
if (set.has("xlsx")) implicits.add("npm:@observablehq/xlsx");
if (set.has("zip")) implicits.add("npm:@observablehq/zip");
if (set.has("xlsx")) implicits.add("observablehq:stdlib/xlsx");
if (set.has("zip")) implicits.add("observablehq:stdlib/zip");
return implicits;
}

Expand Down Expand Up @@ -168,8 +168,8 @@ export function getImplicitDependencies(imports: Iterable<string>): Set<string>
if (set.has("npm:@observablehq/inputs")) implicits.add("npm:htl").add("npm:isoformat");
if (set.has("npm:@observablehq/mermaid")) implicits.add("npm:mermaid");
if (set.has("npm:@observablehq/tex")) implicits.add("npm:katex");
if (set.has("npm:@observablehq/xlsx")) implicits.add("npm:exceljs");
if (set.has("npm:@observablehq/zip")) implicits.add("npm:jszip");
if (set.has("observablehq:stdlib/xlsx")) implicits.add("npm:exceljs");
if (set.has("observablehq:stdlib/zip")) implicits.add("npm:jszip");
if (set.has("observablehq:stdlib/vega-lite")) implicits.add("npm:vega-lite-api").add("npm:vega-lite").add("npm:vega");
if (set.has("observablehq:stdlib/vgplot")) implicits.add("npm:@uwdata/vgplot").add("npm:@observablehq/duckdb").add("npm:@duckdb/duckdb-wasm"); // prettier-ignore
return implicits;
Expand Down
8 changes: 3 additions & 5 deletions src/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export interface ResolversConfig {

const defaultImports = [
"observablehq:client", // Framework client
"npm:@observablehq/runtime", // Runtime
"npm:@observablehq/stdlib" // Standard library
"observablehq:runtime", // Runtime
"observablehq:stdlib" // Standard library
];

export const builtins = new Map<string, string>([
Expand All @@ -53,9 +53,7 @@ export const builtins = new Map<string, string>([
["npm:@observablehq/inputs", "/_observablehq/stdlib/inputs.js"], // TODO publish to npm
["npm:@observablehq/mermaid", "/_observablehq/stdlib/mermaid.js"], // TODO publish to npm
["npm:@observablehq/tex", "/_observablehq/stdlib/tex.js"], // TODO publish to npm
["npm:@observablehq/sqlite", "/_observablehq/stdlib/sqlite.js"], // TODO publish to npm
["npm:@observablehq/xlsx", "/_observablehq/stdlib/xlsx.js"], // TODO publish to npm
["npm:@observablehq/zip", "/_observablehq/stdlib/zip.js"] // TODO publish to npm
["npm:@observablehq/sqlite", "/_observablehq/stdlib/sqlite.js"] // TODO publish to npm
]);

/**
Expand Down
2 changes: 1 addition & 1 deletion test/input/build/data-loaders/test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import {FileAttachment} from "npm:@observablehq/stdlib";
import {FileAttachment} from "observablehq:stdlib";

export const test = FileAttachment("./test.txt").text();
2 changes: 1 addition & 1 deletion test/input/build/fetches/foo/foo.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import {FileAttachment} from "npm:@observablehq/stdlib";
import {FileAttachment} from "observablehq:stdlib";
export const fooJsonData = await FileAttachment("foo-data.json").json();
export const fooCsvData = await FileAttachment("foo-data.csv").text();
2 changes: 1 addition & 1 deletion test/input/build/fetches/top.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {FileAttachment} from "npm:@observablehq/stdlib";
import {FileAttachment} from "observablehq:stdlib";
export {fooCsvData, fooJsonData} from "./foo/foo.js";
export const topJsonData = await FileAttachment("top-data.json").json();
export const topCsvData = await FileAttachment("top-data.csv").text();
2 changes: 1 addition & 1 deletion test/input/imports/baz.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import {FileAttachment} from "npm:@observablehq/stdlib";
import {FileAttachment} from "observablehq:stdlib";

export const data = FileAttachment("./fetch-local-data.json").json();
14 changes: 8 additions & 6 deletions test/javascript/files-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ describe("findFiles(node, input)", () => {
assert.deepStrictEqual(files('FileAttachment("foo.json")'), [{name: "./foo.json", method: "json"}]);
});
it("finds imported FileAttachment", () => {
assert.deepStrictEqual(files('import {FileAttachment} from "observablehq:stdlib";\nFileAttachment("foo.json")'), [{name: "./foo.json", method: "json"}]);
assert.deepStrictEqual(files('import {FileAttachment as F} from "observablehq:stdlib";\nF("foo.json")'), [{name: "./foo.json", method: "json"}]);
assert.deepStrictEqual(files('import {FileAttachment} from "npm:@observablehq/stdlib";\nFileAttachment("foo.json")'), [{name: "./foo.json", method: "json"}]);
assert.deepStrictEqual(files('import {FileAttachment as F} from "npm:@observablehq/stdlib";\nF("foo.json")'), [{name: "./foo.json", method: "json"}]);
});
Expand Down Expand Up @@ -103,25 +105,25 @@ describe("findFiles(node, input)", () => {
assert.deepStrictEqual(files('File("foo.txt").csv', {aliases: ["File"]}), [{name: "./foo.txt", method: "csv"}]);
});
it("finds the import declaration", () => {
assert.deepStrictEqual(files('import {FileAttachment} from "npm:@observablehq/stdlib";\nFileAttachment("foo.txt").csv', {aliases: []}), [{name: "./foo.txt", method: "csv"}]);
assert.deepStrictEqual(files('import {FileAttachment} from "observablehq:stdlib";\nFileAttachment("foo.txt").csv', {aliases: []}), [{name: "./foo.txt", method: "csv"}]);
});
it("finds the import declaration if aliased", () => {
assert.deepStrictEqual(files('import {FileAttachment as F} from "npm:@observablehq/stdlib";\nF("foo.txt").csv', {aliases: []}), [{name: "./foo.txt", method: "csv"}]);
assert.deepStrictEqual(files('import {FileAttachment as F} from "observablehq:stdlib";\nF("foo.txt").csv', {aliases: []}), [{name: "./foo.txt", method: "csv"}]);
});
it("finds the import declaration if aliased and masking a global", () => {
assert.deepStrictEqual(files('import {FileAttachment as File} from "npm:@observablehq/stdlib";\nFile("foo.txt").csv', {aliases: []}), [{name: "./foo.txt", method: "csv"}]);
assert.deepStrictEqual(files('import {FileAttachment as File} from "observablehq:stdlib";\nFile("foo.txt").csv', {aliases: []}), [{name: "./foo.txt", method: "csv"}]);
});
it("finds the import declaration if multiple aliases", () => {
assert.deepStrictEqual(files('import {FileAttachment as F, FileAttachment as G} from "npm:@observablehq/stdlib";\nF("file1.txt");\nG("file2.txt");', {aliases: []}), [{name: "./file1.txt", method: "text"}, {name: "./file2.txt", method: "text"}]);
assert.deepStrictEqual(files('import {FileAttachment as F, FileAttachment as G} from "observablehq:stdlib";\nF("file1.txt");\nG("file2.txt");', {aliases: []}), [{name: "./file1.txt", method: "text"}, {name: "./file2.txt", method: "text"}]);
});
it("ignores import declarations from another module", () => {
assert.deepStrictEqual(files('import {FileAttachment as F} from "npm:@observablehq/not-stdlib";\nFileAttachment("file1.txt");', {aliases: []}), []);
});
it.skip("supports namespace imports", () => {
assert.deepStrictEqual(files('import * as O from "npm:@observablehq/stdlib";\nO.FileAttachment("foo.txt");', {aliases: []}), [{name: "./foo.txt", method: "text"}]);
assert.deepStrictEqual(files('import * as O from "observablehq:stdlib";\nO.FileAttachment("foo.txt");', {aliases: []}), [{name: "./foo.txt", method: "text"}]);
});
it("ignores masked references", () => {
assert.deepStrictEqual(files('import {FileAttachment} from "npm:@observablehq/stdlib";\n((FileAttachment) => FileAttachment("file.txt"))(String);', {aliases: []}), []);
assert.deepStrictEqual(files('import {FileAttachment} from "observablehq:stdlib";\n((FileAttachment) => FileAttachment("file.txt"))(String);', {aliases: []}), []);
});
});

Expand Down
12 changes: 6 additions & 6 deletions test/javascript/module-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ describe("getModuleHash(root, path)", () => {
assert.strictEqual(getModuleHash("test/input/build/imports", "foo/foo.js"), "32f934a52fa34ba1b06aa6089fe5922dc442c9bf2dcddef864bc649a39d9eace"); // prettier-ignore
assert.strictEqual(getModuleHash("test/input/build/imports", "bar/bar.js"), "7fe009c8bb0049d9b84d53a00b29fb172bbf07d8232d2ace5f7c6f220b23eb16"); // prettier-ignore
assert.strictEqual(getModuleHash("test/input/build/imports", "top.js"), "160847a6b4890d59f8e8862911bfbe3b8066955d31f2708cafbe51945c3c57b6"); // prettier-ignore
assert.strictEqual(getModuleHash("test/input/build/fetches", "foo/foo.js"), "6fd063d5f14f3bb844cfdb599bf3bdd643c4d0f89841591f769960fd58104e6c"); // prettier-ignore
assert.strictEqual(getModuleHash("test/input/build/fetches", "top.js"), "d8f5cc36a8b359974a3aa89013db8d6c1dbb43b091bed4ea683a7c8c994b2a3d"); // prettier-ignore
assert.strictEqual(getModuleHash("test/input/build/fetches", "foo/foo.js"), "3bb4a170d2f3539934168741572d4aa3cd11da649d4ca88b408edefb5c287360"); // prettier-ignore
assert.strictEqual(getModuleHash("test/input/build/fetches", "top.js"), "6c858de52de6ff26b19508e95448288da02fac62251b7ca2710a308a0ebfd7ba"); // prettier-ignore
});
it("returns the empty hash if the specified module does not exist", () => {
assert.strictEqual(getModuleHash("test/input/build/imports", "does-not-exist.js"), emptyHash);
Expand Down Expand Up @@ -63,17 +63,17 @@ describe("getModuleInfo(root, path)", () => {
assert.deepStrictEqual(redactModuleInfo("test/input/build/fetches", "foo/foo.js"), {
fileMethods: new Set(["json", "text"]),
files: new Set(["./foo-data.csv", "./foo-data.json"]),
hash: "13349148aade73f9c04f331956a8f48535958a7c7e640b025eebfb52156067fa",
globalStaticImports: new Set(["npm:@observablehq/stdlib"]),
hash: "2b1e6b6f73a700231a3c60915d358d5ac10b1665e65a1647d91bf597d48f874e",
globalStaticImports: new Set(["observablehq:stdlib"]),
globalDynamicImports: new Set(),
localDynamicImports: new Set(),
localStaticImports: new Set()
});
assert.deepStrictEqual(redactModuleInfo("test/input/build/fetches", "top.js"), {
fileMethods: new Set(["json", "text"]),
files: new Set(["./top-data.csv", "./top-data.json"]),
hash: "d755832694c7db24d3606bdef5ecfdfbd820b09576d5790feb310854f48b2228",
globalStaticImports: new Set(["npm:@observablehq/stdlib"]),
hash: "2a35a381b4975e007db01eef32f3dadd74645b61a6cf0fe3746bcb4ae8c8b3cf",
globalStaticImports: new Set(["observablehq:stdlib"]),
globalDynamicImports: new Set(),
localDynamicImports: new Set(),
localStaticImports: new Set(["./foo/foo.js"])
Expand Down
Loading