Launch Week Day 5: Introducing Reachability for PHP.Learn More
Socket
Book a DemoSign in
Socket

javascript-stringify

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

javascript-stringify - npm Package Compare versions

Comparing version
1.6.0
to
2.0.0
+5
dist/array.d.ts
import { Next } from "./types";
/**
* Stringify an array of values.
*/
export declare function arrayToString(array: any[], space: string, next: Next): string;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Stringify an array of values.
*/
function arrayToString(array, space, next) {
// Map array values to their stringified values with correct indentation.
const values = array
.map(function (value, index) {
const result = next(value, index);
if (result === undefined)
return String(result);
return space + result.split("\n").join(`\n${space}`);
})
.join(space ? ",\n" : ",");
// Wrap the array in newlines if we have indentation set.
if (space && values) {
return "[\n" + values + "\n]";
}
return "[" + values + "]";
}
exports.arrayToString = arrayToString;
//# sourceMappingURL=array.js.map
{"version":3,"file":"array.js","sourceRoot":"","sources":["../src/array.ts"],"names":[],"mappings":";;AAEA;;GAEG;AACH,SAAgB,aAAa,CAAC,KAAY,EAAE,KAAa,EAAE,IAAU;IACnE,yEAAyE;IACzE,MAAM,MAAM,GAAG,KAAK;SACjB,GAAG,CAAC,UAAS,KAAK,EAAE,KAAK;QACxB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAElC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;QAEhD,OAAO,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;IACvD,CAAC,CAAC;SACD,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAE7B,yDAAyD;IACzD,IAAI,KAAK,IAAI,MAAM,EAAE;QACnB,OAAO,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;KAC/B;IAED,OAAO,GAAG,GAAG,MAAM,GAAG,GAAG,CAAC;AAC5B,CAAC;AAlBD,sCAkBC","sourcesContent":["import { Next } from \"./types\";\n\n/**\n * Stringify an array of values.\n */\nexport function arrayToString(array: any[], space: string, next: Next) {\n // Map array values to their stringified values with correct indentation.\n const values = array\n .map(function(value, index) {\n const result = next(value, index);\n\n if (result === undefined) return String(result);\n\n return space + result.split(\"\\n\").join(`\\n${space}`);\n })\n .join(space ? \",\\n\" : \",\");\n\n // Wrap the array in newlines if we have indentation set.\n if (space && values) {\n return \"[\\n\" + values + \"\\n]\";\n }\n\n return \"[\" + values + \"]\";\n}\n"]}
import { Next } from "./types";
declare const FUNCTION_PREFIXES: {
Function: string;
GeneratorFunction: string;
AsyncFunction: string;
AsyncGeneratorFunction: string;
};
/**
* Stringify a function.
*/
export declare function functionToString(fn: Function, space: string, next: Next): string;
/**
* Rewrite a stringified function to remove initial indentation.
*/
export declare function dedentFunction(fnString: string): string;
/**
* Function parser and stringify.
*/
export declare class FunctionParser {
fn: Function;
indent: string;
next: Next;
key?: string | number | symbol | undefined;
fnString: string;
fnType: keyof typeof FUNCTION_PREFIXES;
keyQuote: string | undefined;
keyPrefix: string;
isMethodCandidate: boolean;
pos: number;
hadKeyword: boolean;
constructor(fn: Function, indent: string, next: Next, key?: string | number | symbol | undefined);
stringify(): string;
getPrefix(): string;
tryParse(): string | undefined;
/**
* Attempt to parse the function from the current position by first stripping
* the function's name from the front. This is not a fool-proof method on all
* JavaScript engines, but yields good results on Node.js 4 (and slightly
* less good results on Node.js 6 and 8).
*/
tryStrippingName(): string | undefined;
/**
* Attempt to advance the parser past the keywords expected to be at the
* start of this function's definition. This method sets `this.hadKeyword`
* based on whether or not a `function` keyword is consumed.
*
* @return {boolean}
*/
tryParsePrefixTokens(): boolean;
/**
* Advance the parser past one element of JavaScript syntax. This could be a
* matched pair of delimiters, like braces or parentheses, or an atomic unit
* like a keyword, variable, or operator. Return a normalized string
* representation of the element parsed--for example, returns '{}' for a
* matched pair of braces. Comments and whitespace are skipped.
*
* (This isn't a full parser, so the token scanning logic used here is as
* simple as it can be. As a consequence, some things that are one token in
* JavaScript, like decimal number literals or most multicharacter operators
* like '&&', are split into more than one token here. However, awareness of
* some multicharacter sequences like '=>' is necessary, so we match the few
* of them that we care about.)
*/
consumeSyntax(wordLikeToken?: string): string | undefined;
consumeSyntaxUntil(startToken: string, endToken: string): string | undefined;
consumeMatch(re: RegExp): RegExpExecArray | null;
/**
* Advance the parser past an arbitrary regular expression. Return `token`,
* or the match object of the regexp.
*/
consumeRegExp(re: RegExp, token: string): string | undefined;
/**
* Advance the parser past a template string.
*/
consumeTemplate(): "`" | undefined;
/**
* Advance the parser past any whitespace or comments.
*/
consumeWhitespace(): void;
}
export {};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const quote_1 = require("./quote");
/**
* Used in function stringification.
*/
/* istanbul ignore next */
const METHOD_NAMES_ARE_QUOTED = {
" "() {
/* Empty. */
}
}[" "]
.toString()
.charAt(0) === '"';
const FUNCTION_PREFIXES = {
Function: "function ",
GeneratorFunction: "function* ",
AsyncFunction: "async function ",
AsyncGeneratorFunction: "async function* "
};
const METHOD_PREFIXES = {
Function: "",
GeneratorFunction: "*",
AsyncFunction: "async ",
AsyncGeneratorFunction: "async *"
};
const TOKENS_PRECEDING_REGEXPS = new Set(("case delete else in instanceof new return throw typeof void " +
", ; : + - ! ~ & | ^ * / % < > ? =").split(" "));
/**
* Stringify a function.
*/
function functionToString(fn, space, next) {
return new FunctionParser(fn, space, next).stringify();
}
exports.functionToString = functionToString;
/**
* Rewrite a stringified function to remove initial indentation.
*/
function dedentFunction(fnString) {
let found;
for (const line of fnString.split("\n").slice(1)) {
const m = /^[\s\t]+/.exec(line);
if (!m)
return fnString; // Early exit without indent.
const [str] = m;
if (found === undefined)
found = str;
else if (str.length < found.length)
found = str;
}
return found ? fnString.split(`\n${found}`).join("\n") : fnString;
}
exports.dedentFunction = dedentFunction;
/**
* Function parser and stringify.
*/
class FunctionParser {
constructor(fn, indent, next, key) {
this.fn = fn;
this.indent = indent;
this.next = next;
this.key = key;
this.pos = 0;
this.hadKeyword = false;
this.fnString = Function.prototype.toString.call(fn);
this.fnType = fn.constructor.name;
this.keyQuote = key === undefined ? "" : quote_1.quoteKey(key, next);
this.keyPrefix =
key === undefined ? "" : `${this.keyQuote}:${indent ? " " : ""}`;
this.isMethodCandidate =
key === undefined ? false : this.fn.name === "" || this.fn.name === key;
}
stringify() {
const value = this.tryParse();
// If we can't stringify this function, return a void expression; for
// bonus help with debugging, include the function as a string literal.
if (!value) {
return `${this.keyPrefix}void ${this.next(this.fnString)}`;
}
return dedentFunction(value);
}
getPrefix() {
if (this.isMethodCandidate && !this.hadKeyword) {
return METHOD_PREFIXES[this.fnType] + this.keyQuote;
}
return this.keyPrefix + FUNCTION_PREFIXES[this.fnType];
}
tryParse() {
if (this.fnString[this.fnString.length - 1] !== "}") {
// Must be an arrow function.
return this.keyPrefix + this.fnString;
}
// Attempt to remove function prefix.
if (this.fn.name) {
const result = this.tryStrippingName();
if (result)
return result;
}
// Support class expressions.
const prevPos = this.pos;
if (this.consumeSyntax() === "class")
return this.fnString;
this.pos = prevPos;
if (this.tryParsePrefixTokens()) {
const result = this.tryStrippingName();
if (result)
return result;
let offset = this.pos;
switch (this.consumeSyntax("WORD_LIKE")) {
case "WORD_LIKE":
if (this.isMethodCandidate && !this.hadKeyword) {
offset = this.pos;
}
// tslint:disable-next-line no-switch-case-fall-through
case "()":
if (this.fnString.substr(this.pos, 2) === "=>") {
return this.keyPrefix + this.fnString;
}
this.pos = offset;
// tslint:disable-next-line no-switch-case-fall-through
case '"':
case "'":
case "[]":
return this.getPrefix() + this.fnString.substr(this.pos);
}
}
}
/**
* Attempt to parse the function from the current position by first stripping
* the function's name from the front. This is not a fool-proof method on all
* JavaScript engines, but yields good results on Node.js 4 (and slightly
* less good results on Node.js 6 and 8).
*/
tryStrippingName() {
if (METHOD_NAMES_ARE_QUOTED) {
// ... then this approach is unnecessary and yields false positives.
return;
}
let start = this.pos;
const prefix = this.fnString.substr(this.pos, this.fn.name.length);
if (prefix === this.fn.name) {
this.pos += prefix.length;
if (this.consumeSyntax() === "()" &&
this.consumeSyntax() === "{}" &&
this.pos === this.fnString.length) {
// Don't include the function's name if it will be included in the
// prefix, or if it's invalid as a name in a function expression.
if (this.isMethodCandidate || !quote_1.isValidVariableName(prefix)) {
start += prefix.length;
}
return this.getPrefix() + this.fnString.substr(start);
}
}
this.pos = start;
}
/**
* Attempt to advance the parser past the keywords expected to be at the
* start of this function's definition. This method sets `this.hadKeyword`
* based on whether or not a `function` keyword is consumed.
*
* @return {boolean}
*/
tryParsePrefixTokens() {
let posPrev = this.pos;
this.hadKeyword = false;
switch (this.fnType) {
case "AsyncFunction":
if (this.consumeSyntax() !== "async")
return false;
posPrev = this.pos;
// tslint:disable-next-line no-switch-case-fall-through
case "Function":
if (this.consumeSyntax() === "function") {
this.hadKeyword = true;
}
else {
this.pos = posPrev;
}
return true;
case "AsyncGeneratorFunction":
if (this.consumeSyntax() !== "async")
return false;
// tslint:disable-next-line no-switch-case-fall-through
case "GeneratorFunction":
let token = this.consumeSyntax();
if (token === "function") {
token = this.consumeSyntax();
this.hadKeyword = true;
}
return token === "*";
}
}
/**
* Advance the parser past one element of JavaScript syntax. This could be a
* matched pair of delimiters, like braces or parentheses, or an atomic unit
* like a keyword, variable, or operator. Return a normalized string
* representation of the element parsed--for example, returns '{}' for a
* matched pair of braces. Comments and whitespace are skipped.
*
* (This isn't a full parser, so the token scanning logic used here is as
* simple as it can be. As a consequence, some things that are one token in
* JavaScript, like decimal number literals or most multicharacter operators
* like '&&', are split into more than one token here. However, awareness of
* some multicharacter sequences like '=>' is necessary, so we match the few
* of them that we care about.)
*/
consumeSyntax(wordLikeToken) {
const m = this.consumeMatch(/^(?:([A-Za-z_0-9$\xA0-\uFFFF]+)|=>|\+\+|\-\-|.)/);
if (!m)
return;
const [token, match] = m;
this.consumeWhitespace();
if (match)
return wordLikeToken || match;
switch (token) {
case "(":
return this.consumeSyntaxUntil("(", ")");
case "[":
return this.consumeSyntaxUntil("[", "]");
case "{":
return this.consumeSyntaxUntil("{", "}");
case "`":
return this.consumeTemplate();
case '"':
return this.consumeRegExp(/^(?:[^\\"]|\\.)*"/, '"');
case "'":
return this.consumeRegExp(/^(?:[^\\']|\\.)*'/, "'");
}
return token;
}
consumeSyntaxUntil(startToken, endToken) {
let isRegExpAllowed = true;
for (;;) {
const token = this.consumeSyntax();
if (token === endToken)
return startToken + endToken;
if (!token || token === ")" || token === "]" || token === "}")
return;
if (token === "/" &&
isRegExpAllowed &&
this.consumeMatch(/^(?:\\.|[^\\\/\n[]|\[(?:\\.|[^\]])*\])+\/[a-z]*/)) {
isRegExpAllowed = false;
this.consumeWhitespace();
}
else {
isRegExpAllowed = TOKENS_PRECEDING_REGEXPS.has(token);
}
}
}
consumeMatch(re) {
const m = re.exec(this.fnString.substr(this.pos));
if (m)
this.pos += m[0].length;
return m;
}
/**
* Advance the parser past an arbitrary regular expression. Return `token`,
* or the match object of the regexp.
*/
consumeRegExp(re, token) {
const m = re.exec(this.fnString.substr(this.pos));
if (!m)
return;
this.pos += m[0].length;
this.consumeWhitespace();
return token;
}
/**
* Advance the parser past a template string.
*/
consumeTemplate() {
for (;;) {
this.consumeMatch(/^(?:[^`$\\]|\\.|\$(?!{))*/);
if (this.fnString[this.pos] === "`") {
this.pos++;
this.consumeWhitespace();
return "`";
}
if (this.fnString.substr(this.pos, 2) === "${") {
this.pos += 2;
this.consumeWhitespace();
if (this.consumeSyntaxUntil("{", "}"))
continue;
}
return;
}
}
/**
* Advance the parser past any whitespace or comments.
*/
consumeWhitespace() {
this.consumeMatch(/^(?:\s|\/\/.*|\/\*[^]*?\*\/)*/);
}
}
exports.FunctionParser = FunctionParser;
//# sourceMappingURL=function.js.map
{"version":3,"file":"function.js","sourceRoot":"","sources":["../src/function.ts"],"names":[],"mappings":"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","sourcesContent":["import { Next } from \"./types\";\nimport { quoteKey, isValidVariableName } from \"./quote\";\n\n/**\n * Used in function stringification.\n */\n/* istanbul ignore next */\nconst METHOD_NAMES_ARE_QUOTED =\n {\n \" \"() {\n /* Empty. */\n }\n }[\" \"]\n .toString()\n .charAt(0) === '\"';\n\nconst FUNCTION_PREFIXES = {\n Function: \"function \",\n GeneratorFunction: \"function* \",\n AsyncFunction: \"async function \",\n AsyncGeneratorFunction: \"async function* \"\n};\n\nconst METHOD_PREFIXES = {\n Function: \"\",\n GeneratorFunction: \"*\",\n AsyncFunction: \"async \",\n AsyncGeneratorFunction: \"async *\"\n};\n\nconst TOKENS_PRECEDING_REGEXPS = new Set(\n (\n \"case delete else in instanceof new return throw typeof void \" +\n \", ; : + - ! ~ & | ^ * / % < > ? =\"\n ).split(\" \")\n);\n\n/**\n * Stringify a function.\n */\nexport function functionToString(fn: Function, space: string, next: Next) {\n return new FunctionParser(fn, space, next).stringify();\n}\n\n/**\n * Rewrite a stringified function to remove initial indentation.\n */\nexport function dedentFunction(fnString: string) {\n let found: string | undefined;\n\n for (const line of fnString.split(\"\\n\").slice(1)) {\n const m = /^[\\s\\t]+/.exec(line);\n if (!m) return fnString; // Early exit without indent.\n\n const [str] = m;\n\n if (found === undefined) found = str;\n else if (str.length < found.length) found = str;\n }\n\n return found ? fnString.split(`\\n${found}`).join(\"\\n\") : fnString;\n}\n\n/**\n * Function parser and stringify.\n */\nexport class FunctionParser {\n fnString: string;\n fnType: keyof typeof FUNCTION_PREFIXES;\n keyQuote: string | undefined;\n keyPrefix: string;\n isMethodCandidate: boolean;\n\n pos = 0;\n hadKeyword = false;\n\n constructor(\n public fn: Function,\n public indent: string,\n public next: Next,\n public key?: PropertyKey\n ) {\n this.fnString = Function.prototype.toString.call(fn);\n this.fnType = fn.constructor.name as keyof typeof FUNCTION_PREFIXES;\n this.keyQuote = key === undefined ? \"\" : quoteKey(key, next);\n this.keyPrefix =\n key === undefined ? \"\" : `${this.keyQuote}:${indent ? \" \" : \"\"}`;\n this.isMethodCandidate =\n key === undefined ? false : this.fn.name === \"\" || this.fn.name === key;\n }\n\n stringify() {\n const value = this.tryParse();\n\n // If we can't stringify this function, return a void expression; for\n // bonus help with debugging, include the function as a string literal.\n if (!value) {\n return `${this.keyPrefix}void ${this.next(this.fnString)}`;\n }\n\n return dedentFunction(value);\n }\n\n getPrefix() {\n if (this.isMethodCandidate && !this.hadKeyword) {\n return METHOD_PREFIXES[this.fnType] + this.keyQuote;\n }\n\n return this.keyPrefix + FUNCTION_PREFIXES[this.fnType];\n }\n\n tryParse() {\n if (this.fnString[this.fnString.length - 1] !== \"}\") {\n // Must be an arrow function.\n return this.keyPrefix + this.fnString;\n }\n\n // Attempt to remove function prefix.\n if (this.fn.name) {\n const result = this.tryStrippingName();\n if (result) return result;\n }\n\n // Support class expressions.\n const prevPos = this.pos;\n if (this.consumeSyntax() === \"class\") return this.fnString;\n this.pos = prevPos;\n\n if (this.tryParsePrefixTokens()) {\n const result = this.tryStrippingName();\n if (result) return result;\n\n let offset = this.pos;\n\n switch (this.consumeSyntax(\"WORD_LIKE\")) {\n case \"WORD_LIKE\":\n if (this.isMethodCandidate && !this.hadKeyword) {\n offset = this.pos;\n }\n // tslint:disable-next-line no-switch-case-fall-through\n case \"()\":\n if (this.fnString.substr(this.pos, 2) === \"=>\") {\n return this.keyPrefix + this.fnString;\n }\n\n this.pos = offset;\n // tslint:disable-next-line no-switch-case-fall-through\n case '\"':\n case \"'\":\n case \"[]\":\n return this.getPrefix() + this.fnString.substr(this.pos);\n }\n }\n }\n\n /**\n * Attempt to parse the function from the current position by first stripping\n * the function's name from the front. This is not a fool-proof method on all\n * JavaScript engines, but yields good results on Node.js 4 (and slightly\n * less good results on Node.js 6 and 8).\n */\n tryStrippingName() {\n if (METHOD_NAMES_ARE_QUOTED) {\n // ... then this approach is unnecessary and yields false positives.\n return;\n }\n\n let start = this.pos;\n const prefix = this.fnString.substr(this.pos, this.fn.name.length);\n\n if (prefix === this.fn.name) {\n this.pos += prefix.length;\n\n if (\n this.consumeSyntax() === \"()\" &&\n this.consumeSyntax() === \"{}\" &&\n this.pos === this.fnString.length\n ) {\n // Don't include the function's name if it will be included in the\n // prefix, or if it's invalid as a name in a function expression.\n if (this.isMethodCandidate || !isValidVariableName(prefix)) {\n start += prefix.length;\n }\n\n return this.getPrefix() + this.fnString.substr(start);\n }\n }\n\n this.pos = start;\n }\n\n /**\n * Attempt to advance the parser past the keywords expected to be at the\n * start of this function's definition. This method sets `this.hadKeyword`\n * based on whether or not a `function` keyword is consumed.\n *\n * @return {boolean}\n */\n tryParsePrefixTokens() {\n let posPrev = this.pos;\n\n this.hadKeyword = false;\n\n switch (this.fnType) {\n case \"AsyncFunction\":\n if (this.consumeSyntax() !== \"async\") return false;\n\n posPrev = this.pos;\n // tslint:disable-next-line no-switch-case-fall-through\n case \"Function\":\n if (this.consumeSyntax() === \"function\") {\n this.hadKeyword = true;\n } else {\n this.pos = posPrev;\n }\n return true;\n case \"AsyncGeneratorFunction\":\n if (this.consumeSyntax() !== \"async\") return false;\n // tslint:disable-next-line no-switch-case-fall-through\n case \"GeneratorFunction\":\n let token = this.consumeSyntax();\n\n if (token === \"function\") {\n token = this.consumeSyntax();\n this.hadKeyword = true;\n }\n\n return token === \"*\";\n }\n }\n\n /**\n * Advance the parser past one element of JavaScript syntax. This could be a\n * matched pair of delimiters, like braces or parentheses, or an atomic unit\n * like a keyword, variable, or operator. Return a normalized string\n * representation of the element parsed--for example, returns '{}' for a\n * matched pair of braces. Comments and whitespace are skipped.\n *\n * (This isn't a full parser, so the token scanning logic used here is as\n * simple as it can be. As a consequence, some things that are one token in\n * JavaScript, like decimal number literals or most multicharacter operators\n * like '&&', are split into more than one token here. However, awareness of\n * some multicharacter sequences like '=>' is necessary, so we match the few\n * of them that we care about.)\n */\n consumeSyntax(wordLikeToken?: string) {\n const m = this.consumeMatch(\n /^(?:([A-Za-z_0-9$\\xA0-\\uFFFF]+)|=>|\\+\\+|\\-\\-|.)/\n );\n\n if (!m) return;\n\n const [token, match] = m;\n this.consumeWhitespace();\n\n if (match) return wordLikeToken || match;\n\n switch (token) {\n case \"(\":\n return this.consumeSyntaxUntil(\"(\", \")\");\n case \"[\":\n return this.consumeSyntaxUntil(\"[\", \"]\");\n case \"{\":\n return this.consumeSyntaxUntil(\"{\", \"}\");\n case \"`\":\n return this.consumeTemplate();\n case '\"':\n return this.consumeRegExp(/^(?:[^\\\\\"]|\\\\.)*\"/, '\"');\n case \"'\":\n return this.consumeRegExp(/^(?:[^\\\\']|\\\\.)*'/, \"'\");\n }\n\n return token;\n }\n\n consumeSyntaxUntil(startToken: string, endToken: string): string | undefined {\n let isRegExpAllowed = true;\n\n for (;;) {\n const token = this.consumeSyntax();\n if (token === endToken) return startToken + endToken;\n if (!token || token === \")\" || token === \"]\" || token === \"}\") return;\n\n if (\n token === \"/\" &&\n isRegExpAllowed &&\n this.consumeMatch(/^(?:\\\\.|[^\\\\\\/\\n[]|\\[(?:\\\\.|[^\\]])*\\])+\\/[a-z]*/)\n ) {\n isRegExpAllowed = false;\n this.consumeWhitespace();\n } else {\n isRegExpAllowed = TOKENS_PRECEDING_REGEXPS.has(token);\n }\n }\n }\n\n consumeMatch(re: RegExp) {\n const m = re.exec(this.fnString.substr(this.pos));\n if (m) this.pos += m[0].length;\n return m;\n }\n\n /**\n * Advance the parser past an arbitrary regular expression. Return `token`,\n * or the match object of the regexp.\n */\n consumeRegExp(re: RegExp, token: string): string | undefined {\n const m = re.exec(this.fnString.substr(this.pos));\n if (!m) return;\n this.pos += m[0].length;\n this.consumeWhitespace();\n return token;\n }\n\n /**\n * Advance the parser past a template string.\n */\n consumeTemplate() {\n for (;;) {\n this.consumeMatch(/^(?:[^`$\\\\]|\\\\.|\\$(?!{))*/);\n\n if (this.fnString[this.pos] === \"`\") {\n this.pos++;\n this.consumeWhitespace();\n return \"`\";\n }\n\n if (this.fnString.substr(this.pos, 2) === \"${\") {\n this.pos += 2;\n this.consumeWhitespace();\n\n if (this.consumeSyntaxUntil(\"{\", \"}\")) continue;\n }\n\n return;\n }\n }\n\n /**\n * Advance the parser past any whitespace or comments.\n */\n consumeWhitespace() {\n this.consumeMatch(/^(?:\\s|\\/\\/.*|\\/\\*[^]*?\\*\\/)*/);\n }\n}\n"]}
import { ToString } from "./types";
export interface Options {
maxDepth?: number;
maxValues?: number;
references?: boolean;
skipUndefinedProperties?: boolean;
}
/**
* Stringify any JavaScript value.
*/
export declare function stringify(value: any, replacer?: ToString | null, indent?: string | number | null, options?: Options): string | undefined;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const stringify_1 = require("./stringify");
const quote_1 = require("./quote");
/**
* Root path node.
*/
const ROOT_SENTINEL = Symbol("root");
/**
* Stringify any JavaScript value.
*/
function stringify(value, replacer, indent, options = {}) {
const space = typeof indent === "string" ? indent : " ".repeat(indent || 0);
const path = [];
const stack = new Set();
const tracking = new Map();
const unpack = new Map();
let valueCount = 0;
const { maxDepth = 100, references = false, skipUndefinedProperties = false, maxValues = 100000 } = options;
// Wrap replacer function to support falling back on supported stringify.
const valueToString = replacerToString(replacer);
// Every time you call `next(value)` execute this function.
const onNext = (value, key) => {
if (++valueCount > maxValues)
return;
if (skipUndefinedProperties && value === undefined)
return;
if (path.length > maxDepth)
return;
// An undefined key is treated as an out-of-band "value".
if (key === undefined)
return valueToString(value, space, onNext);
path.push(key);
const result = builder(value);
path.pop();
return result;
};
const builder = references
? (value) => {
if (value !== null &&
(typeof value === "object" ||
typeof value === "function" ||
typeof value === "symbol")) {
// Track nodes to restore later.
if (tracking.has(value)) {
unpack.set(path.slice(1), tracking.get(value));
return; // Avoid serializing referenced nodes on an expression.
}
// Track encountered nodes.
tracking.set(value, path.slice(1));
}
return valueToString(value, space, onNext);
}
: (value) => {
// Stop on recursion.
if (stack.has(value))
return;
stack.add(value);
const result = valueToString(value, space, onNext);
stack.delete(value);
return result;
};
const result = onNext(value, ROOT_SENTINEL);
// Attempt to restore circular references.
if (unpack.size) {
const sp = space ? " " : "";
const eol = space ? "\n" : "";
let wrapper = `var x${sp}=${sp}${result};${eol}`;
for (const [key, value] of unpack.entries()) {
const keyPath = quote_1.stringifyPath(key, onNext);
const valuePath = quote_1.stringifyPath(value, onNext);
wrapper += `x${keyPath}${sp}=${sp}x${valuePath};${eol}`;
}
return `(function${sp}()${sp}{${eol}${wrapper}return x;${eol}}())`;
}
return result;
}
exports.stringify = stringify;
/**
* Create `toString()` function from replacer.
*/
function replacerToString(replacer) {
if (!replacer)
return stringify_1.toString;
return (value, space, next) => {
return replacer(value, space, (value) => stringify_1.toString(value, space, next));
};
}
//# sourceMappingURL=index.js.map
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { toString } from \"./stringify\";\nimport { stringifyPath } from \"./quote\";\nimport { Next, ToString } from \"./types\";\n\nexport interface Options {\n maxDepth?: number;\n maxValues?: number;\n references?: boolean;\n skipUndefinedProperties?: boolean;\n}\n\n/**\n * Root path node.\n */\nconst ROOT_SENTINEL = Symbol(\"root\");\n\n/**\n * Stringify any JavaScript value.\n */\nexport function stringify(\n value: any,\n replacer?: ToString | null,\n indent?: string | number | null,\n options: Options = {}\n) {\n const space = typeof indent === \"string\" ? indent : \" \".repeat(indent || 0);\n const path: PropertyKey[] = [];\n const stack = new Set();\n const tracking = new Map<any, PropertyKey[]>();\n const unpack = new Map<PropertyKey[], PropertyKey[]>();\n let valueCount = 0;\n\n const {\n maxDepth = 100,\n references = false,\n skipUndefinedProperties = false,\n maxValues = 100000\n } = options;\n\n // Wrap replacer function to support falling back on supported stringify.\n const valueToString = replacerToString(replacer);\n\n // Every time you call `next(value)` execute this function.\n const onNext: Next = (value, key) => {\n if (++valueCount > maxValues) return;\n if (skipUndefinedProperties && value === undefined) return;\n if (path.length > maxDepth) return;\n\n // An undefined key is treated as an out-of-band \"value\".\n if (key === undefined) return valueToString(value, space, onNext);\n\n path.push(key);\n const result = builder(value);\n path.pop();\n return result;\n };\n\n const builder = references\n ? (value: any): string | undefined => {\n if (\n value !== null &&\n (typeof value === \"object\" ||\n typeof value === \"function\" ||\n typeof value === \"symbol\")\n ) {\n // Track nodes to restore later.\n if (tracking.has(value)) {\n unpack.set(path.slice(1), tracking.get(value)!);\n return; // Avoid serializing referenced nodes on an expression.\n }\n\n // Track encountered nodes.\n tracking.set(value, path.slice(1));\n }\n\n return valueToString(value, space, onNext);\n }\n : (value: any): string | undefined => {\n // Stop on recursion.\n if (stack.has(value)) return;\n\n stack.add(value);\n const result = valueToString(value, space, onNext);\n stack.delete(value);\n return result;\n };\n\n const result = onNext(value, ROOT_SENTINEL);\n\n // Attempt to restore circular references.\n if (unpack.size) {\n const sp = space ? \" \" : \"\";\n const eol = space ? \"\\n\" : \"\";\n let wrapper = `var x${sp}=${sp}${result};${eol}`;\n\n for (const [key, value] of unpack.entries()) {\n const keyPath = stringifyPath(key, onNext);\n const valuePath = stringifyPath(value, onNext);\n\n wrapper += `x${keyPath}${sp}=${sp}x${valuePath};${eol}`;\n }\n\n return `(function${sp}()${sp}{${eol}${wrapper}return x;${eol}}())`;\n }\n\n return result;\n}\n\n/**\n * Create `toString()` function from replacer.\n */\nfunction replacerToString(replacer?: ToString | null) {\n if (!replacer) return toString;\n\n return (value: any, space: string, next: Next) => {\n return replacer(value, space, (value: any) => toString(value, space, next));\n };\n}\n"]}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const fc = require("fast-check");
const semver_1 = require("semver");
const index_1 = require("./index");
// Evaluate a string into JavaScript
const evalValue = (str) => {
// tslint:disable-next-line no-eval
return eval(`(${str})`);
};
/**
* Create a quick test function wrapper.
*/
function test(value, result, indent, options) {
return () => {
expect(index_1.stringify(value, null, indent, options)).toEqual(result);
};
}
/**
* Create a wrapper for round-trip eval tests.
*/
function testRoundTrip(expression, indent, options) {
return () => test(evalValue(expression), expression, indent, options)();
}
/**
* Check if syntax is supported.
*/
function isSupported(expr) {
try {
// tslint:disable-next-line no-eval
eval(expr);
return true;
}
catch (err) {
if (err.name === "SyntaxError")
return false;
throw err;
}
}
/**
* Generate a list of test cases to run.
*/
function cases(cases) {
return () => {
for (const value of cases) {
if (value)
it(value, testRoundTrip(value));
}
};
}
/**
* Conditionally execute test cases.
*/
function describeIf(description, condition, fn) {
return condition ? describe(description, fn) : describe.skip(description, fn);
}
describe("javascript-stringify", () => {
describe("types", () => {
describe("booleans", () => {
it("should be stringified", test(true, "true"));
});
describe("strings", () => {
it("should wrap in single quotes", test("string", "'string'"));
it("should escape quote characters", test("'test'", "'\\'test\\''"));
it("should escape control characters", test("multi\nline", "'multi\\nline'"));
it("should escape back slashes", test("back\\slash", "'back\\\\slash'"));
it("should escape certain unicode sequences", test("\u0602", "'\\u0602'"));
});
describe("numbers", () => {
it("should stringify integers", test(10, "10"));
it("should stringify floats", test(10.5, "10.5"));
it('should stringify "NaN"', test(10.5, "10.5"));
it('should stringify "Infinity"', test(Infinity, "Infinity"));
it('should stringify "-Infinity"', test(-Infinity, "-Infinity"));
it('should stringify "-0"', test(-0, "-0"));
});
describe("arrays", () => {
it("should stringify as array shorthand", test([1, 2, 3], "[1,2,3]"));
it("should indent elements", test([{ x: 10 }], "[\n\t{\n\t\tx: 10\n\t}\n]", "\t"));
});
describe("objects", () => {
it("should stringify as object shorthand", test({ key: "value", "-": 10 }, "{key:'value','-':10}"));
it("should stringify undefined keys", test({ a: true, b: undefined }, "{a:true,b:undefined}"));
it("should stringify omit undefined keys", test({ a: true, b: undefined }, "{a:true}", null, {
skipUndefinedProperties: true
}));
it("should quote reserved word keys", test({ if: true, else: false }, "{'if':true,'else':false}"));
it("should not quote Object.prototype keys", test({ constructor: 1, toString: 2 }, "{constructor:1,toString:2}"));
});
describe("functions", () => {
it("should reindent function bodies", test(evalValue(`function() {
if (true) {
return "hello";
}
}`), 'function () {\n if (true) {\n return "hello";\n }\n}', 2));
it("should reindent function bodies in objects", test(evalValue(`
{
fn: function() {
if (true) {
return "hello";
}
}
}
`), '{\n fn: function () {\n if (true) {\n return "hello";\n }\n }\n}', 2));
it("should reindent function bodies in arrays", test(evalValue(`[
function() {
if (true) {
return "hello";
}
}
]`), '[\n function () {\n if (true) {\n return "hello";\n }\n }\n]', 2));
it("should not need to reindent one-liners", testRoundTrip("{\n fn: function () { return; }\n}", 2));
it("should gracefully handle unexpected Function.toString formats", () => {
const origToString = Function.prototype.toString;
Function.prototype.toString = () => "{nope}";
try {
expect(index_1.stringify(function () {
/* Empty */
})).toEqual("void '{nope}'");
}
finally {
Function.prototype.toString = origToString;
}
});
describe("should not take the names of their keys", cases(["{name:function () {}}", "{'tricky name':function () {}}"]));
});
describe("native instances", () => {
describe("Date", () => {
const date = new Date();
it("should stringify", test(date, "new Date(" + date.getTime() + ")"));
});
describe("RegExp", () => {
it("should stringify as shorthand", test(/[abc]/gi, "/[abc]/gi"));
});
describe("Number", () => {
it("should stringify", test(new Number(10), "new Number(10)"));
});
describe("String", () => {
it("should stringify", test(new String("abc"), "new String('abc')"));
});
describe("Boolean", () => {
it("should stringify", test(new Boolean(true), "new Boolean(true)"));
});
describeIf("Buffer", typeof Buffer === "function", () => {
it("should stringify", test(Buffer.from("test"), "new Buffer('test')"));
});
describe("Error", () => {
it("should stringify", test(new Error("test"), "new Error('test')"));
});
describe("unknown native type", () => {
it("should be omitted", test({
k: typeof process === "undefined"
? window.navigator
: process
}, "{}"));
});
});
describeIf("ES6", typeof Array.from === "function", () => {
describeIf("Map", typeof Map === "function", () => {
it("should stringify", test(new Map([["key", "value"]]), "new Map([['key','value']])"));
});
describeIf("Set", typeof Set === "function", () => {
it("should stringify", test(new Set(["key", "value"]), "new Set(['key','value'])"));
});
describe("arrow functions", () => {
describe("should stringify", cases([
"(a, b) => a + b",
"o => { return o.a + o.b; }",
"(a, b) => { if (a) { return b; } }",
"(a, b) => ({ [a]: b })",
"a => b => () => a + b"
]));
it("should reindent function bodies", test(evalValue(" () => {\n" +
" if (true) {\n" +
' return "hello";\n' +
" }\n" +
" }"), '() => {\n if (true) {\n return "hello";\n }\n}', 2));
describeIf("arrows with patterns", isSupported("({x}) => x"), () => {
describe("should stringify", cases([
"({ x, y }) => x + y",
"({ x, y }) => { if (x === '}') { return y; } }",
"({ x, y = /[/})]/.test(x) }) => { return y ? x : 0; }"
]));
});
});
describe("generators", () => {
it("should stringify", testRoundTrip("function* (x) { yield x; }"));
});
describe("class notation", () => {
it("should stringify classes", testRoundTrip("class {}"));
it("should stringify class and method", testRoundTrip("class { method() {} }"));
it("should stringify with newline", testRoundTrip("class\n{ method() {} }"));
it("should stringify with comment", testRoundTrip("class/*test*/\n{ method() {} }"));
});
describe("method notation", () => {
it("should stringify", testRoundTrip("{a(b, c) { return b + c; }}"));
it("should stringify generator methods", testRoundTrip("{*a(b) { yield b; }}"));
describe("should not be fooled by tricky names", cases([
"{'function a'(b, c) { return b + c; }}",
"{'a(a'(b, c) { return b + c; }}",
"{'() => function '() {}}",
"{'['() { return x[y]()\n{ return true; }}}",
"{'() { return false;//'() { return true;\n}}"
]));
it("should not be fooled by tricky generator names", testRoundTrip("{*'function a'(b, c) { return b + c; }}"));
it("should not be fooled by empty names", testRoundTrip("{''(b, c) { return b + c; }}"));
it("should not be fooled by keys that look like functions", () => {
const fn = evalValue('{ "() => ": () => () => 42 }')["() => "];
expect(index_1.stringify(fn)).toEqual("() => () => 42");
});
describe("should not be fooled by arrow functions", cases([
"{a:(b, c) => b + c}",
"{a:a => a + 1}",
"{'() => ':() => () => 42}",
'{\'() => "\':() => "() {//"}',
'{\'() => "\':() => "() {`//"}',
'{\'() => "\':() => "() {`${//"}',
'{\'() => "\':() => "() {/*//"}',
semver_1.satisfies(process.versions.node, "<=4 || >=10")
? "{'a => function ':a => function () { return a + 1; }}"
: undefined
]));
describe("should not be fooled by regexp literals", cases([
"{' '(s) { return /}/.test(s); }}",
"{' '(s) { return /abc/ .test(s); }}",
"{' '() { return x / y; // /}\n}}",
"{' '() { return / y; }//* } */}}",
"{' '() { return delete / y; }/.x}}",
"{' '() { switch (x) { case / y; }}/: }}}",
"{' '() { if (x) return; else / y;}/; }}",
"{' '() { return x in / y;}/; }}",
"{' '() { return x instanceof / y;}/; }}",
"{' '() { return new / y;}/.x; }}",
"{' '() { throw / y;}/.x; }}",
"{' '() { return typeof / y;}/; }}",
"{' '() { void / y;}/; }}",
"{' '() { return x, / y;}/; }}",
"{' '() { return x; / y;}/; }}",
"{' '() { return { x: / y;}/ }; }}",
"{' '() { return x + / y;}/.x; }}",
"{' '() { return x - / y;}/.x; }}",
"{' '() { return !/ y;}/; }}",
"{' '() { return ~/ y;}/.x; }}",
"{' '() { return x && / y;}/; }}",
"{' '() { return x || / y;}/; }}",
"{' '() { return x ^ / y;}/.x; }}",
"{' '() { return x * / y;}/.x; }}",
"{' '() { return x / / y;}/.x; }}",
"{' '() { return x % / y;}/.x; }}",
"{' '() { return x < / y;}/.x; }}",
"{' '() { return x > / y;}/.x; }}",
"{' '() { return x <= / y;}/.x; }}",
"{' '() { return x /= / y;}/.x; }}",
"{' '() { return x ? / y;}/ : false; }}"
]));
describe("should not be fooled by computed names", () => {
it("1", test(evalValue('{ ["foobar".slice(3)](x) { return x + 1; } }'), "{bar(x) { return x + 1; }}"));
it("2", test(evalValue('{[((s,a,b)=>a+s(a)+","+s(b)+b)(JSON.stringify,"[((s,a,b)=>a+s(a)+\\",\\"+s(b)+b)(JSON.stringify,",")]() {}")]() {}}'), '{\'[((s,a,b)=>a+s(a)+","+s(b)+b)(JSON.stringify,"[((s,a,b)=>a+s(a)+\\\\",\\\\"+s(b)+b)(JSON.stringify,",")]() {}")]() {}\'() {}}'));
it("3", test(evalValue('{[`over${`6${"0".repeat(3)}`.replace("6", "9")}`]() { this.activateHair(); }}'), "{over9000() { this.activateHair(); }}"));
it("4", test(evalValue("{[\"() {'\"]() {''}}"), "{'() {\\''() {''}}"));
it("5", test(evalValue('{["() {`"]() {``}}'), "{'() {`'() {``}}"));
it("6", test(evalValue('{["() {/*"]() {/*`${()=>{/*}*/}}'), "{'() {/*'() {/*`${()=>{/*}*/}}"));
});
// These two cases demonstrate that branching on
// METHOD_NAMES_ARE_QUOTED is unavoidable--you can't write code
// without it that will pass both of these cases on both node.js 4
// and node.js 10. (If you think you can, consider that the name and
// toString of the first case when executed on node.js 10 are
// identical to the name and toString of the second case when
// executed on node.js 4, so good luck telling them apart without
// knowing which node you're on.)
describe("should handle different versions of node correctly", () => {
it("1", test(evalValue('{[((s,a,b)=>a+s(a)+","+s(b)+b)(JSON.stringify,"[((s,a,b)=>a+s(a)+\\",\\"+s(b)+b)(JSON.stringify,",")]() { return 0; /*")]() { return 0; /*() {/* */ return 1;}}'), '{\'[((s,a,b)=>a+s(a)+","+s(b)+b)(JSON.stringify,"[((s,a,b)=>a+s(a)+\\\\",\\\\"+s(b)+b)(JSON.stringify,",")]() { return 0; /*")]() { return 0; /*\'() { return 0; /*() {/* */ return 1;}}'));
it("2", test(evalValue('{\'[((s,a,b)=>a+s(a)+","+s(b)+b)(JSON.stringify,"[((s,a,b)=>a+s(a)+\\\\",\\\\"+s(b)+b)(JSON.stringify,",")]() { return 0; /*")]() { return 0; /*\'() {/* */ return 1;}}'), '{\'[((s,a,b)=>a+s(a)+","+s(b)+b)(JSON.stringify,"[((s,a,b)=>a+s(a)+\\\\",\\\\"+s(b)+b)(JSON.stringify,",")]() { return 0; /*")]() { return 0; /*\'() {/* */ return 1;}}'));
});
it("should not be fooled by comments", test(evalValue("{'method' /* a comment! */ () /* another comment! */ {}}"), "{method() /* another comment! */ {}}"));
it("should stringify extracted methods", () => {
const fn = evalValue("{ foo(x) { return x + 1; } }").foo;
expect(index_1.stringify(fn)).toEqual("function foo(x) { return x + 1; }");
});
it("should stringify extracted generators", () => {
const fn = evalValue("{ *foo(x) { yield x; } }").foo;
expect(index_1.stringify(fn)).toEqual("function* foo(x) { yield x; }");
});
it("should stringify extracted methods with tricky names", () => {
const fn = evalValue('{ "a(a"(x) { return x + 1; } }')["a(a"];
expect(index_1.stringify(fn)).toEqual("function (x) { return x + 1; }");
});
it("should stringify extracted methods with arrow-like tricky names", () => {
const fn = evalValue('{ "() => function "(x) { return x + 1; } }')["() => function "];
expect(index_1.stringify(fn)).toEqual("function (x) { return x + 1; }");
});
it("should stringify extracted methods with empty names", () => {
const fn = evalValue('{ ""(x) { return x + 1; } }')[""];
expect(index_1.stringify(fn)).toEqual("function (x) { return x + 1; }");
});
it("should handle transplanted names", () => {
const fn = evalValue("{ foo(x) { return x + 1; } }").foo;
expect(index_1.stringify({ bar: fn })).toEqual("{bar:function foo(x) { return x + 1; }}");
});
it("should handle transplanted names with generators", () => {
const fn = evalValue("{ *foo(x) { yield x; } }").foo;
expect(index_1.stringify({ bar: fn })).toEqual("{bar:function* foo(x) { yield x; }}");
});
it("should reindent methods", test(evalValue(" {\n" +
" fn() {\n" +
" if (true) {\n" +
' return "hello";\n' +
" }\n" +
" }\n" +
" }"), '{\n fn() {\n if (true) {\n return "hello";\n }\n }\n}', 2));
});
});
describe("ES2017", () => {
describeIf("async functions", isSupported("(async function () {})"), () => {
it("should stringify", testRoundTrip("async function (x) { await x; }"));
it("should gracefully handle unexpected Function.toString formats", () => {
const origToString = Function.prototype.toString;
Function.prototype.toString = () => "{nope}";
try {
expect(index_1.stringify(evalValue("async function () {}"))).toEqual("void '{nope}'");
}
finally {
Function.prototype.toString = origToString;
}
});
});
describeIf("async arrows", isSupported("async () => {}"), () => {
describe("should stringify", cases([
"async (x) => x + 1",
"async x => x + 1",
"async x => { await x.then(y => y + 1); }"
]));
describe("should stringify as object properties", cases([
"{f:async a => a + 1}",
semver_1.satisfies(process.versions.node, "<=4 || >=10")
? "{'async a => function ':async a => function () { return a + 1; }}"
: undefined
]));
});
});
describe("ES2018", () => {
describeIf("async generators", isSupported("(async function* () {})"), () => {
it("should stringify", testRoundTrip("async function* (x) { yield x; }"));
it("should gracefully handle unexpected Function.toString formats", () => {
const origToString = Function.prototype.toString;
Function.prototype.toString = () => "{nope}";
try {
expect(index_1.stringify(evalValue("async function* () {}"))).toEqual("void '{nope}'");
}
finally {
Function.prototype.toString = origToString;
}
});
});
});
describe("global", () => {
it("should access the global in the current environment", testRoundTrip("Function('return this')()"));
});
});
describe("circular references", () => {
it("should omit circular references", () => {
const obj = { key: "value" };
obj.obj = obj;
const result = index_1.stringify(obj);
expect(result).toEqual("{key:'value'}");
});
it("should restore value", () => {
const obj = { key: "value" };
obj.obj = obj;
const result = index_1.stringify(obj, null, null, { references: true });
expect(result).toEqual("(function(){var x={key:'value'};x.obj=x;return x;}())");
});
it("should omit recursive array value", () => {
const obj = [1, 2, 3];
obj.push(obj);
const result = index_1.stringify(obj);
expect(result).toEqual("[1,2,3,undefined]");
});
it("should restore array value", () => {
const obj = [1, 2, 3];
obj.push(obj);
const result = index_1.stringify(obj, null, null, { references: true });
expect(result).toEqual("(function(){var x=[1,2,3,undefined];x[3]=x;return x;}())");
});
it("should print repeated values when no references enabled", () => {
const obj = {};
const child = {};
obj.a = child;
obj.b = child;
const result = index_1.stringify(obj);
expect(result).toEqual("{a:{},b:{}}");
});
it("should restore repeated values", () => {
const obj = {};
const child = {};
obj.a = child;
obj.b = child;
const result = index_1.stringify(obj, null, null, { references: true });
expect(result).toEqual("(function(){var x={a:{}};x.b=x.a;return x;}())");
});
it("should restore repeated values with indentation", function () {
const obj = {};
const child = {};
obj.a = child;
obj.b = child;
const result = index_1.stringify(obj, null, 2, { references: true });
expect(result).toEqual("(function () {\nvar x = {\n a: {}\n};\nx.b = x.a;\nreturn x;\n}())");
});
});
describe("custom indent", () => {
it("string", () => {
const result = index_1.stringify({
test: [1, 2, 3],
nested: {
key: "value"
}
}, null, "\t");
expect(result).toEqual("{\n" +
"\ttest: [\n\t\t1,\n\t\t2,\n\t\t3\n\t],\n" +
"\tnested: {\n\t\tkey: 'value'\n\t}\n" +
"}");
});
it("integer", () => {
const result = index_1.stringify({
test: [1, 2, 3],
nested: {
key: "value"
}
}, null, 2);
expect(result).toEqual("{\n" +
" test: [\n 1,\n 2,\n 3\n ],\n" +
" nested: {\n key: 'value'\n }\n" +
"}");
});
it("float", () => {
const result = index_1.stringify({
test: [1, 2, 3],
nested: {
key: "value"
}
}, null, 2.6);
expect(result).toEqual("{\n" +
" test: [\n 1,\n 2,\n 3\n ],\n" +
" nested: {\n key: 'value'\n }\n" +
"}");
});
});
describe("replacer function", () => {
it("should allow custom replacements", () => {
let callCount = 0;
const result = index_1.stringify({
test: "value"
}, function (value, indent, next) {
callCount++;
if (typeof value === "string") {
return '"hello"';
}
return next(value);
});
expect(callCount).toEqual(2);
expect(result).toEqual('{test:"hello"}');
});
it("change primitive to object", () => {
const result = index_1.stringify({
test: 10
}, function (value, indent, next) {
if (typeof value === "number") {
return next({ obj: "value" });
}
return next(value);
});
expect(result).toEqual("{test:{obj:'value'}}");
});
it("change object to primitive", () => {
const result = index_1.stringify({
test: 10
}, value => Object.prototype.toString.call(value));
expect(result).toEqual("[object Object]");
});
});
describe("max depth", () => {
const obj = { a: { b: { c: 1 } } };
it("should get all object", test(obj, "{a:{b:{c:1}}}"));
it("should get part of the object", test(obj, "{a:{b:{}}}", null, { maxDepth: 2 }));
it("should get part of the object when tracking references", test(obj, "{a:{b:{}}}", null, { maxDepth: 2, references: true }));
});
describe("property based", () => {
it("should produce string evaluating to the original value", () => {
fc.assert(fc.property(fc.anything(), value => {
expect(evalValue(index_1.stringify(value))).toEqual(value);
}));
});
});
});
//# sourceMappingURL=index.spec.js.map
{"version":3,"file":"index.spec.js","sourceRoot":"","sources":["../src/index.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fc from \"fast-check\";\nimport { satisfies } from \"semver\";\nimport { stringify, Options } from \"./index\";\n\n// Declare global window type.\ndeclare const window: any;\n\n// Evaluate a string into JavaScript\nconst evalValue = (str: string | undefined) => {\n // tslint:disable-next-line no-eval\n return eval(`(${str})`);\n};\n\n/**\n * Create a quick test function wrapper.\n */\nfunction test(\n value: any,\n result: string,\n indent?: string | number | null,\n options?: Options\n) {\n return () => {\n expect(stringify(value, null, indent, options)).toEqual(result);\n };\n}\n\n/**\n * Create a wrapper for round-trip eval tests.\n */\nfunction testRoundTrip(\n expression: string,\n indent?: string | number,\n options?: Options\n) {\n return () => test(evalValue(expression), expression, indent, options)();\n}\n\n/**\n * Check if syntax is supported.\n */\nfunction isSupported(expr: string) {\n try {\n // tslint:disable-next-line no-eval\n eval(expr);\n return true;\n } catch (err) {\n if (err.name === \"SyntaxError\") return false;\n throw err;\n }\n}\n\n/**\n * Generate a list of test cases to run.\n */\nfunction cases(cases: (string | undefined)[]) {\n return () => {\n for (const value of cases) {\n if (value) it(value, testRoundTrip(value));\n }\n };\n}\n\n/**\n * Conditionally execute test cases.\n */\nfunction describeIf(\n description: string,\n condition: boolean,\n fn: jest.EmptyFunction\n) {\n return condition ? describe(description, fn) : describe.skip(description, fn);\n}\n\ndescribe(\"javascript-stringify\", () => {\n describe(\"types\", () => {\n describe(\"booleans\", () => {\n it(\"should be stringified\", test(true, \"true\"));\n });\n\n describe(\"strings\", () => {\n it(\"should wrap in single quotes\", test(\"string\", \"'string'\"));\n\n it(\"should escape quote characters\", test(\"'test'\", \"'\\\\'test\\\\''\"));\n\n it(\n \"should escape control characters\",\n test(\"multi\\nline\", \"'multi\\\\nline'\")\n );\n\n it(\"should escape back slashes\", test(\"back\\\\slash\", \"'back\\\\\\\\slash'\"));\n\n it(\n \"should escape certain unicode sequences\",\n test(\"\\u0602\", \"'\\\\u0602'\")\n );\n });\n\n describe(\"numbers\", () => {\n it(\"should stringify integers\", test(10, \"10\"));\n\n it(\"should stringify floats\", test(10.5, \"10.5\"));\n\n it('should stringify \"NaN\"', test(10.5, \"10.5\"));\n\n it('should stringify \"Infinity\"', test(Infinity, \"Infinity\"));\n\n it('should stringify \"-Infinity\"', test(-Infinity, \"-Infinity\"));\n\n it('should stringify \"-0\"', test(-0, \"-0\"));\n });\n\n describe(\"arrays\", () => {\n it(\"should stringify as array shorthand\", test([1, 2, 3], \"[1,2,3]\"));\n\n it(\n \"should indent elements\",\n test([{ x: 10 }], \"[\\n\\t{\\n\\t\\tx: 10\\n\\t}\\n]\", \"\\t\")\n );\n });\n\n describe(\"objects\", () => {\n it(\n \"should stringify as object shorthand\",\n test({ key: \"value\", \"-\": 10 }, \"{key:'value','-':10}\")\n );\n\n it(\n \"should stringify undefined keys\",\n test({ a: true, b: undefined }, \"{a:true,b:undefined}\")\n );\n\n it(\n \"should stringify omit undefined keys\",\n test({ a: true, b: undefined }, \"{a:true}\", null, {\n skipUndefinedProperties: true\n })\n );\n\n it(\n \"should quote reserved word keys\",\n test({ if: true, else: false }, \"{'if':true,'else':false}\")\n );\n\n it(\n \"should not quote Object.prototype keys\",\n test({ constructor: 1, toString: 2 }, \"{constructor:1,toString:2}\")\n );\n });\n\n describe(\"functions\", () => {\n it(\n \"should reindent function bodies\",\n test(\n evalValue(\n `function() {\n if (true) {\n return \"hello\";\n }\n }`\n ),\n 'function () {\\n if (true) {\\n return \"hello\";\\n }\\n}',\n 2\n )\n );\n\n it(\n \"should reindent function bodies in objects\",\n test(\n evalValue(`\n {\n fn: function() {\n if (true) {\n return \"hello\";\n }\n }\n }\n `),\n '{\\n fn: function () {\\n if (true) {\\n return \"hello\";\\n }\\n }\\n}',\n 2\n )\n );\n\n it(\n \"should reindent function bodies in arrays\",\n test(\n evalValue(`[\n function() {\n if (true) {\n return \"hello\";\n }\n }\n ]`),\n '[\\n function () {\\n if (true) {\\n return \"hello\";\\n }\\n }\\n]',\n 2\n )\n );\n\n it(\n \"should not need to reindent one-liners\",\n testRoundTrip(\"{\\n fn: function () { return; }\\n}\", 2)\n );\n\n it(\"should gracefully handle unexpected Function.toString formats\", () => {\n const origToString = Function.prototype.toString;\n\n Function.prototype.toString = () => \"{nope}\";\n\n try {\n expect(\n stringify(function() {\n /* Empty */\n })\n ).toEqual(\"void '{nope}'\");\n } finally {\n Function.prototype.toString = origToString;\n }\n });\n\n describe(\n \"should not take the names of their keys\",\n cases([\"{name:function () {}}\", \"{'tricky name':function () {}}\"])\n );\n });\n\n describe(\"native instances\", () => {\n describe(\"Date\", () => {\n const date = new Date();\n\n it(\"should stringify\", test(date, \"new Date(\" + date.getTime() + \")\"));\n });\n\n describe(\"RegExp\", () => {\n it(\"should stringify as shorthand\", test(/[abc]/gi, \"/[abc]/gi\"));\n });\n\n describe(\"Number\", () => {\n it(\"should stringify\", test(new Number(10), \"new Number(10)\"));\n });\n\n describe(\"String\", () => {\n it(\"should stringify\", test(new String(\"abc\"), \"new String('abc')\"));\n });\n\n describe(\"Boolean\", () => {\n it(\"should stringify\", test(new Boolean(true), \"new Boolean(true)\"));\n });\n\n describeIf(\"Buffer\", typeof (Buffer as any) === \"function\", () => {\n it(\"should stringify\", test(Buffer.from(\"test\"), \"new Buffer('test')\"));\n });\n\n describe(\"Error\", () => {\n it(\"should stringify\", test(new Error(\"test\"), \"new Error('test')\"));\n });\n\n describe(\"unknown native type\", () => {\n it(\n \"should be omitted\",\n test(\n {\n k:\n typeof (process as any) === \"undefined\"\n ? window.navigator\n : process\n },\n \"{}\"\n )\n );\n });\n });\n\n describeIf(\"ES6\", typeof (Array as any).from === \"function\", () => {\n describeIf(\"Map\", typeof (Map as any) === \"function\", () => {\n it(\n \"should stringify\",\n test(new Map([[\"key\", \"value\"]]), \"new Map([['key','value']])\")\n );\n });\n\n describeIf(\"Set\", typeof (Set as any) === \"function\", () => {\n it(\n \"should stringify\",\n test(new Set([\"key\", \"value\"]), \"new Set(['key','value'])\")\n );\n });\n\n describe(\"arrow functions\", () => {\n describe(\n \"should stringify\",\n cases([\n \"(a, b) => a + b\",\n \"o => { return o.a + o.b; }\",\n \"(a, b) => { if (a) { return b; } }\",\n \"(a, b) => ({ [a]: b })\",\n \"a => b => () => a + b\"\n ])\n );\n\n it(\n \"should reindent function bodies\",\n test(\n evalValue(\n \" () => {\\n\" +\n \" if (true) {\\n\" +\n ' return \"hello\";\\n' +\n \" }\\n\" +\n \" }\"\n ),\n '() => {\\n if (true) {\\n return \"hello\";\\n }\\n}',\n 2\n )\n );\n\n describeIf(\"arrows with patterns\", isSupported(\"({x}) => x\"), () => {\n describe(\n \"should stringify\",\n cases([\n \"({ x, y }) => x + y\",\n \"({ x, y }) => { if (x === '}') { return y; } }\",\n \"({ x, y = /[/})]/.test(x) }) => { return y ? x : 0; }\"\n ])\n );\n });\n });\n\n describe(\"generators\", () => {\n it(\"should stringify\", testRoundTrip(\"function* (x) { yield x; }\"));\n });\n\n describe(\"class notation\", () => {\n it(\"should stringify classes\", testRoundTrip(\"class {}\"));\n it(\n \"should stringify class and method\",\n testRoundTrip(\"class { method() {} }\")\n );\n it(\n \"should stringify with newline\",\n testRoundTrip(\"class\\n{ method() {} }\")\n );\n it(\n \"should stringify with comment\",\n testRoundTrip(\"class/*test*/\\n{ method() {} }\")\n );\n });\n\n describe(\"method notation\", () => {\n it(\"should stringify\", testRoundTrip(\"{a(b, c) { return b + c; }}\"));\n\n it(\n \"should stringify generator methods\",\n testRoundTrip(\"{*a(b) { yield b; }}\")\n );\n\n describe(\n \"should not be fooled by tricky names\",\n cases([\n \"{'function a'(b, c) { return b + c; }}\",\n \"{'a(a'(b, c) { return b + c; }}\",\n \"{'() => function '() {}}\",\n \"{'['() { return x[y]()\\n{ return true; }}}\",\n \"{'() { return false;//'() { return true;\\n}}\"\n ])\n );\n\n it(\n \"should not be fooled by tricky generator names\",\n testRoundTrip(\"{*'function a'(b, c) { return b + c; }}\")\n );\n\n it(\n \"should not be fooled by empty names\",\n testRoundTrip(\"{''(b, c) { return b + c; }}\")\n );\n\n it(\"should not be fooled by keys that look like functions\", () => {\n const fn = evalValue('{ \"() => \": () => () => 42 }')[\"() => \"];\n expect(stringify(fn)).toEqual(\"() => () => 42\");\n });\n\n describe(\n \"should not be fooled by arrow functions\",\n cases([\n \"{a:(b, c) => b + c}\",\n \"{a:a => a + 1}\",\n \"{'() => ':() => () => 42}\",\n '{\\'() => \"\\':() => \"() {//\"}',\n '{\\'() => \"\\':() => \"() {`//\"}',\n '{\\'() => \"\\':() => \"() {`${//\"}',\n '{\\'() => \"\\':() => \"() {/*//\"}',\n satisfies(process.versions.node, \"<=4 || >=10\")\n ? \"{'a => function ':a => function () { return a + 1; }}\"\n : undefined\n ])\n );\n\n describe(\n \"should not be fooled by regexp literals\",\n cases([\n \"{' '(s) { return /}/.test(s); }}\",\n \"{' '(s) { return /abc/ .test(s); }}\",\n \"{' '() { return x / y; // /}\\n}}\",\n \"{' '() { return / y; }//* } */}}\",\n \"{' '() { return delete / y; }/.x}}\",\n \"{' '() { switch (x) { case / y; }}/: }}}\",\n \"{' '() { if (x) return; else / y;}/; }}\",\n \"{' '() { return x in / y;}/; }}\",\n \"{' '() { return x instanceof / y;}/; }}\",\n \"{' '() { return new / y;}/.x; }}\",\n \"{' '() { throw / y;}/.x; }}\",\n \"{' '() { return typeof / y;}/; }}\",\n \"{' '() { void / y;}/; }}\",\n \"{' '() { return x, / y;}/; }}\",\n \"{' '() { return x; / y;}/; }}\",\n \"{' '() { return { x: / y;}/ }; }}\",\n \"{' '() { return x + / y;}/.x; }}\",\n \"{' '() { return x - / y;}/.x; }}\",\n \"{' '() { return !/ y;}/; }}\",\n \"{' '() { return ~/ y;}/.x; }}\",\n \"{' '() { return x && / y;}/; }}\",\n \"{' '() { return x || / y;}/; }}\",\n \"{' '() { return x ^ / y;}/.x; }}\",\n \"{' '() { return x * / y;}/.x; }}\",\n \"{' '() { return x / / y;}/.x; }}\",\n \"{' '() { return x % / y;}/.x; }}\",\n \"{' '() { return x < / y;}/.x; }}\",\n \"{' '() { return x > / y;}/.x; }}\",\n \"{' '() { return x <= / y;}/.x; }}\",\n \"{' '() { return x /= / y;}/.x; }}\",\n \"{' '() { return x ? / y;}/ : false; }}\"\n ])\n );\n\n describe(\"should not be fooled by computed names\", () => {\n it(\n \"1\",\n test(\n evalValue('{ [\"foobar\".slice(3)](x) { return x + 1; } }'),\n \"{bar(x) { return x + 1; }}\"\n )\n );\n\n it(\n \"2\",\n test(\n evalValue(\n '{[((s,a,b)=>a+s(a)+\",\"+s(b)+b)(JSON.stringify,\"[((s,a,b)=>a+s(a)+\\\\\",\\\\\"+s(b)+b)(JSON.stringify,\",\")]() {}\")]() {}}'\n ),\n '{\\'[((s,a,b)=>a+s(a)+\",\"+s(b)+b)(JSON.stringify,\"[((s,a,b)=>a+s(a)+\\\\\\\\\",\\\\\\\\\"+s(b)+b)(JSON.stringify,\",\")]() {}\")]() {}\\'() {}}'\n )\n );\n\n it(\n \"3\",\n test(\n evalValue(\n '{[`over${`6${\"0\".repeat(3)}`.replace(\"6\", \"9\")}`]() { this.activateHair(); }}'\n ),\n \"{over9000() { this.activateHair(); }}\"\n )\n );\n\n it(\n \"4\",\n test(evalValue(\"{[\\\"() {'\\\"]() {''}}\"), \"{'() {\\\\''() {''}}\")\n );\n\n it(\"5\", test(evalValue('{[\"() {`\"]() {``}}'), \"{'() {`'() {``}}\"));\n\n it(\n \"6\",\n test(\n evalValue('{[\"() {/*\"]() {/*`${()=>{/*}*/}}'),\n \"{'() {/*'() {/*`${()=>{/*}*/}}\"\n )\n );\n });\n\n // These two cases demonstrate that branching on\n // METHOD_NAMES_ARE_QUOTED is unavoidable--you can't write code\n // without it that will pass both of these cases on both node.js 4\n // and node.js 10. (If you think you can, consider that the name and\n // toString of the first case when executed on node.js 10 are\n // identical to the name and toString of the second case when\n // executed on node.js 4, so good luck telling them apart without\n // knowing which node you're on.)\n describe(\"should handle different versions of node correctly\", () => {\n it(\n \"1\",\n test(\n evalValue(\n '{[((s,a,b)=>a+s(a)+\",\"+s(b)+b)(JSON.stringify,\"[((s,a,b)=>a+s(a)+\\\\\",\\\\\"+s(b)+b)(JSON.stringify,\",\")]() { return 0; /*\")]() { return 0; /*() {/* */ return 1;}}'\n ),\n '{\\'[((s,a,b)=>a+s(a)+\",\"+s(b)+b)(JSON.stringify,\"[((s,a,b)=>a+s(a)+\\\\\\\\\",\\\\\\\\\"+s(b)+b)(JSON.stringify,\",\")]() { return 0; /*\")]() { return 0; /*\\'() { return 0; /*() {/* */ return 1;}}'\n )\n );\n\n it(\n \"2\",\n test(\n evalValue(\n '{\\'[((s,a,b)=>a+s(a)+\",\"+s(b)+b)(JSON.stringify,\"[((s,a,b)=>a+s(a)+\\\\\\\\\",\\\\\\\\\"+s(b)+b)(JSON.stringify,\",\")]() { return 0; /*\")]() { return 0; /*\\'() {/* */ return 1;}}'\n ),\n '{\\'[((s,a,b)=>a+s(a)+\",\"+s(b)+b)(JSON.stringify,\"[((s,a,b)=>a+s(a)+\\\\\\\\\",\\\\\\\\\"+s(b)+b)(JSON.stringify,\",\")]() { return 0; /*\")]() { return 0; /*\\'() {/* */ return 1;}}'\n )\n );\n });\n\n it(\n \"should not be fooled by comments\",\n test(\n evalValue(\n \"{'method' /* a comment! */ () /* another comment! */ {}}\"\n ),\n \"{method() /* another comment! */ {}}\"\n )\n );\n\n it(\"should stringify extracted methods\", () => {\n const fn = evalValue(\"{ foo(x) { return x + 1; } }\").foo;\n expect(stringify(fn)).toEqual(\"function foo(x) { return x + 1; }\");\n });\n\n it(\"should stringify extracted generators\", () => {\n const fn = evalValue(\"{ *foo(x) { yield x; } }\").foo;\n expect(stringify(fn)).toEqual(\"function* foo(x) { yield x; }\");\n });\n\n it(\"should stringify extracted methods with tricky names\", () => {\n const fn = evalValue('{ \"a(a\"(x) { return x + 1; } }')[\"a(a\"];\n expect(stringify(fn)).toEqual(\"function (x) { return x + 1; }\");\n });\n\n it(\"should stringify extracted methods with arrow-like tricky names\", () => {\n const fn = evalValue('{ \"() => function \"(x) { return x + 1; } }')[\n \"() => function \"\n ];\n expect(stringify(fn)).toEqual(\"function (x) { return x + 1; }\");\n });\n\n it(\"should stringify extracted methods with empty names\", () => {\n const fn = evalValue('{ \"\"(x) { return x + 1; } }')[\"\"];\n expect(stringify(fn)).toEqual(\"function (x) { return x + 1; }\");\n });\n\n it(\"should handle transplanted names\", () => {\n const fn = evalValue(\"{ foo(x) { return x + 1; } }\").foo;\n\n expect(stringify({ bar: fn })).toEqual(\n \"{bar:function foo(x) { return x + 1; }}\"\n );\n });\n\n it(\"should handle transplanted names with generators\", () => {\n const fn = evalValue(\"{ *foo(x) { yield x; } }\").foo;\n\n expect(stringify({ bar: fn })).toEqual(\n \"{bar:function* foo(x) { yield x; }}\"\n );\n });\n\n it(\n \"should reindent methods\",\n test(\n evalValue(\n \" {\\n\" +\n \" fn() {\\n\" +\n \" if (true) {\\n\" +\n ' return \"hello\";\\n' +\n \" }\\n\" +\n \" }\\n\" +\n \" }\"\n ),\n '{\\n fn() {\\n if (true) {\\n return \"hello\";\\n }\\n }\\n}',\n 2\n )\n );\n });\n });\n\n describe(\"ES2017\", () => {\n describeIf(\n \"async functions\",\n isSupported(\"(async function () {})\"),\n () => {\n it(\n \"should stringify\",\n testRoundTrip(\"async function (x) { await x; }\")\n );\n\n it(\"should gracefully handle unexpected Function.toString formats\", () => {\n const origToString = Function.prototype.toString;\n\n Function.prototype.toString = () => \"{nope}\";\n\n try {\n expect(stringify(evalValue(\"async function () {}\"))).toEqual(\n \"void '{nope}'\"\n );\n } finally {\n Function.prototype.toString = origToString;\n }\n });\n }\n );\n\n describeIf(\"async arrows\", isSupported(\"async () => {}\"), () => {\n describe(\n \"should stringify\",\n cases([\n \"async (x) => x + 1\",\n \"async x => x + 1\",\n \"async x => { await x.then(y => y + 1); }\"\n ])\n );\n\n describe(\n \"should stringify as object properties\",\n cases([\n \"{f:async a => a + 1}\",\n satisfies(process.versions.node, \"<=4 || >=10\")\n ? \"{'async a => function ':async a => function () { return a + 1; }}\"\n : undefined\n ])\n );\n });\n });\n\n describe(\"ES2018\", () => {\n describeIf(\n \"async generators\",\n isSupported(\"(async function* () {})\"),\n () => {\n it(\n \"should stringify\",\n testRoundTrip(\"async function* (x) { yield x; }\")\n );\n\n it(\"should gracefully handle unexpected Function.toString formats\", () => {\n const origToString = Function.prototype.toString;\n\n Function.prototype.toString = () => \"{nope}\";\n\n try {\n expect(stringify(evalValue(\"async function* () {}\"))).toEqual(\n \"void '{nope}'\"\n );\n } finally {\n Function.prototype.toString = origToString;\n }\n });\n }\n );\n });\n\n describe(\"global\", () => {\n it(\n \"should access the global in the current environment\",\n testRoundTrip(\"Function('return this')()\")\n );\n });\n });\n\n describe(\"circular references\", () => {\n it(\"should omit circular references\", () => {\n const obj: any = { key: \"value\" };\n obj.obj = obj;\n\n const result = stringify(obj);\n\n expect(result).toEqual(\"{key:'value'}\");\n });\n\n it(\"should restore value\", () => {\n const obj: any = { key: \"value\" };\n obj.obj = obj;\n\n const result = stringify(obj, null, null, { references: true });\n\n expect(result).toEqual(\n \"(function(){var x={key:'value'};x.obj=x;return x;}())\"\n );\n });\n\n it(\"should omit recursive array value\", () => {\n const obj: any = [1, 2, 3];\n obj.push(obj);\n\n const result = stringify(obj);\n\n expect(result).toEqual(\"[1,2,3,undefined]\");\n });\n\n it(\"should restore array value\", () => {\n const obj: any = [1, 2, 3];\n obj.push(obj);\n\n const result = stringify(obj, null, null, { references: true });\n\n expect(result).toEqual(\n \"(function(){var x=[1,2,3,undefined];x[3]=x;return x;}())\"\n );\n });\n\n it(\"should print repeated values when no references enabled\", () => {\n const obj: any = {};\n const child = {};\n\n obj.a = child;\n obj.b = child;\n\n const result = stringify(obj);\n\n expect(result).toEqual(\"{a:{},b:{}}\");\n });\n\n it(\"should restore repeated values\", () => {\n const obj: any = {};\n const child = {};\n\n obj.a = child;\n obj.b = child;\n\n const result = stringify(obj, null, null, { references: true });\n\n expect(result).toEqual(\"(function(){var x={a:{}};x.b=x.a;return x;}())\");\n });\n\n it(\"should restore repeated values with indentation\", function() {\n const obj: any = {};\n const child = {};\n\n obj.a = child;\n obj.b = child;\n\n const result = stringify(obj, null, 2, { references: true });\n\n expect(result).toEqual(\n \"(function () {\\nvar x = {\\n a: {}\\n};\\nx.b = x.a;\\nreturn x;\\n}())\"\n );\n });\n });\n\n describe(\"custom indent\", () => {\n it(\"string\", () => {\n const result = stringify(\n {\n test: [1, 2, 3],\n nested: {\n key: \"value\"\n }\n },\n null,\n \"\\t\"\n );\n\n expect(result).toEqual(\n \"{\\n\" +\n \"\\ttest: [\\n\\t\\t1,\\n\\t\\t2,\\n\\t\\t3\\n\\t],\\n\" +\n \"\\tnested: {\\n\\t\\tkey: 'value'\\n\\t}\\n\" +\n \"}\"\n );\n });\n\n it(\"integer\", () => {\n const result = stringify(\n {\n test: [1, 2, 3],\n nested: {\n key: \"value\"\n }\n },\n null,\n 2\n );\n\n expect(result).toEqual(\n \"{\\n\" +\n \" test: [\\n 1,\\n 2,\\n 3\\n ],\\n\" +\n \" nested: {\\n key: 'value'\\n }\\n\" +\n \"}\"\n );\n });\n\n it(\"float\", () => {\n const result = stringify(\n {\n test: [1, 2, 3],\n nested: {\n key: \"value\"\n }\n },\n null,\n 2.6\n );\n\n expect(result).toEqual(\n \"{\\n\" +\n \" test: [\\n 1,\\n 2,\\n 3\\n ],\\n\" +\n \" nested: {\\n key: 'value'\\n }\\n\" +\n \"}\"\n );\n });\n });\n\n describe(\"replacer function\", () => {\n it(\"should allow custom replacements\", () => {\n let callCount = 0;\n\n const result = stringify(\n {\n test: \"value\"\n },\n function(value, indent, next) {\n callCount++;\n\n if (typeof value === \"string\") {\n return '\"hello\"';\n }\n\n return next(value);\n }\n );\n\n expect(callCount).toEqual(2);\n expect(result).toEqual('{test:\"hello\"}');\n });\n\n it(\"change primitive to object\", () => {\n const result = stringify(\n {\n test: 10\n },\n function(value, indent, next) {\n if (typeof value === \"number\") {\n return next({ obj: \"value\" });\n }\n\n return next(value);\n }\n );\n\n expect(result).toEqual(\"{test:{obj:'value'}}\");\n });\n\n it(\"change object to primitive\", () => {\n const result = stringify(\n {\n test: 10\n },\n value => Object.prototype.toString.call(value)\n );\n\n expect(result).toEqual(\"[object Object]\");\n });\n });\n\n describe(\"max depth\", () => {\n const obj = { a: { b: { c: 1 } } };\n\n it(\"should get all object\", test(obj, \"{a:{b:{c:1}}}\"));\n\n it(\n \"should get part of the object\",\n test(obj, \"{a:{b:{}}}\", null, { maxDepth: 2 })\n );\n\n it(\n \"should get part of the object when tracking references\",\n test(obj, \"{a:{b:{}}}\", null, { maxDepth: 2, references: true })\n );\n });\n\n describe(\"property based\", () => {\n it(\"should produce string evaluating to the original value\", () => {\n fc.assert(\n fc.property(fc.anything(), value => {\n expect(evalValue(stringify(value))).toEqual(value);\n })\n );\n });\n });\n});\n"]}
import { Next } from "./types";
/**
* Stringify an object of keys and values.
*/
export declare function objectToString(obj: any, indent: string, next: Next): string;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const quote_1 = require("./quote");
const function_1 = require("./function");
/**
* Stringify an object of keys and values.
*/
function objectToString(obj, indent, next) {
const eol = indent ? "\n" : "";
// Iterate over object keys and concat string together.
const values = Object.keys(obj)
.reduce(function (values, key) {
if (typeof obj[key] === "function") {
const parser = new function_1.FunctionParser(obj[key], indent, next, key);
const result = parser.stringify();
values.push(indent + result.split("\n").join(`\n${indent}`));
return values;
}
const result = next(obj[key], key);
// Omit `undefined` object entries.
if (result === undefined)
return values;
// String format the value data.
const value = result.split("\n").join(`\n${indent}`);
values.push(`${indent}${quote_1.quoteKey(key, next)}:${indent ? " " : ""}${value}`);
return values;
}, [])
.join(`,${eol}`);
// Avoid new lines in an empty object.
if (values === "")
return "{}";
return `{${eol}${values}${eol}}`;
}
exports.objectToString = objectToString;
//# sourceMappingURL=object.js.map
{"version":3,"file":"object.js","sourceRoot":"","sources":["../src/object.ts"],"names":[],"mappings":";;AACA,mCAAmC;AACnC,yCAA4C;AAE5C;;GAEG;AACH,SAAgB,cAAc,CAAC,GAAQ,EAAE,MAAc,EAAE,IAAU;IACjE,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAE/B,uDAAuD;IACvD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;SAC5B,MAAM,CACL,UAAS,MAAM,EAAE,GAAG;QAClB,IAAI,OAAO,GAAG,CAAC,GAAG,CAAC,KAAK,UAAU,EAAE;YAClC,MAAM,MAAM,GAAG,IAAI,yBAAc,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YAC/D,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC;YAC7D,OAAO,MAAM,CAAC;SACf;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;QAEnC,mCAAmC;QACnC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC;QAExC,gCAAgC;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC;QAErD,MAAM,CAAC,IAAI,CACT,GAAG,MAAM,GAAG,gBAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,CAC/D,CAAC;QAEF,OAAO,MAAM,CAAC;IAChB,CAAC,EACD,EAAc,CACf;SACA,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;IAEnB,sCAAsC;IACtC,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAE/B,OAAO,IAAI,GAAG,GAAG,MAAM,GAAG,GAAG,GAAG,CAAC;AACnC,CAAC;AApCD,wCAoCC","sourcesContent":["import { Next } from \"./types\";\nimport { quoteKey } from \"./quote\";\nimport { FunctionParser } from \"./function\";\n\n/**\n * Stringify an object of keys and values.\n */\nexport function objectToString(obj: any, indent: string, next: Next) {\n const eol = indent ? \"\\n\" : \"\";\n\n // Iterate over object keys and concat string together.\n const values = Object.keys(obj)\n .reduce(\n function(values, key) {\n if (typeof obj[key] === \"function\") {\n const parser = new FunctionParser(obj[key], indent, next, key);\n const result = parser.stringify();\n values.push(indent + result.split(\"\\n\").join(`\\n${indent}`));\n return values;\n }\n\n const result = next(obj[key], key);\n\n // Omit `undefined` object entries.\n if (result === undefined) return values;\n\n // String format the value data.\n const value = result.split(\"\\n\").join(`\\n${indent}`);\n\n values.push(\n `${indent}${quoteKey(key, next)}:${indent ? \" \" : \"\"}${value}`\n );\n\n return values;\n },\n [] as string[]\n )\n .join(`,${eol}`);\n\n // Avoid new lines in an empty object.\n if (values === \"\") return \"{}\";\n\n return `{${eol}${values}${eol}}`;\n}\n"]}
import { Next } from "./types";
/**
* Quote a string.
*/
export declare function quoteString(str: string): string;
/**
* Test for valid JavaScript identifier.
*/
export declare const IS_VALID_IDENTIFIER: RegExp;
/**
* Check if a variable name is valid.
*/
export declare function isValidVariableName(name: PropertyKey): name is string;
/**
* Quote JavaScript key access.
*/
export declare function quoteKey(key: PropertyKey, next: Next): string | undefined;
/**
* Serialize the path to a string.
*/
export declare function stringifyPath(path: PropertyKey[], next: Next): string;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Match all characters that need to be escaped in a string. Modified from
* source to match single quotes instead of double.
*
* Source: https://github.com/douglascrockford/JSON-js/blob/master/json2.js
*/
const ESCAPABLE = /[\\\'\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
/**
* Map of characters to escape characters.
*/
const META_CHARS = new Map([
["\b", "\\b"],
["\t", "\\t"],
["\n", "\\n"],
["\f", "\\f"],
["\r", "\\r"],
["'", "\\'"],
['"', '\\"'],
["\\", "\\\\"]
]);
/**
* Escape any character into its literal JavaScript string.
*
* @param {string} char
* @return {string}
*/
function escapeChar(char) {
return (META_CHARS.get(char) ||
`\\u${`0000${char.charCodeAt(0).toString(16)}`.slice(-4)}`);
}
/**
* Quote a string.
*/
function quoteString(str) {
return `'${str.replace(ESCAPABLE, escapeChar)}'`;
}
exports.quoteString = quoteString;
/**
* JavaScript reserved keywords.
*/
const RESERVED_WORDS = new Set(("break else new var case finally return void catch for switch while " +
"continue function this with default if throw delete in try " +
"do instanceof typeof abstract enum int short boolean export " +
"interface static byte extends long super char final native synchronized " +
"class float package throws const goto private transient debugger " +
"implements protected volatile double import public let yield").split(" "));
/**
* Test for valid JavaScript identifier.
*/
exports.IS_VALID_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
/**
* Check if a variable name is valid.
*/
function isValidVariableName(name) {
return (typeof name === "string" &&
!RESERVED_WORDS.has(name) &&
exports.IS_VALID_IDENTIFIER.test(name));
}
exports.isValidVariableName = isValidVariableName;
/**
* Quote JavaScript key access.
*/
function quoteKey(key, next) {
return isValidVariableName(key) ? key : next(key);
}
exports.quoteKey = quoteKey;
/**
* Serialize the path to a string.
*/
function stringifyPath(path, next) {
let result = "";
for (const key of path) {
if (isValidVariableName(key)) {
result += `.${key}`;
}
else {
result += `[${next(key)}]`;
}
}
return result;
}
exports.stringifyPath = stringifyPath;
//# sourceMappingURL=quote.js.map
{"version":3,"file":"quote.js","sourceRoot":"","sources":["../src/quote.ts"],"names":[],"mappings":";;AAEA;;;;;GAKG;AACH,MAAM,SAAS,GAAG,0HAA0H,CAAC;AAE7I;;GAEG;AACH,MAAM,UAAU,GAAG,IAAI,GAAG,CAAiB;IACzC,CAAC,IAAI,EAAE,KAAK,CAAC;IACb,CAAC,IAAI,EAAE,KAAK,CAAC;IACb,CAAC,IAAI,EAAE,KAAK,CAAC;IACb,CAAC,IAAI,EAAE,KAAK,CAAC;IACb,CAAC,IAAI,EAAE,KAAK,CAAC;IACb,CAAC,GAAG,EAAE,KAAK,CAAC;IACZ,CAAC,GAAG,EAAE,KAAK,CAAC;IACZ,CAAC,IAAI,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAEH;;;;;GAKG;AACH,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,CACL,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;QACpB,MAAM,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAC3D,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,WAAW,CAAC,GAAW;IACrC,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,UAAU,CAAC,GAAG,CAAC;AACnD,CAAC;AAFD,kCAEC;AAED;;GAEG;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,CAC5B,CACE,qEAAqE;IACrE,6DAA6D;IAC7D,8DAA8D;IAC9D,0EAA0E;IAC1E,mEAAmE;IACnE,8DAA8D,CAC/D,CAAC,KAAK,CAAC,GAAG,CAAC,CACb,CAAC;AAEF;;GAEG;AACU,QAAA,mBAAmB,GAAG,4BAA4B,CAAC;AAEhE;;GAEG;AACH,SAAgB,mBAAmB,CAAC,IAAiB;IACnD,OAAO,CACL,OAAO,IAAI,KAAK,QAAQ;QACxB,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC;QACzB,2BAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAC/B,CAAC;AACJ,CAAC;AAND,kDAMC;AAED;;GAEG;AACH,SAAgB,QAAQ,CAAC,GAAgB,EAAE,IAAU;IACnD,OAAO,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACpD,CAAC;AAFD,4BAEC;AAED;;GAEG;AACH,SAAgB,aAAa,CAAC,IAAmB,EAAE,IAAU;IAC3D,IAAI,MAAM,GAAG,EAAE,CAAC;IAEhB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE;QACtB,IAAI,mBAAmB,CAAC,GAAG,CAAC,EAAE;YAC5B,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;SACrB;aAAM;YACL,MAAM,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;SAC5B;KACF;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAZD,sCAYC","sourcesContent":["import { Next } from \"./types\";\n\n/**\n * Match all characters that need to be escaped in a string. Modified from\n * source to match single quotes instead of double.\n *\n * Source: https://github.com/douglascrockford/JSON-js/blob/master/json2.js\n */\nconst ESCAPABLE = /[\\\\\\'\\x00-\\x1f\\x7f-\\x9f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g;\n\n/**\n * Map of characters to escape characters.\n */\nconst META_CHARS = new Map<string, string>([\n [\"\\b\", \"\\\\b\"],\n [\"\\t\", \"\\\\t\"],\n [\"\\n\", \"\\\\n\"],\n [\"\\f\", \"\\\\f\"],\n [\"\\r\", \"\\\\r\"],\n [\"'\", \"\\\\'\"],\n ['\"', '\\\\\"'],\n [\"\\\\\", \"\\\\\\\\\"]\n]);\n\n/**\n * Escape any character into its literal JavaScript string.\n *\n * @param {string} char\n * @return {string}\n */\nfunction escapeChar(char: string) {\n return (\n META_CHARS.get(char) ||\n `\\\\u${`0000${char.charCodeAt(0).toString(16)}`.slice(-4)}`\n );\n}\n\n/**\n * Quote a string.\n */\nexport function quoteString(str: string) {\n return `'${str.replace(ESCAPABLE, escapeChar)}'`;\n}\n\n/**\n * JavaScript reserved keywords.\n */\nconst RESERVED_WORDS = new Set(\n (\n \"break else new var case finally return void catch for switch while \" +\n \"continue function this with default if throw delete in try \" +\n \"do instanceof typeof abstract enum int short boolean export \" +\n \"interface static byte extends long super char final native synchronized \" +\n \"class float package throws const goto private transient debugger \" +\n \"implements protected volatile double import public let yield\"\n ).split(\" \")\n);\n\n/**\n * Test for valid JavaScript identifier.\n */\nexport const IS_VALID_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\n/**\n * Check if a variable name is valid.\n */\nexport function isValidVariableName(name: PropertyKey): name is string {\n return (\n typeof name === \"string\" &&\n !RESERVED_WORDS.has(name) &&\n IS_VALID_IDENTIFIER.test(name)\n );\n}\n\n/**\n * Quote JavaScript key access.\n */\nexport function quoteKey(key: PropertyKey, next: Next) {\n return isValidVariableName(key) ? key : next(key);\n}\n\n/**\n * Serialize the path to a string.\n */\nexport function stringifyPath(path: PropertyKey[], next: Next) {\n let result = \"\";\n\n for (const key of path) {\n if (isValidVariableName(key)) {\n result += `.${key}`;\n } else {\n result += `[${next(key)}]`;\n }\n }\n\n return result;\n}\n"]}
import { Next } from "./types";
/**
* Stringify a value recursively.
*/
export declare function toString(value: any, space: string, next: Next): string | undefined;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const quote_1 = require("./quote");
const array_1 = require("./array");
const object_1 = require("./object");
const function_1 = require("./function");
/**
* Stringify primitive values.
*/
const PRIMITIVE_TYPES = {
string: quote_1.quoteString,
number: (value) => (Object.is(value, -0) ? "-0" : String(value)),
boolean: String,
symbol: (value, space, next) => {
const key = Symbol.keyFor(value);
if (key !== undefined)
return `Symbol.for(${next(key)})`;
// ES2018 `Symbol.description`.
return `Symbol(${next(value.description)})`;
},
undefined: String
};
/**
* Stringify global variable access.
*/
function globalToString(value, space, next) {
return `Function(${next("return this")})()`;
}
/**
* Convert JavaScript objects into strings.
*/
const OBJECT_TYPES = {
"[object Array]": array_1.arrayToString,
"[object Object]": object_1.objectToString,
"[object Error]": function (error, space, next) {
return `new Error(${next(error.message)})`;
},
"[object Date]": function (date) {
return `new Date(${date.getTime()})`;
},
"[object String]": function (str, space, next) {
return `new String(${next(str.toString())})`;
},
"[object Number]": function (num) {
return `new Number(${num})`;
},
"[object Boolean]": function (bool) {
return `new Boolean(${bool})`;
},
"[object Set]": function (set, space, next) {
return `new Set(${next(Array.from(set))})`;
},
"[object Map]": function (map, space, next) {
return `new Map(${next(Array.from(map))})`;
},
"[object RegExp]": String,
"[object Function]": function_1.functionToString,
"[object GeneratorFunction]": function_1.functionToString,
"[object AsyncFunction]": function_1.functionToString,
"[object AsyncGeneratorFunction]": function_1.functionToString,
"[object global]": globalToString,
"[object Window]": globalToString
};
/**
* Stringify a value recursively.
*/
function toString(value, space, next) {
if (value === null)
return "null";
const typeOf = typeof value;
if (PRIMITIVE_TYPES.hasOwnProperty(typeOf)) {
return PRIMITIVE_TYPES[typeOf](value, space, next);
}
// Handle buffer objects before object types (node < 6 was an object, node >= 6 is a `Uint8Array`).
if (typeof Buffer === "function" && Buffer.isBuffer(value)) {
return `new Buffer(${next(value.toString())})`;
}
// Use the internal object string to select stringify method.
const toString = Object.prototype.toString.call(value);
// Convert objects into strings.
if (OBJECT_TYPES.hasOwnProperty(toString)) {
return OBJECT_TYPES[toString](value, space, next);
}
}
exports.toString = toString;
//# sourceMappingURL=stringify.js.map
{"version":3,"file":"stringify.js","sourceRoot":"","sources":["../src/stringify.ts"],"names":[],"mappings":"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","sourcesContent":["import { quoteString } from \"./quote\";\nimport { Next } from \"./types\";\nimport { arrayToString } from \"./array\";\nimport { objectToString } from \"./object\";\nimport { functionToString } from \"./function\";\n\n/**\n * Stringify primitive values.\n */\nconst PRIMITIVE_TYPES = {\n string: quoteString,\n number: (value: number) => (Object.is(value, -0) ? \"-0\" : String(value)),\n boolean: String,\n symbol: (value: symbol, space: string, next: Next) => {\n const key = Symbol.keyFor(value);\n\n if (key !== undefined) return `Symbol.for(${next(key)})`;\n\n // ES2018 `Symbol.description`.\n return `Symbol(${next((value as any).description)})`;\n },\n undefined: String\n};\n\n/**\n * Stringify global variable access.\n */\nfunction globalToString(value: any, space: string, next: Next) {\n return `Function(${next(\"return this\")})()`;\n}\n\n/**\n * Convert JavaScript objects into strings.\n */\nconst OBJECT_TYPES = {\n \"[object Array]\": arrayToString,\n \"[object Object]\": objectToString,\n \"[object Error]\": function(error: Error, space: string, next: Next) {\n return `new Error(${next(error.message)})`;\n },\n \"[object Date]\": function(date: Date) {\n return `new Date(${date.getTime()})`;\n },\n \"[object String]\": function(str: String, space: string, next: Next) {\n return `new String(${next(str.toString())})`;\n },\n \"[object Number]\": function(num: number) {\n return `new Number(${num})`;\n },\n \"[object Boolean]\": function(bool: boolean) {\n return `new Boolean(${bool})`;\n },\n \"[object Set]\": function(set: Set<any>, space: string, next: Next) {\n return `new Set(${next(Array.from(set))})`;\n },\n \"[object Map]\": function(map: Map<any, any>, space: string, next: Next) {\n return `new Map(${next(Array.from(map))})`;\n },\n \"[object RegExp]\": String,\n \"[object Function]\": functionToString,\n \"[object GeneratorFunction]\": functionToString,\n \"[object AsyncFunction]\": functionToString,\n \"[object AsyncGeneratorFunction]\": functionToString,\n \"[object global]\": globalToString,\n \"[object Window]\": globalToString\n};\n\n/**\n * Stringify a value recursively.\n */\nexport function toString(value: any, space: string, next: Next) {\n if (value === null) return \"null\";\n\n const typeOf = typeof value;\n\n if (PRIMITIVE_TYPES.hasOwnProperty(typeOf)) {\n return PRIMITIVE_TYPES[typeOf as keyof typeof PRIMITIVE_TYPES](\n value,\n space,\n next\n );\n }\n\n // Handle buffer objects before object types (node < 6 was an object, node >= 6 is a `Uint8Array`).\n if (typeof (Buffer as unknown) === \"function\" && Buffer.isBuffer(value)) {\n return `new Buffer(${next(value.toString())})`;\n }\n\n // Use the internal object string to select stringify method.\n const toString = Object.prototype.toString.call(value);\n\n // Convert objects into strings.\n if (OBJECT_TYPES.hasOwnProperty(toString)) {\n return OBJECT_TYPES[toString as keyof typeof OBJECT_TYPES](\n value,\n space,\n next\n );\n }\n}\n"]}
/**
* Call `next()` every time you want to stringify a new value.
*/
export declare type Next = (value: any, key?: PropertyKey) => string | undefined;
/**
* Stringify a value.
*/
export declare type ToString = (value: any, space: string, next: Next) => string | undefined;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=types.js.map
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Call `next()` every time you want to stringify a new value.\n */\nexport type Next = (value: any, key?: PropertyKey) => string | undefined;\n\n/**\n * Stringify a value.\n */\nexport type ToString = (\n value: any,\n space: string,\n next: Next\n) => string | undefined;\n"]}
+55
-11
{
"name": "javascript-stringify",
"version": "1.6.0",
"version": "2.0.0",
"description": "Stringify is to `eval` as `JSON.stringify` is to `JSON.parse`",
"main": "javascript-stringify.js",
"typings": "javascript-stringify.d.ts",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"javascript-stringify.js",
"javascript-stringify.d.ts"
"dist/"
],
"scripts": {
"test": "istanbul cover node_modules/mocha/bin/_mocha -- -R spec"
"prettier": "prettier --write",
"format": "npm run prettier -- \"{.,src/**}/*.{js,ts,json,md,yml,css}\"",
"lint": "tslint \"src/**/*.ts\" --project tsconfig.json",
"build": "rimraf dist/ && tsc",
"specs": "jest --coverage",
"test": "npm run lint && npm run build && npm run specs",
"prepare": "npm run build"
},

@@ -19,3 +24,5 @@ "repository": "https://github.com/blakeembrey/javascript-stringify.git",

"object",
"string"
"eval",
"string",
"code"
],

@@ -28,8 +35,45 @@ "author": {

"license": "MIT",
"readmeFilename": "README.md",
"bugs": {
"url": "https://github.com/blakeembrey/javascript-stringify/issues"
},
"homepage": "https://github.com/blakeembrey/javascript-stringify",
"jest": {
"roots": [
"<rootDir>/src/"
],
"transform": {
"\\.tsx?$": "ts-jest"
}
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,ts,json,md,yml,css}": [
"npm run prettier",
"git add"
]
},
"publishConfig": {
"access": "public"
},
"devDependencies": {
"chai": "^1.9.1",
"istanbul": "^0.3.0",
"mocha": "^1.21.3"
"@types/jest": "^24.0.9",
"@types/node": "^11.10.4",
"@types/semver": "^5.5.0",
"fast-check": "^1.12.0",
"husky": "^1.3.1",
"jest": "^24.0.0",
"lint-staged": "^8.1.1",
"prettier": "^1.16.1",
"rimraf": "^2.5.4",
"semver": "^5.6.0",
"ts-jest": "^24.0.0",
"tslint": "^5.0.0",
"tslint-config-prettier": "^1.17.0",
"tslint-config-standard": "^8.0.0",
"typescript": "^3.3.1"
}
}
+27
-42

@@ -12,53 +12,38 @@ # JavaScript Stringify

```javascript
```
npm install javascript-stringify --save
bower install javascript-stringify --save
```
### Node
---
```javascript
var javascriptStringify = require('javascript-stringify');
```
<a href="https://www.buymeacoffee.com/blakeembrey" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" ></a>
### AMD
```javascript
define(function (require, exports, module) {
var javascriptStringify = require('javascript-stringify');
});
```
### `<script>` tag
```html
<script src="javascript-stringify.js"></script>
```
## Usage
```javascript
javascriptStringify(value[, replacer [, space [, options]]])
import { stringify } from "javascript-stringify";
```
The API is similar to `JSON.stringify`. However, any value returned by the replacer will be used literally. For this reason, the replacer is passed three arguments - `value`, `indentation` and `stringify`. If you need to continue the stringification process inside your replacer, you can call `stringify(value)` with the new value.
The API is similar `JSON.stringify`:
The `options` object allows some additional configuration:
- `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`
* **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
```javascript
javascriptStringify({}); // "{}"
javascriptStringify(true); // "true"
javascriptStringify('foo'); // "'foo'"
stringify({}); // "{}"
stringify(true); // "true"
stringify("foo"); // "'foo'"
javascriptStringify({ x: 5, y: 6}); // "{x:5,y:6}"
javascriptStringify([1, 2, 3, 'string']); // "[1,2,3,'string']"
stringify({ x: 5, y: 6 }); // "{x:5,y:6}"
stringify([1, 2, 3, "string"]); // "[1,2,3,'string']"
javascriptStringify({ a: { b: { c: 1 } } }, null, null, { maxDepth: 2 }); // "{a:{b:{}}}"
stringify({ a: { b: { c: 1 } } }, null, null, { maxDepth: 2 }); // "{a:{b:{}}}"

@@ -69,3 +54,3 @@ /**

javascriptStringify({ 'some-key': 10 }); // "{'some-key':10}"
stringify({ "some-key": 10 }); // "{'some-key':10}"

@@ -76,3 +61,3 @@ /**

javascriptStringify([/.+/ig, new Number(10), new Date()]); // "[/.+/gi,new Number(10),new Date(1406623295732)]"
stringify([/.+/gi, new Number(10), new Date()]); // "[/.+/gi,new Number(10),new Date(1406623295732)]"

@@ -86,4 +71,4 @@ /**

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

@@ -94,4 +79,4 @@ /**

javascriptStringify({ a: 2 }, null, ' '); // "{\n a: 2\n}"
javascriptStringify({ uno: 1, dos : 2 }, null, '\t'); // "{\n\tuno: 1,\n\tdos: 2\n}"
stringify({ a: 2 }, null, " "); // "{\n a: 2\n}"
stringify({ uno: 1, dos: 2 }, null, "\t"); // "{\n\tuno: 1,\n\tdos: 2\n}"

@@ -102,4 +87,4 @@ /**

javascriptStringify(['test', 'string'], function (value, indent, stringify) {
if (typeof value === 'string') {
stringify(["test", "string"], function(value, indent, stringify) {
if (typeof value === "string") {
return '"' + value.replace(/"/g, '\\"') + '"';

@@ -106,0 +91,0 @@ }

declare function stringify (value: any, replacer?: Function, space?: string | number, options?: javascriptStringify.Options): string;
declare namespace javascriptStringify {
export interface Options {
maxDepth?: number;
references?: boolean;
}
}
export = stringify;
(function (root, stringify) {
/* istanbul ignore else */
if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
// Node.
module.exports = stringify();
} else if (typeof define === 'function' && define.amd) {
// AMD, registers as an anonymous module.
define(function () {
return stringify();
});
} else {
// Browser global.
root.javascriptStringify = stringify();
}
})(this, function () {
/**
* Match all characters that need to be escaped in a string. Modified from
* source to match single quotes instead of double.
*
* Source: https://github.com/douglascrockford/JSON-js/blob/master/json2.js
*/
var ESCAPABLE = /[\\\'\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
/**
* Map of characters to escape characters.
*/
var META_CHARS = {
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
"'": "\\'",
'"': '\\"',
'\\': '\\\\'
};
/**
* Escape any character into its literal JavaScript string.
*
* @param {string} char
* @return {string}
*/
function escapeChar (char) {
var meta = META_CHARS[char];
return meta || '\\u' + ('0000' + char.charCodeAt(0).toString(16)).slice(-4);
};
/**
* JavaScript reserved word list.
*/
var RESERVED_WORDS = {};
/**
* Map reserved words to the object.
*/
(
'break else new var case finally return void catch for switch while ' +
'continue function this with default if throw delete in try ' +
'do instanceof typeof abstract enum int short boolean export ' +
'interface static byte extends long super char final native synchronized ' +
'class float package throws const goto private transient debugger ' +
'implements protected volatile double import public let yield'
).split(' ').map(function (key) {
RESERVED_WORDS[key] = true;
});
/**
* Test for valid JavaScript identifier.
*/
var IS_VALID_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
/**
* Check if a variable name is valid.
*
* @param {string} name
* @return {boolean}
*/
function isValidVariableName (name) {
return !RESERVED_WORDS[name] && IS_VALID_IDENTIFIER.test(name);
}
/**
* Return the global variable name.
*
* @return {string}
*/
function toGlobalVariable (value) {
return 'Function(' + stringify('return this;') + ')()';
}
/**
* Serialize the path to a string.
*
* @param {Array} path
* @return {string}
*/
function toPath (path) {
var result = '';
for (var i = 0; i < path.length; i++) {
if (isValidVariableName(path[i])) {
result += '.' + path[i];
} else {
result += '[' + stringify(path[i]) + ']';
}
}
return result;
}
/**
* Stringify an array of values.
*
* @param {Array} array
* @param {string} indent
* @param {Function} next
* @return {string}
*/
function stringifyArray (array, indent, next) {
// Map array values to their stringified values with correct indentation.
var values = array.map(function (value, index) {
var str = next(value, index);
if (str === undefined) {
return String(str);
}
return indent + str.split('\n').join('\n' + indent);
}).join(indent ? ',\n' : ',');
// Wrap the array in newlines if we have indentation set.
if (indent && values) {
return '[\n' + values + '\n]';
}
return '[' + values + ']';
}
/**
* Stringify a map of values.
*
* @param {Object} object
* @param {string} indent
* @param {Function} next
* @return {string}
*/
function stringifyObject (object, indent, next) {
// Iterate over object keys and concat string together.
var values = Object.keys(object).reduce(function (values, key) {
var value = next(object[key], key);
// Omit `undefined` object values.
if (value === undefined) {
return values;
}
// String format the key and value data.
key = isValidVariableName(key) ? key : stringify(key);
value = String(value).split('\n').join('\n' + indent);
// Push the current object key and value into the values array.
values.push(indent + key + ':' + (indent ? ' ' : '') + value);
return values;
}, []).join(indent ? ',\n' : ',');
// Wrap the object in newlines if we have indentation set.
if (indent && values) {
return '{\n' + values + '\n}';
}
return '{' + values + '}';
}
/**
* Convert JavaScript objects into strings.
*/
var OBJECT_TYPES = {
'[object Array]': stringifyArray,
'[object Object]': stringifyObject,
'[object Error]': function (error) {
return 'new Error(' + stringify(error.message) + ')';
},
'[object Date]': function (date) {
return 'new Date(' + date.getTime() + ')';
},
'[object String]': function (string) {
return 'new String(' + stringify(string.toString()) + ')';
},
'[object Number]': function (number) {
return 'new Number(' + number + ')';
},
'[object Boolean]': function (boolean) {
return 'new Boolean(' + boolean + ')';
},
'[object Uint8Array]': function (array, indent) {
return 'new Uint8Array(' + stringifyArray(array) + ')';
},
'[object Set]': function (array, indent, next) {
if (typeof Array.from === 'function') {
return 'new Set(' + stringify(Array.from(array), indent, next) + ')';
} else return undefined;
},
'[object Map]': function (array, indent, next) {
if (typeof Array.from === 'function') {
return 'new Map(' + stringify(Array.from(array), indent, next) + ')';
} else return undefined;
},
'[object RegExp]': String,
'[object Function]': String,
'[object global]': toGlobalVariable,
'[object Window]': toGlobalVariable
};
/**
* Convert JavaScript primitives into strings.
*/
var PRIMITIVE_TYPES = {
'string': function (string) {
return "'" + string.replace(ESCAPABLE, escapeChar) + "'";
},
'number': String,
'object': String,
'boolean': String,
'symbol': String,
'undefined': String
};
/**
* Convert any value to a string.
*
* @param {*} value
* @param {string} indent
* @param {Function} next
* @return {string}
*/
function stringify (value, indent, next) {
// Convert primitives into strings.
if (Object(value) !== value) {
return PRIMITIVE_TYPES[typeof value](value, indent, next);
}
// Handle buffer objects before recursing (node < 6 was an object, node >= 6 is a `Uint8Array`).
if (typeof Buffer === 'function' && Buffer.isBuffer(value)) {
return 'new Buffer(' + next(value.toString()) + ')';
}
// Use the internal object string to select stringification method.
var toString = OBJECT_TYPES[Object.prototype.toString.call(value)];
// Convert objects into strings.
return toString ? toString(value, indent, next) : undefined;
}
/**
* Stringify an object into the literal string.
*
* @param {*} value
* @param {Function} [replacer]
* @param {(number|string)} [space]
* @param {Object} [options]
* @return {string}
*/
return function (value, replacer, space, options) {
options = options || {}
// Convert the spaces into a string.
if (typeof space !== 'string') {
space = new Array(Math.max(0, space|0) + 1).join(' ');
}
var maxDepth = Number(options.maxDepth) || 100;
var references = !!options.references;
var skipUndefinedProperties = !!options.skipUndefinedProperties;
var valueCount = Number(options.maxValues) || 100000;
var path = [];
var stack = [];
var encountered = [];
var paths = [];
var restore = [];
/**
* Stringify the next value in the stack.
*
* @param {*} value
* @param {string} key
* @return {string}
*/
function next (value, key) {
if (skipUndefinedProperties && value === undefined) {
return undefined;
}
path.push(key);
var result = recurse(value, stringify);
path.pop();
return result;
}
/**
* Handle recursion by checking if we've visited this node every iteration.
*
* @param {*} value
* @param {Function} stringify
* @return {string}
*/
var recurse = references ?
function (value, stringify) {
if (value && (typeof value === 'object' || typeof value === 'function')) {
var seen = encountered.indexOf(value);
// Track nodes to restore later.
if (seen > -1) {
restore.push(path.slice(), paths[seen]);
return;
}
// Track encountered nodes.
encountered.push(value);
paths.push(path.slice());
}
// Stop when we hit the max depth.
if (path.length > maxDepth || valueCount-- <= 0) {
return;
}
// Stringify the value and fallback to
return stringify(value, space, next);
} :
function (value, stringify) {
var seen = stack.indexOf(value);
if (seen > -1 || path.length > maxDepth || valueCount-- <= 0) {
return;
}
stack.push(value);
var value = stringify(value, space, next);
stack.pop();
return value;
};
// If the user defined a replacer function, make the recursion function
// a double step process - `recurse -> replacer -> stringify`.
if (typeof replacer === 'function') {
var before = recurse
// Intertwine the replacer function with the regular recursion.
recurse = function (value, stringify) {
return before(value, function (value, space, next) {
return replacer(value, space, function (value) {
return stringify(value, space, next);
});
});
};
}
var result = recurse(value, stringify);
// Attempt to restore circular references.
if (restore.length) {
var sep = space ? '\n' : '';
var assignment = space ? ' = ' : '=';
var eol = ';' + sep;
var before = space ? '(function () {' : '(function(){'
var after = '}())'
var results = ['var x' + assignment + result];
for (var i = 0; i < restore.length; i += 2) {
results.push('x' + toPath(restore[i]) + assignment + 'x' + toPath(restore[i + 1]));
}
results.push('return x');
return before + sep + results.join(eol) + eol + after
}
return result;
};
});