123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- 'use strict';
- const utils = require('./utils.cjs');
- function encodeString(value) {
- if (/[^\x20\x21\x23-\x5B\x5D-\uD799]/.test(value)) { // [^\x20-\uD799]|[\x22\x5c]
- return JSON.stringify(value);
- }
- return '"' + value + '"';
- }
- function* stringifyChunked(value, ...args) {
- const { replacer, getKeys, space, ...options } = utils.normalizeStringifyOptions(...args);
- const highWaterMark = Number(options.highWaterMark) || 0x4000; // 16kb by default
- const keyStrings = new Map();
- const stack = [];
- const rootValue = { '': value };
- let prevState = null;
- let state = () => printEntry('', value);
- let stateValue = rootValue;
- let stateEmpty = true;
- let stateKeys = [''];
- let stateIndex = 0;
- let buffer = '';
- while (true) {
- state();
- if (buffer.length >= highWaterMark || prevState === null) {
- // flush buffer
- yield buffer;
- buffer = '';
- if (prevState === null) {
- break;
- }
- }
- }
- function printObject() {
- if (stateIndex === 0) {
- stateKeys = getKeys(stateValue);
- buffer += '{';
- }
- // when no keys left
- if (stateIndex === stateKeys.length) {
- buffer += space && !stateEmpty
- ? `\n${space.repeat(stack.length - 1)}}`
- : '}';
- popState();
- return;
- }
- const key = stateKeys[stateIndex++];
- printEntry(key, stateValue[key]);
- }
- function printArray() {
- if (stateIndex === 0) {
- buffer += '[';
- }
- if (stateIndex === stateValue.length) {
- buffer += space && !stateEmpty
- ? `\n${space.repeat(stack.length - 1)}]`
- : ']';
- popState();
- return;
- }
- printEntry(stateIndex, stateValue[stateIndex++]);
- }
- function printEntryPrelude(key) {
- if (stateEmpty) {
- stateEmpty = false;
- } else {
- buffer += ',';
- }
- if (space && prevState !== null) {
- buffer += `\n${space.repeat(stack.length)}`;
- }
- if (state === printObject) {
- let keyString = keyStrings.get(key);
- if (keyString === undefined) {
- keyStrings.set(key, keyString = encodeString(key) + (space ? ': ' : ':'));
- }
- buffer += keyString;
- }
- }
- function printEntry(key, value) {
- value = utils.replaceValue(stateValue, key, value, replacer);
- if (value === null || typeof value !== 'object') {
- // primitive
- if (state !== printObject || value !== undefined) {
- printEntryPrelude(key);
- pushPrimitive(value);
- }
- } else {
- // If the visited set does not change after adding a value, then it is already in the set
- if (stack.includes(value)) {
- throw new TypeError('Converting circular structure to JSON');
- }
- printEntryPrelude(key);
- stack.push(value);
- pushState();
- state = Array.isArray(value) ? printArray : printObject;
- stateValue = value;
- stateEmpty = true;
- stateIndex = 0;
- }
- }
- function pushPrimitive(value) {
- switch (typeof value) {
- case 'string':
- buffer += encodeString(value);
- break;
- case 'number':
- buffer += Number.isFinite(value) ? String(value) : 'null';
- break;
- case 'boolean':
- buffer += value ? 'true' : 'false';
- break;
- case 'undefined':
- case 'object': // typeof null === 'object'
- buffer += 'null';
- break;
- default:
- throw new TypeError(`Do not know how to serialize a ${value.constructor?.name || typeof value}`);
- }
- }
- function pushState() {
- prevState = {
- keys: stateKeys,
- index: stateIndex,
- prev: prevState
- };
- }
- function popState() {
- stack.pop();
- const value = stack.length > 0 ? stack[stack.length - 1] : rootValue;
- // restore state
- state = Array.isArray(value) ? printArray : printObject;
- stateValue = value;
- stateEmpty = false;
- stateKeys = prevState.keys;
- stateIndex = prevState.index;
- // pop state
- prevState = prevState.prev;
- }
- }
- exports.stringifyChunked = stringifyChunked;
|