├── .prettierrc ├── .surgeignore ├── src ├── index.js ├── utils.js └── compiler.js ├── test ├── executable_attachment.js ├── test_cell.html ├── compiler-test.js ├── test_notebook_json.html ├── test.html ├── test_ES_module.html └── compileESModule-test.js ├── rollup.config.js ├── .github └── workflows │ ├── main.yml │ └── pull_request.yml ├── .circleci └── config.yml ├── package.json ├── .gitignore ├── index.html ├── README.md └── yarn.lock /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | } 3 | -------------------------------------------------------------------------------- /.surgeignore: -------------------------------------------------------------------------------- 1 | src/ 2 | node_modules/ 3 | yarn.lock 4 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { Compiler } from "./compiler.js"; 2 | -------------------------------------------------------------------------------- /test/executable_attachment.js: -------------------------------------------------------------------------------- 1 | // https://observablehq.com/@jashkenas/executable-js-fileattachment-test 2 | export const html = document.createElement("H1"); 3 | 4 | html.innerHTML = "Successfully executed JS in attachment!"; 5 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import node from "rollup-plugin-node-resolve"; 2 | import commonjs from "rollup-plugin-commonjs"; 3 | 4 | export default { 5 | input: "src/index.js", 6 | output: [ 7 | { 8 | compact: true, 9 | file: "dist/index.js", 10 | format: "umd", 11 | name: "index.js" 12 | }, 13 | { 14 | compact: true, 15 | file: "dist/index-esm.js", 16 | format: "esm", 17 | name: "esm" 18 | } 19 | ], 20 | plugins: [node(), commonjs()] 21 | }; 22 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Demo Site 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v1 14 | - uses: actions/setup-node@v1.1.0 15 | - run: npm install 16 | - run: npm run build 17 | - run: npm install -g surge 18 | - run: surge --domain unofficial-observablehq-compiler-demo.surge.sh --project . 19 | env: 20 | SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }} 21 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | export const extractPath = path => { 2 | let source = path; 3 | let m; 4 | 5 | // "https://api.observablehq.com/@jashkenas/inputs.js?v=3" => strip off ".js" 6 | if ((m = /\.js(\?|$)/i.exec(source))) source = source.slice(0, m.index); 7 | 8 | // "74f872c4fde62e35" => "d/..." 9 | if ((m = /^[0-9a-f]{16}$/i.test(source))) source = `d/${source}`; 10 | 11 | // link of notebook 12 | if ((m = /^https:\/\/(api\.|beta\.|)observablehq\.com\//i.exec(source))) 13 | source = source.slice(m[0].length); 14 | return source; 15 | }; 16 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:10.16.0 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | working_directory: ~/repo 18 | 19 | steps: 20 | - checkout 21 | 22 | # Download and cache dependencies 23 | - restore_cache: 24 | keys: 25 | - v1-dependencies-{{ checksum "package.json" }} 26 | # fallback to using the latest cache if no exact match is found 27 | - v1-dependencies- 28 | 29 | - run: yarn install 30 | 31 | - save_cache: 32 | paths: 33 | - node_modules 34 | key: v1-dependencies-{{ checksum "package.json" }} 35 | 36 | # run tests! 37 | - run: yarn test 38 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request Demos 2 | 3 | on: 4 | pull_request: 5 | types: [opened, reopened, synchronize] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v1 13 | - uses: actions/setup-node@v1.1.0 14 | - run: npm install 15 | - run: npm run build 16 | - run: npm install -g surge 17 | - run: surge --domain unofficial-observablehq-compiler-demo-${{ github.event.number }}.surge.sh --project . 18 | env: 19 | SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }} 20 | - uses: actions/github-script@0.2.0 21 | with: 22 | github-token: ${{github.token}} 23 | script: | 24 | await github.issues.createComment({ 25 | issue_number: context.issue.number, 26 | owner: context.repo.owner, 27 | repo: context.repo.repo, 28 | body: 'New demo site for this PR deployed to [unofficial-observablehq-compiler-demo-${{ github.event.number }}.surge.sh](https://unofficial-observablehq-compiler-demo-${{ github.event.number }}.surge.sh) !' 29 | }) 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@alex.garcia/unofficial-observablehq-compiler", 3 | "version": "0.5.0", 4 | "description": "An unofficial compiler to bind @observablehq/parser and @observablehq/runtime", 5 | "main": "dist/index.js", 6 | "author": "Alex Garcia ", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/asg017/unofficial-observablehq-compiler.git" 10 | }, 11 | "license": "ISC", 12 | "publishConfig": { 13 | "access": "public" 14 | }, 15 | "files": [ 16 | "dist/**/*.js" 17 | ], 18 | "scripts": { 19 | "build": "rollup -c", 20 | "format": "prettier --write src/**/*.js", 21 | "test": "npm run build && tape 'test/**/*-test.js' && npm run test-format", 22 | "test-format": "prettier --check src/**/*.js", 23 | "test-html": "http-server", 24 | "prepublishOnly": "npm run build", 25 | "postpublish": "git push && git push --tags" 26 | }, 27 | "keywords": [ 28 | "observable", 29 | "observablehq", 30 | "observable-notebooks", 31 | "notebooks", 32 | "parser", 33 | "compiler" 34 | ], 35 | "dependencies": { 36 | "@observablehq/parser": "^3.0.0", 37 | "acorn-walk": "^7.0.0" 38 | }, 39 | "devDependencies": { 40 | "@observablehq/runtime": "^4.6.4", 41 | "http-server": "^0.11.1", 42 | "prettier": "1.19.1", 43 | "rollup": "^1.26.4", 44 | "rollup-plugin-commonjs": "^10.1.0", 45 | "rollup-plugin-node-resolve": "^5.2.0", 46 | "tape": "^4.11.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | .env.test 68 | 69 | # parcel-bundler cache (https://parceljs.org/) 70 | .cache 71 | 72 | # next.js build output 73 | .next 74 | 75 | # nuxt.js build output 76 | .nuxt 77 | 78 | # vuepress build output 79 | .vuepress/dist 80 | 81 | # Serverless directories 82 | .serverless/ 83 | 84 | # FuseBox cache 85 | .fusebox/ 86 | 87 | # DynamoDB Local files 88 | .dynamodb/ 89 | 90 | dist/ 91 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 18 | 19 | 20 |
21 | 58 | 59 | -------------------------------------------------------------------------------- /test/test_cell.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 |
13 | 76 | 77 | -------------------------------------------------------------------------------- /test/compiler-test.js: -------------------------------------------------------------------------------- 1 | const test = require("tape"); 2 | const runtime = require("@observablehq/runtime"); 3 | const compiler = require("../dist/index"); 4 | 5 | test("compiler", async t => { 6 | const rt = new runtime.Runtime(); 7 | const compile = new compiler.Compiler(); 8 | const define = await compile.module(` 9 | a = 1 10 | 11 | b = 2 12 | 13 | c = a + b 14 | 15 | d = { 16 | yield 1; 17 | yield 2; 18 | yield 3; 19 | } 20 | 21 | viewof e = { 22 | let output = {}; 23 | let listeners = []; 24 | output.value = 10; 25 | output.addEventListener = (listener) => listeners.push(listener);; 26 | output.removeEventListener = (listener) => { 27 | listeners = listeners.filter(l => l !== listener); 28 | }; 29 | return output; 30 | } 31 | `); 32 | const main = rt.module(define); 33 | await rt._compute(); 34 | 35 | t.equal(await main.value("a"), 1); 36 | t.equal(await main.value("b"), 2); 37 | t.equal(await main.value("c"), 3); 38 | 39 | t.equal(await main.value("d"), 1); 40 | t.equal(await main.value("d"), 2); 41 | t.equal(await main.value("d"), 3); 42 | 43 | t.equal(await main.value("e"), 10); 44 | t.deepEqual(Object.keys(await main.value("viewof e")), [ 45 | "value", 46 | "addEventListener", 47 | "removeEventListener" 48 | ]); 49 | 50 | const { redefine: aRedefine } = await compile.cell(`a = 10`); 51 | aRedefine(main); 52 | await rt._compute(); 53 | t.equal(await main.value("a"), 10); 54 | t.equal(await main.value("c"), 12); 55 | 56 | const { define: xDefine } = await compile.cell(`x = y - 1`); 57 | xDefine(main, () => true); 58 | await rt._compute(); 59 | 60 | try { 61 | await main.value("x"); 62 | t.fail(); 63 | } catch (error) { 64 | t.equal(error.constructor, runtime.RuntimeError); 65 | } 66 | 67 | const { define: yDefine } = await compile.cell(`y = 101`); 68 | yDefine(main, () => true); 69 | await rt._compute(); 70 | 71 | t.equal(await main.value("y"), 101); 72 | t.equal(await main.value("x"), 100); 73 | 74 | const { redefine: eRedefine } = await compile.cell(`viewof e = { 75 | let output = {}; 76 | let listeners = []; 77 | output.value = 20; 78 | output.addEventListener = (listener) => listeners.push(listener);; 79 | output.removeEventListener = (listener) => { 80 | listeners = listeners.filter(l => l !== listener); 81 | }; 82 | return output; 83 | }`); 84 | eRedefine(main); 85 | await rt._compute(); 86 | 87 | t.equal(await main.value("e"), 20); 88 | 89 | rt.dispose(); 90 | t.end(); 91 | }); 92 | -------------------------------------------------------------------------------- /test/test_notebook_json.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 |
13 | 120 | 121 | -------------------------------------------------------------------------------- /test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 |
13 | 144 | 145 | -------------------------------------------------------------------------------- /test/test_ES_module.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 |
13 | 154 | 155 | -------------------------------------------------------------------------------- /test/compileESModule-test.js: -------------------------------------------------------------------------------- 1 | const test = require("tape"); 2 | const compiler = require("../dist/index"); 3 | 4 | test("ES module: simple", async t => { 5 | const compile = new compiler.Compiler(); 6 | const src = compile.moduleToESModule(` 7 | a = 1 8 | 9 | b = 2 10 | 11 | c = a + b 12 | 13 | d = { 14 | yield 1; 15 | yield 2; 16 | yield 3; 17 | } 18 | 19 | { 20 | await d; 21 | } 22 | 23 | viewof e = { 24 | let output = {}; 25 | let listeners = []; 26 | output.value = 10; 27 | output.addEventListener = (listener) => listeners.push(listener);; 28 | output.removeEventListener = (listener) => { 29 | listeners = listeners.filter(l => l !== listener); 30 | }; 31 | return output; 32 | } 33 | `); 34 | t.equal(src, `export default function define(runtime, observer) { 35 | const main = runtime.module(); 36 | 37 | main.variable(observer("a")).define("a", function(){return( 38 | 1 39 | )}); 40 | main.variable(observer("b")).define("b", function(){return( 41 | 2 42 | )}); 43 | main.variable(observer("c")).define("c", ["a","b"], function(a,b){return( 44 | a + b 45 | )}); 46 | main.variable(observer("d")).define("d", function*() 47 | { 48 | yield 1; 49 | yield 2; 50 | yield 3; 51 | } 52 | ); 53 | main.variable(observer()).define(["d"], async function(d) 54 | { 55 | await d; 56 | } 57 | ); 58 | main.variable(observer("viewof e")).define("viewof e", function() 59 | { 60 | let output = {}; 61 | let listeners = []; 62 | output.value = 10; 63 | output.addEventListener = (listener) => listeners.push(listener);; 64 | output.removeEventListener = (listener) => { 65 | listeners = listeners.filter(l => l !== listener); 66 | }; 67 | return output; 68 | } 69 | ); 70 | main.variable(observer("e")).define("e", ["Generators", "viewof e"], (G, _) => G.input(_)); 71 | return main; 72 | }`); 73 | 74 | t.end(); 75 | }); 76 | 77 | test("ES module: imports", async t => { 78 | const compile = new compiler.Compiler(); 79 | const src = compile.moduleToESModule(`import {a} from "b" 80 | b = { 81 | return 3*a 82 | } 83 | import {c as bc} from "b" 84 | import {d} with {b as cb} from "c" 85 | `); 86 | 87 | t.equal(src, `import define1 from "https://api.observablehq.com/b.js?v=3"; 88 | import define2 from "https://api.observablehq.com/c.js?v=3"; 89 | 90 | export default function define(runtime, observer) { 91 | const main = runtime.module(); 92 | 93 | main.variable(observer()).define( 94 | null, 95 | ["md"], 96 | md => md\`~~~javascript 97 | import {a as a} from "b" 98 | ~~~\` 99 | ); 100 | const child1 = runtime.module(define1); 101 | main.import("a", "a", child1); 102 | main.variable(observer("b")).define("b", ["a"], function(a) 103 | { 104 | return 3*a 105 | } 106 | ); 107 | main.variable(observer()).define( 108 | null, 109 | ["md"], 110 | md => md\`~~~javascript 111 | import {c as bc} from "b" 112 | ~~~\` 113 | ); 114 | const child2 = runtime.module(define1); 115 | main.import("c", "bc", child2); 116 | main.variable(observer()).define( 117 | null, 118 | ["md"], 119 | md => md\`~~~javascript 120 | import {d as d} with {b as cb} from "c" 121 | ~~~\` 122 | ); 123 | const child3 = runtime.module(define2).derive([{"name":"b","alias":"cb"}], main); 124 | main.import("d", "d", child3); 125 | return main; 126 | }`); 127 | 128 | t.end(); 129 | }); 130 | 131 | 132 | test("ES module: custom resolvePath function", async t => { 133 | const resolvePath = name => `https://gist.github.com/${name}`; 134 | const compile = new compiler.Compiler(undefined, undefined, resolvePath); 135 | const src = compile.moduleToESModule(`import {a} from "b" 136 | b = { 137 | return 3*a 138 | } 139 | import {c as bc} from "b" 140 | import {d} with {b as cb} from "c" 141 | `); 142 | 143 | t.equal(src, `import define1 from "https://gist.github.com/b"; 144 | import define2 from "https://gist.github.com/c"; 145 | 146 | export default function define(runtime, observer) { 147 | const main = runtime.module(); 148 | 149 | main.variable(observer()).define( 150 | null, 151 | ["md"], 152 | md => md\`~~~javascript 153 | import {a as a} from "b" 154 | ~~~\` 155 | ); 156 | const child1 = runtime.module(define1); 157 | main.import("a", "a", child1); 158 | main.variable(observer("b")).define("b", ["a"], function(a) 159 | { 160 | return 3*a 161 | } 162 | ); 163 | main.variable(observer()).define( 164 | null, 165 | ["md"], 166 | md => md\`~~~javascript 167 | import {c as bc} from "b" 168 | ~~~\` 169 | ); 170 | const child2 = runtime.module(define1); 171 | main.import("c", "bc", child2); 172 | main.variable(observer()).define( 173 | null, 174 | ["md"], 175 | md => md\`~~~javascript 176 | import {d as d} with {b as cb} from "c" 177 | ~~~\` 178 | ); 179 | const child3 = runtime.module(define2).derive([{"name":"b","alias":"cb"}], main); 180 | main.import("d", "d", child3); 181 | return main; 182 | }`); 183 | 184 | t.end(); 185 | }); 186 | 187 | test("ES module: viewof + mutable", async t => { 188 | const compile = new compiler.Compiler(); 189 | const src = compile.moduleToESModule(`viewof a = { 190 | const div = html\`\`; 191 | div.value = 3; 192 | return div; 193 | } 194 | mutable b = 3 195 | { 196 | return b*b 197 | } 198 | d = { 199 | mutable b++; 200 | return a + b; 201 | } 202 | import {viewof v as w, mutable m} from "notebook"`); 203 | 204 | t.equal(src, `import define1 from "https://api.observablehq.com/notebook.js?v=3"; 205 | 206 | export default function define(runtime, observer) { 207 | const main = runtime.module(); 208 | 209 | main.variable(observer("viewof a")).define("viewof a", ["html"], function(html) 210 | { 211 | const div = html\`\`; 212 | div.value = 3; 213 | return div; 214 | } 215 | ); 216 | main.variable(observer("a")).define("a", ["Generators", "viewof a"], (G, _) => G.input(_)); 217 | main.define("initial b", function(){return( 218 | 3 219 | )}); 220 | main.variable(observer("mutable b")).define("mutable b", ["Mutable", "initial b"], (M, _) => new M(_)); 221 | main.variable(observer("b")).define("b", ["mutable b"], _ => _.generator); 222 | main.variable(observer()).define(["b"], function(b) 223 | { 224 | return b*b 225 | } 226 | ); 227 | main.variable(observer("d")).define("d", ["mutable b","a","b"], function($0,a,b) 228 | { 229 | $0.value++; 230 | return a + b; 231 | } 232 | ); 233 | main.variable(observer()).define( 234 | null, 235 | ["md"], 236 | md => md\`~~~javascript 237 | import {viewof v as viewof w, v as w, mutable m as mutable m, m as m} from "notebook" 238 | ~~~\` 239 | ); 240 | const child1 = runtime.module(define1); 241 | main.import("viewof v", "viewof w", child1); 242 | main.import("v", "w", child1); 243 | main.import("mutable m", "mutable m", child1); 244 | main.import("m", "m", child1); 245 | return main; 246 | }`); 247 | 248 | t.end(); 249 | }); 250 | 251 | test("ES module: FileAttachment", async t => { 252 | const compile = new compiler.Compiler(); 253 | const src = compile.moduleToESModule(`md\`Here's a cell with a file attachment! \``); 254 | 255 | t.equal(src, `export default function define(runtime, observer) { 256 | const main = runtime.module(); 257 | const fileAttachments = new Map([["image.png","image.png"]]); 258 | main.builtin("FileAttachment", runtime.fileAttachments(name => fileAttachments.get(name))); 259 | main.variable(observer()).define(["md","FileAttachment"], async function(md,FileAttachment){return( 260 | md\`Here's a cell with a file attachment! \` 261 | )}); 262 | return main; 263 | }`); 264 | 265 | t.end(); 266 | }); 267 | 268 | test("ES module: custom fileAttachmentsResolve", async t => { 269 | const fileAttachmentsResolve = name => `https://example.com/${name}`; 270 | const compile = new compiler.Compiler(undefined, fileAttachmentsResolve); 271 | const src = compile.moduleToESModule(`md\`Here's a cell with a file attachment! \``); 272 | 273 | t.equal(src, `export default function define(runtime, observer) { 274 | const main = runtime.module(); 275 | const fileAttachments = new Map([["image.png","https://example.com/image.png"]]); 276 | main.builtin("FileAttachment", runtime.fileAttachments(name => fileAttachments.get(name))); 277 | main.variable(observer()).define(["md","FileAttachment"], async function(md,FileAttachment){return( 278 | md\`Here's a cell with a file attachment! \` 279 | )}); 280 | return main; 281 | }`); 282 | 283 | t.end(); 284 | }); 285 | 286 | 287 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @alex.garcia/unofficial-observablehq-compiler [![CircleCI](https://circleci.com/gh/asg017/unofficial-observablehq-compiler.svg?style=svg)](https://circleci.com/gh/asg017/unofficial-observablehq-compiler) 2 | 3 | 4 | 5 | 6 | An unoffical compiler for Observable notebooks (glue between the Observable parser and runtime) 7 | 8 | ## ⚠️This library is currently under construction!⚠️ 9 | 10 | The README below is for `v0.5.0`, but `v0.6.0` is currently in alpha and has several improvements to this library + API that you probably care about. Check out [#29](https://github.com/asg017/unofficial-observablehq-compiler/pull/29) to stay in the loop, and check out [that version's README](https://github.com/asg017/unofficial-observablehq-compiler/tree/beta) for docs on the soon-to-be-released `v0.6.0`. 11 | 12 | --- 13 | 14 | 15 | This compiler will compile "observable syntax" into "javascript syntax". 16 | For example - 17 | 18 | ```javascript 19 | import compiler from "@alex.garcia/unofficial-observablehq-compiler"; 20 | import { Inspector, Runtime } from "@observablehq/runtime"; 21 | 22 | const compile = new compiler.Compiler(); 23 | 24 | compile.module(` 25 | import {text} from '@jashkenas/inputs' 26 | 27 | viewof name = text({ 28 | title: "what's your name?", 29 | value: '' 30 | }) 31 | 32 | md\`Hello **\${name}**, it's nice to meet you!\` 33 | 34 | `).then(define => { 35 | const runtime = new Runtime(); 36 | 37 | const module = runtime.module(define, Inpsector.into(document.body)); 38 | }); 39 | ``` 40 | 41 | For more live examples and functionality, take a look at the [announcement notebook](https://observablehq.com/d/74f872c4fde62e35) 42 | and this [test page](https://github.com/asg017/unofficial-observablehq-compiler/blob/master/test/test.html). 43 | 44 | ## API Reference 45 | 46 | ### Compiler 47 | 48 | # new Compiler(resolve = defaultResolver, fileAttachmentsResolve = name => name, resolvePath = defaultResolvePath) [<>](https://github.com/asg017/unofficial-observablehq-compiler/blob/master/src/compiler.js#L119 "Source") 49 | 50 | Returns a new compiler. `resolve` is an optional function that, given a `path` 51 | string, will resolve a new define function for a new module. This is used when 52 | the compiler comes across an import statement - for example: 53 | 54 | ```javascript 55 | import {chart} from "@d3/bar-chart" 56 | ``` 57 | 58 | In this case, `resolve` gets called with `path="@d3/bar-chart"`. The `defaultResolver` 59 | function will lookup the given path on observablehq.com and return the define 60 | function to define that notebook. 61 | 62 | For example, if you have your own set of notebooks on some other server, you 63 | could use something like: 64 | 65 | ```javascript 66 | const resolve = path => 67 | import(`other.server.com/notebooks/${path}.js`).then( 68 | module => module.default 69 | ); 70 | 71 | const compile = new Compiler(resolve); 72 | ``` 73 | 74 | `fileAttachmentsResolve` is an optional function from strings to URLs which is used as a resolve function in the standard library's FileAttachments function. For example, if you wanted to reference `example.com/my_file.png` in a cell which reads: 75 | 76 | ```javascript 77 | await FileAttachment("my_file.png").url(); 78 | ``` 79 | 80 | Then you could compile this cell with: 81 | 82 | ```javascript 83 | const fileAttachmentsResolve = name => `example.com/${name}`; 84 | 85 | const compile = new Compiler(, fileAttachmentsResolve); 86 | ``` 87 | 88 | By default, `fileAtachmentsResolve` simply returns the same string, so you would have to use valid absolute or relative URLs in your `FileAttachment`s. 89 | 90 | `resolvePath` is an optional function from strings to URLs which is used to turn the strings in `import` cells to URLs in [`compile.moduleToESModule`](#compile_moduleToESModule) and [`compile.notebookToESModule`](#compile_notebookToESModule). For instance, if those functions encounter this cell: 91 | ```javascript 92 | import {chart} from "@d3/bar-chart" 93 | ``` 94 | then `resolvePath` is called with `path="@d3/bar-chart"` and the resulting URL is included in the static `import` statements at the beginning of the generated ES module source. 95 | 96 | #compile.module(contents) 97 | 98 | Returns a define function. `contents` is a string that defines a "module", which 99 | is a list of "cells" (both defintions from [@observablehq/parser](https://github.com/observablehq/parser)). 100 | It must be compatible with [`parseModule`](https://github.com/observablehq/parser#parseModule). This fetches all imports so it is asynchronous. 101 | 102 | For example: 103 | 104 | ```javascript 105 | const define = await compile.module(`a = 1 106 | b = 2 107 | c = a + b`); 108 | ``` 109 | 110 | You can now use `define` with the Observable [runtime](https://github.com/observablehq/runtime): 111 | 112 | ```javascript 113 | const runtime = new Runtime(); 114 | const main = runtime.module(define, Inspector.into(document.body)); 115 | ``` 116 | 117 | #compile.notebook(object) 118 | 119 | Returns a define function. `object` is a "notebook JSON object" as used by the 120 | ObservableHQ notebook app to display notebooks. Such JSON files are served by 121 | the API endpoint at `https://api.observablehq.com/document/:slug` (see the 122 | [`observable-client`](https://github.com/mootari/observable-client) for a 123 | convenient way to authenticate and make requests). 124 | 125 | `compile.notebook` requires that `object` has a field named `"nodes"` 126 | consisting of an array of cell objects. Each of the cell objects must have a 127 | field `"value"` consisting of a string with the source code for that cell. 128 | 129 | The notebook JSON objects also ordinarily contain some other metadata fields, 130 | e.g. `"id"`, `"slug"`, `"owner"`, etc. which are currently ignored by the 131 | compiler. Similarly, the cell objects in `"nodes"` ordinarily contain `"id"` and 132 | `"pinned"` fields which are also unused here. 133 | 134 | This fetches all imports so it is asynchronous. 135 | 136 | For example: 137 | 138 | ```javascript 139 | const define = await compile.notebook({ 140 | nodes: [{ value: "a = 1" }, { value: "b = 2" }, { value: "c = a + b" }] 141 | }); 142 | ``` 143 | 144 | You can now use `define` with the Observable [runtime](https://github.com/observablehq/runtime): 145 | 146 | ```javascript 147 | const runtime = new Runtime(); 148 | const main = runtime.module(define, Inspector.into(document.body)); 149 | ``` 150 | 151 | #compile.cell(contents) 152 | 153 | Returns an object that has `define` and `redefine` functions that would define or redefine variables in the given cell to a specified module. `contents` is input for the [`parseCell`](https://github.com/observablehq/parser#parseCell) function. If the cell is not an ImportDeclaration, then the `redefine` functions can be used to redefine previously existing variables in a module. This is an asynchronous function because if the cell is an import, the imported notebook is fetched. 154 | 155 | ```javascript 156 | let define, redefine; 157 | 158 | define = await compile.module(`a = 1; 159 | b = 2; 160 | 161 | c = a + b`); 162 | 163 | const runtime = new Runtime(); 164 | const main = runtime.module(define, Inspector.into(document.body)); 165 | 166 | await main.value("a") // 1 167 | 168 | {define, redefine} = await compile.cell(`a = 20`); 169 | 170 | redefine(main); 171 | 172 | await main.value("a"); // 20 173 | await main.value("c"); // 22 174 | 175 | define(main); // would throw an error, since a is already defined in main 176 | 177 | {define} = await compile.cell(`x = 2`); 178 | define(main); 179 | {define} = await compile.cell(`y = x * 4`); 180 | define(main); 181 | 182 | await main.value("y") // 8 183 | 184 | ``` 185 | 186 | Keep in mind, if you want to use `define` from `compile.cell`, you'll have to provide an `observer` function, which will most likely be the same observer that was used when defining the module. For example: 187 | 188 | ```javascript 189 | 190 | let define, redefine; 191 | 192 | define = await compile.module(`a = 1; 193 | b = 2;`); 194 | 195 | const runtime = new Runtime(); 196 | const observer = Inspector.into(document.body); 197 | const main = runtime.module(define, observer); 198 | 199 | {define} = await compile.cell(`c = a + b`); 200 | 201 | define(main, observer); 202 | 203 | ``` 204 | 205 | Since `redefine` is done on a module level, an observer is not required. 206 | 207 | #compile.moduleToESModule(contents) 208 | 209 | Returns a string containing the source code of an ES module. This ES module is compiled from the Observable runtime module in the string `contents`. 210 | 211 | For example: 212 | 213 | ```javascript 214 | const src = compile.moduleToESModule(`a = 1 215 | b = 2 216 | c = a + b`); 217 | ``` 218 | 219 | Now `src` contains the following: 220 | 221 | ```javascript 222 | export default function define(runtime, observer) { 223 | const main = runtime.module(); 224 | 225 | main.variable(observer("a")).define("a", function(){return( 226 | 1 227 | )}); 228 | main.variable(observer("b")).define("b", function(){return( 229 | 2 230 | )}); 231 | main.variable(observer("c")).define("c", ["a","b"], function(a,b){return( 232 | a + b 233 | )}); 234 | return main; 235 | } 236 | ``` 237 | 238 | #compile.notebookToESModule(object) 239 | 240 | Returns a string containing the source code of an ES module. This ES module is compiled from the Observable runtime module in the notebok object `object`. (See [compile.notebook](#compile_notebook)). 241 | 242 | For example: 243 | 244 | ```javascript 245 | const src = compile.notebookToESModule({ 246 | nodes: [{ value: "a = 1" }, { value: "b = 2" }, { value: "c = a + b" }] 247 | }); 248 | ``` 249 | 250 | Now `src` contains the following: 251 | 252 | ```javascript 253 | export default function define(runtime, observer) { 254 | const main = runtime.module(); 255 | 256 | main.variable(observer("a")).define("a", function(){return( 257 | 1 258 | )}); 259 | main.variable(observer("b")).define("b", function(){return( 260 | 2 261 | )}); 262 | main.variable(observer("c")).define("c", ["a","b"], function(a,b){return( 263 | a + b 264 | )}); 265 | return main; 266 | } 267 | ``` 268 | 269 | ## License 270 | 271 | This library is MIT, but it relies and gets heavy inspiration from the following 272 | libraries licensed under ISC: 273 | 274 | - [@observablehq/runtime](https://github.com/observablehq/runtime) 275 | - [@observablehq/stdlib](https://github.com/observablehq/stdlib) 276 | - [@observablehq/inspector](https://github.com/observablehq/inspector) 277 | - [@observablehq/parser](https://github.com/observablehq/parser) 278 | 279 | ## Contributing 280 | 281 | Feel free to send in PR's as you wish! Take a look at the [issues](https://github.com/asg017/unofficial-observablehq-compiler/issues) 282 | to find something to work on. Just please follow the [Contributor Covenant](https://www.contributor-covenant.org/) 283 | in all your interactions :smile: 284 | -------------------------------------------------------------------------------- /src/compiler.js: -------------------------------------------------------------------------------- 1 | import { parseCell, parseModule, walk } from "@observablehq/parser"; 2 | import { simple } from "acorn-walk"; 3 | import { extractPath } from "./utils"; 4 | 5 | const AsyncFunction = Object.getPrototypeOf(async function() {}).constructor; 6 | const GeneratorFunction = Object.getPrototypeOf(function*() {}).constructor; 7 | const AsyncGeneratorFunction = Object.getPrototypeOf(async function*() {}) 8 | .constructor; 9 | 10 | const setupRegularCell = cell => { 11 | let name = null; 12 | if (cell.id && cell.id.name) name = cell.id.name; 13 | else if (cell.id && cell.id.id && cell.id.id.name) name = cell.id.id.name; 14 | let bodyText = cell.input.substring(cell.body.start, cell.body.end); 15 | const cellReferences = (cell.references || []).map(ref => { 16 | if (ref.type === "ViewExpression") { 17 | return "viewof " + ref.id.name; 18 | } else if (ref.type === "MutableExpression") { 19 | return "mutable " + ref.id.name; 20 | } else return ref.name; 21 | }); 22 | let $count = 0; 23 | let indexShift = 0; 24 | const references = (cell.references || []).map(ref => { 25 | if (ref.type === "ViewExpression") { 26 | const $string = "$" + $count; 27 | $count++; 28 | // replace "viewof X" in bodyText with "$($count)" 29 | simple( 30 | cell.body, 31 | { 32 | ViewExpression(node) { 33 | const start = node.start - cell.body.start; 34 | const end = node.end - cell.body.start; 35 | bodyText = 36 | bodyText.slice(0, start + indexShift) + 37 | $string + 38 | bodyText.slice(end + indexShift); 39 | indexShift += $string.length - (end - start); 40 | } 41 | }, 42 | walk 43 | ); 44 | return $string; 45 | } else if (ref.type === "MutableExpression") { 46 | const $string = "$" + $count; 47 | const $stringValue = $string + ".value"; 48 | $count++; 49 | // replace "mutable Y" in bodyText with "$($count).value" 50 | simple( 51 | cell.body, 52 | { 53 | MutableExpression(node) { 54 | const start = node.start - cell.body.start; 55 | const end = node.end - cell.body.start; 56 | bodyText = 57 | bodyText.slice(0, start + indexShift) + 58 | $stringValue + 59 | bodyText.slice(end + indexShift); 60 | indexShift += $stringValue.length - (end - start); 61 | } 62 | }, 63 | walk 64 | ); 65 | return $string; 66 | } else return ref.name; 67 | }); 68 | return { cellName: name, references, bodyText, cellReferences }; 69 | }; 70 | 71 | const createRegularCellDefintion = cell => { 72 | const { cellName, references, bodyText, cellReferences } = setupRegularCell( 73 | cell 74 | ); 75 | 76 | let code; 77 | if (cell.body.type !== "BlockStatement") { 78 | if (cell.async) 79 | code = `return (async function(){ return (${bodyText});})()`; 80 | else code = `return (function(){ return (${bodyText});})()`; 81 | } else code = bodyText; 82 | 83 | let f; 84 | if (cell.generator && cell.async) 85 | f = new AsyncGeneratorFunction(...references, code); 86 | else if (cell.async) f = new AsyncFunction(...references, code); 87 | else if (cell.generator) f = new GeneratorFunction(...references, code); 88 | else f = new Function(...references, code); 89 | return { 90 | cellName, 91 | cellFunction: f, 92 | cellReferences 93 | }; 94 | }; 95 | 96 | const setupImportCell = cell => { 97 | const specifiers = []; 98 | if (cell.body.specifiers) 99 | for (const specifier of cell.body.specifiers) { 100 | if (specifier.view) { 101 | specifiers.push({ 102 | name: "viewof " + specifier.imported.name, 103 | alias: "viewof " + specifier.local.name 104 | }); 105 | } else if (specifier.mutable) { 106 | specifiers.push({ 107 | name: "mutable " + specifier.imported.name, 108 | alias: "mutable " + specifier.local.name 109 | }); 110 | } 111 | specifiers.push({ 112 | name: specifier.imported.name, 113 | alias: specifier.local.name 114 | }); 115 | } 116 | // If injections is undefined, do not derive! 117 | const hasInjections = cell.body.injections !== undefined; 118 | const injections = []; 119 | if (hasInjections) 120 | for (const injection of cell.body.injections) { 121 | // This currently behaves like notebooks on observablehq.com 122 | // Commenting out the if & else if blocks result in behavior like Example 3 here: https://observablehq.com/d/7ccad009e4d89969 123 | if (injection.view) { 124 | injections.push({ 125 | name: "viewof " + injection.imported.name, 126 | alias: "viewof " + injection.local.name 127 | }); 128 | } else if (injection.mutable) { 129 | injections.push({ 130 | name: "mutable " + injection.imported.name, 131 | alias: "mutable " + injection.local.name 132 | }); 133 | } 134 | injections.push({ 135 | name: injection.imported.name, 136 | alias: injection.local.name 137 | }); 138 | } 139 | const importString = `import {${specifiers 140 | .map(specifier => `${specifier.name} as ${specifier.alias}`) 141 | .join(", ")}} ${ 142 | hasInjections 143 | ? `with {${injections 144 | .map(injection => `${injection.name} as ${injection.alias}`) 145 | .join(", ")}} ` 146 | : `` 147 | }from "${cell.body.source.value}"`; 148 | 149 | return { specifiers, hasInjections, injections, importString }; 150 | }; 151 | 152 | const createCellDefinition = ( 153 | cell, 154 | main, 155 | observer, 156 | dependencyMap, 157 | define = true 158 | ) => { 159 | if (cell.body.type === "ImportDeclaration") { 160 | const { 161 | specifiers, 162 | hasInjections, 163 | injections, 164 | importString 165 | } = setupImportCell(cell); 166 | // this will display extra names for viewof / mutable imports (for now?) 167 | main.variable(observer()).define( 168 | null, 169 | ["md"], 170 | md => md`~~~javascript 171 | ${importString} 172 | ~~~` 173 | ); 174 | 175 | const other = main._runtime.module( 176 | dependencyMap.get(cell.body.source.value) 177 | ); 178 | 179 | if (hasInjections) { 180 | const child = other.derive(injections, main); 181 | for (const { name, alias } of specifiers) main.import(name, alias, child); 182 | } else { 183 | for (const { name, alias } of specifiers) main.import(name, alias, other); 184 | } 185 | } else { 186 | const { 187 | cellName, 188 | cellFunction, 189 | cellReferences 190 | } = createRegularCellDefintion(cell); 191 | if (cell.id && cell.id.type === "ViewExpression") { 192 | const reference = `viewof ${cellName}`; 193 | if (define) { 194 | main 195 | .variable(observer(reference)) 196 | .define(reference, cellReferences, cellFunction); 197 | main 198 | .variable(observer(cellName)) 199 | .define(cellName, ["Generators", reference], (G, _) => G.input(_)); 200 | } else { 201 | main.redefine(reference, cellReferences, cellFunction); 202 | main.redefine(cellName, ["Generators", reference], (G, _) => 203 | G.input(_) 204 | ); 205 | } 206 | } else if (cell.id && cell.id.type === "MutableExpression") { 207 | const initialName = `initial ${cellName}`; 208 | const mutableName = `mutable ${cellName}`; 209 | if (define) { 210 | main.variable(null).define(initialName, cellReferences, cellFunction); 211 | main 212 | .variable(observer(mutableName)) 213 | .define(mutableName, ["Mutable", initialName], (M, _) => new M(_)); 214 | main 215 | .variable(observer(cellName)) 216 | .define(cellName, [mutableName], _ => _.generator); 217 | } else { 218 | main.redefine(initialName, cellReferences, cellFunction); 219 | main.redefine( 220 | mutableName, 221 | ["Mutable", initialName], 222 | (M, _) => new M(_) 223 | ); 224 | main.redefine(cellName, [mutableName], _ => _.generator); 225 | } 226 | } else { 227 | if (define) 228 | main 229 | .variable(observer(cellName)) 230 | .define(cellName, cellReferences, cellFunction); 231 | else main.redefine(cellName, cellReferences, cellFunction); 232 | } 233 | } 234 | }; 235 | const createModuleDefintion = async ( 236 | moduleObject, 237 | resolveModule, 238 | resolveFileAttachments 239 | ) => { 240 | const filteredImportCells = new Set(); 241 | const importCells = moduleObject.cells.filter(({ body }) => { 242 | if ( 243 | body.type !== "ImportDeclaration" || 244 | filteredImportCells.has(body.source.value) 245 | ) 246 | return false; 247 | filteredImportCells.add(body.source.value); 248 | return true; 249 | }); 250 | 251 | const dependencyMap = new Map(); 252 | const importCellsPromise = importCells.map(async ({ body }) => { 253 | const fromModule = await resolveModule(body.source.value); 254 | dependencyMap.set(body.source.value, fromModule); 255 | }); 256 | await Promise.all(importCellsPromise); 257 | 258 | return function define(runtime, observer) { 259 | const main = runtime.module(); 260 | main.builtin( 261 | "FileAttachment", 262 | runtime.fileAttachments(resolveFileAttachments) 263 | ); 264 | for (const cell of moduleObject.cells) 265 | createCellDefinition(cell, main, observer, dependencyMap); 266 | }; 267 | }; 268 | 269 | const ESMImports = (moduleObject, resolvePath) => { 270 | const importMap = new Map(); 271 | let importSrc = ""; 272 | let j = 0; 273 | 274 | for (const { body } of moduleObject.cells) { 275 | if (body.type !== "ImportDeclaration" || importMap.has(body.source.value)) 276 | continue; 277 | 278 | const defineName = `define${++j}`; 279 | const fromPath = resolvePath(body.source.value); 280 | importMap.set(body.source.value, { defineName, fromPath }); 281 | importSrc += `import ${defineName} from "${fromPath}";\n`; 282 | } 283 | 284 | if (importSrc.length) importSrc += "\n"; 285 | return { importSrc, importMap }; 286 | }; 287 | 288 | const ESMAttachments = (moduleObject, resolveFileAttachments) => { 289 | const attachmentMapEntries = []; 290 | // loop over cells with fileAttachments 291 | for (const cell of moduleObject.cells) { 292 | if (cell.fileAttachments.size === 0) continue; 293 | // add filenames and resolved URLs to array 294 | for (const file of cell.fileAttachments.keys()) 295 | attachmentMapEntries.push([file, resolveFileAttachments(file)]); 296 | } 297 | 298 | return attachmentMapEntries.length === 0 299 | ? "" 300 | : ` const fileAttachments = new Map(${JSON.stringify( 301 | attachmentMapEntries 302 | )}); 303 | main.builtin("FileAttachment", runtime.fileAttachments(name => fileAttachments.get(name)));`; 304 | }; 305 | 306 | const ESMVariables = (moduleObject, importMap) => { 307 | let childJ = 0; 308 | return moduleObject.cells 309 | .map(cell => { 310 | let src = ""; 311 | 312 | if (cell.body.type === "ImportDeclaration") { 313 | const { 314 | specifiers, 315 | hasInjections, 316 | injections, 317 | importString 318 | } = setupImportCell(cell); 319 | // this will display extra names for viewof / mutable imports (for now?) 320 | src += 321 | ` main.variable(observer()).define( 322 | null, 323 | ["md"], 324 | md => md\`~~~javascript 325 | ${importString} 326 | ~~~\` 327 | );` + "\n"; 328 | // name imported notebook define functions 329 | const childName = `child${++childJ}`; 330 | src += ` const ${childName} = runtime.module(${ 331 | importMap.get(cell.body.source.value).defineName 332 | })${ 333 | hasInjections ? `.derive(${JSON.stringify(injections)}, main)` : "" 334 | }; 335 | ${specifiers 336 | .map( 337 | specifier => 338 | ` main.import("${specifier.name}", "${specifier.alias}", ${childName});` 339 | ) 340 | .join("\n")}`; 341 | } else { 342 | const { 343 | cellName, 344 | references, 345 | bodyText, 346 | cellReferences 347 | } = setupRegularCell(cell); 348 | 349 | const cellNameString = cellName ? `"${cellName}"` : ""; 350 | const referenceString = references.join(","); 351 | let code = ""; 352 | if (cell.body.type !== "BlockStatement") 353 | code = `{return( 354 | ${bodyText} 355 | )}`; 356 | else code = "\n" + bodyText + "\n"; 357 | const cellReferencesString = cellReferences.length 358 | ? JSON.stringify(cellReferences) + ", " 359 | : ""; 360 | let cellFunction = ""; 361 | if (cell.generator && cell.async) 362 | cellFunction = `async function*(${referenceString})${code}`; 363 | else if (cell.async) 364 | cellFunction = `async function(${referenceString})${code}`; 365 | else if (cell.generator) 366 | cellFunction = `function*(${referenceString})${code}`; 367 | else cellFunction = `function(${referenceString})${code}`; 368 | 369 | if (cell.id && cell.id.type === "ViewExpression") { 370 | const reference = `"viewof ${cellName}"`; 371 | src += ` main.variable(observer(${reference})).define(${reference}, ${cellReferencesString}${cellFunction}); 372 | main.variable(observer("${cellName}")).define("${cellName}", ["Generators", ${reference}], (G, _) => G.input(_));`; 373 | } else if (cell.id && cell.id.type === "MutableExpression") { 374 | const initialName = `"initial ${cellName}"`; 375 | const mutableName = `"mutable ${cellName}"`; 376 | src += ` main.define(${initialName}, ${cellReferencesString}${cellFunction}); 377 | main.variable(observer(${mutableName})).define(${mutableName}, ["Mutable", ${initialName}], (M, _) => new M(_)); 378 | main.variable(observer("${cellName}")).define("${cellName}", [${mutableName}], _ => _.generator);`; 379 | } else { 380 | src += ` main.variable(observer(${cellNameString})).define(${ 381 | cellName ? cellNameString + ", " : "" 382 | }${cellReferencesString}${cellFunction});`; 383 | } 384 | } 385 | return src; 386 | }) 387 | .join("\n"); 388 | }; 389 | const createESModule = (moduleObject, resolvePath, resolveFileAttachments) => { 390 | const { importSrc, importMap } = ESMImports(moduleObject, resolvePath); 391 | return `${importSrc}export default function define(runtime, observer) { 392 | const main = runtime.module(); 393 | ${ESMAttachments(moduleObject, resolveFileAttachments)} 394 | ${ESMVariables(moduleObject, importMap) || ""} 395 | return main; 396 | }`; 397 | }; 398 | 399 | const defaultResolver = async path => { 400 | const source = extractPath(path); 401 | return import(`https://api.observablehq.com/${source}.js?v=3`).then( 402 | m => m.default 403 | ); 404 | }; 405 | const defaultResolvePath = path => { 406 | const source = extractPath(path); 407 | return `https://api.observablehq.com/${source}.js?v=3`; 408 | }; 409 | 410 | export class Compiler { 411 | constructor( 412 | resolve = defaultResolver, 413 | resolveFileAttachments = name => name, 414 | resolvePath = defaultResolvePath 415 | ) { 416 | this.resolve = resolve; 417 | this.resolveFileAttachments = resolveFileAttachments; 418 | this.resolvePath = resolvePath; 419 | } 420 | async cell(text) { 421 | const cell = parseCell(text); 422 | cell.input = text; 423 | const dependencyMap = new Map(); 424 | if (cell.body.type === "ImportDeclaration") { 425 | const fromModule = await this.resolve(cell.body.source.value); 426 | dependencyMap.set(cell.body.source.value, fromModule); 427 | } 428 | return { 429 | define(module, observer) { 430 | createCellDefinition(cell, module, observer, dependencyMap, true); 431 | }, 432 | redefine(module, observer) { 433 | createCellDefinition(cell, module, observer, dependencyMap, false); 434 | } 435 | }; 436 | } 437 | 438 | async module(text) { 439 | const m1 = parseModule(text); 440 | return await createModuleDefintion( 441 | m1, 442 | this.resolve, 443 | this.resolveFileAttachments 444 | ); 445 | } 446 | async notebook(obj) { 447 | const cells = obj.nodes.map(({ value }) => { 448 | const cell = parseCell(value); 449 | cell.input = value; 450 | return cell; 451 | }); 452 | return await createModuleDefintion( 453 | { cells }, 454 | this.resolve, 455 | this.resolveFileAttachments 456 | ); 457 | } 458 | 459 | moduleToESModule(text) { 460 | const m1 = parseModule(text); 461 | return createESModule(m1, this.resolvePath, this.resolveFileAttachments); 462 | } 463 | notebookToESModule(obj) { 464 | const cells = obj.nodes.map(({ value }) => { 465 | const cell = parseCell(value); 466 | cell.input = value; 467 | return cell; 468 | }); 469 | return createESModule( 470 | { cells }, 471 | this.resolvePath, 472 | this.resolveFileAttachments 473 | ); 474 | } 475 | } 476 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@observablehq/inspector@^3.2.0": 6 | version "3.2.1" 7 | resolved "https://registry.yarnpkg.com/@observablehq/inspector/-/inspector-3.2.1.tgz#c953fd95f1b90fac6f39e41965a72cc079d3e465" 8 | integrity sha512-U8EASUAUYCQmCprOF9HafRMnU4yyD0IXjRuDwAjBMjVpm36KXPzXWxHcdzlt/3CbIEu8GOhKQqHBAX9pv6OyUQ== 9 | dependencies: 10 | esm "^3.2.25" 11 | 12 | "@observablehq/parser@^3.0.0": 13 | version "3.0.0" 14 | resolved "https://registry.yarnpkg.com/@observablehq/parser/-/parser-3.0.0.tgz#6332909fcff5680d994a581b77cadaba765a0d4a" 15 | integrity sha512-BuIfvay+INd2kmUnXNbxYwZ/MnMTYjPEIhns7NuJ9YVIAV+TmxAkZlZdUeCkDpC2XET81BJM8hpOnIzJrqx+1w== 16 | dependencies: 17 | acorn "^7.0.0" 18 | acorn-walk "^7.0.0" 19 | 20 | "@observablehq/runtime@^4.6.4": 21 | version "4.6.4" 22 | resolved "https://registry.yarnpkg.com/@observablehq/runtime/-/runtime-4.6.4.tgz#d90ded08bd9c10110f76d79eb1c4e12ea7fb974d" 23 | integrity sha512-aPiADs8aQfa2ktlZh25ElvRhEZ1WBQ0S/rSfc+aEHCyrtvk+lJ9OMo+FCa01IK4doi6Vkp3xJd2RakbJ6o1hEg== 24 | dependencies: 25 | "@observablehq/inspector" "^3.2.0" 26 | "@observablehq/stdlib" "^3.2.0" 27 | esm "^3.0.84" 28 | 29 | "@observablehq/stdlib@^3.2.0": 30 | version "3.2.1" 31 | resolved "https://registry.yarnpkg.com/@observablehq/stdlib/-/stdlib-3.2.1.tgz#770a5c2a85d4cb281267a07c6a1e8732ec71b374" 32 | integrity sha512-Zsk6C2zccu+71alrv1hyrEfeSaesQ+27KHoDvuuPEdNH9XVC17sHo4HVsBZAroIMBsBjiuFUzuSuiqvD72WX1A== 33 | dependencies: 34 | d3-require "^1.2.4" 35 | marked "https://github.com/observablehq/marked.git#94c6b946f462fd25db4465d71a6859183f86c57f" 36 | 37 | "@types/estree@*", "@types/estree@0.0.39": 38 | version "0.0.39" 39 | resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" 40 | integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== 41 | 42 | "@types/node@*": 43 | version "12.7.5" 44 | resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.5.tgz#e19436e7f8e9b4601005d73673b6dc4784ffcc2f" 45 | integrity sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w== 46 | 47 | "@types/resolve@0.0.8": 48 | version "0.0.8" 49 | resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" 50 | integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== 51 | dependencies: 52 | "@types/node" "*" 53 | 54 | acorn-walk@^7.0.0: 55 | version "7.0.0" 56 | resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.0.0.tgz#c8ba6f0f1aac4b0a9e32d1f0af12be769528f36b" 57 | integrity sha512-7Bv1We7ZGuU79zZbb6rRqcpxo3OY+zrdtloZWoyD8fmGX+FeXRjE+iuGkZjSXLVovLzrsvMGMy0EkwA0E0umxg== 58 | 59 | acorn@^7.0.0: 60 | version "7.0.0" 61 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.0.0.tgz#26b8d1cd9a9b700350b71c0905546f64d1284e7a" 62 | integrity sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ== 63 | 64 | acorn@^7.1.0: 65 | version "7.1.0" 66 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" 67 | integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== 68 | 69 | async@^1.5.2: 70 | version "1.5.2" 71 | resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" 72 | integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= 73 | 74 | balanced-match@^1.0.0: 75 | version "1.0.0" 76 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 77 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 78 | 79 | brace-expansion@^1.1.7: 80 | version "1.1.11" 81 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 82 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 83 | dependencies: 84 | balanced-match "^1.0.0" 85 | concat-map "0.0.1" 86 | 87 | builtin-modules@^3.1.0: 88 | version "3.1.0" 89 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484" 90 | integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw== 91 | 92 | colors@1.0.3: 93 | version "1.0.3" 94 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" 95 | integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= 96 | 97 | concat-map@0.0.1: 98 | version "0.0.1" 99 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 100 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 101 | 102 | corser@~2.0.0: 103 | version "2.0.1" 104 | resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87" 105 | integrity sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c= 106 | 107 | d3-require@^1.2.4: 108 | version "1.2.4" 109 | resolved "https://registry.yarnpkg.com/d3-require/-/d3-require-1.2.4.tgz#59afc591d5089f99fecd8c45ef7539e1fee112b3" 110 | integrity sha512-8UseEGCkBkBxIMouLMPONUBmU8DUPC1q12LARV1Lk/2Jwa32SVgmRfX8GdIeR06ZP+CG85YD3N13K2s14qCNyA== 111 | 112 | debug@^2.2.0: 113 | version "2.6.9" 114 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 115 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 116 | dependencies: 117 | ms "2.0.0" 118 | 119 | debug@^3.0.0: 120 | version "3.2.6" 121 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" 122 | integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== 123 | dependencies: 124 | ms "^2.1.1" 125 | 126 | deep-equal@~1.0.1: 127 | version "1.0.1" 128 | resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" 129 | integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= 130 | 131 | define-properties@^1.1.2, define-properties@^1.1.3: 132 | version "1.1.3" 133 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" 134 | integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== 135 | dependencies: 136 | object-keys "^1.0.12" 137 | 138 | defined@~1.0.0: 139 | version "1.0.0" 140 | resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" 141 | integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= 142 | 143 | ecstatic@^3.0.0: 144 | version "3.3.2" 145 | resolved "https://registry.yarnpkg.com/ecstatic/-/ecstatic-3.3.2.tgz#6d1dd49814d00594682c652adb66076a69d46c48" 146 | integrity sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog== 147 | dependencies: 148 | he "^1.1.1" 149 | mime "^1.6.0" 150 | minimist "^1.1.0" 151 | url-join "^2.0.5" 152 | 153 | es-abstract@^1.5.0: 154 | version "1.14.2" 155 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.14.2.tgz#7ce108fad83068c8783c3cdf62e504e084d8c497" 156 | integrity sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg== 157 | dependencies: 158 | es-to-primitive "^1.2.0" 159 | function-bind "^1.1.1" 160 | has "^1.0.3" 161 | has-symbols "^1.0.0" 162 | is-callable "^1.1.4" 163 | is-regex "^1.0.4" 164 | object-inspect "^1.6.0" 165 | object-keys "^1.1.1" 166 | string.prototype.trimleft "^2.0.0" 167 | string.prototype.trimright "^2.0.0" 168 | 169 | es-to-primitive@^1.2.0: 170 | version "1.2.0" 171 | resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" 172 | integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== 173 | dependencies: 174 | is-callable "^1.1.4" 175 | is-date-object "^1.0.1" 176 | is-symbol "^1.0.2" 177 | 178 | esm@^3.0.84, esm@^3.2.25: 179 | version "3.2.25" 180 | resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" 181 | integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== 182 | 183 | estree-walker@^0.6.1: 184 | version "0.6.1" 185 | resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" 186 | integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== 187 | 188 | eventemitter3@^3.0.0: 189 | version "3.1.2" 190 | resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" 191 | integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== 192 | 193 | follow-redirects@^1.0.0: 194 | version "1.9.0" 195 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.9.0.tgz#8d5bcdc65b7108fe1508649c79c12d732dcedb4f" 196 | integrity sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A== 197 | dependencies: 198 | debug "^3.0.0" 199 | 200 | for-each@~0.3.3: 201 | version "0.3.3" 202 | resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" 203 | integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== 204 | dependencies: 205 | is-callable "^1.1.3" 206 | 207 | fs.realpath@^1.0.0: 208 | version "1.0.0" 209 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 210 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 211 | 212 | function-bind@^1.0.2, function-bind@^1.1.1, function-bind@~1.1.1: 213 | version "1.1.1" 214 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 215 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 216 | 217 | glob@~7.1.4: 218 | version "7.1.4" 219 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" 220 | integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== 221 | dependencies: 222 | fs.realpath "^1.0.0" 223 | inflight "^1.0.4" 224 | inherits "2" 225 | minimatch "^3.0.4" 226 | once "^1.3.0" 227 | path-is-absolute "^1.0.0" 228 | 229 | has-symbols@^1.0.0: 230 | version "1.0.0" 231 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" 232 | integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= 233 | 234 | has@^1.0.1, has@^1.0.3, has@~1.0.3: 235 | version "1.0.3" 236 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 237 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 238 | dependencies: 239 | function-bind "^1.1.1" 240 | 241 | he@^1.1.1: 242 | version "1.2.0" 243 | resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" 244 | integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== 245 | 246 | http-proxy@^1.8.1: 247 | version "1.17.0" 248 | resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" 249 | integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g== 250 | dependencies: 251 | eventemitter3 "^3.0.0" 252 | follow-redirects "^1.0.0" 253 | requires-port "^1.0.0" 254 | 255 | http-server@^0.11.1: 256 | version "0.11.1" 257 | resolved "https://registry.yarnpkg.com/http-server/-/http-server-0.11.1.tgz#2302a56a6ffef7f9abea0147d838a5e9b6b6a79b" 258 | integrity sha512-6JeGDGoujJLmhjiRGlt8yK8Z9Kl0vnl/dQoQZlc4oeqaUoAKQg94NILLfrY3oWzSyFaQCVNTcKE5PZ3cH8VP9w== 259 | dependencies: 260 | colors "1.0.3" 261 | corser "~2.0.0" 262 | ecstatic "^3.0.0" 263 | http-proxy "^1.8.1" 264 | opener "~1.4.0" 265 | optimist "0.6.x" 266 | portfinder "^1.0.13" 267 | union "~0.4.3" 268 | 269 | inflight@^1.0.4: 270 | version "1.0.6" 271 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 272 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 273 | dependencies: 274 | once "^1.3.0" 275 | wrappy "1" 276 | 277 | inherits@2, inherits@~2.0.4: 278 | version "2.0.4" 279 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 280 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 281 | 282 | is-callable@^1.1.3, is-callable@^1.1.4: 283 | version "1.1.4" 284 | resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" 285 | integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== 286 | 287 | is-date-object@^1.0.1: 288 | version "1.0.1" 289 | resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" 290 | integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= 291 | 292 | is-module@^1.0.0: 293 | version "1.0.0" 294 | resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" 295 | integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= 296 | 297 | is-reference@^1.1.2: 298 | version "1.1.3" 299 | resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.1.3.tgz#e99059204b66fdbe09305cfca715a29caa5c8a51" 300 | integrity sha512-W1iHHv/oyBb2pPxkBxtaewxa1BC58Pn5J0hogyCdefwUIvb6R+TGbAcIa4qPNYLqLhb3EnOgUf2MQkkF76BcKw== 301 | dependencies: 302 | "@types/estree" "0.0.39" 303 | 304 | is-regex@^1.0.4: 305 | version "1.0.4" 306 | resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" 307 | integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= 308 | dependencies: 309 | has "^1.0.1" 310 | 311 | is-symbol@^1.0.2: 312 | version "1.0.2" 313 | resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" 314 | integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== 315 | dependencies: 316 | has-symbols "^1.0.0" 317 | 318 | magic-string@^0.25.2: 319 | version "0.25.3" 320 | resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.3.tgz#34b8d2a2c7fec9d9bdf9929a3fd81d271ef35be9" 321 | integrity sha512-6QK0OpF/phMz0Q2AxILkX2mFhi7m+WMwTRg0LQKq/WBB0cDP4rYH3Wp4/d3OTXlrPLVJT/RFqj8tFeAR4nk8AA== 322 | dependencies: 323 | sourcemap-codec "^1.4.4" 324 | 325 | "marked@https://github.com/observablehq/marked.git#94c6b946f462fd25db4465d71a6859183f86c57f": 326 | version "0.3.12" 327 | resolved "https://github.com/observablehq/marked.git#94c6b946f462fd25db4465d71a6859183f86c57f" 328 | 329 | mime@^1.6.0: 330 | version "1.6.0" 331 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" 332 | integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== 333 | 334 | minimatch@^3.0.4: 335 | version "3.0.4" 336 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 337 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 338 | dependencies: 339 | brace-expansion "^1.1.7" 340 | 341 | minimist@0.0.8: 342 | version "0.0.8" 343 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 344 | integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= 345 | 346 | minimist@^1.1.0, minimist@~1.2.0: 347 | version "1.2.0" 348 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 349 | integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= 350 | 351 | minimist@~0.0.1: 352 | version "0.0.10" 353 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" 354 | integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= 355 | 356 | mkdirp@0.5.x: 357 | version "0.5.1" 358 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 359 | integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= 360 | dependencies: 361 | minimist "0.0.8" 362 | 363 | ms@2.0.0: 364 | version "2.0.0" 365 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 366 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 367 | 368 | ms@^2.1.1: 369 | version "2.1.2" 370 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 371 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 372 | 373 | object-inspect@^1.6.0, object-inspect@~1.6.0: 374 | version "1.6.0" 375 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" 376 | integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== 377 | 378 | object-keys@^1.0.12, object-keys@^1.1.1: 379 | version "1.1.1" 380 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" 381 | integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== 382 | 383 | once@^1.3.0: 384 | version "1.4.0" 385 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 386 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 387 | dependencies: 388 | wrappy "1" 389 | 390 | opener@~1.4.0: 391 | version "1.4.3" 392 | resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" 393 | integrity sha1-XG2ixdflgx6P+jlklQ+NZnSskLg= 394 | 395 | optimist@0.6.x: 396 | version "0.6.1" 397 | resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" 398 | integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= 399 | dependencies: 400 | minimist "~0.0.1" 401 | wordwrap "~0.0.2" 402 | 403 | path-is-absolute@^1.0.0: 404 | version "1.0.1" 405 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 406 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 407 | 408 | path-parse@^1.0.6: 409 | version "1.0.6" 410 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" 411 | integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== 412 | 413 | portfinder@^1.0.13: 414 | version "1.0.24" 415 | resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.24.tgz#11efbc6865f12f37624b6531ead1d809ed965cfa" 416 | integrity sha512-ekRl7zD2qxYndYflwiryJwMioBI7LI7rVXg3EnLK3sjkouT5eOuhS3gS255XxBksa30VG8UPZYZCdgfGOfkSUg== 417 | dependencies: 418 | async "^1.5.2" 419 | debug "^2.2.0" 420 | mkdirp "0.5.x" 421 | 422 | prettier@1.19.1: 423 | version "1.19.1" 424 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" 425 | integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== 426 | 427 | qs@~2.3.3: 428 | version "2.3.3" 429 | resolved "https://registry.yarnpkg.com/qs/-/qs-2.3.3.tgz#e9e85adbe75da0bbe4c8e0476a086290f863b404" 430 | integrity sha1-6eha2+ddoLvkyOBHaghikPhjtAQ= 431 | 432 | requires-port@^1.0.0: 433 | version "1.0.0" 434 | resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" 435 | integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= 436 | 437 | resolve@^1.11.0, resolve@^1.11.1: 438 | version "1.12.0" 439 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" 440 | integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== 441 | dependencies: 442 | path-parse "^1.0.6" 443 | 444 | resolve@~1.11.1: 445 | version "1.11.1" 446 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" 447 | integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw== 448 | dependencies: 449 | path-parse "^1.0.6" 450 | 451 | resumer@~0.0.0: 452 | version "0.0.0" 453 | resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759" 454 | integrity sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k= 455 | dependencies: 456 | through "~2.3.4" 457 | 458 | rollup-plugin-commonjs@^10.1.0: 459 | version "10.1.0" 460 | resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz#417af3b54503878e084d127adf4d1caf8beb86fb" 461 | integrity sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q== 462 | dependencies: 463 | estree-walker "^0.6.1" 464 | is-reference "^1.1.2" 465 | magic-string "^0.25.2" 466 | resolve "^1.11.0" 467 | rollup-pluginutils "^2.8.1" 468 | 469 | rollup-plugin-node-resolve@^5.2.0: 470 | version "5.2.0" 471 | resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz#730f93d10ed202473b1fb54a5997a7db8c6d8523" 472 | integrity sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw== 473 | dependencies: 474 | "@types/resolve" "0.0.8" 475 | builtin-modules "^3.1.0" 476 | is-module "^1.0.0" 477 | resolve "^1.11.1" 478 | rollup-pluginutils "^2.8.1" 479 | 480 | rollup-pluginutils@^2.8.1: 481 | version "2.8.1" 482 | resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz#8fa6dd0697344938ef26c2c09d2488ce9e33ce97" 483 | integrity sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg== 484 | dependencies: 485 | estree-walker "^0.6.1" 486 | 487 | rollup@^1.26.4: 488 | version "1.27.0" 489 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.27.0.tgz#7afe0da89c967cec5ccea7e919da6c89a1a68666" 490 | integrity sha512-yaMna4MJ8LLEHhHl1ilgHakylf0LKeQctDxhngZLQ+W57GnXa5vtH7XKaK8zlAhNEhlWiH5YFVFt+QCDPUmNkw== 491 | dependencies: 492 | "@types/estree" "*" 493 | "@types/node" "*" 494 | acorn "^7.1.0" 495 | 496 | sourcemap-codec@^1.4.4: 497 | version "1.4.6" 498 | resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz#e30a74f0402bad09807640d39e971090a08ce1e9" 499 | integrity sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg== 500 | 501 | string.prototype.trim@~1.1.2: 502 | version "1.1.2" 503 | resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea" 504 | integrity sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo= 505 | dependencies: 506 | define-properties "^1.1.2" 507 | es-abstract "^1.5.0" 508 | function-bind "^1.0.2" 509 | 510 | string.prototype.trimleft@^2.0.0: 511 | version "2.1.0" 512 | resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" 513 | integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw== 514 | dependencies: 515 | define-properties "^1.1.3" 516 | function-bind "^1.1.1" 517 | 518 | string.prototype.trimright@^2.0.0: 519 | version "2.1.0" 520 | resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" 521 | integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg== 522 | dependencies: 523 | define-properties "^1.1.3" 524 | function-bind "^1.1.1" 525 | 526 | tape@^4.11.0: 527 | version "4.11.0" 528 | resolved "https://registry.yarnpkg.com/tape/-/tape-4.11.0.tgz#63d41accd95e45a23a874473051c57fdbc58edc1" 529 | integrity sha512-yixvDMX7q7JIs/omJSzSZrqulOV51EC9dK8dM0TzImTIkHWfe2/kFyL5v+d9C+SrCMaICk59ujsqFAVidDqDaA== 530 | dependencies: 531 | deep-equal "~1.0.1" 532 | defined "~1.0.0" 533 | for-each "~0.3.3" 534 | function-bind "~1.1.1" 535 | glob "~7.1.4" 536 | has "~1.0.3" 537 | inherits "~2.0.4" 538 | minimist "~1.2.0" 539 | object-inspect "~1.6.0" 540 | resolve "~1.11.1" 541 | resumer "~0.0.0" 542 | string.prototype.trim "~1.1.2" 543 | through "~2.3.8" 544 | 545 | through@~2.3.4, through@~2.3.8: 546 | version "2.3.8" 547 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 548 | integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= 549 | 550 | union@~0.4.3: 551 | version "0.4.6" 552 | resolved "https://registry.yarnpkg.com/union/-/union-0.4.6.tgz#198fbdaeba254e788b0efcb630bc11f24a2959e0" 553 | integrity sha1-GY+9rrolTniLDvy2MLwR8kopWeA= 554 | dependencies: 555 | qs "~2.3.3" 556 | 557 | url-join@^2.0.5: 558 | version "2.0.5" 559 | resolved "https://registry.yarnpkg.com/url-join/-/url-join-2.0.5.tgz#5af22f18c052a000a48d7b82c5e9c2e2feeda728" 560 | integrity sha1-WvIvGMBSoACkjXuCxenC4v7tpyg= 561 | 562 | wordwrap@~0.0.2: 563 | version "0.0.3" 564 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" 565 | integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= 566 | 567 | wrappy@1: 568 | version "1.0.2" 569 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 570 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 571 | --------------------------------------------------------------------------------