diff --git a/.claude/rules/formats/ojs.md b/.claude/rules/formats/ojs.md new file mode 100644 index 00000000000..c354eb8d28b --- /dev/null +++ b/.claude/rules/formats/ojs.md @@ -0,0 +1,13 @@ +--- +paths: + - "src/execute/ojs/**" + - "packages/ojs/**" + - "src/resources/formats/html/ojs/**" + - "src/resources/filters/quarto-post/ojs.lua" +--- + +# OJS + +`src/resources/formats/html/ojs/quarto-ojs-runtime{,.min}.js` are build output of `packages/ojs`: change the sources there, then rebuild with `npm ci && npm run build` in `packages/ojs` and commit both bundles with the source change. CI fails when committed bundles differ from a rebuild. `quarto-ojs.css` is edited in place. + +Architecture (compile path, runtime injection, inline expressions, `ojs_define`, tests): `llm-docs/ojs-architecture.md`. Update procedure and verification checklist: `dev-docs/update-ojs-runtime.md`. diff --git a/.claude/rules/formats/vendored-html-dependencies.md b/.claude/rules/formats/vendored-html-dependencies.md index 20c6217f865..daaf5919b41 100644 --- a/.claude/rules/formats/vendored-html-dependencies.md +++ b/.claude/rules/formats/vendored-html-dependencies.md @@ -9,6 +9,9 @@ Files under `src/resources/formats/html/*/` (e.g. `axe`, `anchor`, `popper`, `cl `tippy`, `glightbox`, `fuse`, `bootstrap`) are vendored third-party dependencies, managed by `package/src/common/update-html-dependencies.ts` — not hand-edited. +Exception: `ojs/` is not managed by the updater. Its runtime bundles are built from `packages/ojs` +and `quarto-ojs.css` is edited in place; see `.claude/rules/formats/ojs.md`. + ## Verify provenance before trusting a change When reviewing a change to one of these files, fetch the same version from upstream and compare diff --git a/dev-docs/update-ojs-runtime.md b/dev-docs/update-ojs-runtime.md new file mode 100644 index 00000000000..916cc7eb09a --- /dev/null +++ b/dev-docs/update-ojs-runtime.md @@ -0,0 +1,66 @@ +# Updating the OJS runtime + +How to change the browser runtime behind `{ojs}` cells: Quarto's own runtime code, a bundled npm dependency, or one of the Observable forks. + +The sources live in [`packages/ojs/`](../packages/ojs/). Its [README](../packages/ojs/README.md) describes the layout, the build, how the forks are maintained, and the dependency pins. Read it first; this page is the end-to-end procedure around it. For how Quarto compiles `{ojs}` cells and injects the runtime, see [`llm-docs/ojs-architecture.md`](../llm-docs/ojs-architecture.md). + +## What the build produces + +`npm run build` in `packages/ojs` writes two bundles into [`src/resources/formats/html/ojs/`](../src/resources/formats/html/ojs/): + +- `quarto-ojs-runtime.js` is the bundle Quarto uses. Renders link it as a module script or inline it when self-contained. +- `quarto-ojs-runtime.min.js` is built, committed, and shipped in the distribution with the rest of `src/resources/`, but nothing in quarto-cli references it. + +`quarto-ojs.css` in the same directory is not built. Edit it there directly. + +## Procedure + +1. Make the change in `packages/ojs/`: + - Quarto's runtime: edit `quarto-ojs-runtime/src/`. + - A bundled npm dependency: `npm install @ --workspace quarto-ojs-runtime` from `packages/ojs`, which updates `quarto-ojs-runtime/package.json` and `package-lock.json`. Check the README notes for pinned packages (for example `@observablehq/inputs` stays on 0.10). + - An upstream fork change: apply it by hand in the matching `external-*/` directory (see the README). +2. Bump the runtime version, so the bundle banner (`// quarto-ojs-runtime vX.Y.Z ...`) identifies the build: + + ```bash + cd packages/ojs + npm version patch --workspace quarto-ojs-runtime --no-git-tag-version + ``` + + This updates `quarto-ojs-runtime/package.json` and the matching entry in `package-lock.json`. +3. Rebuild with Node.js 24 or later: + + ```bash + cd packages/ojs + npm ci + npm run build + ``` + +4. Commit the source change, `package-lock.json`, and both bundles together. CI rebuilds them and fails if the committed bundles differ. + +The build is reproducible: the banner holds no date, so the same sources produce identical bundles. On Windows, rollup writes the unminified bundle with CRLF line endings because the sources are checked out with CRLF. The `eol=lf` attribute on `src/**` normalizes them, so `git diff` shows no line-ending changes. + +npm workspaces create junctions under `node_modules`. To remove `node_modules` on Windows, unlink the junctions first (`Remove-Item` without `-Recurse` on each reparse point); `Remove-Item -Recurse` on a junction deletes the files of the package it points to. + +## Verify + +- The bundle must not contain a closing script tag, because self-contained renders inline it without escaping: + + ```bash + rg -c '` id and collects the cell source. It then writes an include-after-body file containing: + +- `` inside an include-in-header file. The code carries an open `FIXME` asking whether `sharePath()` is the right way to locate the resource. + +Invariant: the inlined bundle is not escaped, so `quarto-ojs-runtime.js` must never contain the text ``) and appends a hidden `ojs-auto-generated` div holding its own base64 `ojs-module-contents` script, which `interpretFromScriptTags()` picks up alongside the compile output. + +Other Lua touch points: + +- `quarto-pre/outputs.lua` and `layout/hugo.lua` recognise `ojs-cell-` ids so Hugo output keeps OJS cell divs. +- `modules/constants.lua` lists `//` as the `ojs` comment syntax. + +## `ojs_define` (engine side) + +`ojs_define()` passes data from R, Python, or Julia to OJS by emitting `