@observablehq/notebook-kit 1.0.1 → 1.1.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/bin/build.js +7 -0
  2. package/dist/bin/download.js +15 -19
  3. package/dist/package.json +24 -1
  4. package/dist/src/databases/duckdb.d.ts +2 -0
  5. package/dist/src/databases/duckdb.js +67 -0
  6. package/dist/src/databases/index.d.ts +36 -0
  7. package/dist/src/databases/index.js +15 -0
  8. package/dist/src/databases/options.d.ts +3 -0
  9. package/dist/src/databases/options.js +6 -0
  10. package/dist/src/databases/postgres.d.ts +2 -0
  11. package/dist/src/databases/postgres.js +95 -0
  12. package/dist/src/databases/snowflake.d.ts +2 -0
  13. package/dist/src/databases/snowflake.js +102 -0
  14. package/dist/src/javascript/imports/npm.js +2 -0
  15. package/dist/src/javascript/observable.js +24 -3
  16. package/dist/src/javascript/template.d.ts +3 -0
  17. package/dist/src/javascript/template.js +17 -1
  18. package/dist/src/javascript/transpile.d.ts +4 -2
  19. package/dist/src/javascript/transpile.js +30 -11
  20. package/dist/src/javascript/transpile.test.js +16 -0
  21. package/dist/src/lib/error.d.ts +6 -0
  22. package/dist/src/lib/error.js +6 -0
  23. package/dist/src/lib/notebook.d.ts +11 -1
  24. package/dist/src/lib/notebook.js +10 -3
  25. package/dist/src/lib/notebook.test.js +10 -2
  26. package/dist/src/lib/serialize.d.ts +3 -1
  27. package/dist/src/lib/serialize.js +13 -4
  28. package/dist/src/lib/serialize.test.js +10 -3
  29. package/dist/src/runtime/define.d.ts +2 -2
  30. package/dist/src/runtime/define.js +2 -2
  31. package/dist/src/runtime/display.d.ts +2 -0
  32. package/dist/src/runtime/display.js +5 -1
  33. package/dist/src/runtime/index.d.ts +100 -1
  34. package/dist/src/runtime/index.js +29 -3
  35. package/dist/src/runtime/stdlib/databaseClient.d.ts +46 -0
  36. package/dist/src/runtime/stdlib/databaseClient.js +80 -0
  37. package/dist/src/runtime/stdlib/duckdb.js +4 -4
  38. package/dist/src/runtime/stdlib/fileAttachment.d.ts +38 -11
  39. package/dist/src/runtime/stdlib/fileAttachment.js +21 -10
  40. package/dist/src/runtime/stdlib/generators/input.d.ts +1 -1
  41. package/dist/src/runtime/stdlib/index.d.ts +43 -6
  42. package/dist/src/runtime/stdlib/index.js +5 -5
  43. package/dist/src/styles/global.css +1 -1
  44. package/dist/src/templates/default.html +18 -18
  45. package/dist/src/vite/config.js +5 -2
  46. package/dist/src/vite/observable.d.ts +25 -6
  47. package/dist/src/vite/observable.js +58 -15
  48. package/package.json +24 -1
  49. package/dist/src/runtime/stdlib/sql.d.ts +0 -5
  50. package/dist/src/runtime/stdlib/sql.js +0 -5
package/dist/bin/build.js CHANGED
@@ -23,6 +23,10 @@ export default async function run(args) {
23
23
  type: "string",
24
24
  default: "./"
25
25
  },
26
+ minify: {
27
+ type: "boolean",
28
+ default: true
29
+ },
26
30
  template: {
27
31
  type: "string"
28
32
  },
@@ -42,6 +46,7 @@ export default async function run(args) {
42
46
  --template <path> path to the HTML template
43
47
  -o, --out <dir> path to the output directory (relative to root)
44
48
  --base <path> serving base path; defaults to ./
49
+ --no-minify disable JS/CSS minification
45
50
  --empty whether to empty the output directory before building
46
51
  -h, --help show this message
47
52
  `);
@@ -53,6 +58,8 @@ export default async function run(args) {
53
58
  root: values.root,
54
59
  base: values.base,
55
60
  build: {
61
+ minify: values.minify ? "esbuild" : false,
62
+ cssMinify: values.minify ? "esbuild" : false,
56
63
  outDir: values.out,
57
64
  emptyOutDir: values.empty,
58
65
  rollupOptions: {
@@ -3,6 +3,8 @@ import { parseArgs } from "node:util";
3
3
  import { JSDOM } from "jsdom";
4
4
  import { toNotebook } from "../src/lib/notebook.js";
5
5
  import { serialize } from "../src/lib/serialize.js";
6
+ const UI_ORIGIN = "https://observablehq.com";
7
+ const API_ORIGIN = "https://api.observablehq.com";
6
8
  if (process.argv[1] === import.meta.filename)
7
9
  run();
8
10
  export default async function run(args) {
@@ -25,25 +27,19 @@ export default async function run(args) {
25
27
  `);
