├── .gitignore ├── README.md ├── f.js ├── index.js ├── package.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # obsy 2 | 3 | Just a tiny, silly experiment. It is: 4 | 5 | - The Observable tech (runtime, parser) 6 | - A cli interface (blessed) 7 | - A syntax for writing "notebooks" (not really) 8 | in vanilla javascript. 9 | -------------------------------------------------------------------------------- /f.js: -------------------------------------------------------------------------------- 1 | const { parseCell } = require("@observablehq/parser"); 2 | const { Runtime } = require("@observablehq/runtime"); 3 | const blessed = require("blessed"); 4 | const runtime = new Runtime(); 5 | const Fun = Object.getPrototypeOf(async function () {}).constructor; 6 | const GFun = Object.getPrototypeOf(async function* () {}).constructor; 7 | const mod = runtime.module(); 8 | 9 | let values = new Map(); 10 | let states = new Map(); 11 | let srces = new Map(); 12 | let vars = new Map(); 13 | 14 | const f = function f(name, val) { 15 | let src = val 16 | .toString() 17 | .replace(/^(async)?\s?\(\) =>/, "") 18 | .replace(/^(async)?\s?function\*?\s\(\)\s*/, "") 19 | .trim(); 20 | 21 | const cell = parseCell(src); 22 | src = src.trim().replace(/^{/, "").replace(/}$/, ""); 23 | const inputs = cell.references.map((ref) => ref.name); 24 | if (srces.get(name) === src) { 25 | return; 26 | } else { 27 | srces.set(name, src); 28 | } 29 | if (val.toString().match(/^\(\)/) && !val.toString().match(/}$/)) { 30 | src = `return ${src}`; 31 | } 32 | const fn = cell.async ? new GFun(inputs, src) : new Fun(inputs, src); 33 | let v = vars.has(name) 34 | ? vars.get(name) 35 | : mod.variable({ 36 | pending() { 37 | states.set(name, "pending"); 38 | update(); 39 | }, 40 | fulfilled(value) { 41 | values.set(name, JSON.stringify(value)); 42 | states.set(name, "fulfilled"); 43 | update(); 44 | }, 45 | rejected(value) { 46 | values.set(name, JSON.stringify(value)); 47 | states.set(name, "rejected"); 48 | update(); 49 | }, 50 | }); 51 | 52 | v = v.define(name, inputs, fn); 53 | vars.set(name, v); 54 | }; 55 | 56 | var screen = blessed.screen({ 57 | smartCSR: true, 58 | }); 59 | 60 | screen.title = "fn"; 61 | 62 | // Create a box perfectly centered horizontally and vertically. 63 | var table = blessed.listtable({ 64 | top: "center", 65 | left: "center", 66 | width: "100%", 67 | height: "100%", 68 | align: "left", 69 | }); 70 | 71 | screen.append(table); 72 | 73 | function update() { 74 | let data = [["CELL", "STATE", "VALUE"]]; 75 | for (let [name, value] of values) { 76 | data.push([name, states.get(name), value]); 77 | } 78 | table.setData(data); 79 | screen.render(); 80 | } 81 | 82 | const target = process.argv[2]; 83 | 84 | (async () => { 85 | while (true) { 86 | const m = require(target); 87 | delete require.cache[require.resolve(target)]; 88 | m(f); 89 | await new Promise((r) => setTimeout(r, 100)); 90 | } 91 | })(); 92 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = (f) => { 2 | f("a", () => 1); 3 | f("b", () => a); 4 | f("c", () => d + 10); 5 | f("c", () => now); 6 | f("d", async function* () { 7 | let x = 0; 8 | while (true) { 9 | yield ++x; 10 | await Promises.delay(200); 11 | } 12 | }); 13 | f("s", () => d + now); 14 | }; 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsy", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "@observablehq/parser": "^4.2.0", 8 | "@observablehq/runtime": "^4.8.2", 9 | "blessed": "^0.1.81" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /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.2": 6 | version "3.2.2" 7 | resolved "https://registry.yarnpkg.com/@observablehq/inspector/-/inspector-3.2.2.tgz#df4e9774e2461836a6e09ff22d79a18a9e77ee11" 8 | integrity sha512-HBHtruB7hcNqyapyNF77fPVpiCyqEZsmLD6rhCy7nHPaciAEPML4NtQG2QBuTkYlEu64Zulky6g3ABrD0PhX2Q== 9 | 10 | "@observablehq/parser@^4.2.0": 11 | version "4.2.0" 12 | resolved "https://registry.yarnpkg.com/@observablehq/parser/-/parser-4.2.0.tgz#74f34b4fbed4b71e54c6f97e34d9209a6a745f18" 13 | integrity sha512-i1v95BrUB4urKEBIL1CEPWCU8DovxKyMlWf03pEfpOfi5/DiESQ58QRQM6Ws+Sy/nwKXnIx8IMj4kX28Bif4xQ== 14 | dependencies: 15 | acorn "^7.1.1" 16 | acorn-walk "^7.0.0" 17 | 18 | "@observablehq/runtime@^4.8.2": 19 | version "4.8.2" 20 | resolved "https://registry.yarnpkg.com/@observablehq/runtime/-/runtime-4.8.2.tgz#0fe8c692f207051761dec870fa43046d99c98dca" 21 | integrity sha512-wik8Uu92a7w3/cS8r69oHLyHTuZgVGNc+eIz0UwOi10dfpquMC0BzG8AbZzMQQEr1EryxSqNvALFY+vf199dww== 22 | dependencies: 23 | "@observablehq/inspector" "^3.2.2" 24 | "@observablehq/stdlib" "^3.4.1" 25 | 26 | "@observablehq/stdlib@^3.4.1": 27 | version "3.4.1" 28 | resolved "https://registry.yarnpkg.com/@observablehq/stdlib/-/stdlib-3.4.1.tgz#39d88fb9d3b79f3c987e9a0099ac4b6ff9ea8c5d" 29 | integrity sha512-v0aoQV3e2NphWvkJC/jDI60bV4Nvwy8XD7PFZdzBZYmcQt/cO1ly7TVRZTHGljXdK5e7W6blFY6lNgevUwfrNw== 30 | dependencies: 31 | d3-require "^1.2.4" 32 | 33 | acorn-walk@^7.0.0: 34 | version "7.2.0" 35 | resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" 36 | integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== 37 | 38 | acorn@^7.1.1: 39 | version "7.4.1" 40 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" 41 | integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== 42 | 43 | blessed@^0.1.81: 44 | version "0.1.81" 45 | resolved "https://registry.yarnpkg.com/blessed/-/blessed-0.1.81.tgz#f962d687ec2c369570ae71af843256e6d0ca1129" 46 | integrity sha1-+WLWh+wsNpVwrnGvhDJW5tDKESk= 47 | 48 | d3-require@^1.2.4: 49 | version "1.2.4" 50 | resolved "https://registry.yarnpkg.com/d3-require/-/d3-require-1.2.4.tgz#59afc591d5089f99fecd8c45ef7539e1fee112b3" 51 | integrity sha512-8UseEGCkBkBxIMouLMPONUBmU8DUPC1q12LARV1Lk/2Jwa32SVgmRfX8GdIeR06ZP+CG85YD3N13K2s14qCNyA== 52 | --------------------------------------------------------------------------------