/* MIT License http://www.opensource.org/licenses/mit-license.php Author Natsu @xiaoxiaojx This file contains code ported from strip-json-comments: https://github.com/sindresorhus/strip-json-comments Original license: MIT Original author: Sindre Sorhus */ "use strict"; /** * @typedef {object} StripJsonCommentsOptions * @property {boolean=} whitespace Replace comments with whitespace * @property {boolean=} trailingCommas Strip trailing commas */ const singleComment = Symbol("singleComment"); const multiComment = Symbol("multiComment"); /** * Strip without whitespace (returns empty string) * @param {string} _string Unused * @param {number} _start Unused * @param {number} _end Unused * @returns {string} Empty string for all input */ const stripWithoutWhitespace = (_string, _start, _end) => ""; /** * Replace all characters except ASCII spaces, tabs and line endings with regular spaces to ensure valid JSON output. * @param {string} string String to process * @param {number} start Start index * @param {number} end End index * @returns {string} Processed string with comments replaced by whitespace */ const stripWithWhitespace = (string, start, end) => string.slice(start, end).replace(/[^ \t\r\n]/g, " "); /** * Check if a quote is escaped * @param {string} jsonString JSON string * @param {number} quotePosition Position of the quote * @returns {boolean} True if the quote at the given position is escaped */ const isEscaped = (jsonString, quotePosition) => { let index = quotePosition - 1; let backslashCount = 0; while (jsonString[index] === "\\") { index -= 1; backslashCount += 1; } return Boolean(backslashCount % 2); }; /** * Strip comments from JSON string * @param {string} jsonString JSON string with potential comments * @param {StripJsonCommentsOptions} options Options * @returns {string} JSON string without comments */ function stripJsonComments( jsonString, { whitespace = true, trailingCommas = false } = {}, ) { if (typeof jsonString !== "string") { throw new TypeError( `Expected argument \`jsonString\` to be a \`string\`, got \`${typeof jsonString}\``, ); } const strip = whitespace ? stripWithWhitespace : stripWithoutWhitespace; let isInsideString = false; /** @type {false | typeof singleComment | typeof multiComment} */ let isInsideComment = false; let offset = 0; let buffer = ""; let result = ""; let commaIndex = -1; for (let index = 0; index < jsonString.length; index++) { const currentCharacter = jsonString[index]; const nextCharacter = jsonString[index + 1]; if (!isInsideComment && currentCharacter === '"') { // Enter or exit string const escaped = isEscaped(jsonString, index); if (!escaped) { isInsideString = !isInsideString; } } if (isInsideString) { continue; } if (!isInsideComment && currentCharacter + nextCharacter === "//") { // Enter single-line comment buffer += jsonString.slice(offset, index); offset = index; isInsideComment = singleComment; index++; } else if ( isInsideComment === singleComment && currentCharacter + nextCharacter === "\r\n" ) { // Exit single-line comment via \r\n index++; isInsideComment = false; buffer += strip(jsonString, offset, index); offset = index; continue; } else if (isInsideComment === singleComment && currentCharacter === "\n") { // Exit single-line comment via \n isInsideComment = false; buffer += strip(jsonString, offset, index); offset = index; } else if (!isInsideComment && currentCharacter + nextCharacter === "/*") { // Enter multiline comment buffer += jsonString.slice(offset, index); offset = index; isInsideComment = multiComment; index++; continue; } else if ( isInsideComment === multiComment && currentCharacter + nextCharacter === "*/" ) { // Exit multiline comment index++; isInsideComment = false; buffer += strip(jsonString, offset, index + 1); offset = index + 1; continue; } else if (trailingCommas && !isInsideComment) { if (commaIndex !== -1) { if (currentCharacter === "}" || currentCharacter === "]") { // Strip trailing comma buffer += jsonString.slice(offset, index); result += strip(buffer, 0, 1) + buffer.slice(1); buffer = ""; offset = index; commaIndex = -1; } else if ( currentCharacter !== " " && currentCharacter !== "\t" && currentCharacter !== "\r" && currentCharacter !== "\n" ) { // Hit non-whitespace following a comma; comma is not trailing buffer += jsonString.slice(offset, index); offset = index; commaIndex = -1; } } else if (currentCharacter === ",") { // Flush buffer prior to this point, and save new comma index result += buffer + jsonString.slice(offset, index); buffer = ""; offset = index; commaIndex = index; } } } const remaining = isInsideComment === singleComment ? strip(jsonString, offset, jsonString.length) : jsonString.slice(offset); return result + buffer + remaining; } module.exports = stripJsonComments;