26
28
  return;
27
29
  }
28
- const globalDocument = globalThis.document;
29
- globalThis.document = new JSDOM().window.document;
30
- try {
31
- for (const positional of positionals) {
32
- let url = new URL(positional, "https://observablehq.com");
33
- if (url.origin === "https://observablehq.com") {
34
- url = new URL(`/document${url.pathname.replace(/^\/d\//, "/")}`, "https://api.observablehq.com");
35
- }
36
- const response = await fetch(url);
37
- if (!response.ok)
38
- throw new Error(`unable to fetch: ${url}`);
39
- const { title, nodes } = await response.json();
40
- for (const node of nodes)
41
- if (node.mode === "js")
42
- node.mode = "ojs";
43
- process.stdout.write(serialize(toNotebook({ title, cells: nodes })));
30
+ const { window } = new JSDOM();
31
+ for (const positional of positionals) {
32
+ let url = new URL(positional, UI_ORIGIN);
33
+ if (url.origin === UI_ORIGIN) {
34
+ url = new URL(`/document${url.pathname.replace(/^\/d\//, "/")}`, API_ORIGIN);
44
35
  }
45
- }
46
- finally {
47
- globalThis.document = globalDocument;
36
+ const response = await fetch(url);
37
+ if (!response.ok)
38
+ throw new Error(`unable to fetch: ${url}`);
39
+ const { title, nodes } = await response.json();
40
+ for (const node of nodes)
41
+ if (node.mode === "js")
42
+ node.mode = "ojs";
43
+ process.stdout.write(serialize(toNotebook({ title, cells: nodes }), { document: window.document }));
48
44
  }
49
45
  }
package/dist/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/observablehq/notebook-kit.git"
7
7
  },
8
- "version": "1.0.1",
8
+ "version": "1.1.0-rc.2",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "test": "vitest",
@@ -27,6 +27,10 @@
27
27
  "types": "./dist/src/index.d.ts",
28
28
  "import": "./dist/src/index.js"
29
29
  },
30
+ "./databases": {
31
+ "types": "./dist/src/databases/index.d.ts",
32
+ "import": "./dist/src/databases/index.js"
33
+ },
30
34
  "./runtime": {
31
35
  "types": "./dist/src/runtime/index.d.ts",
32
36
  "import": "./dist/src/runtime/index.js"
@@ -59,15 +63,34 @@
59
63
  "vite": "^7.0.0"
60
64
  },
61
65
  "devDependencies": {
66
+ "@duckdb/node-api": "^1.3.2-alpha.26",
62
67
  "@eslint/js": "^9.29.0",
63
68
  "@types/jsdom": "^21.1.7",
64
69
  "@types/markdown-it": "^14.1.2",
65
70
  "eslint": "^9.29.0",
66
71
  "globals": "^16.2.0",
67
72
  "htl": "^0.3.1",
73
+ "postgres": "^3.4.7",
74
+ "snowflake-sdk": "^2.1.3",
68
75
  "tsx": "^4.20.3",
69
76
  "typescript": "^5.8.3",
70
77
  "typescript-eslint": "^8.35.0",
71
78
  "vitest": "^3.2.4"
79
+ },
80
+ "peerDependencies": {
81
+ "@duckdb/node-api": "^1.3.2-alpha.26",
82
+ "postgres": "^3.4.7",
83
+ "snowflake-sdk": "^2.1.3"
84
+ },
85
+ "peerDependenciesMeta": {
86
+ "@duckdb/node-api": {
87
+ "optional": true
88
+ },
89
+ "postgres": {
90
+ "optional": true
91
+ },
92
+ "snowflake-sdk": {
93
+ "optional": true
94
+ }
72
95
  }
73
96
  }
