@hpcc-js/observablehq-compiler
Version:
hpcc-js - ObservableHQ Compiler (unoffical)
5 lines • 72.2 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../src/index.node.ts", "../../src/kit/compiler.ts", "../../src/kit/util.ts", "../../src/compiler.ts", "../../src/parse.ts", "../../../../node_modules/@observablehq/parser/src/references.js", "../../../../node_modules/@observablehq/parser/src/walk.js", "../../../../node_modules/@observablehq/parser/src/features.js", "../../src/cst.ts", "../../src/writer.ts", "../../src/util.ts"],
"sourcesContent": ["import { JSDOM } from \"jsdom\";\n\nconst { window } = new JSDOM();\nglobalThis.document = window.document;\nglobalThis.DOMParser = window.DOMParser;\n\nexport * from \"./index.ts\";\n", "\nimport { type Notebook, type Cell, transpile } from \"@observablehq/notebook-kit\";\nimport { type Definition } from \"@observablehq/notebook-kit/runtime\";\nimport { constructFunction } from \"./util.ts\";\n\nexport { Notebook, Cell };\n\nexport interface CompileCellOptions {\n inline?: boolean;\n resolveLocalImports?: boolean;\n includePinned?: boolean;\n}\n\nexport function compileCell(cell: Cell, { inline = true, resolveLocalImports = false, includePinned = true }: CompileCellOptions = {}): Definition[] {\n const retVal: Definition[] = [];\n const sourceIDOffset = 1000000;\n try {\n const compiled = transpile(cell, { resolveLocalImports });\n retVal.push({\n id: cell.id,\n ...compiled,\n body: inline ? constructFunction(compiled.body, `cell_${cell.id}`) : compiled.body,\n });\n if (includePinned && cell.pinned) {\n const compiled = transpile({\n ...cell,\n mode: \"md\",\n value: `\\\n\\`\\`\\`${cell.mode}\n${cell.value}\n\\`\\`\\``\n });\n retVal.push({\n id: sourceIDOffset + cell.id,\n ...compiled,\n body: inline ? constructFunction(compiled.body, `cell_${sourceIDOffset + cell.id}`) : compiled.body,\n });\n }\n } catch (error) {\n console.error(`Error compiling cell ${cell.id}:`, error);\n }\n return retVal;\n}\n\nexport function compileNotebook(notebook: Notebook, { inline = true, resolveLocalImports = false, includePinned = true }: CompileCellOptions = {}): Definition[] {\n const retVal: Definition[] = [];\n for (const cell of notebook.cells) {\n const cellDefs = compileCell(cell, { inline, resolveLocalImports, includePinned });\n retVal.push(...cellDefs);\n }\n return retVal;\n}\n\nexport function resetCellIDs(notebook: Notebook, start: number = 0, increment: number = 1): Notebook {\n for (const cell of notebook.cells) {\n cell.id = start;\n start += increment;\n }\n return notebook;\n}\n", "import { type Notebook, type Cell, parseJavaScript, serialize, deserialize, toNotebook, toCell } from \"@observablehq/notebook-kit\";\nimport { type Definition } from \"@observablehq/notebook-kit/runtime\";\n\nexport type { Notebook, Cell, Definition };\nexport { toNotebook, toCell };\n\n// Shared function constructor utilities to avoid duplication between util modules.\n\nexport type RegularFunction = (...args: any[]) => any;\ninterface RegularFunctionConstructor {\n new(...args: string[]): RegularFunction;\n (...args: string[]): RegularFunction;\n readonly prototype: RegularFunction;\n}\n\nexport type AsyncFunction = (...args: any[]) => Promise<any>;\ninterface AsyncFunctionConstructor {\n new(...args: string[]): AsyncFunction;\n (...args: string[]): AsyncFunction;\n readonly prototype: AsyncFunction;\n}\n\nexport type GeneratorFunction = (...args: any[]) => Generator<any, any, any>;\ninterface GeneratorFunctionConstructor {\n new(...args: string[]): GeneratorFunction;\n (...args: string[]): GeneratorFunction;\n readonly prototype: GeneratorFunction;\n}\n\nexport type AsyncGeneratorFunction = (...args: any[]) => AsyncGenerator<any, any, any>;\ninterface AsyncGeneratorFunctionConstructor {\n new(...args: string[]): AsyncGeneratorFunction;\n (...args: string[]): AsyncGeneratorFunction;\n readonly prototype: AsyncGeneratorFunction;\n}\n\nexport type AnyFunction = RegularFunction | AsyncFunction | GeneratorFunction | AsyncGeneratorFunction;\n\nexport const FunctionConstructors: {\n regular: RegularFunctionConstructor;\n async: AsyncFunctionConstructor;\n generator: GeneratorFunctionConstructor;\n asyncGenerator: AsyncGeneratorFunctionConstructor;\n} = {\n regular: Object.getPrototypeOf(function () { }).constructor as RegularFunctionConstructor,\n async: Object.getPrototypeOf(async function () { }).constructor as AsyncFunctionConstructor,\n generator: Object.getPrototypeOf(function* () { }).constructor as GeneratorFunctionConstructor,\n asyncGenerator: Object.getPrototypeOf(async function* () { }).constructor as AsyncGeneratorFunctionConstructor,\n};\n\nfunction funcType(async: boolean = false, generator: boolean = false) {\n if (!async && !generator) return FunctionConstructors.regular;\n if (async && !generator) return FunctionConstructors.async;\n if (!async && generator) return FunctionConstructors.generator;\n return FunctionConstructors.asyncGenerator;\n}\n\ninterface Ref {\n start: number,\n end: number,\n newText: string\n}\n\nexport interface Refs {\n inputs: string[];\n args: string[];\n patches: Ref[];\n}\n\nexport function createFunction(refs: Refs, async = false, generator = false, blockStatement = false, body?: string) {\n if (body === undefined) {\n return undefined;\n }\n\n refs.patches.sort((l, r) => r.start - l.start);\n refs.patches.forEach(r => {\n body = body!.substring(0, r.start) + r.newText + body!.substring(r.end);\n });\n return new (funcType(async, generator))(...refs.args, blockStatement ?\n body.substring(1, body.length - 1).trim() :\n `return (\\n${body}\\n);`);\n}\n\nfunction join(baseURL: string, relativeURL: string) {\n return relativeURL\n ? baseURL.replace(/\\/+$/, \"\") + \"/\" + relativeURL.replace(/^\\/+/, \"\")\n : baseURL;\n}\n\nexport const isRelativePath = (path: string) => path[0] === \".\";\nexport const fixRelativeUrl = (path: string, basePath: string) => {\n if (isRelativePath(path)) {\n return join(basePath, path);\n }\n return path;\n};\n\n// Hide \"import\" from bundlers as they have a habit of replacing \"import\" with \"require\"\nconst obfuscatedImportFunction = new FunctionConstructors.async(\"url\", \"return import(url)\");\nexport async function obfuscatedImport(url: string) {\n return obfuscatedImportFunction(url);\n}\n\nfunction _constructFunction(body: any, bodyStr: string, name?: string) {\n if (body.type !== \"FunctionExpression\" && body.type !== \"FunctionDeclaration\" && body.type !== \"ArrowFunctionExpression\") {\n throw new Error(`Unsupported function type: ${body.type}`);\n }\n const func = body.async && body.generator ?\n FunctionConstructors.asyncGenerator :\n body.async ?\n FunctionConstructors.async :\n body.generator ?\n FunctionConstructors.generator :\n FunctionConstructors.regular;\n\n const params = body.params?.map((param) => bodyStr.slice(param.start, param.end)).join(\", \") ?? \"\";\n const isBlock = body.body.type === \"BlockStatement\";\n const { start, end } = body.body;\n const inner = isBlock\n ? bodyStr.slice(start + 1, end - 1)\n : `return ${bodyStr.slice(start, end)}`;\n // If a name is provided, build a true named function expression for reliable naming.\n if (name) {\n // Sanitize to a valid identifier; fallback to underscored name when necessary.\n const sanitize = (n: string): string => {\n let s = n.replace(/[^A-Za-z0-9_$]/g, \"_\");\n if (!/^[A-Za-z_$]/.test(s)) s = \"_\" + s;\n return s;\n };\n const id = sanitize(name);\n const asyncKw = body.async ? \"async \" : \"\";\n const genMark = body.generator ? \"*\" : \"\";\n const src = `return ${asyncKw}function${genMark} ${id}(${params}) {\\n${inner}\\n}`;\n // Use a plain Function wrapper to evaluate the named function expression.\n const builtNamed = (new Function(src)()) as AnyFunction;\n // Expose the intended display name even if sanitization changed the identifier.\n try { (builtNamed as any).displayName = name; } catch { /* noop */ }\n return builtNamed;\n }\n\n const built = func(params, inner) as AnyFunction;\n return built;\n}\n\nexport function constructFunction(bodyStr: string, name?: string) {\n const { body } = parseJavaScript(bodyStr);\n if (body.type === \"Program\") {\n if (body.body.length !== 1) {\n throw new Error(`Expected a single function, but found ${body.body.length} statements`);\n }\n return _constructFunction(body.body[0], bodyStr, name);\n }\n return _constructFunction(body, bodyStr, name);\n}\n\nexport const html2notebook = (html: string): Notebook => deserialize(html);\nexport const notebook2html = (notebook: Notebook): string => serialize(notebook);\n", "import { type Notebook, type Definition, compileNotebook, fixRelativeUrl, isRelativePath, obfuscatedImport } from \"./kit/index.ts\";\nimport { FileAttachments } from \"@observablehq/stdlib\";\nimport { ohq, splitModule } from \"./observable-shim.ts\";\nimport { parseCell, ParsedImportCell } from \"./cst.ts\";\nimport { Writer } from \"./writer.ts\";\nimport { encodeBacktick, fetchEx, ojs2notebook, omd2notebook } from \"./util.ts\";\n\n// Inspector Factory ---\nexport type InspectorFactoryEx = (name: string | undefined, id: string | number) => Inspector;\n\nexport interface Inspector {\n _node?: HTMLDivElement;\n pending(): void;\n fulfilled(value: any): void;\n rejected(error: Error): void;\n}\n\n// Module ---\n\ninterface ImportDefine {\n (runtime: ohq.Runtime, inspector?: InspectorFactoryEx): ohq.Module;\n delete: () => void;\n write: (w: Writer) => void;\n}\n\nasync function importFile(relativePath: string, baseUrl: string) {\n const path = fixRelativeUrl(relativePath, baseUrl);\n const content = await fetchEx(path).then(r => r.text());\n let notebook: ohq.Notebook;\n if (relativePath.endsWith(\".ojsnb\")) {\n notebook = JSON.parse(content);\n } else if (relativePath.endsWith(\".ojs\")) {\n notebook = ojs2notebook(content);\n } else if (relativePath.endsWith(\".omd\")) {\n notebook = omd2notebook(content);\n } else {\n console.warn(`Unknown file type: ${relativePath}, assuming .ojsnb`);\n notebook = JSON.parse(content);\n }\n const retVal: ImportDefine = compile(notebook, { baseUrl }) as any;\n retVal.delete = () => { };\n retVal.write = (w: Writer) => {\n w.import(path);\n };\n return retVal;\n}\n\n// Import precompiled notebook from observable ---\nasync function importCompiledNotebook(partial: string) {\n const url = `https://api.observablehq.com/${partial[0] === \"@\" ? partial : `d/${partial}`}.js?v=3`;\n let impMod = {\n default: function (runtime: ohq.Runtime, inspector?: InspectorFactoryEx): ohq.Module | undefined {\n return undefined;\n } as any\n };\n try {\n impMod = await obfuscatedImport(url);\n } catch (e) {\n }\n const retVal: ImportDefine = impMod.default;\n retVal.delete = () => { };\n retVal.write = (w: Writer) => {\n w.import(url);\n };\n return retVal;\n}\n\n// Recursive notebook parsing and compiling\nasync function importNotebook(partial: string) {\n const url = `https://api.observablehq.com/document/${partial}`;\n const notebook = fetchEx(url)\n .then(r => {\n return r.json();\n }).catch(e => {\n console.error(url);\n console.error(e);\n });\n const retVal: ImportDefine = compile(await notebook) as any;\n retVal.delete = () => { };\n retVal.write = (w: Writer) => {\n w.import(url);\n };\n return retVal;\n}\n\nasync function createModule(node: ohq.Node, parsed: ParsedImportCell, text: string, { baseUrl, importMode }: CompileOptions) {\n const otherModule = isRelativePath(parsed.src) ?\n await importFile(parsed.src, baseUrl ?? \"\") :\n importMode === \"recursive\" ?\n await importNotebook(parsed.src) :\n await importCompiledNotebook(parsed.src);\n\n const importVariables: ImportVariableFunc[] = [];\n const variables: VariableFunc[] = [];\n parsed.specifiers.forEach(spec => {\n const viewof = spec.view ? \"viewof \" : \"\";\n importVariables.push(createImportVariable(viewof + spec.name, viewof + spec.alias));\n if (spec.view) {\n importVariables.push(createImportVariable(spec.name, spec.alias));\n }\n });\n\n const retVal = (runtime: ohq.Runtime, main: ohq.Module, inspector?: InspectorFactoryEx) => {\n\n let mod = runtime.module(otherModule);\n if (parsed.injections.length) {\n mod = mod.derive(parsed.injections, main);\n }\n variables.forEach(v => v(main, inspector));\n importVariables.forEach(v => v(main, mod));\n return mod;\n };\n retVal.importVariables = importVariables;\n retVal.variables = variables;\n retVal.delete = () => {\n importVariables.forEach(v => v.delete());\n variables.forEach(v => v.delete());\n otherModule.delete();\n };\n retVal.write = (w: Writer) => {\n otherModule.write(w);\n w.importDefine(parsed);\n };\n return retVal;\n}\ntype ModuleFunc = Awaited<ReturnType<typeof createModule>>;\n\n// Variable ---\nfunction createVariable(node: ohq.Node, inspect: boolean, name?: string, inputs?: string[], definition?: any, inline = false) {\n\n let i: ohq.Inspector | undefined;\n let v: ohq.Variable | undefined;\n\n const retVal = (module: ohq.Module, inspector?: InspectorFactoryEx) => {\n if (inspect && inspector) {\n i = inspector(name, node.id);\n }\n v = module.variable(i);\n if (arguments.length > 1) {\n try {\n v.define(name, inputs, definition);\n } catch (e: any) {\n console.error(e?.message);\n }\n }\n if (node.pinned) {\n v = inspector ? module.variable(inspector(name, node.id)) : module.variable();\n try {\n v.define(undefined, [\"md\"], (md: any) => {\n return md`\\`\\`\\`js\n${node.value}\n\\`\\`\\``;\n });\n } catch (e: any) {\n console.error(e?.message);\n }\n }\n return v;\n };\n retVal.delete = () => {\n try {\n i?._node?.remove();\n } catch (e) {\n }\n i = undefined;\n try {\n v?.delete();\n } catch (e) {\n }\n v = undefined;\n };\n retVal.write = (w: Writer) => {\n if (inline) {\n w.define({ id: name, inputs, func: definition }, inspect, true);\n } else {\n const id = w.function({ id: name, func: definition });\n w.define({ id: name, inputs, func: definition }, inspect, false, id);\n }\n };\n return retVal;\n}\ntype VariableFunc = ReturnType<typeof createVariable>;\n\nfunction createImportVariable(name: string, alias?: string) {\n\n let v: ohq.Variable;\n\n const retVal = (main: ohq.Module, otherModule: ohq.Module) => {\n v = main.variable();\n if (alias === undefined) {\n v.import(name, otherModule);\n } else {\n v.import(name, alias, otherModule);\n }\n };\n\n retVal.delete = () => {\n v?.delete();\n };\n return retVal;\n}\ntype ImportVariableFunc = ReturnType<typeof createImportVariable>;\n\n// Cell ---\nasync function createCell(node: ohq.Node, options: CompileOptions) {\n const modules: ModuleFunc[] = [];\n const variables: VariableFunc[] = [];\n try {\n const text = node.mode && node.mode !== \"js\" ? `${node.mode}\\`${encodeBacktick(node.value)}\\`` : node.value;\n const parsedModule = splitModule(text);\n for (const cell of parsedModule) {\n const parsed = parseCell(cell.text, options.baseUrl ?? \"\");\n switch (parsed.type) {\n case \"import\":\n modules.push(await createModule(node, parsed, cell.text, options));\n break;\n case \"viewof\":\n variables.push(createVariable(node, true, parsed.variable.id, parsed.variable.inputs, parsed.variable.func));\n variables.push(createVariable(node, false, parsed.variableValue.id, parsed.variableValue.inputs, parsed.variableValue.func, true));\n break;\n case \"mutable\":\n variables.push(createVariable(node, false, parsed.initial.id, parsed.initial.inputs, parsed.initial.func));\n variables.push(createVariable(node, false, parsed.variable.id, parsed.variable.inputs, parsed.variable.func));\n variables.push(createVariable(node, true, parsed.variableValue.id, parsed.variableValue.inputs, parsed.variableValue.func, true));\n break;\n case \"variable\":\n variables.push(createVariable(node, true, parsed.id, parsed.inputs, parsed.func));\n break;\n }\n }\n } catch (e: any) {\n variables.push(createVariable(node, true, undefined, [], e.message ?? \"Unkown error\"));\n }\n\n const retVal = (runtime: ohq.Runtime, main: ohq.Module, inspector?: InspectorFactoryEx) => {\n modules.forEach(imp => imp(runtime, main, inspector));\n variables.forEach(v => v(main, inspector));\n };\n retVal.id = node.id;\n retVal.modules = modules;\n retVal.variables = variables;\n retVal.delete = () => {\n variables.forEach(v => v.delete());\n modules.forEach(mod => mod.delete());\n };\n retVal.write = (w: Writer) => {\n modules.forEach(imp => imp.write(w));\n variables.forEach(v => v.write(w));\n };\n return retVal;\n}\nexport type CellFunc = Awaited<ReturnType<typeof createCell>>;\n\n// File ---\nfunction createFile(file: ohq.File, options: CompileOptions): [string, any] {\n function toString() {\n // TODO Double check url should not be URL?\n return (globalThis as any).url ?? \"\";\n }\n return [file.name, { url: new URL(fixRelativeUrl(file.url, options.baseUrl ?? \"\")), mimeType: file.mime_type, toString }];\n}\ntype FileFunc = ReturnType<typeof createFile>;\n\n// Interpret ---\nexport interface CompileOptions {\n baseUrl?: string;\n importMode?: \"recursive\" | \"precompiled\";\n}\nexport function notebook(_files: ohq.File[] = [], _cells: CellFunc[] = [], { baseUrl = \".\", importMode = \"precompiled\" }: CompileOptions = {}) {\n const files: FileFunc[] = _files.map(f => createFile(f, { baseUrl, importMode }));\n const fileAttachments = new Map<string, any>(files);\n const cells = new Map<string | number, CellFunc>(_cells.map(c => [c.id, c]));\n\n const retVal = (runtime: ohq.Runtime, inspector?: InspectorFactoryEx): ohq.Module => {\n if (!runtime.fileAttachments) {\n runtime.fileAttachments = FileAttachments;\n }\n const main = runtime.module();\n main.builtin(\"FileAttachment\", FileAttachments(name => {\n return fileAttachments.get(name) ?? { url: new URL(fixRelativeUrl(name, baseUrl)), mimeType: null };\n }));\n main.builtin(\"fetchEx\", fetchEx);\n\n cells.forEach(cell => {\n cell(runtime, main, inspector);\n });\n return main;\n };\n retVal.fileAttachments = fileAttachments;\n retVal.cells = cells;\n retVal.set = async (n: ohq.Node): Promise<CellFunc> => {\n const cell = await createCell(n, { baseUrl, importMode });\n retVal.delete(cell.id);\n cells.set(cell.id, cell);\n return cell;\n };\n retVal.get = (id: string | number): CellFunc | undefined => {\n return cells.get(id);\n };\n retVal.delete = (id: string | number): boolean => {\n const cell = cells.get(id);\n if (cell) {\n cell.delete();\n return cells.delete(id);\n }\n return false;\n };\n retVal.clear = () => {\n cells.forEach(cell => cell.delete());\n cells.clear();\n };\n retVal.write = (w: Writer) => {\n w.files(_files);\n cells.forEach(cell => cell.write(w));\n };\n retVal.toString = (w = new Writer()) => {\n retVal.write(w);\n return w.toString().trim();\n };\n return retVal;\n}\ntype NotebookFunc = ReturnType<typeof notebook>;\n\nexport function isNotebookKit(value: any): value is Notebook {\n return !!value && Array.isArray(value.cells);\n}\n\nexport function isOhqNotebook(value: any): value is ohq.Notebook {\n return !!value && Array.isArray(value.nodes);\n}\nexport async function compile(notebookOrOjs: Notebook): Promise<Definition[]>;\nexport async function compile(notebookOrOjs: ohq.Notebook, options?: CompileOptions): Promise<NotebookFunc>;\nexport async function compile(notebookOrOjs: string, options?: CompileOptions): Promise<NotebookFunc>;\nexport async function compile(notebookOrOjs: Notebook | ohq.Notebook | string, { baseUrl = \".\", importMode = \"precompiled\" }: CompileOptions = {}) {\n if (isNotebookKit(notebookOrOjs)) {\n return compileNotebook(notebookOrOjs);\n } else if (typeof notebookOrOjs === \"string\") {\n notebookOrOjs = ojs2notebook(notebookOrOjs);\n }\n const _cells: CellFunc[] = await Promise.all(notebookOrOjs.nodes.map(n => createCell(n, { baseUrl, importMode })));\n return notebook(notebookOrOjs.files, _cells, { baseUrl, importMode });\n}\nexport type CompileFunc = Awaited<ReturnType<typeof compile>>;\n", "// Compare with ../../../node_modules/@observablehq/parser/src/parse.js\nimport { getLineInfo, tokTypes, Statement, ModuleDeclaration, Expression as ExpressionBase, Node } from \"acorn\";\nimport { ancestor, RecursiveVisitors, AncestorVisitors } from \"acorn-walk\";\nimport { CellParser, parseCell as ohqParseCell, walk as ohqWalk } from \"@observablehq/parser\";\nimport defaultGlobals from \"../../../node_modules/@observablehq/parser/src/globals.js\";\nimport findReferences from \"../../../node_modules/@observablehq/parser/src/references.js\";\nimport findFeatures from \"../../../node_modules/@observablehq/parser/src/features.js\";\n\nexport interface MutableExpression extends Node {\n type: \"MutableExpression\"\n}\n\nexport interface ViewExpression extends Node {\n type: \"ViewExpression\"\n}\n\nexport type Expression = ExpressionBase | MutableExpression | ViewExpression;\n\n// Find references.\n// Check for illegal references to arguments.\n// Check for illegal assignments to global references.\nfunction parseReferences(cell, input, globals = defaultGlobals) {\n if (!cell.body) {\n cell.references = [];\n } else if (cell.body.type === \"ImportDeclaration\") {\n // This is correct?!?\n cell.references = cell.body.specifiers\n ? cell.body.specifiers.map(i => i.imported)\n : [];\n } else {\n try {\n cell.references = findReferences(cell, globals);\n } catch (error: any) {\n if (error.node) {\n const loc = getLineInfo(input, error.node.start);\n error.message += ` (${loc.line}:${loc.column})`;\n error.pos = error.node.start;\n error.loc = loc;\n delete error.node;\n }\n throw error;\n }\n }\n return cell;\n}\n\n// Find features: file attachments, secrets, database clients.\n// Check for illegal references to arguments.\n// Check for illegal assignments to global references.\nfunction parseFeatures(cell, input) {\n if (cell.body && cell.body.type !== \"ImportDeclaration\") {\n try {\n cell.fileAttachments = findFeatures(cell, \"FileAttachment\");\n cell.databaseClients = findFeatures(cell, \"DatabaseClient\");\n cell.secrets = findFeatures(cell, \"Secret\");\n } catch (error: any) {\n if (error.node) {\n const loc = getLineInfo(input, error.node.start);\n error.message += ` (${loc.line}:${loc.column})`;\n error.pos = error.node.start;\n error.loc = loc;\n delete error.node;\n }\n throw error;\n }\n } else {\n cell.fileAttachments = new Map();\n cell.databaseClients = new Map();\n cell.secrets = new Map();\n }\n return cell;\n}\n\nclass ModuleParser extends CellParser {\n\n parseTopLevel(node: { cells?: Cell[] }) {\n if (!node.cells) node.cells = [];\n // @ts-ignore\n while (this.type !== tokTypes.eof) {\n // @ts-ignore\n const cell: Cell = this.parseCell(this.startNode());\n // @ts-ignore\n cell.input = this.input;\n node.cells.push(cell);\n }\n // @ts-ignore\n this.next();\n // @ts-ignore\n return this.finishNode(node, \"Program\");\n }\n}\n\n// @ts-ignore\nexport function parseModule(input, { globals }: { globals: any } = {}) {\n // @ts-ignore\n const program = ModuleParser.parse(input, { ecmaVersion: 2020 });\n for (const cell of program.cells) {\n parseReferences(cell, input, globals);\n parseFeatures(cell, input);\n }\n return program;\n}\n\nexport interface Cell extends Node {\n type: \"Cell\";\n id: Expression;\n text: string;\n body?: Statement | ModuleDeclaration | Expression;\n references: unknown[];\n async: boolean;\n generator: boolean;\n strict: boolean;\n}\n\nexport function splitModule(input: string): Cell[] {\n return (ModuleParser as any)\n .parse(input, { ecmaVersion: \"latest\" })\n .cells.map((cell: any) => ({\n type: \"Cell\",\n text: input.substring(cell.start, cell.end),\n start: cell.start,\n end: cell.end\n }));\n}\n\nexport {\n type Node,\n ancestor,\n type AncestorVisitors\n};\n\nexport function parseCell(input: string): Cell {\n return ohqParseCell(input);\n}\n\nexport const walk: RecursiveVisitors<any> = ohqWalk;\n", "// Based on https://github.com/ForbesLindesay/acorn-globals\n// Copyright (c) 2014 Forbes Lindesay\n// https://github.com/ForbesLindesay/acorn-globals/blob/master/LICENSE\n\nimport {ancestor} from \"acorn-walk\";\nimport walk from \"./walk.js\";\n\nfunction isScope(node) {\n return node.type === \"FunctionExpression\"\n || node.type === \"FunctionDeclaration\"\n || node.type === \"ArrowFunctionExpression\"\n || node.type === \"Program\";\n}\n\nfunction isBlockScope(node) {\n return node.type === \"BlockStatement\"\n || node.type === \"ForInStatement\"\n || node.type === \"ForOfStatement\"\n || node.type === \"ForStatement\"\n || isScope(node);\n}\n\nfunction declaresArguments(node) {\n return node.type === \"FunctionExpression\"\n || node.type === \"FunctionDeclaration\";\n}\n\nexport default function findReferences(cell, globals) {\n const ast = {type: \"Program\", body: [cell.body]};\n const locals = new Map;\n const globalSet = new Set(globals);\n const references = [];\n\n function hasLocal(node, name) {\n const l = locals.get(node);\n return l ? l.has(name) : false;\n }\n\n function declareLocal(node, id) {\n const l = locals.get(node);\n if (l) l.add(id.name);\n else locals.set(node, new Set([id.name]));\n }\n\n function declareClass(node) {\n if (node.id) declareLocal(node, node.id);\n }\n\n function declareFunction(node) {\n node.params.forEach(param => declarePattern(param, node));\n if (node.id) declareLocal(node, node.id);\n }\n\n function declareCatchClause(node) {\n if (node.param) declarePattern(node.param, node);\n }\n\n function declarePattern(node, parent) {\n switch (node.type) {\n case \"Identifier\":\n declareLocal(parent, node);\n break;\n case \"ObjectPattern\":\n node.properties.forEach(node => declarePattern(node, parent));\n break;\n case \"ArrayPattern\":\n node.elements.forEach(node => node && declarePattern(node, parent));\n break;\n case \"Property\":\n declarePattern(node.value, parent);\n break;\n case \"RestElement\":\n declarePattern(node.argument, parent);\n break;\n case \"AssignmentPattern\":\n declarePattern(node.left, parent);\n break;\n default:\n throw new Error(\"Unrecognized pattern type: \" + node.type);\n }\n }\n\n function declareModuleSpecifier(node) {\n declareLocal(ast, node.local);\n }\n\n ancestor(\n ast,\n {\n VariableDeclaration: (node, parents) => {\n let parent = null;\n for (let i = parents.length - 1; i >= 0 && parent === null; --i) {\n if (node.kind === \"var\" ? isScope(parents[i]) : isBlockScope(parents[i])) {\n parent = parents[i];\n }\n }\n node.declarations.forEach(declaration => declarePattern(declaration.id, parent));\n },\n FunctionDeclaration: (node, parents) => {\n let parent = null;\n for (let i = parents.length - 2; i >= 0 && parent === null; --i) {\n if (isScope(parents[i])) {\n parent = parents[i];\n }\n }\n declareLocal(parent, node.id);\n declareFunction(node);\n },\n Function: declareFunction,\n ClassDeclaration: (node, parents) => {\n let parent = null;\n for (let i = parents.length - 2; i >= 0 && parent === null; i--) {\n if (isScope(parents[i])) {\n parent = parents[i];\n }\n }\n declareLocal(parent, node.id);\n },\n Class: declareClass,\n CatchClause: declareCatchClause,\n ImportDefaultSpecifier: declareModuleSpecifier,\n ImportSpecifier: declareModuleSpecifier,\n ImportNamespaceSpecifier: declareModuleSpecifier\n },\n walk\n );\n\n function identifier(node, parents) {\n let name = node.name;\n if (name === \"undefined\") return;\n for (let i = parents.length - 2; i >= 0; --i) {\n if (name === \"arguments\") {\n if (declaresArguments(parents[i])) {\n return;\n }\n }\n if (hasLocal(parents[i], name)) {\n return;\n }\n if (parents[i].type === \"ViewExpression\") {\n node = parents[i];\n name = `viewof ${node.id.name}`;\n }\n if (parents[i].type === \"MutableExpression\") {\n node = parents[i];\n name = `mutable ${node.id.name}`;\n }\n }\n if (!globalSet.has(name)) {\n if (name === \"arguments\") {\n throw Object.assign(new SyntaxError(`arguments is not allowed`), {node});\n }\n references.push(node);\n }\n }\n\n ancestor(\n ast,\n {\n VariablePattern: identifier,\n Identifier: identifier\n },\n walk\n );\n\n function checkConst(node, parents) {\n if (!node) return;\n switch (node.type) {\n case \"Identifier\":\n case \"VariablePattern\": {\n for (const parent of parents) {\n if (hasLocal(parent, node.name)) {\n return;\n }\n }\n if (parents[parents.length - 2].type === \"MutableExpression\") {\n return;\n }\n throw Object.assign(new SyntaxError(`Assignment to constant variable ${node.name}`), {node});\n }\n case \"ArrayPattern\": {\n for (const element of node.elements) {\n checkConst(element, parents);\n }\n return;\n }\n case \"ObjectPattern\": {\n for (const property of node.properties) {\n checkConst(property, parents);\n }\n return;\n }\n case \"Property\": {\n checkConst(node.value, parents);\n return;\n }\n case \"RestElement\": {\n checkConst(node.argument, parents);\n return;\n }\n }\n }\n\n function checkConstArgument(node, parents) {\n checkConst(node.argument, parents);\n }\n\n function checkConstLeft(node, parents) {\n checkConst(node.left, parents);\n }\n\n ancestor(\n ast,\n {\n AssignmentExpression: checkConstLeft,\n AssignmentPattern: checkConstLeft,\n UpdateExpression: checkConstArgument,\n ForOfStatement: checkConstLeft,\n ForInStatement: checkConstLeft\n },\n walk\n );\n\n return references;\n}\n", "import {make} from \"acorn-walk\";\n\nexport default make({\n Import() {},\n ViewExpression(node, st, c) {\n c(node.id, st, \"Identifier\");\n },\n MutableExpression(node, st, c) {\n c(node.id, st, \"Identifier\");\n }\n});\n", "import {simple} from \"acorn-walk\";\nimport walk from \"./walk.js\";\n\nexport default function findFeatures(cell, featureName) {\n const ast = {type: \"Program\", body: [cell.body]};\n const features = new Map();\n const {references} = cell;\n\n simple(\n ast,\n {\n CallExpression: node => {\n const {callee, arguments: args} = node;\n\n // Ignore function calls that are not references to the feature.\n if (\n callee.type !== \"Identifier\" ||\n callee.name !== featureName ||\n references.indexOf(callee) < 0\n ) return;\n\n // Forbid dynamic calls.\n if (\n args.length !== 1 ||\n !((args[0].type === \"Literal\" && /^['\"]/.test(args[0].raw)) ||\n (args[0].type === \"TemplateLiteral\" && args[0].expressions.length === 0))\n ) {\n throw Object.assign(new SyntaxError(`${featureName} requires a single literal string argument`), {node});\n }\n\n const [arg] = args;\n const name = arg.type === \"Literal\" ? arg.value : arg.quasis[0].value.cooked;\n const location = {start: arg.start, end: arg.end};\n if (features.has(name)) features.get(name).push(location);\n else features.set(name, [location]);\n }\n },\n walk\n );\n\n return features;\n}\n", "import { ancestor, parseCell as ohqParseCell, Cell, Node, walk, AncestorVisitors } from \"./observable-shim.ts\";\nimport { fixRelativeUrl, createFunction, Refs } from \"./kit/index.ts\";\n\nfunction calcRefs(cellAst: Cell, cellStr: string): Refs {\n if (cellAst.references === undefined) return { inputs: [], args: [], patches: [] };\n\n const dedup: { [id: string]: boolean } = {};\n cellAst.references.forEach((r: any) => dedup[cellStr.substring(r.start, r.end)] = true);\n const retVal: Refs = {\n inputs: Object.keys(dedup),\n args: Object.keys(dedup).map(r => r.split(\" \").join(\"_\")),\n patches: []\n };\n const pushPatch = (node: Node, newText: string) => retVal.patches.push({ start: node.start - (cellAst.body?.start ?? 0), end: node.end - (cellAst.body?.start ?? 0), newText });\n if (cellAst.body) {\n ancestor(cellAst.body, {\n Identifier(node) {\n const value = cellStr.substring(node.start, node.end);\n if (dedup[value]) {\n }\n },\n MutableExpression(node: Node) {\n const value = cellStr.substring(node.start, node.end);\n const newText = value.split(\" \").join(\"_\") + \".value\";\n pushPatch(node, newText);\n },\n ViewExpression(node: Node) {\n const value = cellStr.substring(node.start, node.end);\n const newText = value.split(\" \").join(\"_\");\n pushPatch(node, newText);\n },\n ThisExpression(node: Node, ancestors: Node[]) {\n const value = cellStr.substring(node.start, node.end);\n if (value === \"this\" && !ancestors.find(n => n.type === \"FunctionExpression\")) {\n pushPatch(node, \"((this === globalThis || this === globalThis.window)? undefined : this?.valueOf())\");\n }\n }\n } as AncestorVisitors<any>, walk);\n }\n return retVal;\n}\n\nexport interface ParsedCell {\n type: \"import\" | \"viewof\" | \"mutable\" | \"variable\" | \"identifier\"\n}\n\nexport interface ParsedImportCell extends ParsedCell {\n type: \"import\"\n src: string;\n specifiers: { view: boolean, name: string, alias?: string }[];\n injections: { name: string, alias: string }[];\n}\n\nfunction parseImportDeclaration(cellAst: any): ParsedImportCell {\n return {\n type: \"import\",\n src: cellAst.body.source.value,\n specifiers: cellAst.body.specifiers?.map((spec: any) => {\n return {\n view: spec.view,\n name: spec.imported.name,\n alias: (spec.local?.name && spec.imported.name !== spec.local.name) ? spec.local.name : spec.imported.name\n };\n }) ?? [],\n injections: cellAst.body.injections?.map((inj: any) => {\n return {\n name: inj.imported.name,\n alias: inj.local?.name ?? inj.imported.name\n };\n }) ?? [],\n };\n}\n\nexport interface ParsedVariable extends ParsedCell {\n type: \"variable\"\n id?: string,\n inputs: string[],\n func: any,\n}\n\nexport interface ParsedViewCell extends ParsedCell {\n type: \"viewof\",\n variable: ParsedVariable;\n variableValue: ParsedVariable;\n}\n\nfunction parseViewExpression(cellStr: string, cellAst: any, refs: Refs, bodyStr?: string): ParsedViewCell {\n const id = cellAst.id && cellStr.substring(cellAst.id.start, cellAst.id.end);\n return {\n type: \"viewof\",\n variable: {\n type: \"variable\",\n id,\n inputs: refs.inputs,\n func: createFunction(refs, cellAst.async, cellAst.generator, cellAst.body.type === \"BlockStatement\", bodyStr)\n },\n variableValue: {\n type: \"variable\",\n id: cellAst?.id?.id?.name,\n inputs: [\"Generators\", id],\n func: (G: any, _: any) => G.input(_)\n }\n };\n}\n\ninterface ParsedMutableCell extends ParsedCell {\n type: \"mutable\",\n initial: ParsedVariable;\n variable: ParsedVariable;\n variableValue: ParsedVariable;\n}\n\nfunction parseMutableExpression(cellStr: string, cellAst: any, refs: Refs, bodyStr?: string): ParsedMutableCell {\n const id = cellAst.id && cellStr.substring(cellAst.id.start, cellAst.id.end);\n const initialValueId = cellAst?.id?.id?.name;\n const initialId = `initial ${initialValueId}`;\n return {\n type: \"mutable\",\n initial: {\n type: \"variable\",\n id: initialId,\n inputs: refs.inputs,\n func: createFunction(refs, cellAst.async, cellAst.generator, cellAst.body.type === \"BlockStatement\", bodyStr)\n },\n variable: {\n type: \"variable\",\n id,\n inputs: [\"Mutable\", initialId],\n func: (M: any, _: any) => new M(_)\n },\n variableValue: {\n type: \"variable\",\n id: initialValueId,\n inputs: [id],\n func: (_: any) => _.generator\n }\n };\n}\n\ninterface ParsedVariableCell extends ParsedCell {\n type: \"variable\",\n id: string,\n inputs: string[],\n func: any,\n}\n\nfunction parseVariableExpression(cellStr: string, cellAst: any, refs: Refs, bodyStr?: string): ParsedVariableCell {\n return {\n type: \"variable\",\n id: cellAst.id && cellStr.substring(cellAst.id?.start, cellAst.id?.end),\n inputs: refs.inputs,\n func: createFunction(refs, cellAst.async, cellAst.generator, cellAst.body.type === \"BlockStatement\", bodyStr)\n };\n}\n\nexport function parseCell(cellStr: string, baseUrl: string): ParsedImportCell | ParsedViewCell | ParsedMutableCell | ParsedVariableCell {\n const cellAst = ohqParseCell(cellStr);\n let bodyStr = cellAst.body && cellStr.substring(cellAst.body.start, cellAst.body.end);\n switch ((cellAst.body)?.type) {\n case \"ImportDeclaration\":\n return parseImportDeclaration(cellAst);\n case \"ImportExpression\":\n switch (cellAst.body.source.type) {\n case \"Literal\":\n bodyStr = `import(\"${fixRelativeUrl(\"\" + cellAst.body.source.value, baseUrl)}\")`;\n break;\n default:\n console.error(\"Unexpected import value\");\n }\n }\n const refs = calcRefs(cellAst, cellStr);\n switch (cellAst.id?.type) {\n case \"ViewExpression\":\n return parseViewExpression(cellStr, cellAst, refs, bodyStr);\n case \"MutableExpression\":\n return parseMutableExpression(cellStr, cellAst, refs, bodyStr);\n default:\n return parseVariableExpression(cellStr, cellAst, refs, bodyStr);\n }\n}\n\n", "import { ohq } from \"./observable-shim.ts\";\nimport { ParsedImportCell, ParsedVariable } from \"./cst.ts\";\n\nexport class Writer {\n\n protected _files: ohq.File[] = [];\n protected _imports: string[] = [];\n protected _functions: string[] = [];\n protected _defines: string[] = [];\n protected _defineUid = 0;\n protected _functionUid = 0;\n\n constructor() {\n }\n\n toString() {\n return `\\\n${this._imports.join(\"\\n\")}\n\n${this._functions.join(\"\\n\").split(\"\\n) {\").join(\"){\")}\n\nexport default function define(runtime, observer) {\n const main = runtime.module();\n\n function toString() { return this.url; }\n const fileAttachments = new Map([\n ${this._files.map(f => `[\"${f.name}\", { url: new URL(\"${f.url}\"), mimeType: ${JSON.stringify(f.mime_type)}, toString }]`).join(\",\\n \")}\n ]);\n main.builtin(\"FileAttachment\", runtime.fileAttachments(name => fileAttachments.get(name)));\n\n ${this._defines.join(\"\\n \")}\n\n return main;\n}\\n`;\n }\n\n files(files: ohq.File[]) {\n this._files = [...this._files, ...files];\n }\n\n import(url: string) {\n this._imports.push(`import define${++this._defineUid} from \"${url}\"; `);\n }\n\n importDefine(imp: Partial<ParsedImportCell>) {\n const impInjections = imp.injections ?? [];\n const injections = impInjections.map(inj => {\n return inj.name === inj.alias ?\n `\"${inj.name}\"` :\n `{name: \"${inj.name}\", alias: \"${inj.alias}\"}`;\n });\n const derive = impInjections.length ? `.derive([${injections.join(\", \")}], main)` : \"\";\n this._defines.push(`const child${this._defineUid} = runtime.module(define${this._defineUid})${derive};`);\n const impSpecifiers = imp.specifiers ?? [];\n impSpecifiers.forEach(s => {\n this._defines.push(`main.import(\"${s.name}\"${s.alias && s.alias !== s.name ? `, \"${s.alias}\"` : \"\"}, child${this._defineUid}); `);\n });\n }\n\n function(variable: Partial<ParsedVariable>) {\n let id = variable.id ?? `${++this._functionUid}`;\n const idParts = id.split(\" \");\n id = `_${idParts[idParts.length - 1]}`;\n this._functions.push(`${variable.func?.toString()?.replace(\"anonymous\", `${id}`)}`);\n return id;\n }\n\n define(variable: Partial<ParsedVariable>, observable = true, inlineFunc = false, funcId?: string) {\n funcId = funcId ?? variable.id;\n const observe = observable ? `.variable(observer(${variable.id ? JSON.stringify(variable.id) : \"\"}))` : \"\";\n const id = variable.id ? `${JSON.stringify(variable.id)}, ` : \"\";\n const variableInputs = variable.inputs ?? [];\n const inputs = variableInputs.length ? `[${variableInputs.map(i => JSON.stringify(i)).join(\", \")}], ` : \"\";\n const func = inlineFunc ?\n variable.func?.toString() :\n funcId;\n this._defines.push(`main${observe}.define(${id}${inputs}${func});`);\n }\n\n error(msg: string) {\n }\n}\n\n", "import { type Notebook, type Cell, toNotebook, toCell } from \"./kit/index.ts\";\n\nimport type { ohq } from \"./observable-shim.ts\";\nimport { parseCell, splitModule } from \"./observable-shim.ts\";\n\ninterface ParsedOJS {\n ojs: string;\n offset: number;\n inlineMD: boolean;\n cell: any;\n error: any;\n}\n\nexport function encodeBacktick(str: string) {\n return str\n .split(\"`\").join(\"\\\\`\")\n ;\n}\n\nfunction createParsedOJS(ojs: string, offset: number, inlineMD: boolean): ParsedOJS {\n let cell;\n let error;\n try {\n cell = parseCell(ojs);\n } catch (e) {\n error = e;\n }\n return {\n ojs,\n offset,\n inlineMD,\n cell,\n error\n };\n}\n\nfunction splitOmd(_: string): ParsedOJS[] {\n const retVal: ParsedOJS[] = [];\n // Load Markdown ---\n const re = /(```(?:\\s|\\S)[\\s\\S]*?```)/g;\n let prevOffset = 0;\n let match = re.exec(_);\n while (match !== null) {\n if (match.index > prevOffset) {\n retVal.push(createParsedOJS(_.substring(prevOffset, match.index), prevOffset, true));\n }\n\n const outer = match[0];\n if (outer.indexOf(\"``` \") === 0 || outer.indexOf(\"```\\n\") === 0 || outer.indexOf(\"```\\r\\n\") === 0) {\n const prefixLen = 3;\n const inner = outer.substring(prefixLen, outer.length - prefixLen);\n retVal.push(createParsedOJS(inner, match.index + prefixLen, false));\n } else {\n retVal.push(createParsedOJS(outer, match.index, true));\n }\n\n prevOffset = match.index + match[0].length;\n match = re.exec(_);\n }\n if (_.length > prevOffset) {\n retVal.push(createParsedOJS(_.substring(prevOffset, _.length), prevOffset, true));\n }\n return retVal;\n}\n\nexport function notebook2ojs(_: string): ParsedOJS[] {\n const parsed: ohq.Notebook = JSON.parse(_);\n return parsed.nodes.map(node => createParsedOJS(node.value, 0, node.mode === \"md\"));\n}\n\nexport function ojs2notebook(ojs: string): ohq.Notebook {\n const cells = splitModule(ojs);\n return {\n files: [],\n nodes: cells.map((cell, idx) => {\n return {\n id: idx,\n mode: \"js\",\n value: cell.text,\n start: cell.start,\n end: cell.end\n };\n })\n } as ohq.Notebook;\n}\n\nexport function ojs2notebookKit(ojs: string): Notebook {\n const cells: Cell[] = splitModule(ojs).map((cell, idx) => {\n return toCell({\n id: idx,\n mode: \"ojs\",\n value: cell.text\n });\n });\n return toNotebook({ cells });\n}\n\nexport function omd2notebook(omd: string): ohq.Notebook {\n const cells = splitOmd(omd);\n return {\n files: [],\n nodes: cells.map((cell, idx) => {\n return {\n id: idx,\n mode: cell.inlineMD ? \"md\" : \"js\",\n value: cell.ojs,\n start: cell.offset,\n end: cell.offset + cell.ojs.length\n };\n })\n } as ohq.Notebook;\n}\n\nexport function omd2notebookKit(omd: string): Notebook {\n const cells: Cell[] = [];\n splitOmd(omd).forEach((cell) => {\n if (!cell.inlineMD) {\n splitModule(cell.ojs).forEach((subCell) => {\n cells.push(toCell({\n id: cells.length + 1,\n mode: \"ojs\",\n value: subCell.text\n }));\n });\n } else {\n cells.push(toCell({\n id: cells.length + 1,\n mode: \"md\",\n value: cell.ojs\n }));\n }\n });\n return toNotebook({ cells });\n}\n\nexport function fetchEx(url: string, proxyPrefix = \"https://api.codetabs.com/v1/proxy/?quest=\", proxyPostfix = \"\") {\n const matches = url.match(/^(?:https?:\\/\\/)?(?:[^@\\n]+@)?(?:www\\.)?([^:\\/\\n?]+)/img);\n if (!matches || matches.length === 0) {\n throw new Error(`Invalid URL: ${url}`);\n }\n return fetch(url, { headers: { origin: matches[0], referer: url } }).then(response => {\n if (response.ok) return response;\n throw new Error(\"CORS?\");\n }).catch(e => {\n url = `${proxyPrefix}${url}${proxyPostfix}`;\n return fetch(url, { headers: { origin: matches[0], referer: url } });\n });\n}\n\nexport function download(impUrl: string, proxyPrefix?: string, proxyPostfix?: string): Promise<ohq.Notebook> {\n const isShared = impUrl.indexOf(\"https://observablehq.com/d\") === 0;\n return fetchEx(impUrl.replace(`https://observablehq.com/${isShared ? \"d/\" : \"\"}`, \"https://api.observablehq.com/document/\"), proxyPrefix, proxyPostfix)\n .then(r => r.json())\n ;\n}\n"],
"mappings": "mbAAA,IAAAA,GAAA,GAAAC,GAAAD,GAAA,0BAAAE,EAAA,WAAAC,EAAA,YAAAC,EAAA,gBAAAC,EAAA,oBAAAC,EAAA,sBAAAC,EAAA,mBAAAC,EAAA,aAAAC,EAAA,mBAAAC,EAAA,kBAAAC,GAAA,kBAAAC,GAAA,kBAAAC,GAAA,mBAAAC,EAAA,aAAAC,EAAA,kBAAAC,GAAA,qBAAAC,EAAA,iBAAAC,EAAA,oBAAAC,EAAA,iBAAAC,EAAA,oBAAAC,EAAA,iBAAAC,GAAA,iEAAAC,GAAAvB,IAAA,IAAAwB,G