@observablehq/notebook-kit 1.0.0 → 1.1.0-rc.1
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.
- package/dist/bin/build.js +7 -0
- package/dist/bin/download.js +15 -19
- package/dist/package.json +25 -2
- package/dist/src/databases/duckdb.d.ts +2 -0
- package/dist/src/databases/duckdb.js +67 -0
- package/dist/src/databases/index.d.ts +36 -0
- package/dist/src/databases/index.js +15 -0
- package/dist/src/databases/options.d.ts +3 -0
- package/dist/src/databases/options.js +6 -0
- package/dist/src/databases/postgres.d.ts +2 -0
- package/dist/src/databases/postgres.js +95 -0
- package/dist/src/databases/snowflake.d.ts +2 -0
- package/dist/src/databases/snowflake.js +102 -0
- package/dist/src/javascript/observable.js +24 -3
- package/dist/src/javascript/template.d.ts +3 -0
- package/dist/src/javascript/template.js +17 -1
- package/dist/src/javascript/transpile.d.ts +4 -2
- package/dist/src/javascript/transpile.js +30 -11
- package/dist/src/javascript/transpile.test.js +16 -0
- package/dist/src/lib/error.d.ts +6 -0
- package/dist/src/lib/error.js +6 -0
- package/dist/src/lib/notebook.d.ts +11 -1
- package/dist/src/lib/notebook.js +10 -3
- package/dist/src/lib/notebook.test.js +10 -2
- package/dist/src/lib/serialize.d.ts +3 -1
- package/dist/src/lib/serialize.js +13 -4
- package/dist/src/lib/serialize.test.js +10 -3
- package/dist/src/runtime/define.d.ts +2 -2
- package/dist/src/runtime/define.js +2 -2
- package/dist/src/runtime/display.d.ts +2 -0
- package/dist/src/runtime/display.js +5 -1
- package/dist/src/runtime/index.d.ts +100 -1
- package/dist/src/runtime/index.js +29 -3
- package/dist/src/runtime/stdlib/databaseClient.d.ts +46 -0
- package/dist/src/runtime/stdlib/databaseClient.js +80 -0
- package/dist/src/runtime/stdlib/fileAttachment.d.ts +38 -11
- package/dist/src/runtime/stdlib/fileAttachment.js +21 -10
- package/dist/src/runtime/stdlib/generators/input.d.ts +1 -1
- package/dist/src/runtime/stdlib/index.d.ts +43 -6
- package/dist/src/runtime/stdlib/index.js +5 -5
- package/dist/src/styles/global.css +1 -1
- package/dist/src/templates/default.html +18 -18
- package/dist/src/vite/config.js +1 -0
- package/dist/src/vite/observable.d.ts +25 -6
- package/dist/src/vite/observable.js +58 -15
- package/package.json +25 -2
- package/dist/src/runtime/stdlib/sql.d.ts +0 -5
- 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: {
|
package/dist/bin/download.js
CHANGED
|
@@ -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
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
47
|
-
|
|
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.
|
|
8
|
+
"version": "1.1.0-rc.1",
|
|
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"
|
|
@@ -53,21 +57,40 @@
|
|
|
53
57
|
"@sindresorhus/slugify": "^2.2.1",
|
|
54
58
|
"acorn": "^8.15.0",
|
|
55
59
|
"acorn-walk": "^8.3.4",
|
|
60
|
+
"jsdom": "^26.1.0",
|
|
56
61
|
"markdown-it": "^14.1.0",
|
|
57
62
|
"markdown-it-anchor": "^9.2.0",
|
|
58
63
|
"vite": "^7.0.0"
|
|
59
64
|
},
|
|
60
65
|
"devDependencies": {
|
|
66
|
+
"@duckdb/node-api": "^1.3.2-alpha.26",
|
|
61
67
|
"@eslint/js": "^9.29.0",
|
|
62
68
|
"@types/jsdom": "^21.1.7",
|
|
63
69
|
"@types/markdown-it": "^14.1.2",
|
|
64
70
|
"eslint": "^9.29.0",
|
|
65
71
|
"globals": "^16.2.0",
|
|
66
72
|
"htl": "^0.3.1",
|
|
67
|
-
"
|
|
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,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,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,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
|
+
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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;
|
|
13
|
+
/** whether to implicitly derive a view; requires viewof output */
|
|
14
14
|
autoview?: boolean;
|
|
15
|
-
/** whether to implicitly derive a mutable;
|
|
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
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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);
|