🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
178 lines
5.0 KiB
JavaScript
Executable File
178 lines
5.0 KiB
JavaScript
Executable File
/*
|
|
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;
|