Repository navigation
Move the OJS runtime and its Observable forks from quarto-dev/quarto #15046
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
e9a53b7
Copy packages/ojs from quarto-dev/quarto
nealrichardson 4e43fa9
Build the OJS runtime with npm in packages/ojs
nealrichardson 60fa617
Rebuild the OJS runtime bundles and check them in CI
nealrichardson 730faee
Address review feedback on the OJS runtime move
nealrichardson File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| # Rebuilds the OJS runtime bundles from packages/ojs and fails if they differ | ||
| # from the bundles committed in src/resources/formats/html/ojs. | ||
| name: OJS Runtime Bundle | ||
| on: | ||
| workflow_dispatch: | ||
| pull_request: | ||
| branches: [main] | ||
| paths: | ||
| - "packages/ojs/**" | ||
| - "src/resources/formats/html/ojs/quarto-ojs-runtime*.js" | ||
| - ".github/workflows/test-ojs-runtime.yml" | ||
| push: | ||
| # only trigger on branches, not on tags | ||
| branches: [main] | ||
| paths: | ||
| - "packages/ojs/**" | ||
| - "src/resources/formats/html/ojs/quarto-ojs-runtime*.js" | ||
| - ".github/workflows/test-ojs-runtime.yml" | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| check-bundle: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout Repo | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - uses: actions/setup-node@v6 | ||
| with: | ||
| node-version: 24 | ||
|
|
||
| - name: Build OJS runtime | ||
| working-directory: packages/ojs | ||
| run: | | ||
| npm ci | ||
| npm run build | ||
|
|
||
| - name: Check committed bundles match the sources | ||
| run: | | ||
| if ! git diff --exit-code --stat -- src/resources/formats/html/ojs; then | ||
| echo "::error::The committed OJS runtime bundles don't match packages/ojs. Run 'npm ci && npm run build' in packages/ojs and commit the result." | ||
| exit 1 | ||
| fi | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| node_modules/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| # OJS runtime | ||
|
|
||
| Sources for `quarto-ojs-runtime.js` and `quarto-ojs-runtime.min.js`, the bundles that | ||
| Quarto serves for `{ojs}` cells. The built bundles are committed in | ||
| `src/resources/formats/html/ojs/`. | ||
|
|
||
| - `quarto-ojs-runtime/`: Quarto's runtime: code transforms, the Observable connector, | ||
| the inspector, and Shiny integration. | ||
| - `external-*/`: Quarto forks of Observable's | ||
| [runtime](https://github.com/observablehq/runtime), | ||
| [stdlib](https://github.com/observablehq/stdlib) and | ||
| [parser](https://github.com/observablehq/parser), of | ||
| [d3-require](https://github.com/d3/d3-require), and of | ||
| [unofficial-observablehq-compiler](https://github.com/asg017/unofficial-observablehq-compiler). | ||
|
|
||
| All of these packages are under the ISC license, as given in each `package.json`, except | ||
| two files vendored in `quarto-ojs-runtime/src/`: `escodegen.js` is a fork of | ||
| [escodegen](https://github.com/estools/escodegen) (BSD-2-Clause) and `escape.js` comes from | ||
| [escape-html](https://github.com/component/escape-html) (MIT). Both keep their license | ||
| headers, which are preserved in `quarto-ojs-runtime.js`. | ||
|
|
||
| These moved from `packages/ojs/` in [quarto-dev/quarto](https://github.com/quarto-dev/quarto) | ||
| ([#15043](https://github.com/quarto-dev/quarto-cli/issues/15043)), where the forks were | ||
| `git subtree`s. They were copied from | ||
| [quarto-dev/quarto@42177e13](https://github.com/quarto-dev/quarto/tree/42177e13074cf905619bbb52b1dba313fb5b4bb1/packages/ojs), | ||
| the head of [quarto-dev/quarto#1180](https://github.com/quarto-dev/quarto/pull/1180) before | ||
| it deleted them. That commit is not on quarto-dev/quarto's `main` branch. | ||
|
|
||
| ## Building | ||
|
|
||
| With Node.js 24 or later: | ||
|
|
||
| ```bash | ||
| cd packages/ojs | ||
| npm ci | ||
| npm run build | ||
| ``` | ||
|
|
||
| This writes both bundles directly into `src/resources/formats/html/ojs/`. The build is | ||
| reproducible: CI rebuilds the bundles and fails if they differ from the committed | ||
| ones, so commit the rebuilt bundles together with any change here. | ||
|
|
||
| The npm workspaces link the forks to each other and to the runtime. Rollup bundles | ||
| everything from source. The compiler fork's `module` field points to its ES module | ||
| source, so its `dist/` build isn't needed. | ||
|
|
||
| ## Maintaining the forks | ||
|
|
||
| The forks are plain copies here, so `git subtree pull` doesn't work. To take an | ||
| upstream change, apply it by hand and rebuild. Upstream development of these | ||
| packages has largely stopped. | ||
|
|
||
| Changes from the copies in quarto-dev/quarto: | ||
|
|
||
| - The forks' `devDependencies` are removed. They were for each fork's own build, | ||
| tests and linting, which don't run here, and their old rollup plugins conflict | ||
| with npm's peer dependency checks. The forks' `scripts` won't work without them. | ||
| The forks' `yarn.lock` files are removed too; `package-lock.json` here covers | ||
| every package. | ||
| - The forks' `.github/` and `.circleci/` directories are removed. GitHub only runs | ||
| workflows from the repository root, and the forks' scripts can't run here anyway. | ||
| - The compiler fork's `package.json` gains `"module": "src/index.js"`. | ||
|
|
||
| ## Notes | ||
|
|
||
| - Keep `@observablehq/inputs` on 0.10. Version 0.11 renames the form class | ||
| (`__ns__` to `inputs-3a86ea`), which breaks Shiny's button styling. | ||
| - `src/resources/formats/html/ojs/quarto-ojs.css` is maintained directly in that | ||
| directory. quarto-dev/quarto had an older copy of it, which wasn't brought over. |
90 changes: 90 additions & 0 deletions
90
packages/ojs/external-asg017-unofficial-observablehq-compiler/.gitignore
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| # Logs | ||
| logs | ||
| *.log | ||
| npm-debug.log* | ||
| yarn-debug.log* | ||
| yarn-error.log* | ||
| lerna-debug.log* | ||
|
|
||
| # Diagnostic reports (https://nodejs.org/api/report.html) | ||
| report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | ||
|
|
||
| # Runtime data | ||
| pids | ||
| *.pid | ||
| *.seed | ||
| *.pid.lock | ||
|
|
||
| # Directory for instrumented libs generated by jscoverage/JSCover | ||
| lib-cov | ||
|
|
||
| # Coverage directory used by tools like istanbul | ||
| coverage | ||
| *.lcov | ||
|
|
||
| # nyc test coverage | ||
| .nyc_output | ||
|
|
||
| # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) | ||
| .grunt | ||
|
|
||
| # Bower dependency directory (https://bower.io/) | ||
| bower_components | ||
|
|
||
| # node-waf configuration | ||
| .lock-wscript | ||
|
|
||
| # Compiled binary addons (https://nodejs.org/api/addons.html) | ||
| build/Release | ||
|
|
||
| # Dependency directories | ||
| node_modules/ | ||
| jspm_packages/ | ||
|
|
||
| # TypeScript v1 declaration files | ||
| typings/ | ||
|
|
||
| # TypeScript cache | ||
| *.tsbuildinfo | ||
|
|
||
| # Optional npm cache directory | ||
| .npm | ||
|
|
||
| # Optional eslint cache | ||
| .eslintcache | ||
|
|
||
| # Optional REPL history | ||
| .node_repl_history | ||
|
|
||
| # Output of 'npm pack' | ||
| *.tgz | ||
|
|
||
| # Yarn Integrity file | ||
| .yarn-integrity | ||
|
|
||
| # dotenv environment variables file | ||
| .env | ||
| .env.test | ||
|
|
||
| # parcel-bundler cache (https://parceljs.org/) | ||
| .cache | ||
|
|
||
| # next.js build output | ||
| .next | ||
|
|
||
| # nuxt.js build output | ||
| .nuxt | ||
|
|
||
| # vuepress build output | ||
| .vuepress/dist | ||
|
|
||
| # Serverless directories | ||
| .serverless/ | ||
|
|
||
| # FuseBox cache | ||
| .fusebox/ | ||
|
|
||
| # DynamoDB Local files | ||
| .dynamodb/ | ||
|
|
||
| dist/ |
2 changes: 2 additions & 0 deletions
2
packages/ojs/external-asg017-unofficial-observablehq-compiler/.prettierrc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| { | ||
| } |
3 changes: 3 additions & 0 deletions
3
packages/ojs/external-asg017-unofficial-observablehq-compiler/.surgeignore
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| src/ | ||
| node_modules/ | ||
| yarn.lock |
120 changes: 120 additions & 0 deletions
120
packages/ojs/external-asg017-unofficial-observablehq-compiler/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| # unofficial-observablehq-compiler | ||
|
|
||
| An unoffical compiler _and_ interpreter for Observable notebooks (the glue between the Observable parser and runtime) | ||
|
|
||
| This library has two parts: The Interpreter and the Compiler. The Interpreter will interpret "Observable syntax" into "javascript syntax" live in a javascript environment. For example: | ||
|
|
||
| ```javascript | ||
| import { Intepreter } from "@alex.garcia/unofficial-observablehq-compiler"; | ||
| import { Inspector, Runtime } from "@observablehq/runtime"; | ||
|
|
||
| async function main() { | ||
| const runtime = new Runtime(); | ||
| const main = runtime.module(); | ||
| const observer = Inspector.into(document.body); | ||
| const interpret = new Intepreter({ module: main, observer }); | ||
|
|
||
| await interpret.module(` | ||
| import {text} from '@jashkenas/inputs' | ||
|
|
||
| viewof name = text({ | ||
| title: "what's your name?", | ||
| value: '' | ||
| }) | ||
|
|
||
| md\`Hello **\${name}**, it's nice to meet you!\` | ||
|
|
||
| `); | ||
| } | ||
| main(); | ||
| ``` | ||
|
|
||
| The Compiler will compile "Observable syntax" into javascript source code (as an ES module). | ||
|
|
||
| For more live examples and functionality, take a look at the [announcement notebook](https://observablehq.com/d/74f872c4fde62e35) | ||
| and this [test page](https://github.com/asg017/unofficial-observablehq-compiler/blob/master/test/test.html). | ||
|
|
||
| ## API Reference | ||
|
|
||
| ### new Interpreter(_params_) | ||
|
|
||
| `Interpreter` is a class that encompasses all logic to interpret Observable js code. _params_ is an optional object with the following allowed configuration: | ||
|
|
||
| `resolveImportPath(path, specifers)`: An async function that resolves to the `define` definition for a notebook. _path_ is the string provided in the import statement, and _specifiers_ is a array of string if the imported cell name specifiers in the statement (cells inside `import {...}`). _specifiers_ is useful when implementing tree-shaking. For example, `import {chart as CHART} from "@d3/bar-chart"` would supply `path="@d3/bar-chart"` and `specifiers=["chart"]`. Default imports from observablehq.com, eg `https://api.observablehq.com/@d3/bar-chart.js?v=3` | ||
| `resolveFileAttachments`: A function that, given the name of a FileAttachment, returns the URL that the FileAttachment will be fetched from. Defaults to `name => name`. | ||
| `defineImportMarkdown`: A boolean, whether to define a markdown description cell for imports in the notebook. Defaults true. | ||
| `observeViewofValues`: A boolean, whether to pass in an _observer_ for the value of viewof cells. | ||
| `module`: A default Observable runtime [module](https://github.com/observablehq/runtime#modules) that will be used in operations such as `.cell` and `.module`, if no other module is passed in. | ||
| `observer`: A default Observable runtime [observer](https://github.com/observablehq/runtime#observer) that will be used in operations such as `.cell` and `.module`, if no other observer is passed in. | ||
|
|
||
| Keep in mind, there is no sandboxing done, so it has the same security implications as `eval()`. | ||
|
|
||
| interpret.**cell**(_source_ [, *module*, *observer*]) | ||
|
|
||
| Parses the given string _source_ with the Observable parser [`.parseCell()`](https://github.com/observablehq/parser#parseCell) and interprets the source, passing it and the _observer_ along to the Observable _module_. Returns a Promise that resolves to an array of runtime [variables](https://github.com/observablehq/runtime#variables) that were defined when interpreting the source. More than one variable can be defined with a single cell, like with `viewof`, `mutable`, and `import` cells. _source_ can also be a pre-parsed [cell](https://github.com/observablehq/parser#cell) instead of source code. | ||
|
|
||
| interpret.**module**(_source_ [, *module*, *observer*]) | ||
|
|
||
| Parses the given string _source_ with the Observable parser [`.parseModule()`](https://github.com/observablehq/parser#parseModule) and interprets the source, passing it and the _observer_ along to the Observable _module_. Returns a Promise that resolves to an array of an array of runtime [variables](https://github.com/observablehq/runtime#variables) that were defined when interpreting the source. _source_ can also be a pre-parsed [program](https://github.com/observablehq/parser#program) instead of source code. | ||
|
|
||
| interpret.**notebook**(_source_ [, *module*, *observer*]) | ||
|
|
||
| TODO | ||
|
|
||
| new **Compiler**(_params_) | ||
|
|
||
| `Compiler` is a class that encompasses all logic to compile Observable javascript code into vanilla Javascript code, as an ES module. _params_ is an optional object with the following allowed configuration. | ||
|
|
||
| `resolveImportPath(path, specifers)`: A function that returns a URL to where the notebook is defined. _path_ is the string provided in the import statement, and _specifiers_ is a array of string if the imported cell name specifiers in the statement (cells inside `import {...}`). _specifiers_ is useful when implementing tree-shaking. For example, `import {chart as CHART} from "@d3/bar-chart"` would supply `path="@d3/bar-chart"` and `specifiers=["chart"]`. Default imports from observablehq.com, eg `https://api.observablehq.com/@d3/bar-chart.js?v=3` | ||
|
|
||
| `resolveFileAttachments`: A function that, given the name of a FileAttachment, returns the URL that the FileAttachment will be fetched from. Defaults to `name => name`. | ||
|
|
||
| `UNSAFE_allowJavascriptFileAttachments` A boolean. When true, the `resolveFileAttachments` function will resolve to raw JavaScript when calculating the value of a FileAttachment reference. This is useful if you need to use `new URL` or `import.meta.url` when determining where a FileAttachment url should resolve too. This is unsafe because the Compiler will not escape any quotes when including it in the compiled output, so do use with extreme caution when dealing with user input. | ||
|
|
||
| ```javascript | ||
|
|
||
| // This can be unsafe since FileAttachment names can include quotes. | ||
| // Instead, map file attachments names to something deterministic and escape-safe, | ||
| // like SHA hashes. | ||
| const resolveFileAttachments = name => `new URL("./files/${name}", import.meta.url)` | ||
|
|
||
| Compiled output when: | ||
|
|
||
| // UNSAFE_allowJavascriptFileAttachments == false | ||
| const fileAttachments = new Map([["a", "new URL(\"./files/a\", import.meta.url)"]]); | ||
|
|
||
| // UNSAFE_allowJavascriptFileAttachments == true | ||
| const fileAttachments = new Map([["a", new URL("./files/a", import.meta.url)]]); | ||
|
|
||
|
|
||
| ``` | ||
|
|
||
| `defineImportMarkdown` - A boolean, whether to define a markdown description cell for imports in the notebook. Defaults true. | ||
|
|
||
| `observeViewofValues` - A boolean, whether or not to pass in the `observer` function for viewof value cells. Defaults true. | ||
|
|
||
| `observeMutableValues` - A boolean, whether or not to pass in the `observer` function for mutable value cells. Defaults true. | ||
|
|
||
| compile.**module**(_source_) | ||
|
|
||
| TODO | ||
|
|
||
| compile.**notebook**(_source_) | ||
|
|
||
| TODO | ||
|
|
||
| ## License | ||
|
|
||
| This library is MIT, but it relies and gets heavy inspiration from the following | ||
| libraries licensed under ISC: | ||
|
|
||
| - [@observablehq/runtime](https://github.com/observablehq/runtime) | ||
| - [@observablehq/stdlib](https://github.com/observablehq/stdlib) | ||
| - [@observablehq/inspector](https://github.com/observablehq/inspector) | ||
| - [@observablehq/parser](https://github.com/observablehq/parser) | ||
|
|
||
| ## Contributing | ||
|
|
||
| Feel free to send in PR's as you wish! Take a look at the [issues](https://github.com/asg017/unofficial-observablehq-compiler/issues) | ||
| to find something to work on. Just please follow the [Contributor Covenant](https://www.contributor-covenant.org/) | ||
| in all your interactions :smile: |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.