first commit

This commit is contained in:
monjack
2025-06-20 18:01:48 +08:00
commit 6daa6d65c1
24611 changed files with 2512443 additions and 0 deletions

View File

@ -0,0 +1,5 @@
import { ToString } from "./types";
/**
* Stringify an array of values.
*/
export declare const arrayToString: ToString;

View File

@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.arrayToString = void 0;
/**
* Stringify an array of values.
*/
const 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" : ",");
const eol = space && values ? "\n" : "";
return `[${eol}${values}${eol}]`;
};
exports.arrayToString = arrayToString;
//# sourceMappingURL=array.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"array.js","sourceRoot":"","sources":["../src/array.ts"],"names":[],"mappings":";;;AAEA;;GAEG;AACI,MAAM,aAAa,GAAa,CAAC,KAAY,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;IACnE,yEAAyE;IACzE,MAAM,MAAM,GAAG,KAAK;SACjB,GAAG,CAAC,UAAU,KAAK,EAAE,KAAK;QACzB,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,MAAM,GAAG,GAAG,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACxC,OAAO,IAAI,GAAG,GAAG,MAAM,GAAG,GAAG,GAAG,CAAC;AACnC,CAAC,CAAC;AAdW,QAAA,aAAa,iBAcxB","sourcesContent":["import { ToString } from \"./types\";\n\n/**\n * Stringify an array of values.\n */\nexport const arrayToString: ToString = (array: any[], space, 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 const eol = space && values ? \"\\n\" : \"\";\n return `[${eol}${values}${eol}]`;\n};\n"]}

View File

@ -0,0 +1,83 @@
import { Next, ToString } from "./types";
declare const FUNCTION_PREFIXES: {
Function: string;
GeneratorFunction: string;
AsyncFunction: string;
AsyncGeneratorFunction: string;
};
/**
* Track function parser usage.
*/
export declare const USED_METHOD_KEY: WeakSet<(...args: unknown[]) => unknown>;
/**
* Stringify a function.
*/
export declare const functionToString: ToString;
/**
* Rewrite a stringified function to remove initial indentation.
*/
export declare function dedentFunction(fnString: string): string;
/**
* Function parser and stringify.
*/
export declare class FunctionParser {
fn: (...args: unknown[]) => unknown;
indent: string;
next: Next;
key?: string | undefined;
fnString: string;
fnType: keyof typeof FUNCTION_PREFIXES;
keyQuote: string | undefined;
keyPrefix: string;
isMethodCandidate: boolean;
pos: number;
hadKeyword: boolean;
constructor(fn: (...args: unknown[]) => unknown, indent: string, next: Next, key?: string | 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.
*/
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 multi-character operators
* like '&&', are split into more than one token here. However, awareness of
* some multi-character 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 {};

View File

@ -0,0 +1,299 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FunctionParser = exports.dedentFunction = exports.functionToString = exports.USED_METHOD_KEY = void 0;
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(" "));
/**
* Track function parser usage.
*/
exports.USED_METHOD_KEY = new WeakSet();
/**
* Stringify a function.
*/
const functionToString = (fn, space, next, key) => {
const name = typeof key === "string" ? key : undefined;
// Track in function parser for object stringify to avoid duplicate output.
if (name !== undefined)
exports.USED_METHOD_KEY.add(fn);
return new FunctionParser(fn, space, next, name).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;
}
case "()":
if (this.fnString.substr(this.pos, 2) === "=>") {
return this.keyPrefix + this.fnString;
}
this.pos = offset;
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.
*/
tryParsePrefixTokens() {
let posPrev = this.pos;
this.hadKeyword = false;
switch (this.fnType) {
case "AsyncFunction":
if (this.consumeSyntax() !== "async")
return false;
posPrev = this.pos;
case "Function":
if (this.consumeSyntax() === "function") {
this.hadKeyword = true;
}
else {
this.pos = posPrev;
}
return true;
case "AsyncGeneratorFunction":
if (this.consumeSyntax() !== "async")
return false;
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 multi-character operators
* like '&&', are split into more than one token here. However, awareness of
* some multi-character 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

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
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;

View File

@ -0,0 +1,91 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.stringify = void 0;
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, key);
path.push(key);
const result = builder(value, key === ROOT_SENTINEL ? undefined : key);
path.pop();
return result;
};
const builder = references
? (value, key) => {
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));
// Use `undefined` as temporaray stand-in for referenced nodes
return valueToString(undefined, space, onNext, key);
}
// Track encountered nodes.
tracking.set(value, path.slice(1));
}
return valueToString(value, space, onNext, key);
}
: (value, key) => {
// Stop on recursion.
if (stack.has(value))
return;
stack.add(value);
const result = valueToString(value, space, onNext, key);
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, key) => {
return replacer(value, space, (value) => stringify_1.toString(value, space, next, key), key);
};
}
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
import { ToString } from "./types";
/**
* Transform an object into a string.
*/
export declare const objectToString: ToString;

View File

