@observablehq/notebook-kit 1.3.0-rc.1 → 1.3.0-rc.3

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/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.3.0-rc.1",
8
+ "version": "1.3.0-rc.3",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "test": "vitest",
@@ -51,6 +51,7 @@
51
51
  "@lezer/html": "^1.3.10",
52
52
  "@lezer/javascript": "^1.5.1",
53
53
  "@lezer/markdown": "^1.4.3",
54
+ "@lezer/python": "^1.1.18",
54
55
  "@observablehq/inspector": "^5.0.1",
55
56
  "@observablehq/parser": "^6.1.0",
56
57
  "@observablehq/runtime": "^6.0.0",
@@ -1,2 +1,3 @@
1
1
  import type { Cell } from "../lib/notebook.js";
2
2
  export declare function getInterpreterCachePath(sourcePath: string, interpreter: string, format: Cell["format"], input: string): Promise<string>;
3
+ export declare function getInterpreterCommand(interpreter: string): [command: string, args: string[]];
@@ -6,3 +6,13 @@ export async function getInterpreterCachePath(sourcePath, interpreter, format, i
6
6
  const cacheName = `${await nameHash(interpreter)}-${await stringHash(input)}${getInterpreterExtension(format)}`; // TODO avoid conflict with database cache?
7
7
  return join(sourceDir, ".observable", "cache", cacheName);
8
8
  }
