UNPKG

@hpcc-js/observablehq-compiler

Version:
137 lines (123 loc) 4.39 kB
// Compare with ../../../node_modules/@observablehq/parser/src/parse.js import { getLineInfo, tokTypes, Statement, ModuleDeclaration, Expression as ExpressionBase, Node } from "acorn"; import { ancestor, RecursiveVisitors, AncestorVisitors } from "acorn-walk"; import { CellParser, parseCell as ohqParseCell, walk as ohqWalk } from "@observablehq/parser"; import defaultGlobals from "../../../node_modules/@observablehq/parser/src/globals.js"; import findReferences from "../../../node_modules/@observablehq/parser/src/references.js"; import findFeatures from "../../../node_modules/@observablehq/parser/src/features.js"; export interface MutableExpression extends Node { type: "MutableExpression" } export interface ViewExpression extends Node { type: "ViewExpression" } export type Expression = ExpressionBase | MutableExpression | ViewExpression; // Find references. // Check for illegal references to arguments. // Check for illegal assignments to global references. function parseReferences(cell, input, globals = defaultGlobals) { if (!cell.body) { cell.references = []; } else if (cell.body.type === "ImportDeclaration") { // This is correct?!? cell.references = cell.body.specifiers ? cell.body.specifiers.map(i => i.imported) : []; } else { try { cell.references = findReferences(cell, globals); } catch (error: any) { if (error.node) { const loc = getLineInfo(input, error.node.start); error.message += ` (${loc.line}:${loc.column})`; error.pos = error.node.start; error.loc = loc; delete error.node; } throw error; } } return cell; } // Find features: file attachments, secrets, database clients. // Check for illegal references to arguments. // Check for illegal assignments to global references. function parseFeatures(cell, input) { if (cell.body && cell.body.type !== "ImportDeclaration") { try { cell.fileAttachments = findFeatures(cell, "FileAttachment"); cell.databaseClients = findFeatures(cell, "DatabaseClient"); cell.secrets = findFeatures(cell, "Secret"); } catch (error: any) { if (error.node) { const loc = getLineInfo(input, error.node.start); error.message += ` (${loc.line}:${loc.column})`; error.pos = error.node.start; error.loc = loc; delete error.node; } throw error; } } else { cell.fileAttachments = new Map(); cell.databaseClients = new Map(); cell.secrets = new Map(); } return cell; } class ModuleParser extends CellParser { parseTopLevel(node: { cells?: Cell[] }) { if (!node.cells) node.cells = []; // @ts-ignore while (this.type !== tokTypes.eof) { // @ts-ignore const cell: Cell = this.parseCell(this.startNode()); // @ts-ignore cell.input = this.input; node.cells.push(cell); } // @ts-ignore this.next(); // @ts-ignore return this.finishNode(node, "Program"); } } // @ts-ignore export function parseModule(input, { globals }: { globals: any } = {}) { // @ts-ignore const program = ModuleParser.parse(input, { ecmaVersion: 2020 }); for (const cell of program.cells) { parseReferences(cell, input, globals); parseFeatures(cell, input); } return program; } export interface Cell extends Node { type: "Cell"; id: Expression; text: string; body?: Statement | ModuleDeclaration | Expression; references: unknown[]; async: boolean; generator: boolean; strict: boolean; } export function splitModule(input: string): Cell[] { return (ModuleParser as any) .parse(input, { ecmaVersion: "latest" }) .cells.map((cell: any) => ({ type: "Cell", text: input.substring(cell.start, cell.end), start: cell.start, end: cell.end })); } export { type Node, ancestor, type AncestorVisitors }; export function parseCell(input: string): Cell { return ohqParseCell(input); } export const walk: RecursiveVisitors<any> = ohqWalk;