Observable Notebook Kit

repository·main·Indexed 43 days ago

https://github.com/observablehq/notebook-kit

A toolkit for transforming Observable Notebooks into static sites or integrating them into web applications. It provides a CLI for building, previewing, and downloading notebooks, a Vite plugin for development workflows, and a low-level JavaScript API for custom integrations. Version 2.6.3.

Tokens
14.6K
Snippets
57
Records
70
Agent score
57%

What's inside Observable Notebook Kit

  1. Overview of Observable Notebook Kit

    main

    Observable Notebook Kit is an open-source command-line tool designed to build static sites from Observable Notebooks using an open file format. It provides three primary ways to interact with notebooks:

    1. CLI Tool: For building static sites from notebooks.
    2. Vite Plugin: For integrating notebooks into Vite-based development workflows.
    3. JavaScript Interface: A low-level API for deep integration of Observable Notebooks into custom web applications.
  2. Understand `DefineState` and `Definition` relationships

    main

    A DefineState object tracks the lifecycle and display of a cell. It extends DisplayState by adding a variables array, which stores the Variable instances created by the define function.

    When define is called, it manages the relationship between the cell's body and its inputs/outputs by registering them as variables within the Module. If the cell uses display or view inputs, define automatically sets up internal shadow variables to manage versioning and prevent 'stale display' errors during asynchronous updates.

  3. Configure ImportOptions for rewriting

    main

    When using rewriteImportDeclarations or rewriteImportExpressions, you can provide an ImportOptions object to customize resolution behavior.

    KeyTypeDescription
    resolveImport(specifier: string) => stringFunction to resolve a specifier to a URL. Defaults to resolveImportDefault.
    resolveLocalImportsbooleanIf true, resolves local imports relative to document.baseURI.
    renameObservableImport(name: string) => stringAllows renaming of imported variables (e.g., for viewof$foo compatibility).
  4. Use the notebook-kit Vite plugin

    main

    The @observablehq/notebook-kit package provides a Vite plugin to integrate Observable Notebooks into your Vite-based projects. The plugin exports a config object for configuration and an observable function to initialize the plugin in your vite.config.ts (or .js).

    import { observable } from '@observablehq/notebook-kit/vite';
    
    export default {
      plugins: [
        observable()
      ]
    };
  5. Define a Notebook using NotebookSpec

    main

    The NotebookSpec interface defines the structure for creating a notebook. It allows you to specify cells, a title, a theme, and whether the notebook is read-only. You can use the toNotebook function to transform a NotebookSpec into a validated Notebook object, which ensures all required fields (like cells, title, theme, and readOnly) are populated with non-nullable values.

    import { toNotebook, type NotebookSpec } from './notebook.js';
    
    const spec: NotebookSpec = {
      title: "My Analysis",
      theme: "ocean-floor",
      readOnly: false,
      cells: [
        { id: 1, value: "console.log('hello')", mode: "js" }
      ]
    };
    
    const notebook = toNotebook(spec);
  6. Access file attachments with FileAttachment()

    main

    Use the FileAttachment(name, base) function to create a reference to a file. This returns a FileAttachment object that provides various methods to load and parse the file's content. If the file has already been requested, it returns the existing instance.

    Parameters:

    • name: The path or name of the file.
    • base (optional): The base URL to resolve the name against. Defaults to document.baseURI.

    Note: If requireFileRegistration(true) has been called, FileAttachment() will throw an error if the file is not explicitly registered via registerFile().

    import { FileAttachment } from "./fileAttachment.js";
    
    // Access a CSV file
    const csvFile = FileAttachment("data/results.csv");
    const data = await csvFile.csv();
  7. Use the default notebook exports

    main

    For simple use cases, the package exports pre-instantiated singleton instances of the runtime components, allowing you to start defining cells immediately without manual instantiation.

    import { runtime, main, define } from "@observablehq/notebook-kit";
    
    // Use the shared runtime and main module directly
    define("hello", () => "world");
  8. Access the Observable Notebook Kit standard library

    main

    The standard library is exposed via a library object which provides access to core utilities, generators, database clients, and recommended libraries. You can access these utilities through the library object in the runtime environment.

    Note that Promises, Files, and DOM are marked as deprecated and should be avoided in favor of newer patterns.

    // Accessing core utilities from the library object
    const generators = library.Generators();
    const sql = library.sql();
    const databaseClient = library.DatabaseClient();
    
    // Accessing recommended libraries and sample datasets
    // (These are spread into the library object)
  9. Clear a cell's display with clear()

    main

    The clear function removes all content from the cell's root element. Before clearing, it updates the state.expanded array by extracting the current expansion state of existing child nodes using getExpanded. This ensures that if a cell is cleared and then re-displayed, the user's previous inspection state can be preserved.

    import {clear} from "./runtime/display.js";
    
    // Clears the root element and preserves expansion state in state.expanded
    clear(state);
  10. Initialize a database query function with getDatabase

    main

    The getDatabase function takes a DatabaseConfig and returns a QueryTemplateFunction. This returned function is used to execute queries against the specified database.

    Supported database types:

    • bigquery
    • databricks
    • duckdb
    • sqlite (uses sqlite-bun.js if running in Bun, otherwise sqlite-node.js)
    • snowflake
    • postgres
    import { getDatabase, type DatabaseConfig } from "@observablehq/notebook-kit/databases";
    
    const config: DatabaseConfig = { type: "duckdb", path: "data.duckdb" };
    const query = await getDatabase(config);
    
    // The returned query function is a QueryTemplateFunction
    const results = await query`SELECT * FROM my_table WHERE id = ${123}`;
  11. Collect assets from a DOM root with collectAssets()

    main

    Use collectAssets to scan a DOM element (and its descendants) for asset references like images, audio, video, stylesheets, and download links. This function populates a provided Set<string> with the paths of all discovered assets.

    It automatically handles:

    • src attributes on audio, img, and video elements.
    • srcset attributes on img and picture source elements.
    • href attributes on a[download] links and link[href] stylesheets.
    • Converting relative paths into import paths (e.g., prefixing with ./).
    • Skipping elements marked with rel="external".
    • Stripping query strings and anchor fragments from paths.
    const assets = new Set<string>();
    const root = document.body;
    
    // Populates the 'assets' set with discovered asset paths
    collectAssets(assets, root);
    
    console.log(Array.from(assets));
  12. Rewrite JavaScript import declarations

    main

    Use rewriteImportDeclarations to transform standard ESM import statements into asynchronous import() calls suitable for browser environments. This is particularly useful for resolving NPM, JSR, or Observable-specific imports into URLs.

    Note: This function mutates the provided Sourcemap input.

    Key Behaviors:

    • Type Imports: import type { ... } declarations are automatically deleted.
    • Observable Imports: If an import is identified as an Observable import (e.g., using viewof or mutable), it is rendered using renderObservableImport.
    • Batching: If multiple declarations exist, they are combined into a single await Promise.all([...]) block at the top of the file to optimize loading.
    • Local Imports: If resolveLocalImports is true, local paths (starting with ./, ../, or /) are wrapped in new URL(..., document.baseURI).
    import { rewriteImportDeclarations, resolveImportDefault } from '@observablehq/notebook-kit/src/javascript/imports.js';
    
    // Assuming 'output' is a Sourcemap instance and 'body' is an Acorn AST Node
    rewriteImportDeclarations(
      output,
      body,
      ['existing-input'],
      {
        resolveImport: resolveImportDefault,
        resolveLocalImports: true,
        renameObservableImport: (name) => name // or use dedollar
      }
    );