9
+ export function getInterpreterCommand(interpreter) {
10
+ switch (interpreter) {
11
+ case "node":
12
+ return ["node", ["--input-type=module", "--permission", "--allow-fs-read=."]];
13
+ case "python":
14
+ return ["python3", []];
15
+ default:
16
+ throw new Error(`unknown interpreter: ${interpreter}`);
17
+ }
18
+ }
@@ -82,7 +82,7 @@ export function transpileTemplate(input, tag = "", raw = false) {
82
82
  return input;
83
83
  const source = new Sourcemap(input);
84
84
  let node;
85
- if (cell && isInterpreter(cell)) {
85
+ if (cell && isInterpreter(cell.mode)) {
86
86
  node = { type: "Literal", start: 0, end: input.length };
87
87
  escapeBacktick(source, node);
88
88
  escapeBackslash(source, node);
@@ -106,7 +106,7 @@ function getTag(cell) {
106
106
  ? "tex.block"
107
107
  : cell.mode === "sql"
108
108
  ? getSqlTag(cell)
109
- : isInterpreter(cell)
109
+ : isInterpreter(cell.mode)
110
110
  ? getInterpreterTag(cell)
111
111
  : cell.mode;
112
112
  }
@@ -123,7 +123,7 @@ function getInterpreterTag(cell) {
123
123
  function getSuffix(cell) {
124
124
  return cell.mode === "sql" && !cell.hidden
125
125
  ? ".then(Inputs.table)"
126
- : isInterpreter(cell)
126
+ : isInterpreter(cell.mode)
127
127
  ? getInterpreterSuffix(cell)
128
128
  : "";
129
129
  }
@@ -1,4 +1,4 @@
1
1
  import type { Cell } from "./notebook.js";
2
- export declare function isInterpreter(cell: Cell): boolean;
2
+ export declare function isInterpreter(mode: Cell["mode"]): boolean;
3
3
  export declare function getInterpreterExtension(format: Cell["format"]): string;
4
4
  export declare function getInterpreterMethod(format: Cell["format"]): string;
@@ -1,5 +1,5 @@
1
- export function isInterpreter(cell) {
2
- return cell.mode === "node";
1
+ export function isInterpreter(mode) {
2
+ return mode === "node" || mode === "python";
3
3
  }
4
4
  export function getInterpreterExtension(format) {
5
5
  switch (format) {
@@ -21,7 +21,7 @@ export interface CellSpec {
21
21
  /** the committed cell value; defaults to empty */
22
22
  value?: string;
23
23
  /** the mode; affects how the value is evaluated; defaults to js */
24
- mode?: "js" | "ojs" | "md" | "html" | "tex" | "dot" | "sql" | "node";
24
+ mode?: "js" | "ojs" | "md" | "html" | "tex" | "dot" | "sql" | "node" | "python";
25
25
  /** if true, the editor will stay open when not focused; defaults to false */
26
26
  pinned?: boolean;
27
27
  /** if true, implicit display will be suppressed; defaults to false */
@@ -1,3 +1,4 @@
1
+ import { isInterpreter } from "./interpreters.js";
1
2
  export function toNotebook({ cells = [], title = "Untitled", theme = "air", readOnly = false }) {
2
3
  return {
3
4
  cells: cells.map(toCell),
@@ -6,7 +7,7 @@ export function toNotebook({ cells = [], title = "Untitled", theme = "air", read
6
7
  readOnly
7
8
  };
8
9
  }
9
- export function toCell({ id, value = "", mode = "js", pinned = defaultPinned(mode), hidden = false, output, format = mode === "node" ? "buffer" : undefined, database = mode === "sql" ? "var:db" : undefined, since }) {
10
+ export function toCell({ id, value = "", mode = "js", pinned = defaultPinned(mode), hidden = false, output, format = isInterpreter(mode) ? "buffer" : undefined, database = mode === "sql" ? "var:db" : undefined, since }) {
10
11
  return {
11
12
  id,
12
13
  value,
@@ -14,7 +15,7 @@ export function toCell({ id, value = "", mode = "js", pinned = defaultPinned(mod
14
15
  pinned,
15
16
  hidden,
16
17
  output,
17
- format: mode === "node" ? format : undefined,
18
+ format: isInterpreter(mode) ? format : undefined,
18
19
  database: mode === "sql" ? database : undefined,
19
20
  since: since !== undefined ? asDate(since) : undefined
20
21
  };
@@ -23,5 +24,5 @@ function asDate(date) {
23
24
  return date instanceof Date ? date : new Date(date);
24
25
  }
25
26
  export function defaultPinned(mode) {
26
- return mode === "js" || mode === "sql" || mode === "node" || mode === "ojs";
27
+ return mode === "js" || mode === "sql" || isInterpreter(mode) || mode === "ojs";
27
28
  }
@@ -70,6 +70,8 @@ function serializeMode(mode) {
70
70
  return "text/vnd.graphviz";
71
71
  case "node":
72
72
  return "application/vnd.node.javascript";
73
+ case "python":
74
+ return "text/x-python";
73
75
  case "ojs":
74
76
  return "application/vnd.observable.javascript";
75
77
  default:
@@ -90,6 +92,8 @@ function deserializeMode(mode) {
90
92
  return "dot";
91
93
  case "application/vnd.node.javascript":
92
94
  return "node";
95
+ case "text/x-python":
96
+ return "python";
93
97
  case "application/vnd.observable.javascript":
94
98
  return "ojs";
95
99
  default:
@@ -51,14 +51,14 @@ async function getParser(language) {
51
51
  case "js":
52
52
  case "ts":
53
53
  case "jsx":
54
- case "node":
55
54
  return (await import("@lezer/javascript")).parser.configure({ dialect: language });
55
+ case "python":
56
+ return (await import("@lezer/python")).parser;
56
57
  case "html":
57
58
  return (await import("@lezer/html")).parser;
58
59
  case "css":
59
60
  return (await import("@lezer/css")).parser;
60
61
  case "md":
61
- case "markdown":
62
62
  return (await import("@lezer/markdown")).parser;
63
63
  }
64
64
  }
@@ -68,10 +68,15 @@ function getLanguage(code) {
68
68
  ?.slice("language-".length)
69
69
  ?.toLowerCase();
70
70
  switch (language) {
71
+ case "node":
71
72
  case "javascript":
72
73
  return "js";
73
74
  case "typescript":
74
75
  return "ts";
76
+ case "py":
77
+ return "python";
78
+ case "markdown":
79
+ return "md";
75
80
  }
76
81
  return language;
77
82
  }
@@ -6,8 +6,8 @@ import { relative } from "node:path/posix";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { JSDOM } from "jsdom";
8
8
  import { getQueryCachePath } from "../databases/index.js";
9
- import { getInterpreterCachePath } from "../interpreters/index.js";
10
- import { getInterpreterMethod } from "../lib/interpreters.js";
9
+ import { getInterpreterCachePath, getInterpreterCommand } from "../interpreters/index.js";
10
+ import { getInterpreterMethod, isInterpreter } from "../lib/interpreters.js";
11
11
  import { deserialize } from "../lib/serialize.js";
12
12
  import { Sourcemap } from "../javascript/sourcemap.js";
13
13
  import { transpile } from "../javascript/transpile.js";
@@ -85,14 +85,14 @@ export function observable({ window = new JSDOM().window, parser = new window.DO
85
85
  cell.value = `FileAttachment(${JSON.stringify(relative(dir, cachePath))}).json().then(DatabaseClient.revive)${hidden ? "" : `.then(Inputs.table)${cell.output ? ".then(view)" : ""}`}`;
86
86
  }
87
87
  }
88
- else if (mode === "node") {
88
+ else if (isInterpreter(mode)) {
89
89
  const { filename: sourcePath } = context;
90
90
  const sourceDir = dirname(sourcePath);
91
91
  const cachePath = await getInterpreterCachePath(sourcePath, mode, format, value);
92
92
  if (!existsSync(cachePath)) {
93
93
  await mkdir(dirname(cachePath), { recursive: true });
94
- const args = ["--input-type=module", "--permission", "--allow-fs-read=."];
95
- const child = spawn("node", args, { cwd: sourceDir });
94
+ const [command, args] = getInterpreterCommand(mode);
95
+ const child = spawn(command, args, { cwd: sourceDir });
96
96
  child.stdin.end(value);
97
97
  child.stderr.pipe(process.stderr);
98
98
  child.stdout.pipe(createWriteStream(cachePath));
package/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.3.0-rc.1",
8
+ "version": "1.3.0-rc.3",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "test": "vitest",
@@ -51,6 +51,7 @@
51
51
  "@lezer/html": "^1.3.10",
52
52
  "@lezer/javascript": "^1.5.1",
53
53
  "@lezer/markdown": "^1.4.3",
54
+ "@lezer/python": "^1.1.18",
54
55
  "@observablehq/inspector": "^5.0.1",
55
56
  "@observablehq/parser": "^6.1.0",
56
57
  "@observablehq/runtime": "^6.0.0",