https://github.com/observablehq/parser
The Observable parser.
https://github.com/observablehq/parser
Last synced: 7 months ago
JSON representation
The Observable parser.
- Host: GitHub
- URL: https://github.com/observablehq/parser
- Owner: observablehq
- License: isc
- Created: 2017-12-28T22:25:50.000Z (almost 8 years ago)
- Default Branch: main
- Last Pushed: 2024-12-11T22:20:37.000Z (about 1 year ago)
- Last Synced: 2025-05-15T20:02:41.401Z (7 months ago)
- Language: JavaScript
- Homepage:
- Size: 848 KB
- Stars: 179
- Watchers: 12
- Forks: 28
- Open Issues: 7
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# @observablehq/parser
To parse a cell:
```js
import {parseCell} from "@observablehq/parser";
const cell = parseCell(`hello = "world"`);
```
## Examples
(In these examples, the *node*.start and *node*.end indexes into the *input* string are not shown for brevity. If *options*.locations is true, *node*.loc will also be populated with the line and column numbers.)
An expression cell (where [*cell*.body](#cell_body) is a type of expression):
```js
1 + 2
```
```json
{
"type": "Cell",
"id": null,
"async": false,
"generator": false,
"body": {
"type": "BinaryExpression",
"left": {
"type": "Literal",
"value": 1,
"raw": "1"
},
"operator": "+",
"right": {
"type": "Literal",
"value": 2,
"raw": "2"
}
}
}
```
A block cell (where [*cell*.body](#cell_body) is a BlockStatement):
```js
{
return 1 + 2;
}
```
```json
{
"type": "Cell",
"id": null,
"async": false,
"generator": false,
"body": {
"type": "BlockStatement",
"body": [
{
"type": "ReturnStatement",
"argument": {
"type": "BinaryExpression",
"left": {
"type": "Literal",
"value": 1,
"raw": "1"
},
"operator": "+",
"right": {
"type": "Literal",
"value": 2,
"raw": "2"
}
}
}
]
}
}
```
An empty cell (where [*cell*.body](#cell_body) is null):
```js
```
```json
{
"type": "Cell",
"id": null,
"async": false,
"generator": false,
"body": null
}
```
A named expression cell (where [*cell*.id](#cell_id) is an Identifier):
```js
foo = 42
```
```json
{
"type": "Cell",
"id": {
"type": "Identifier",
"name": "foo"
},
"async": false,
"generator": false,
"body": {
"type": "Literal",
"value": 42,
"raw": "42"
}
}
```
A named block cell (where [*cell*.id](#cell_id) is an Identifier):
```js
foo = {
return 42;
}
```
```json
{
"type": "Cell",
"id": {
"type": "Identifier",
"name": "foo"
},
"async": false,
"generator": false,
"body": {
"type": "BlockStatement",
"body": [
{
"type": "ReturnStatement",
"argument": {
"type": "Literal",
"value": 42,
"raw": "42"
}
}
]
}
}
```
An asynchronous expression cell (where [*cell*.async](#cell_async) is true):
```js
2 * await value
```
```json
{
"type": "Cell",
"id": null,
"async": true,
"generator": false,
"body": {
"type": "BinaryExpression",
"left": {
"type": "Literal",
"value": 2,
"raw": "2"
},
"operator": "*",
"right": {
"type": "AwaitExpression",
"argument": {
"type": "Identifier",
"name": "value"
}
}
}
}
```
A generator expression cell (where [*cell*.generator](#cell_generator) is true):
```js
yield* [1, 2, 3]
```
```json
{
"type": "Cell",
"id": null,
"async": false,
"generator": true,
"body": {
"type": "YieldExpression",
"delegate": true,
"argument": {
"type": "ArrayExpression",
"elements": [
{
"type": "Literal",
"value": 1,
"raw": "1"
},
{
"type": "Literal",
"value": 2,
"raw": "2"
},
{
"type": "Literal",
"value": 3,
"raw": "3"
}
]
}
}
}
```
A viewof expression cell (where [*cell*.id](#cell_id) is a [ViewExpression](#viewexpression)):
```js
viewof x = DOM.range()
```
```json
{
"type": "Cell",
"id": {
"type": "ViewExpression",
"id": {
"type": "Identifier",
"name": "x"
}
},
"async": false,
"generator": false,
"body": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"object": {
"type": "Identifier",
"name": "DOM"
},
"property": {
"type": "Identifier",
"name": "range"
},
"computed": false
},
"arguments": []
}
}
```
A viewof reference within an expression cell (where [*cell*.body](#cell_body) contains a [ViewExpression](#viewexpression)):
```js
viewof x.tagName
```
```json
{
"type": "Cell",
"id": null,
"async": false,
"generator": false,
"body": {
"type": "MemberExpression",
"object": {
"type": "ViewExpression",
"id": {
"type": "Identifier",
"name": "x"
}
},
"property": {
"type": "Identifier",
"name": "tagName"
},
"computed": false
}
}
```
An import cell (where [*cell*.body](#cell_body) is an [ImportDeclaration](#importdeclaration)):
```js
import {foo} from "module"
```
```json
{
"type": "Cell",
"id": null,
"async": false,
"generator": false,
"body": {
"type": "ImportDeclaration",
"specifiers": [
{
"type": "ImportSpecifier",
"view": false,
"imported": {
"type": "Identifier",
"name": "foo"
},
"local": {
"type": "Identifier",
"name": "foo"
}
}
],
"source": {
"type": "Literal",
"value": "module",
"raw": "\"module\""
}
}
}
```
Importing a view (where [*specifier*.view](#specifier_view) is true):
```js
import {viewof foo} from "module"
```
```json
{
"type": "Cell",
"id": null,
"async": false,
"generator": false,
"body": {
"type": "ImportDeclaration",
"specifiers": [
{
"type": "ImportSpecifier",
"view": true,
"imported": {
"type": "Identifier",
"name": "foo"
},
"local": {
"type": "Identifier",
"name": "foo"
}
}
],
"source": {
"type": "Literal",
"value": "module",
"raw": "\"module\""
}
}
}
```
Importing a view imports both the view symbol (`viewof foo`) and the value symbol (`foo`). Likewise, if the specified view is renamed during import (*e.g.*, `viewof foo as bar`), both the view symbol and the value symbol are renamed (*e.g.*, `viewof bar` and `bar`).
Importing with injection (where [*declaration*.injections](#declaration_injections) is present):
```js
import {chart} with {sales as data} from "@mbostock/d3-bar-chart"
```
```json
{
"type": "Cell",
"id": null,
"async": false,
"generator": false,
"body": {
"type": "ImportDeclaration",
"specifiers": [
{
"type": "ImportSpecifier",
"view": false,
"imported": {
"type": "Identifier",
"name": "chart"
},
"local": {
"type": "Identifier",
"name": "chart"
}
}
],
"injections": [
{
"type": "ImportSpecifier",
"view": false,
"imported": {
"type": "Identifier",
"name": "sales"
},
"local": {
"type": "Identifier",
"name": "data"
}
}
],
"source": {
"type": "Literal",
"value": "@mbostock/d3-bar-chart",
"raw": "\"@mbostock/d3-bar-chart\""
}
}
}
```
For an injection, *specifier*.imported and *specifier*.local are reversed compared to a normal specifier: they are from the perspective of the imported module rather than the importing module. So in the example above, the importing module’s variable *sales* is injected into the imported module (the chart), replacing the variable *data*.
Injecting a view injects both the view symbol (`viewof foo`) and the value symbol (`foo`). Likewise, if the specified view is renamed during injection (*e.g.*, `viewof foo as bar`), both the view symbol and the value symbol are renamed (*e.g.*, `viewof bar` and `bar`).
## API Reference
# parseCell(input[, options]) [<>](https://github.com/observablehq/parser/blob/main/src/parse.js "Source")
Returns a [cell](#cell).
# peekId(input) [<>](https://github.com/observablehq/parser/blob/main/src/peek.js "Source")
Tries to find the ID of a cell given a snippet of its contents, and returns it as a string if found.
### Cell
# cell.id
The name of the cell: null if the cell is anonymous; otherwise an Identifier or a ViewExpression.
# cell.body
The body of the cell: null for an empty cell; an ImportDeclaration for an import cell; otherwise a BlockStatement or an expression node.
# cell.async
A boolean indicating whether the cell body is asynchronous (*i.e.*, whether it contains an `await` statement). False for import and empty cells.
# cell.generator
A boolean indicating whether the cell body is a generator (*i.e.*, whether it contains a `yield` statement). False for import and empty cells.
### ViewExpression
# view.id
The view identifier: an Identifier.
### ImportDeclaration
# declaration.injections
An array of ImportSpecifier nodes, if the import declaration has a `with` clause, and otherwise null.
### ImportSpecifier
# specifier.view
A boolean indicating whether the import specifies a view.