Published
Edited
Jun 6, 2019
1 fork
23 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
body = getNotebook(notebookName)
Insert cell
program = ({
type: "Program",
body: exports.map(exp => getBareCell(exp, exp === entry))
})
Insert cell
cells = {
if (body.length !== 3) throw new Error("no support for imports yet");
const cells =
body[0].declarations[0].init.properties[1].value.elements;
const cellMap = new Map();
for (let cell of cells) {
const name = getProperty(cell, "name");
if (name) {
cellMap.set(name.value.value, cell);
}
}
return cellMap;
}
Insert cell
entry = {
let entry = cells.get(entryPoint);
if (!entry) throw new Error("Entry not found");
return entry;
}
Insert cell
exports = {
let exported = new Set();
let exports = [];

// A depth-first search that aims to order
// cells in dependency (topological) order, so that they
// always have their dependencies defined before them.
function crawlTree(cell) {
if (exported.has(cell)) return;
getInputs(cell).forEach(input => {
crawlTree(cells.get(input));
});
if (exported.add(cell));
exports.push(cell);
}

crawlTree(entry);

return exports;
}
Insert cell
function getBareCell(cell, isEntry) {
const decl = {
type: "VariableDeclaration",
kind: "const",
declarations: [
{
type: "VariableDeclarator",
id: {
type: "Identifier",
name: getName(cell)
},
init: getValue(cell)
}
]
};
if (isEntry) {
return {
type: "ExportNamedDeclaration",
declaration: decl
};
}
return decl;
}
Insert cell
getValue = promisify ? getPromiseValue : getConstantValue
Insert cell
function getName(cell) {
return getProperty(cell, "name").value.value;
}
Insert cell
function getConstantValue(cell) {
const value = getProperty(cell, "value").value;
if (value.body.type === "BlockStatement"
&& value.body.body.length === 1
&& value.body.body[0].type === "ReturnStatement") {
return value.body.body[0].argument;
}
return {
type: "CallExpression",
callee: {
...value,
params: []
}
};
}
Insert cell
function getPromiseValue(cell) {
const value = getProperty(cell, "value").value;
const inputs = getInputs(cell).map(name => ({type: "Identifier", name}));
return {
type: "CallExpression",
callee: {
type: "MemberExpression",
object: inputs.length === 0
? {
type: "CallExpression",
callee: {
type: "MemberExpression",
object: {
type: "Identifier",
name: "Promise"
},
property: {
type: "Identifier",
name: "resolve"
}
},
arguments: []
}
: inputs.length === 1 ? inputs[0]
: {
type: "CallExpression",
callee: {
type: "MemberExpression",
object: {
type: "Identifier",
name: "Promise"
},
property: {
type: "Identifier",
name: "all"
}
},
arguments: [
{
type: "ArrayExpression",
elements: inputs
}
]
},
property: {
type: "Identifier",
name: "then"
}
},
arguments: [
{
...value,
params: inputs.length > 1
? [{type: "ArrayPattern", elements: value.params}]
: value.params
}
]
};
}
Insert cell
function getInputs(cell) {
const inputs = getProperty(cell, "inputs");
if (!inputs) return [];
return inputs.value.elements.map(e => e.value);
}
Insert cell
async function getNotebook(name) {
const src = await (await fetch(
`https://api.observablehq.com/${name}.js`
)).text();
const { body } = acorn.parse(src, { sourceType: "module" });
return body;
}
Insert cell
getProperty = (cell, name) => cell.properties.find(p => p.key.name === name)
Insert cell
acorn = require("acorn")
Insert cell
astring = import("astring")
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more