Skip to content

Commit 36e2153

Browse files
authored
Merge branch 'main' into toph/onramp
2 parents a23c35c + 324c7eb commit 36e2153

File tree

72 files changed

+292
-101
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+292
-101
lines changed

docs/lib/duckdb.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,11 @@ SELECT ST_Area('POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))'::GEOMETRY) as area;
140140

141141
To tell which extensions have been loaded, you can run the following query:
142142

143-
```sql echo
143+
```sql
144+
FROM duckdb_extensions() WHERE loaded AND JSON '1';
145+
```
146+
147+
```sql run=false
144148
FROM duckdb_extensions() WHERE loaded;
145149
```
146150

docs/lib/sqlite.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
import SQLite from "npm:@observablehq/sqlite";
77
```
88

9+
If you prefer to use sql.js directly, you can import and initialize it like so:
10+
11+
```js run=false
12+
import initSqlJs from "npm:sql.js";
13+
14+
const SQLite = await initSqlJs({locateFile: (name) => import.meta.resolve("npm:sql.js/dist/") + name});
15+
```
16+
917
We also provide `SQLiteDatabaseClient`, a [`DatabaseClient`](https://observablehq.com/@observablehq/database-client-specification) implementation.
1018
1119
```js run=false

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
"@clack/prompts": "^0.7.0",
5959
"@observablehq/inputs": "^0.12.0",
6060
"@observablehq/inspector": "^5.0.1",
61-
"@observablehq/runtime": "^6.0.0-rc.1",
61+
"@observablehq/runtime": "^6.0.0",
6262
"@rollup/plugin-commonjs": "^25.0.7",
6363
"@rollup/plugin-json": "^6.1.0",
6464
"@rollup/plugin-node-resolve": "^15.2.3",

src/build.ts

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {copyFile, readFile, rm, stat, writeFile} from "node:fs/promises";
44
import {basename, dirname, extname, join} from "node:path/posix";
55
import type {Config} from "./config.js";
66
import {getDuckDBManifest} from "./duckdb.js";
7-
import {CliError} from "./error.js";
7+
import {CliError, enoent} from "./error.js";
88
import {getClientPath, prepareOutput} from "./files.js";
99
import {findModule, getModuleHash, readJavaScript} from "./javascript/module.js";
1010
import {transpileModule} from "./javascript/transpile.js";
@@ -54,7 +54,7 @@ export async function build(
5454
{config}: BuildOptions,
5555
effects: BuildEffects = new FileBuildEffects(config.output, join(config.root, ".observablehq", "cache"))
5656
): Promise<void> {
57-
const {root, loaders, duckdb} = config;
57+
const {root, loaders, title, duckdb} = config;
5858
Telemetry.record({event: "build", step: "start"});
5959

6060
// Prepare for build (such as by emptying the existing output root).
@@ -75,6 +75,25 @@ export async function build(
7575
let assetCount = 0;
7676
let pageCount = 0;
7777
const pagePaths = new Set<string>();
78+
79+
const buildManifest: BuildManifest = {
80+
...(title && {title}),
81+
config: {root},
82+
pages: [],
83+
modules: [],
84+
files: []
85+
};
86+
87+
// file is the serving path relative to the base (e.g., /foo)
88+
// path is the source file relative to the source root (e.g., /foo.md)
89+
const addToManifest = (type: string, file: string, {title, path}: {title?: string | null; path: string}) => {
90+
buildManifest[type].push({
91+
path: config.normalizePath(file),
92+
source: join("/", path), // TODO have route return path with leading slash?
93+
...(title != null && {title})
94+
});
95+
};
96+
7897
for await (const path of config.paths()) {
7998
effects.output.write(`${faint("load")} ${path} `);
8099
const start = performance.now();
@@ -91,6 +110,7 @@ export async function build(
91110
effects.output.write(`${faint("in")} ${(elapsed >= 100 ? yellow : faint)(`${elapsed}ms`)}\n`);
92111
outputs.set(path, {type: "module", resolvers});
93112
++assetCount;
113+
addToManifest("modules", path, module);
94114
continue;
95115
}
96116
}
@@ -99,6 +119,7 @@ export async function build(
99119
effects.output.write(`${faint("copy")} ${join(root, path)} ${faint("→")} `);
100120
const sourcePath = join(root, await file.load({useStale: true}, effects));
101121
await effects.copyFile(sourcePath, path);
122+
addToManifest("files", path, file);
102123
++assetCount;
103124
continue;
104125
}
@@ -209,7 +230,10 @@ export async function build(
209230
// Copy over referenced files, accumulating hashed aliases.
210231
for (const file of files) {
211232
effects.output.write(`${faint("copy")} ${join(root, file)} ${faint("→")} `);
212-
const sourcePath = join(root, await loaders.loadFile(join("/", file), {useStale: true}, effects));
233+
const path = join("/", file);
234+
const loader = loaders.find(path);
235+
if (!loader) throw enoent(path);
236+
const sourcePath = join(root, await loader.load({useStale: true}, effects));
213237
const contents = await readFile(sourcePath);
214238
const hash = createHash("sha256").update(contents).digest("hex").slice(0, 8);
215239
const alias = applyHash(join("/_file", file), hash);
@@ -338,15 +362,13 @@ export async function build(
338362
}
339363

340364
// Render pages!
341-
const buildManifest: BuildManifest = {pages: []};
342-
if (config.title) buildManifest.title = config.title;
343365
for (const [path, output] of outputs) {
344366
effects.output.write(`${faint("render")} ${path} ${faint("→")} `);
345367
if (output.type === "page") {
346368
const {page, resolvers} = output;
347369
const html = await renderPage(page, {...config, path, resolvers});
348370
await effects.writeFile(`${path}.html`, html);
349-
buildManifest.pages.push({path: config.normalizePath(path), title: page.title});
371+
addToManifest("pages", path, page);
350372
} else {
351373
const {resolvers} = output;
352374
const source = await renderModule(root, path, resolvers);
@@ -507,5 +529,8 @@ export class FileBuildEffects implements BuildEffects {
507529

508530
export interface BuildManifest {
509531
title?: string;
510-
pages: {path: string; title: string | null}[];
532+
config: {root: string};
533+
pages: {path: string; title?: string | null; source?: string}[];
534+
modules: {path: string; source?: string}[];
535+
files: {path: string; source?: string}[];
511536
}

src/client/stdlib/sqlite.js

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
// https://github.com/sql-js/sql.js/issues/284
2-
const SQLite = await (async () => {
3-
const exports = {};
4-
const response = await fetch(import.meta.resolve("npm:sql.js/dist/sql-wasm.js"));
5-
new Function("exports", await response.text())(exports);
6-
return exports.Module({locateFile: (name) => import.meta.resolve("npm:sql.js/dist/") + name});
7-
})();
1+
import initSqlJs from "npm:sql.js";
2+
3+
const SQLite = initSqlJs({locateFile: (name) => import.meta.resolve("npm:sql.js/dist/") + name});
84

95
export default SQLite;
106

@@ -15,7 +11,8 @@ export class SQLiteDatabaseClient {
1511
});
1612
}
1713
static async open(source) {
18-
return new SQLiteDatabaseClient(new SQLite.Database(await load(await source)));
14+
const [sqlite, data] = await Promise.all([SQLite, Promise.resolve(source).then(load)]);
15+
return new SQLiteDatabaseClient(new sqlite.Database(data));
1916
}
2017
async query(query, params) {
2118
return await exec(this._db, query, params);

src/files.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,8 @@ import {cwd} from "node:process";
77
import {fileURLToPath} from "node:url";
88
import {isEnoent} from "./error.js";
99

10-
export function toOsPath(path: string): string {
11-
return path.split(sep).join(op.sep);
12-
}
13-
14-
export function fromOsPath(path: string): string {
15-
return path.split(op.sep).join(sep);
16-
}
10+
export const toOsPath = sep === op.sep ? (path: string) => path : (path: string) => path.split(sep).join(op.sep);
11+
export const fromOsPath = sep === op.sep ? (path: string) => path : (path: string) => path.split(op.sep).join(sep);
1712

1813
/**
1914
* Returns the relative path from the current working directory to the given

src/loader.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,25 +73,15 @@ export class LoaderResolver {
7373
);
7474
}
7575

76-
/**
77-
* Loads the file at the specified path, returning a promise to the path to
78-
* the (possibly generated) file relative to the source root.
79-
*/
80-
async loadFile(path: string, options?: LoadOptions, effects?: LoadEffects): Promise<string> {
81-
const loader = this.find(path);
82-
if (!loader) throw enoent(path);
83-
return await loader.load(options, effects);
84-
}
85-
8676
/**
8777
* Loads the page at the specified path, returning a promise to the parsed
8878
* page object.
8979
*/
9080
async loadPage(path: string, options: LoadOptions & ParseOptions, effects?: LoadEffects): Promise<MarkdownPage> {
9181
const loader = this.findPage(path);
9282
if (!loader) throw enoent(path);
93-
const source = await readFile(join(this.root, await loader.load(options, effects)), "utf8");
94-
return parseMarkdown(source, {params: loader.params, ...options});
83+
const input = await readFile(join(this.root, await loader.load(options, effects)), "utf8");
84+
return parseMarkdown(input, {source: loader.path, params: loader.params, ...options});
9585
}
9686

9787
/**

src/markdown.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export interface MarkdownPage {
4040
data: FrontMatter;
4141
style: string | null;
4242
code: MarkdownCode[];
43+
path: string;
4344
params?: Params;
4445
}
4546

@@ -216,6 +217,7 @@ export interface ParseOptions {
216217
head?: Config["head"];
217218
header?: Config["header"];
218219
footer?: Config["footer"];
220+
source?: string;
219221
params?: Params;
220222
}
221223

@@ -242,7 +244,7 @@ export function createMarkdownIt({
242244
}
243245

244246
export function parseMarkdown(input: string, options: ParseOptions): MarkdownPage {
245-
const {md, path, params} = options;
247+
const {md, path, source = path, params} = options;
246248
const {content, data} = readFrontMatter(input);
247249
const code: MarkdownCode[] = [];
248250
const context: ParseContext = {code, startLine: 0, currentLine: 0, path, params};
@@ -258,6 +260,7 @@ export function parseMarkdown(input: string, options: ParseOptions): MarkdownPag
258260
title,
259261
style: getStyle(data, options),
260262
code,
263+
path: source,
261264
params
262265
};
263266
}

src/npm.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,22 @@ export async function populateNpmCache(root: string, path: string): Promise<stri
8383
let promise = npmRequests.get(outputPath);
8484
if (promise) return promise; // coalesce concurrent requests
8585
promise = (async () => {
86-
const specifier = extractNpmSpecifier(path);
86+
let specifier = extractNpmSpecifier(path);
87+
const s = parseNpmSpecifier(specifier);
88+
// https://github.com/sql-js/sql.js/issues/284
89+
if (s.name === "sql.js" && s.path === "+esm") {
90+
specifier = formatNpmSpecifier({...s, path: "dist/sql-wasm.js"});
91+
}
8792
const href = `https://cdn.jsdelivr.net/npm/${specifier}`;
8893
console.log(`npm:${specifier} ${faint("→")} ${outputPath}`);
8994
const response = await fetch(href);
9095
if (!response.ok) throw new Error(`unable to fetch: ${href}`);
9196
await mkdir(dirname(outputPath), {recursive: true});
9297
if (/^application\/javascript(;|$)/i.test(response.headers.get("content-type")!)) {
93-
const source = await response.text();
98+
let source = await response.text();
99+
if (s.name === "sql.js" && s.path === "+esm") {
100+
source = "var module;\n" + source + "\nexport default initSqlJs;";
101+
}
94102
const resolver = await getDependencyResolver(root, path, source);
95103
await writeFile(outputPath, rewriteNpmImports(source, resolver), "utf-8");
96104
} else {

src/observableApiClient.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import fs from "node:fs/promises";
2+
import type {BuildManifest} from "./build.js";
23
import type {ClackEffects} from "./clack.js";
34
import {CliError, HttpError, isApiError} from "./error.js";
45
import {formatByteSize} from "./format.js";
@@ -229,7 +230,7 @@ export class ObservableApiClient {
229230
});
230231
}
231232

232-
async postDeployUploaded(deployId: string, buildManifest: PostDeployUploadedRequest | null): Promise<DeployInfo> {
233+
async postDeployUploaded(deployId: string, buildManifest: BuildManifest | null): Promise<DeployInfo> {
233234
return await this._fetch<DeployInfo>(new URL(`/cli/deploy/${deployId}/uploaded`, this._apiOrigin), {
234235
method: "POST",
235236
headers: {"content-type": "application/json"},
@@ -383,10 +384,3 @@ export interface PostDeployManifestResponse {
383384
detail: string | null;
384385
}[];
385386
}
386-
387-
export interface PostDeployUploadedRequest {
388-
pages: {
389-
path: string;
390-
title: string | null;
391-
}[];
392-
}

0 commit comments

Comments
 (0)