npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@observablehq/parser

v6.1.0

Published

An Acorn parser for Observable JavaScript

Downloads

8,464

Readme

@observablehq/parser

To parse a cell:

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 is a type of expression):

1 + 2
{
  "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 is a BlockStatement):

{
  return 1 + 2;
}
{
  "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 is null):

{
  "type": "Cell",
  "id": null,
  "async": false,
  "generator": false,
  "body": null
}

A named expression cell (where cell.id is an Identifier):

foo = 42
{
  "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 is an Identifier):

foo = {
  return 42;
}
{
  "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 is true):

2 * await value
{
  "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 is true):

yield* [1, 2, 3]
{
  "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 is a ViewExpression):

viewof x = DOM.range()
{
  "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 contains a ViewExpression):

viewof x.tagName
{
  "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 is an ImportDeclaration):

import {foo} from "module"
{
  "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 is true):

import {viewof foo} from "module"
{
  "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 is present):

import {chart} with {sales as data} from "@mbostock/d3-bar-chart"
{
  "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]) <>

Returns a cell.

# peekId(input) <>

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.