GithubHelp home page GithubHelp logo

blakeembrey / javascript-stringify Goto Github PK

View Code? Open in Web Editor NEW
138.0 5.0 16.0 357 KB

Stringify is to `eval` as `JSON.stringify` is to `JSON.parse`

License: MIT License

TypeScript 100.00%
json javascript-stringify stringify javascript serialize code

javascript-stringify's Introduction

JavaScript Stringify

NPM version NPM downloads Build status Build coverage

Stringify is to eval as JSON.stringify is to JSON.parse.

Installation

npm install javascript-stringify --save

Usage

import { stringify } from "javascript-stringify";

The API is similar JSON.stringify:

  • value The value to convert to a string
  • replacer A function that alters the behavior of the stringification process
  • space A string or number that's used to insert white space into the output for readability purposes
  • options
    • maxDepth (number, default: 100) The maximum depth of values to stringify
    • maxValues (number, default: 100000) The maximum number of values to stringify
    • references (boolean, default: false) Restore circular/repeated references in the object (uses IIFE)
    • skipUndefinedProperties (boolean, default: false) Omits undefined properties instead of restoring as undefined

Examples

stringify({}); // "{}"
stringify(true); // "true"
stringify("foo"); // "'foo'"

stringify({ x: 5, y: 6 }); // "{x:5,y:6}"
stringify([1, 2, 3, "string"]); // "[1,2,3,'string']"

stringify({ a: { b: { c: 1 } } }, null, null, { maxDepth: 2 }); // "{a:{b:{}}}"

/**
 * Invalid key names are automatically stringified.
 */
stringify({ "some-key": 10 }); // "{'some-key':10}"

/**
 * Some object types and values can remain identical.
 */
stringify([/.+/gi, new Number(10), new Date()]); // "[/.+/gi,new Number(10),new Date(1406623295732)]"

/**
 * Unknown or circular references are removed.
 */
var obj = { x: 10 };
obj.circular = obj;

stringify(obj); // "{x:10}"
stringify(obj, null, null, { references: true }); // "(function(){var x={x:10};x.circular=x;return x;}())"

/**
 * Specify indentation - just like `JSON.stringify`.
 */
stringify({ a: 2 }, null, " "); // "{\n a: 2\n}"
stringify({ uno: 1, dos: 2 }, null, "\t"); // "{\n\tuno: 1,\n\tdos: 2\n}"

/**
 * Add custom replacer behaviour - like double quoted strings.
 */
stringify(["test", "string"], function (value, indent, stringify) {
  if (typeof value === "string") {
    return '"' + value.replace(/"/g, '\\"') + '"';
  }

  return stringify(value);
});
//=> '["test","string"]'

Formatting

You can use your own code formatter on the result of javascript-stringify. Here is an example using eslint:

const { CLIEngine } = require("eslint");
const { stringify } = require("javascript-stringify");

const { APP_ROOT_PATH, ESLINTRC_FILE_PATH } = require("./constants");

const ESLINT_CLI = new CLIEngine({
  fix: true,
  cwd: APP_ROOT_PATH,
  configFile: ESLINTRC_FILE_PATH,
});

module.exports = (objectToStringify) => {
  return ESLINT_CLI.executeOnText(stringify(objectToStringify)).results[0]
    .output;
};

License

MIT

javascript-stringify's People

Contributors

blakeembrey avatar dependabot[bot] avatar dubzzz avatar eemeli avatar greenkeeper[bot] avatar pkit avatar quilicicf avatar rhendric avatar skateborden avatar zalmoxisus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

javascript-stringify's Issues

allow identifier when maxDepth/maxValues is hit

currently when using maxDepth/ maxValue, there is no way to know if a item is an empty object/undefined or if its been truncated by this param, I propose adding either a custom text param, or at least a flag to set default text variables to tell the client that the data is truncated and not in fact empty/undefined

max depth example

//currently
const obj = {"hello":{"world":{"i am":"an Object"}}}
stringify(obj,null,null,{maxDepth:2}); 
//outputs: '{hello:{world:{}}}'

// unknown if depth has been reached or if "world" key just goes to an empty object

//feature with same object
const str = "[JSON max depth has been reached]"
stringify(obj,null,null,{maxDepth:2, maxDepthText: str}); 
//outputs:'{hello:{world:[JSON max depth has been reached]}}'

max value example

//currently
const obj2 = {"hello":{"world":{"i": "am","an": "Object"}, test1: true, test2:false}}
stringify(obj2,null,null,{maxValues:3}); 
//outputs:'{hello:{world:{}}}'

// unknown if value limt has been reached or if the value for the  "hello" key just has an object with a "world" key

//feature with same object
const str = "max values for object reached"
stringify(obj,null,null,{maxValues:3, maxValuesText: str}); 
//outputs: '{hello:{world:{"..." : "max values for object reached" }, "...": "max values for object reached"}}'
// notice not "..." for both test1 and test2, just 1 "..." for the object that is missing keys

Feature idea: serialize from required CJS modules

Hi Blake,

I have a feature idea: serializing cross-file. Even with the references option enabled, requires/imports evaluate to their references (not the fully-serialized data).

Worse comes to worse, one could just bundle their code and then execute this lib's stringify fn. Although, ideally, this build step could go away.

Here's a simple example of where this might come in handy.

Identifying the difference between node & browser environments seems to be a reason not to implement this... although I'm sure there're good workarounds.

Anyways, please let me know your thoughts on this. Would love to discuss implementations & work on a PR :)