@ -0,0 +1,91 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.objectToString = void 0;
const quote_1 = require("./quote");
const function_1 = require("./function");
const array_1 = require("./array");
/**
* Transform an object into a string.
*/
const objectToString = (value, space, next, key) => {
// Support buffer in all environments.
if (typeof Buffer === "function" && Buffer.isBuffer(value)) {
return `Buffer.from(${next(value.toString("base64"))}, 'base64')`;
}
// Support `global` under test environments that don't print `[object global]`.
if (typeof global === "object" && value === global) {
return globalToString(value, space, next, key);
}
// Use the internal object string to select stringify method.
const toString = OBJECT_TYPES[Object.prototype.toString.call(value)];
return toString ? toString(value, space, next, key) : undefined;
};
exports.objectToString = objectToString;
/**
* Stringify an object of keys and values.
*/
const rawObjectToString = (obj, indent, next, key) => {
const eol = indent ? "\n" : "";
const space = indent ? " " : "";
// Iterate over object keys and concat string together.
const values = Object.keys(obj)
.reduce(function (values, key) {
const fn = obj[key];
const result = next(fn, key);
// Omit `undefined` object entries.
if (result === undefined)
return values;
// String format the value data.
const value = result.split("\n").join(`\n${indent}`);
// Skip `key` prefix for function parser.
if (function_1.USED_METHOD_KEY.has(fn)) {
values.push(`${indent}${value}`);
return values;
}
values.push(`${indent}${quote_1.quoteKey(key, next)}:${space}${value}`);
return values;
}, [])
.join(`,${eol}`);
// Avoid new lines in an empty object.
if (values === "")
return "{}";
return `{${eol}${values}${eol}}`;
};
/**
* Stringify global variable access.
*/
const globalToString = (value, space, next) => {
return `Function(${next("return this")})()`;
};
/**
* Convert JavaScript objects into strings.
*/
const OBJECT_TYPES = {
"[object Array]": array_1.arrayToString,
"[object Object]": rawObjectToString,
"[object Error]": (error, space, next) => {
return `new Error(${next(error.message)})`;
},
"[object Date]": (date) => {
return `new Date(${date.getTime()})`;
},
"[object String]": (str, space, next) => {
return `new String(${next(str.toString())})`;
},
"[object Number]": (num) => {
return `new Number(${num})`;
},
"[object Boolean]": (bool) => {
return `new Boolean(${bool})`;
},
"[object Set]": (set, space, next) => {
return `new Set(${next(Array.from(set))})`;
},
"[object Map]": (map, space, next) => {
return `new Map(${next(Array.from(map))})`;
},
"[object RegExp]": String,
"[object global]": globalToString,
"[object Window]": globalToString,
};
//# sourceMappingURL=object.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,21 @@
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;

View File

@ -0,0 +1,86 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.stringifyPath = exports.quoteKey = exports.isValidVariableName = exports.IS_VALID_IDENTIFIER = exports.quoteString = void 0;
/**
* 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

View File

@ -0,0 +1 @@
{"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"]}

View File

@ -0,0 +1,5 @@
import { ToString } from "./types";
/**
* Stringify a value recursively.
*/
export declare const toString: ToString;

View File

@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.toString = void 0;
const quote_1 = require("./quote");
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)})`;
},
bigint: (value, space, next) => {
return `BigInt(${next(String(value))})`;
},
undefined: String,
object: object_1.objectToString,
function: function_1.functionToString,
};
/**
* Stringify a value recursively.
*/
const toString = (value, space, next, key) => {
if (value === null)
return "null";
return PRIMITIVE_TYPES[typeof value](value, space, next, key);
};
exports.toString = toString;
//# sourceMappingURL=stringify.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"stringify.js","sourceRoot":"","sources":["../src/stringify.ts"],"names":[],"mappings":";;;AAAA,mCAAsC;AAEtC,qCAA0C;AAC1C,yCAA8C;AAE9C;;GAEG;AACH,MAAM,eAAe,GAA6B;IAChD,MAAM,EAAE,mBAAW;IACnB,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxE,OAAO,EAAE,MAAM;IACf,MAAM,EAAE,CAAC,KAAa,EAAE,KAAa,EAAE,IAAU,EAAE,EAAE;QACnD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAEjC,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,cAAc,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;QAEzD,+BAA+B;QAC/B,OAAO,UAAU,IAAI,CAAE,KAAa,CAAC,WAAW,CAAC,GAAG,CAAC;IACvD,CAAC;IACD,MAAM,EAAE,CAAC,KAAa,EAAE,KAAa,EAAE,IAAU,EAAE,EAAE;QACnD,OAAO,UAAU,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC;IAC1C,CAAC;IACD,SAAS,EAAE,MAAM;IACjB,MAAM,EAAE,uBAAc;IACtB,QAAQ,EAAE,2BAAgB;CAC3B,CAAC;AAEF;;GAEG;AACI,MAAM,QAAQ,GAAa,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;IAC5D,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAElC,OAAO,eAAe,CAAC,OAAO,KAAK,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;AAChE,CAAC,CAAC;AAJW,QAAA,QAAQ,YAInB","sourcesContent":["import { quoteString } from \"./quote\";\nimport { Next, ToString } from \"./types\";\nimport { objectToString } from \"./object\";\nimport { functionToString } from \"./function\";\n\n/**\n * Stringify primitive values.\n */\nconst PRIMITIVE_TYPES: Record<string, ToString> = {\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 bigint: (value: bigint, space: string, next: Next) => {\n return `BigInt(${next(String(value))})`;\n },\n undefined: String,\n object: objectToString,\n function: functionToString,\n};\n\n/**\n * Stringify a value recursively.\n */\nexport const toString: ToString = (value, space, next, key) => {\n if (value === null) return \"null\";\n\n return PRIMITIVE_TYPES[typeof value](value, space, next, key);\n};\n"]}

View File

@ -0,0 +1,8 @@
/**
* 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, key: PropertyKey | undefined) => string | undefined;

View File

@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=types.js.map

View File

@ -0,0 +1 @@
{"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 key: PropertyKey | undefined\n) => string | undefined;\n"]}