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

lave

v1.1.10

Published

eval in reverse: stringifying all the stuff that JSON.stringify won't

Downloads

744

Readme

lave Build Status

lave is eval in reverse; it does for JavaScript what JSON.stringify does for JSON, turning an arbitrary object in memory into the expression, function, or ES6 module needed to create it.

Why not just use JSON.stringify?

JSON is great data transport, but can only handle a subset of the objects expressible in a JavaScript runtime. This usually results in lossy serializations at best, and TypeError: Converting circular structure to JSON at worst. While we can get around such issues by writing JavaScript code to parse this JSON back into the structures we want, now we have to ship that code out of band, which can be a headache.

Instead of writing a parser for a new language that can represent arbitrary JavaScript runtime values, I built lave to use the best language for the job: JavaScript itself. This allows it to handle the following structures that JSON can't.

Type | JavaScript | JSON.stringify | lave ------------------- | --------------------- | -------------------------------------- | ------------------------- Circular references | a={}; a.self=a | :x: TypeError | :white_check_mark: var a={};a.self=a;a Repeated references | a={}; [a, a] | :warning: [{}, {}] | :white_check_mark: var a={};[a,a] Global object | global | :x: TypeError | :white_check_mark: (0,eval)('this') ? Built-in objects | Array.prototype | :warning: [] | :white_check_mark: Array.prototype Boxed primitives | Object('abc') | :warning: "abc" | :white_check_mark: Object('abc') Functions | [function(){}] | :warning: [null] | :white_check_mark: [function(){}] Dates | new Date(1e12) | :warning: "2001-09-09T01:46:40.000Z" | :white_check_mark: new Date(1000000000000) NaN | NaN | :warning: null | :white_check_mark: NaN Infinity | Infinity | :warning: null | :white_check_mark: Infinity Sets and Maps | new Set([1,2,3]) | :warning: {} | :white_check_mark: new Set([1,2,3]) Sparse arrays | a=[]; a[2]=0; a | :warning: [null,null,0] | :white_check_mark: var a=[];a[2]=0;a Object properties | a=[0,1]; a.b=2; a | :warning: [0,1] | :white_check_mark: var a=[0,1];a.b=2;a Custom prototypes | Object.create(null) | :warning: {} | :white_check_mark: Object.create(null)

Keep in mind that there are some things that not even lave can stringify, such as function closures, or built-in native functions.

Example

This command...

node << EOF
  var generate = require('escodegen').generate
  var lave = require('lave')

  var a = [function(){}, new Date, new Buffer('A'), global]
  a.splice(2, 0, a)

  var js = lave(a, {generate, format: 'module'})
  console.log(js)
EOF

...outputs the following JavaScript:

var a = [
    function (){},
    new Date(1456522677247),
    null,
    Buffer('QQ==', 'base64'),
    (0, eval)('this')
];
a[2] = a;
export default a;

When would I want to use this?

  • To transport relational data. Since JSON can only represent hierarchical trees, attempts to serialize a graph require you to define a schema and ship code that reifies the relationship between objects at runtime. For example, if you have list of orders and a list of customers, and each order has a customerId property, you need to write and ship code that turns this property into one that references the customer object directly.

  • To colocate data and dependent logic. The past few years have seen a long overdue rethink of common best practices for web developers. From markup and logic in React components to styles and markup in Radium, we've improved developer productivity by slicing concerns vertically (many concerns per component) instead of horizontally (many components per concern). Having the ability to ship tightly coupled logic and data in one file means fewer moving parts during deployment.

  • To avoid runtime dependencies. There are several libraries that give you the ability to serialize and parse graph data, but most of add a parser dependency to your recipients (in the case of JavaScript, usually a browser). Since lave requires only a JavaScript parser, you do not need to incorporate a runtime library to use it. If you prefer the safety of parsing without evaluation and don't mind the dependency, consider using something like @benjamn's excellent arson.

How does lave work?

lave uses all of the syntax available in JavaScript to build the most concise representation of an object, such as by preferring literals ([1,2]) over assignment (var a=[];a[0]=1;a[1]=2). Here's how it works:

  • lave traverses the global object to cache paths for any host object. So if your structure contains [].slice, lave knows that you're looking for Array.prototype.slice, and uses that path in its place.

  • lave then traverses your object, converting each value that it finds into an abstract syntax graph. It never converts the same object twice; instead it caches all nodes it creates and reuses them any time their corresponding value appears.

  • lave then finds all expressions referenced more than once, and for each one, pulls the expression into a variable declaration, and replaces everywhere that it occurs with its corresponding identifier. This process of removing dipoles converts the abstract syntax graph into a serializable abstract syntax tree.

  • Finally, lave adds any assignment statements needed to fulfil circular references in your original graph, and then returns the expression corresponding to your original root value.

Installation

Please run the following command to install lave:

$ npm install lave

The library has been tested on Node 4.x and 5.x.

API

ast = lave(object, [options])

By default, lave takes an object and returns an abstract syntax tree (AST) representing the generated JavaScript. Any of the following options can also be specified:

  • generate: A function that takes an ESTree AST and returns JavaScript code, such as through escodegen or babel-generator. If this is omitted, an AST will be returned, with any functions in the original object serialized using toString, and wrapped in an eval call. If this is specified, a JavaScript string will be returned.
  • format: A string specifying the type of code to output, from the following:
    • expression (default): Returns code in which the last statement is result expression, such as var a={};[a, a];. This is useful when the code is evaluated with eval.
    • function: Returns the code as a function expression, such as (function(){var a={};return[a, a]}). This is useful for inlining as an expression without polluting scope.
    • module: Returns the code as an ES6 module export, such as var a={};export default[a, a];. This is currently useful for integration with a module build process, such as Rollup or Babel transforms.

Addenda

  • Many thanks to Jamen Marz for graciously providing the lave name on npm.
  • Right before publishing this, I discovered that uneval was a thing.