Add support for dangling commas

Hi,

Thanks for developing this, it is useful.

It would be super nice if you could add dangling commas as an option though. Would make it pass linting in cases where it's required.

I currently proceed like so:

stringify(object, null, 2)
    .replace(/([^,;{[])\n/g, '$1,\n'); // Adds dangling commas

But that's a bit sub-optimal.

Properly escape control characters

var stringify = require('javascript-stringify');
console.log(stringify({ x: 'hello\nworld\'' }));

this results in:

{x:\'hello\nworld\\\'\'}

This is not valid javascript (the newline). This means it can not be evaled.
I would expect:

{x:\'hello\\nworld\\\'\'}

for now I work around this issue by doing the following:

var escapable = /[\\\"\'\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
var meta = {
  '\b': '\\b',
  '\t': '\\t',
  '\n': '\\n',
  '\f': '\\f',
  '\r': '\\r',
  '\"': '\\\"',
  '\'': '\\\'',
  '\\': '\\\\'
};
var valString = jsStringify(value, function (value, indent, stringify) {
  if (typeof value === 'string') {
    return '\'' + value.replace(escapable, function (a) {
        var c = meta[a];
        return typeof c === 'string'
            ? c
            : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
    }) + '\'';
  }

  return stringify(value);
});

(copied and slightly modified from https://github.com/douglascrockford/JSON-js/blob/master/json2.js)

But I really think this should be standard behavior.

Support for async function?

Hi many thanks for authoring this library, honestly never thought it is possible to stringify js code before. :)

I noticed that this library does not support async functions, which is supported in nodejs LTS currently.

I might try to work out a PR if such enhancement is suitable, though I do not fully understand the logic yet so it might need some time to figure out.

Repeated references in Maps & Sets produce broken output

Briefly, this happens:

const obj = {}
const map = new Map([['a', obj], ['b', obj]])
const ok = stringify(map)
// "new Map([['a',{}],['b',{}]])"
const bad = stringify(map, null, null, { references: true })
// "(function(){var x=new Map([['a',{}],['b',undefined]]);x[1][1]=x[0][1];return x;}())"
eval(bad)
// Uncaught TypeError: Cannot read property '1' of undefined

The problem here is that the object type isn't tracked, and therefore isn't taken into account by stringifyPath. The . and [] forms work fine for arrays and objects, but don't for Maps and Sets. The path in those cases is referring to the Array.from() constructions, rather than the resulting objects.

Add support for getters & setters in objects

Currently, objects with getters and setters are not handled correctly:

const input = {
    _foo: null,
    get foo() {
        return this._foo;
    },
    set foo(value) {
        this._foo = value;
    }
};

const output = "{_foo:null,foo:null}";

This problem can be solved by adding an extra case to the object stringification logic that checks for the given descriptors:

const descriptor = Object.getOwnPropertyDescriptor(obj, key);
if (descriptor && (descriptor.get || descriptor.set)) { ... }

I made a demo in a forked repo with this commit demonstrating the approach.
Sadly, I didn't have time to figure out all of the lexer logic on the go so it isn't clean enough for a flat out PR.

javascript-parse (create object from string generated with javascript-stringify)

Just wanted to A) see if there is an existing way to do this without being too hacky (see hacky solution below) and if not, B) suggest the feature or a supplementary module to do parsing of javascript-stringified output so that we can create an object from a string generated with javascript-stringify.

Regarding potential existing way, I tried this lib but does not seem to be able to parse an object output with javascript-stringify

https://github.com/greenpioneersolutions/auto-parse

(note for my test I wrote the javascript-stringify output to a file and then performed a fs.readFile(objString, 'utf8') on it and then attempted to read the resulting string with auto-parse)

The way that I found which actually works is a bit hacky and requires Node:

you can write the javascript-stringify output to a string, append module.exports=, write to disk, and perform a require on the resulting file.

related:
#32

stringifying an error is funny

The following:

import { stringify } from 'javascript-stringify'
stringify(new Error('problem'))

stringifies like so:
"new Error('problem')"

I was hoping it would stringify something more like:
'{"message":"problem","stack":"Error: hi\nat <anonymous>:1:5 [<root>]","name":"Error"}'

I overcame this using custom replacers, which might be the only real solution seeing as how widely used this package is:

stringify(error , (obj) => {
  if (obj instanceof Error) {
    return stringify({
      message: obj.message,
      stack: obj.stack,
      name: obj.name,
    })
  } else {
    return stringify(obj)
  }
}, null, { references: true })

Restore properties on a function

Currently it's just stringifying the function - I should really be using Function.prototype.toString and restoring the function properties though (since a function is just another object).

Edit: Also for RegExp.

Replacer doesn't work for nested values

2.0.0 has a regression compared to 1.6.0. The replacer function doesn't seem to work on nested values, only on root-level ones.

Reproduction

This example code doesn't produce the desired output:

import { stringify } from "javascript-stringify";

function makeRaw(str) {
  const fn = () => {};
  fn.__expression = str;
  return fn;
}

function str(value) {
  return stringify(
    value,
    (val, indent, stringify) => {
      if (val && val.__expression) {
        return val.__expression;
      }
      return stringify(val);
    },
    2
  );
}

console.log(str({
  "no-console": makeRaw(`process.env.NODE_ENV === 'production' ? 'error' : 'off'`),
  "no-debugger": makeRaw(`process.env.NODE_ENV === 'production' ? 'error' : 'off'`)
}))

It outputs () => {} instead of process.env.NODE_ENV === 'production' ? 'error' : 'off' for example.

Stringify does not JSON stringify

If you stringify an object it will not JSON stringify it:

s = require('javascript-stringify')

console.log(JSON.stringify({hi:'howdy'}))
console.log(s.stringify({hi:'howdy'}))

outputs:

{"hi":"howdy"}
{hi:'howdy'}

This means it is not a real stringifyer. it is a make human readable stringifyer. If you try to run JSON.parse(s.stringify({hi:'howdy'})) it will throw an error Unexpected token h in JSON at position 0

Handle different error types

Currently, all error types seem to be represented as new Error(<message>). Other error types like SyntaxError, ReferenceError etc, are not serialized any differently

Suggestion:

stringify(new SyntaxError('syntax')); // "new SyntaxError(syntax')"
stringify(new ReferenceError('syntax')); // "new ReferenceError(syntax')"
...

It should be possible to do this using Error.prototype.name with the caveat that it can be overridden to an empty string, which should probably be handled

Support for Deno?

Hi there, this is a cool library but does not have built-in support for Deno. I've forked the repo and made minor changes to get it to work on Deno. Any interest in rolling in Deno support to your master branch?

If you're interested, Denoify is a tool that could make it easier?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.