@@ -0,0 +1,2 @@
1
+ import type { DatabaseContext, DuckDBConfig, QueryTemplateFunction } from "./index.js";
2
+ export default function duckdb(_options: DuckDBConfig, context: DatabaseContext): QueryTemplateFunction;
@@ -0,0 +1,67 @@
1
+ import { DuckDBConnection } from "@duckdb/node-api";
2
+ import { BIGINT, BIT, BLOB, BOOLEAN, DATE, DOUBLE, FLOAT, HUGEINT, INTEGER, INTERVAL, SMALLINT, TIME, TIMESTAMP, TIMESTAMP_MS, TIMESTAMP_NS, TIMESTAMP_S, TIMESTAMPTZ, TINYINT, UBIGINT, UHUGEINT, UINTEGER, USMALLINT, UTINYINT, UUID, VARCHAR, VARINT } from "@duckdb/node-api"; // prettier-ignore
3
+ export default function duckdb(_options, context) {
4
+ return async (strings, ...params) => {
5
+ const connection = await DuckDBConnection.create();
6
+ await connection.run(`SET file_search_path=$0`, [context.cwd]);
7
+ const date = new Date();
8
+ let result;
9
+ let rows;
10
+ try {
11
+ result = await connection.run(strings.reduce((p, c, i) => `${p}$${i - 1}${c}`), params);
12
+ rows = await result.getRowObjectsJson();
13
+ }
14
+ finally {
15
+ connection.disconnectSync();
16
+ }
17
+ return {
18
+ rows,
19
+ schema: getResultSchema(result),
20
+ duration: Date.now() - +date,
21
+ date
22
+ };
23
+ };
24
+ }
25
+ function getResultSchema(result) {
26
+ return result.columnNames().map((name, i) => ({ name, type: getColumnType(result.columnType(i)) }));
27
+ }
28
+ function getColumnType(type) {
29
+ switch (type) {
30
+ case BOOLEAN:
31
+ return "boolean";
32
+ case BIT:
33
+ case TINYINT:
34
+ case SMALLINT:
35
+ case INTEGER:
36
+ case UTINYINT:
37
+ case USMALLINT:
38
+ case UINTEGER:
39
+ case VARINT:
40
+ return "integer";
41
+ case BIGINT:
42
+ case UBIGINT:
43
+ case HUGEINT:
44
+ case UHUGEINT:
45
+ return "bigint";
46
+ case FLOAT:
47
+ case DOUBLE:
48
+ return "number";
49
+ case TIMESTAMP:
50
+ case TIMESTAMP_S:
51
+ case TIMESTAMP_MS:
52
+ case TIMESTAMP_NS:
53
+ case TIMESTAMPTZ:
54
+ case DATE:
55
+ return "date";
56
+ case TIME:
57
+ case VARCHAR:
58
+ case UUID:
59
+ return "string";
60
+ case BLOB:
61
+ return "buffer";
62
+ case INTERVAL:
63
+ return "array";
64
+ default:
65
+ return "other";
66
+ }
67
+ }
@@ -0,0 +1,36 @@
1
+ import type { ColumnSchema, QueryParam } from "../runtime/index.js";
2
+ export type DatabaseConfig = DuckDBConfig | SnowflakeConfig | PostgresConfig;
3
+ export type DuckDBConfig = {
4
+ type: "duckdb";
5
+ };
6
+ export type SnowflakeConfig = {
7
+ type: "snowflake";
8
+ account: string;
9
+ database?: string;
10
+ role?: string;
11
+ schema?: string;
12
+ username?: string;
13
+ warehouse?: string;
14
+ password?: string;
15
+ };
16
+ export type PostgresConfig = {
17
+ type: "postgres";
18
+ host?: string;
19
+ port?: string | number;
20
+ username?: string;
21
+ password?: string;
22
+ database?: string;
23
+ ssl?: boolean;
24
+ };
25
+ export type DatabaseContext = {
26
+ cwd: string;
27
+ };
28
+ export type QueryTemplateFunction = (strings: string[], ...params: QueryParam[]) => Promise<SerializableQueryResult>;
29
+ export type SerializableQueryResult = {
30
+ rows: Record<string, unknown>[];
31
+ schema: ColumnSchema[];
32
+ duration: number;
33
+ date: Date;
34
+ };
35
+ export declare function getDatabase(config: DatabaseConfig, context: DatabaseContext): Promise<QueryTemplateFunction>;
36
+ export declare function isDefaultDatabase(name: string): name is "postgres" | "duckdb";
@@ -0,0 +1,15 @@
1
+ export async function getDatabase(config, context) {
2
+ switch (config.type) {
3
+ case "duckdb":
4
+ return (await import("./duckdb.js")).default(config, context);
5
+ case "snowflake":
6
+ return (await import("./snowflake.js")).default(config);
7
+ case "postgres":
8
+ return (await import("./postgres.js")).default(config);
9
+ default:
10
+ throw new Error(`unsupported database type: ${config["type"]}`);
11
+ }
12
+ }
13
+ export function isDefaultDatabase(name) {
14
+ return name === "postgres" || name === "duckdb";
15
+ }
@@ -0,0 +1,3 @@
1
+ export declare const optionalString: (value: unknown) => string | undefined;
2
+ export declare const optionalNumber: (value: unknown) => number | undefined;
3
+ export declare const optionalBoolean: (value: unknown) => boolean | undefined;
@@ -0,0 +1,6 @@
1
+ function optional(type) {
2
+ return (value) => (value == null ? undefined : type(value));
3
+ }
4
+ export const optionalString = optional(String);
5
+ export const optionalNumber = optional(Number);
6
+ export const optionalBoolean = optional(Boolean);
@@ -0,0 +1,2 @@
1
+ import type { PostgresConfig, QueryTemplateFunction } from "./index.js";
2
+ export default function postgres(options: PostgresConfig): QueryTemplateFunction;
@@ -0,0 +1,95 @@
1
+ import Postgres from "postgres";
2
+ import { optionalBoolean, optionalNumber, optionalString } from "./options.js";
3
+ export default function postgres(options) {
4
+ return async (strings, ...params) => {
5
+ const sql = Postgres({
6
+ host: optionalString(options.host),
7
+ port: optionalNumber(options.port),
8
+ username: optionalString(options.username),
9
+ password: optionalString(options.password),
10
+ database: optionalString(options.database),
11
+ ssl: optionalBoolean(options.ssl) ? { rejectUnauthorized: false } : false
12
+ });
13
+ const date = new Date();
14
+ let rows;
15
+ try {
16
+ rows = await sql.unsafe(strings.reduce((p, c, i) => `${p}$${i}${c}`), params);
17
+ }
18
+ finally {
19
+ await sql.end();
20
+ }
21
+ return {
22
+ rows,
23
+ schema: rows.columns.map(getColumnSchema),
24
+ duration: Date.now() - +date,
25
+ date
26
+ };
27
+ };
28
+ }
29
+ function getColumnSchema(column) {
30
+ return { name: column.name, type: getColumnType(column.type) };
31
+ }
32
+ // https://github.com/brianc/node-pg-types/blob/master/lib/textParsers.js#L166
33
+ function getColumnType(oid) {
34
+ switch (oid) {
35
+ case 20: // int8
36
+ return "bigint";
37
+ case 21: // int2
38
+ case 23: // int4
39
+ case 26: // oid
40
+ return "integer";
41
+ case 700: // float4/real
42
+ case 701: // float8/double
43
+ return "number";
44
+ case 16: // bool
45
+ return "boolean";
46
+ case 1082: // date
47
+ case 1114: // timestamp without timezone
48
+ case 1184: // timestamp
49
+ return "date";
50
+ case 651: // cidr[]
51
+ case 1000: // bool[]
52
+ case 1001: // byte[]
53
+ case 1002: // string[]
54
+ case 1005: // int2[]
55
+ case 1007: // int4[]
56
+ case 1028: // oid[]
57
+ case 1016: // int8[]
58
+ case 1017: // point[]
59
+ case 1021: // float4[]
60
+ case 1022: // float8[]
61
+ case 1231: // numeric[]
62
+ case 1014: // char[]
63
+ case 1015: // varchar[]
64
+ case 1008: // string[]
65
+ case 1009: // string[]
66
+ case 1040: // macaddr[]
67
+ case 1041: // inet[]
68
+ case 1115: // timestamp without time zone[]
69
+ case 1182: // date[]
70
+ case 1185: // timestamp with time zone[]
71
+ case 1187: // interval[]
72
+ case 199: // json[]
73
+ case 3807: // jsonb[]
74
+ case 3907: // numrange[]
75
+ case 2951: // uuid[]
76
+ case 791: // money[]
77
+ case 1183: // time[]
78
+ case 1270: // timetz[]
79
+ return "array";
80
+ case 1186: // interval
81
+ case 114: // json
82
+ case 3802: // jsonb
83
+ case 600: // point
84
+ case 718: // circle
85
+ return "object";
86
+ case 17: // bytea
87
+ return "buffer";
88
+ case 18: // char
89
+ case 1700: // numeric
90
+ case 25: // text
91
+ case 24: // regproc
92
+ default:
93
+ return "string";
94
+ }
95
+ }
@@ -0,0 +1,2 @@
1
+ import { QueryTemplateFunction, SnowflakeConfig } from "./index.js";
2
+ export default function snowflake(options: SnowflakeConfig): QueryTemplateFunction;
@@ -0,0 +1,102 @@
1
+ import Snowflake from "snowflake-sdk";
2
+ import { optionalString } from "./options.js";
3
+ Snowflake.configure({ logLevel: "OFF" });
4
+ export default function snowflake(options) {
5
+ return async (strings, ...params) => {
6
+ const connection = await connect({
7
+ account: String(options.account),
8
+ username: optionalString(options.username),
9
+ password: optionalString(options.password),
10
+ database: optionalString(options.database),
11
+ schema: optionalString(options.schema),
12
+ warehouse: optionalString(options.warehouse),
13
+ role: optionalString(options.role)
14
+ });
15
+ let result;
16
+ try {
17
+ result = await execute(connection, strings.join("?"), params);
18
+ }
19
+ finally {
20
+ await destroy(connection);
21
+ }
22
+ return result;
23
+ };
24
+ }
25
+ async function connect(options) {
26
+ const connection = Snowflake.createConnection(options);
27
+ await new Promise((resolve, reject) => {
28
+ connection.connect((error) => {
29
+ if (error)
30
+ return reject(error);
31
+ resolve();
32
+ });
33
+ });
34
+ return connection;
35
+ }
36
+ async function destroy(connection) {
37
+ await new Promise((resolve, reject) => {
38
+ connection.destroy((error) => {
39
+ if (error)
40
+ return reject(error);
41
+ resolve();
42
+ });
43
+ });
44
+ }
45
+ async function execute(connection, sql, params) {
46
+ return new Promise((resolve, reject) => {
47
+ const date = new Date();
48
+ connection.execute({
49
+ sqlText: sql,
50
+ binds: params,
51
+ complete(error, statement, rows) {
52
+ if (error)
53
+ return reject(error);
54
+ resolve({
55
+ rows: rows,
56
+ schema: getStatementSchema(statement),
57
+ duration: Date.now() - +date,
58
+ date
59
+ });
60
+ }
61
+ });
62
+ });
63
+ }
64
+ function getStatementSchema(statement) {
65
+ return statement.getColumns().map(getColumnSchema);
66
+ }
67
+ function getColumnSchema(column) {
68
+ return { name: column.getName(), type: getColumnType(column), nullable: column.isNullable() };
69
+ }
70
+ function getColumnType(column) {
71
+ const type = column.getType();
72
+ switch (type.toLowerCase()) {
73
+ case "date":
74
+ case "datetime":
75
+ case "timestamp":
76
+ case "timestamp_ltz":
77
+ case "timestamp_ntz":
78
+ case "timestamp_tz":
79
+ return "date";
80
+ case "time":
81
+ case "text":
82
+ return "string";
83
+ case "fixed":
84
+ return column.getScale() === 0 ? "integer" : "number";
85
+ case "float":
86
+ case "number":
87
+ case "real":
88
+ return "number";
89
+ case "binary":
90
+ return "buffer";
91
+ case "array":
92
+ return "array";
93
+ case "boolean":
94
+ return "boolean";
95
+ case "object":
96
+ case "variant":
97
+ return "object";
98
+ default:
99
+ console.warn(`unknown type: ${type}`);
100
+ return "other";
101
+ }
102
+ }
@@ -25,6 +25,8 @@ function getDefaultRange(name) {
25
25
  switch (name) {
26
26
  case "@duckdb/duckdb-wasm":
27
27
  return "@1.29.0"; // https://github.com/duckdb/duckdb-wasm/issues/1994
28
+ case "apache-arrow":
29
+ return "@17.0.0"; // to match @duckdb/duckdb-wasm 1.29.0
28
30
  default:
29
31
  return "";
30
32
  }
@@ -10,13 +10,13 @@ export function transpileObservable(input, options) {
10
10
  if (cell.tag)
11
11
  throw new Error("tagged ojs cells are not supported");
12
12
  const output = new Sourcemap(input).trim();
13
+ rewriteSpecialReferences(output, cell.body);
13
14
  if (cell.body.type === "ImportDeclaration") {
14
15
  rewriteImportSource(output, cell.body);
15
16
  return transpileJavaScript(String(output));
16
17
  }
17
18
  if (options?.resolveFiles)
18
19
  rewriteFileExpressions(output, cell.body);
19
- rewriteSpecialReferences(output, cell.body);
20
20
  const inputs = Array.from(new Set(cell.references.map(asReference)));
21
21
  let start = "";
22
22
  let end = "";
@@ -39,13 +39,15 @@ export function transpileObservable(input, options) {
39
39
  autoview: cell.id?.type === "ViewExpression"
40
40
  };
41
41
  }
42
+ /** Rewrite bare module specifiers to have the observable: protocol. */
42
43
  function rewriteImportSource(output, body) {
43
44
  const specifier = body.source.value;
44
- if (typeof specifier === "string" && !/^\w+:/.test(specifier))
45
+ if (typeof specifier === "string" && !/^\w+:/.test(specifier)) {
45
46
  output.insertLeft(body.source.start + 1, "observable:");
47
+ }
46
48
  output.insertRight(body.end, ";");
47
49
  }
48
- // Rewrite viewof x ↦ viewof$x, and mutable x ↦ mutable$x.value.
50
+ /** Rewrite viewof x ↦ viewof$x, and mutable x ↦ mutable$x.value. */
49
51
  function rewriteSpecialReferences(output, body) {
50
52
  simple(body, {
51
53
  MutableExpression(node) {
@@ -53,9 +55,28 @@ function rewriteSpecialReferences(output, body) {
53
55
  },
54
56
  ViewExpression(node) {
55
57
  output.replaceLeft(node.start, node.end, asReference(node));
58
+ },
59
+ ImportSpecifier(node) {
60
+ const inode = node;
61
+ const prefix = inode.view ? "viewof$" : inode.mutable ? "mutable$" : null;
62
+ if (prefix) {
63
+ const imported = asImportName(node.imported);
64
+ output.replaceLeft(node.start, node.imported.start, prefix);
65
+ if (node.imported === node.local) {
66
+ output.insertLeft(node.start, `${imported},`);
67
+ }
68
+ else {
69
+ const local = asImportName(node.local);
70
+ output.insertLeft(node.start, `${imported} as ${local},`);
71
+ output.insertLeft(node.local.start, prefix);
72
+ }
73
+ }
56
74
  }
57
75
  });
58
76
  }
77
+ function asImportName(ref) {
78
+ return ref.type === "Identifier" ? ref.name : ref.raw;
79
+ }
59
80
  function asReference(ref) {
60
81
  return ref.type === "ViewExpression"
61
82
  ? `viewof$${ref.id.name}`
@@ -1,3 +1,6 @@
1
1
  import type { TemplateLiteral } from "acorn";
2
+ import type { Cell } from "../lib/notebook.js";
2
3
  export declare function parseTemplate(input: string): TemplateLiteral;
4
+ /** @deprecated */
3
5
  export declare function transpileTemplate(input: string, tag?: string, raw?: boolean): string;
6
+ export declare function transpileTemplate(cell: Cell): string;
@@ -70,6 +70,13 @@ export function parseTemplate(input) {
70
70
  return TemplateCellParser.parse(input, acornOptions);
71
71
  }
72
72
  export function transpileTemplate(input, tag = "", raw = false) {
73
+ let cell;
74
+ if (typeof input !== "string") {
75
+ cell = input;
76
+ input = cell.value;
77
+ tag = cell.mode === "tex" ? "tex.block" : cell.mode === "sql" ? getSqlTag(cell) : cell.mode;
78
+ raw = cell.mode !== "md";
79
+ }
73
80
  if (!input)
74
81
  return input;
75
82
  const source = new Sourcemap(input);
@@ -78,7 +85,16 @@ export function transpileTemplate(input, tag = "", raw = false) {
78
85
  source.insertLeft(node.start, "`");
79
86
  source.insertRight(node.end, "`");
80
87
  source.insertLeft(node.start, tag);
81
- return String(source);
88
+ let output = String(source);
89
+ if (cell?.mode === "sql" && !cell.hidden)
90
+ output += ".then(Inputs.table)";
91
+ return output;
92
+ }
93
+ function getSqlTag(cell) {
94
+ const { id, database = "var:db", since } = cell;
95
+ return database.startsWith("var:")
96
+ ? `${database.slice("var:".length)}.sql`
97
+ : `DatabaseClient(${JSON.stringify(database)}, {id: ${id}${since === undefined ? "" : `, since: ${JSON.stringify(since)}`}}).sql`;
82
98
  }
83
99
  function escapeTemplateElements(source, node) {
84
100
  for (const quasi of node.quasis) {
@@ -10,9 +10,9 @@ export type TranspiledJavaScript = {
10
10
  output?: string;
11
11
  /** whether to implicitly display the body value (e.g., an expression) */
12
12
  autodisplay?: boolean;
13
- /** whether to implicitly derive a view; for ojs compatibility; requires viewof output */
13
+ /** whether to implicitly derive a view; requires viewof output */
14
14
  autoview?: boolean;
15
- /** whether to implicitly derive a mutable; for ojs compatibility; requires mutable output */
15
+ /** whether to implicitly derive a mutable; requires mutable output */
16
16
  automutable?: boolean;
17
17
  };
18
18
  export type TranspileOptions = {
@@ -21,5 +21,7 @@ export type TranspileOptions = {
21
21
  /** If true, resolve file using import.meta.url (so Vite treats it as an asset). */
22
22
  resolveFiles?: boolean;
23
23
  };
24
+ /** @deprecated */
24
25
  export declare function transpile(input: string, mode: Cell["mode"], options?: TranspileOptions): TranspiledJavaScript;
26
+ export declare function transpile(input: Cell, options?: TranspileOptions): TranspiledJavaScript;
25
27
  export declare function transpileJavaScript(input: string, options?: TranspileOptions): TranspiledJavaScript;
@@ -1,20 +1,39 @@
1
+ import { toCell } from "../lib/notebook.js";
1
2
  import { rewriteFileExpressions } from "./files.js";
2
- import { hasImportDeclaration, rewriteImportDeclarations, rewriteImportExpressions } from "./imports.js";
3
+ import { hasImportDeclaration } from "./imports.js";
4
+ import { rewriteImportDeclarations, rewriteImportExpressions } from "./imports.js";
3
5
  import { transpileObservable } from "./observable.js";
4
6
  import { parseJavaScript } from "./parse.js";
5
7
  import { Sourcemap } from "./sourcemap.js";
6
8
  import { transpileTemplate } from "./template.js";
7
9
  export function transpile(input, mode, options) {
8
- return mode === "ojs"
9
- ? transpileObservable(input, options) // TODO ojs+md etc.
10
- : transpileJavaScript(transpileMode(input, mode), options);
11
- }
12
- function transpileMode(input, mode) {
13
- if (mode === "js")
14
- return input;
15
- const tag = mode === "tex" ? "tex.block" : mode === "sql" ? "__sql(db, Inputs.table)" : mode; // for now
16
- const raw = mode === "html" || mode === "tex" || mode === "dot";
17
- return transpileTemplate(input, tag, raw);
10
+ let cell;
11
+ if (typeof input === "string") {
12
+ mode = mode;
13
+ cell = toCell({ id: -1, value: input, mode });
14
+ }
15
+ else {
16
+ options = mode;
17
+ mode = input.mode;
18
+ cell = input;
19
+ input = cell.value;
20
+ }
21
+ const transpiled = mode === "ojs"
22
+ ? transpileObservable(input, options)
23
+ : mode !== "js"
24
+ ? transpileJavaScript(transpileTemplate(cell), options)
25
+ : transpileJavaScript(input, options);
26
+ if (transpiled.output === undefined)
27
+ transpiled.output = cell.output;
28
+ if (cell.hidden)
29
+ transpiled.autodisplay = false;
30
+ else if (mode !== "js" && mode !== "ojs") {
31
+ transpiled.autodisplay = !!input;
32
+ transpiled.autoview = mode === "sql" && transpiled.autodisplay && !!transpiled.output;
33
+ if (transpiled.autoview)
34
+ transpiled.output = `viewof$${transpiled.output}`;
35
+ }
36
+ return transpiled;
18
37
  }
19
38
  export function transpileJavaScript(input, options) {
20
39
  const cell = parseJavaScript(input);