Files

1358 lines
39 KiB
JavaScript

import { t as __exportAll } from "./chunk-C7Uep-_p.mjs";
import { createRequire } from "node:module";
import * as Evk from "eslint-visitor-keys";
import evkPkg from "eslint-visitor-keys/package.json" with { type: "json" };
import path from "node:path";
import { lte } from "semver";
import * as acorn from "acorn";
import acornPkg from "acorn/package.json" with { type: "json" };
//#region src/parser/modules/require-utils.ts
/**
* Get NodeJS.Require from Linter
*/
function getRequireFromLinter() {
try {
const eslintPkgPath = getRequireFromCwd()?.resolve("eslint/package.json");
if (!eslintPkgPath) return null;
return createRequire(path.join(path.dirname(eslintPkgPath), "__placeholder__.js"));
} catch {}
return null;
}
/**
* Get NodeJS.Require from Cwd
*/
function getRequireFromCwd() {
try {
const cwd = process.cwd();
return createRequire(path.join(cwd, "__placeholder__.js"));
} catch {}
return null;
}
/**
* Get module from Linter
*/
function requireFromLinter(module) {
try {
return getRequireFromLinter()?.(module);
} catch {}
return null;
}
/**
* Get module path from Linter
*/
function resolveFromLinter(module) {
try {
return getRequireFromLinter()?.resolve(module) ?? null;
} catch {}
return null;
}
/**
* Get module from Cwd
*/
function requireFromCwd(module) {
try {
return getRequireFromCwd()?.(module);
} catch {}
return null;
}
/**
* Get module path from Cwd
*/
function resolveFromCwd(module) {
try {
return getRequireFromCwd()?.resolve(module) ?? null;
} catch {}
return null;
}
/**
* Get the newest `espree` kind from the loaded ESLint or dependency.
*/
function loadNewest(items) {
let target = null;
for (const item of items) {
const pkg = item.getPkg();
if (pkg != null && (!target || lte(target.version, pkg.version))) target = {
version: pkg.version,
get: item.get
};
}
return target.get();
}
//#endregion
//#region src/parser/visitor-keys.ts
const jsonKeys = {
Program: ["body"],
JSONExpressionStatement: ["expression"],
JSONArrayExpression: ["elements"],
JSONObjectExpression: ["properties"],
JSONProperty: ["key", "value"],
JSONIdentifier: [],
JSONLiteral: [],
JSONUnaryExpression: ["argument"],
JSONTemplateLiteral: ["quasis", "expressions"],
JSONTemplateElement: [],
JSONBinaryExpression: ["left", "right"]
};
let cache = null;
/**
* Get visitor keys
*/
function getVisitorKeys() {
if (!cache) cache = loadNewest([
{
getPkg() {
return requireFromCwd("eslint-visitor-keys/package.json");
},
get() {
return requireFromCwd("eslint-visitor-keys");
}
},
{
getPkg() {
return requireFromLinter("eslint-visitor-keys/package.json");
},
get() {
return requireFromLinter("eslint-visitor-keys");
}
},
{
getPkg() {
return evkPkg;
},
get() {
return Evk;
}
}
]).unionWith(jsonKeys);
return cache;
}
//#endregion
//#region src/parser/utils.ts
/**
* Check if the given node is RegExpLiteral
*/
function isRegExpLiteral(node) {
return Boolean(node.regex) || node.raw.startsWith("/");
}
//#endregion
//#region src/parser/errors.ts
/**
* JSON parse errors.
*/
var ParseError = class extends SyntaxError {
/**
* Initialize this ParseError instance.
* @param message The error message.
* @param code The error code. See also: https://html.spec.whatwg.org/multipage/parsing.html#parse-errors
* @param offset The offset number of this error.
* @param line The line number of this error.
* @param column The column number of this error.
*/
constructor(message, offset, line, column) {
super(message);
this.index = offset;
this.lineNumber = line;
this.column = column;
}
};
/**
* Throw syntax error for expected token.
* @param name The token name.
* @param token The token object to get that location.
*/
function throwExpectedTokenError(name, beforeToken) {
const locs = getLocation(beforeToken);
throw new ParseError(`Expected token '${name}'.`, locs.end, locs.loc.end.line, locs.loc.end.column + 1);
}
/**
* Throw syntax error for unexpected name.
* @param name The unexpected name.
* @param token The token object to get that location.
*/
function throwUnexpectedError(name, token) {
const locs = getLocation(token);
throw new ParseError(`Unexpected ${name}.`, locs.start, locs.loc.start.line, locs.loc.start.column + 1);
}
/**
* Throw syntax error for unexpected token.
* @param name The token name.
* @param token The token object to get that location.
*/
function throwUnexpectedTokenError(name, token) {
return throwUnexpectedError(`token '${name}'`, token);
}
/**
* Throw syntax error for unexpected comment.
* @param name The token name.
* @param token The token object to get that location.
*/
function throwUnexpectedCommentError(token) {
return throwUnexpectedError("comment", token);
}
/**
* Throw syntax error for unexpected whitespace.
*/
function throwUnexpectedSpaceError(beforeToken) {
const locs = getLocation(beforeToken);
throw new ParseError("Unexpected whitespace.", locs.end, locs.loc.end.line, locs.loc.end.column + 1);
}
/**
* Throw syntax error for unexpected invalid number.
*/
function throwInvalidNumberError(text, token) {
const locs = getLocation(token);
throw new ParseError(`Invalid number ${text}.`, locs.start, locs.loc.start.line, locs.loc.start.column + 1);
}
/**
* Throw syntax error for unexpected token.
* @param node The token object to get that location.
*/
function throwUnexpectedNodeError(node, tokens, offset) {
if (node.type === "Identifier" || node.type === "JSONIdentifier") {
const locs = getLocation(node);
throw new ParseError(`Unexpected identifier '${node.name}'.`, locs.start, locs.loc.start.line, locs.loc.start.column + 1);
}
if (node.type === "Literal" || node.type === "JSONLiteral") {
const type = node.bigint ? "bigint" : isRegExpLiteral(node) ? "regex" : node.value === null ? "null" : typeof node.value;
const locs = getLocation(node);
throw new ParseError(`Unexpected ${type} literal.`, locs.start, locs.loc.start.line, locs.loc.start.column + 1);
}
if (node.type === "TemplateLiteral" || node.type === "JSONTemplateLiteral") {
const locs = getLocation(node);
throw new ParseError("Unexpected template literal.", locs.start, locs.loc.start.line, locs.loc.start.column + 1);
}
if (node.type.endsWith("Expression") && node.type !== "FunctionExpression") {
const name = node.type.replace(/^JSON/u, "").replace(/\B([A-Z])/gu, " $1").toLowerCase();
const locs = getLocation(node);
throw new ParseError(`Unexpected ${name}.`, locs.start, locs.loc.start.line, locs.loc.start.column + 1);
}
const index = node.range[0] + (offset || 0);
const t = tokens.findTokenByOffset(index);
const name = t?.value || "unknown";
const locs = getLocation(t || node);
throw new ParseError(`Unexpected token '${name}'.`, locs.start, locs.loc.start.line, locs.loc.start.column + 1);
}
/** get locations */
function getLocation(token) {
return {
start: token.range?.[0] ?? token.start,
end: token.range?.[1] ?? token.end,
loc: token.loc
};
}
//#endregion
//#region src/parser/token-store.ts
var TokenStore = class {
constructor(tokens) {
this.tokens = tokens;
}
add(token) {
this.tokens.push(token);
}
findIndexByOffset(offset) {
return this.tokens.findIndex((token) => token.range[0] <= offset && offset < token.range[1]);
}
findTokenByOffset(offset) {
return this.tokens[this.findIndexByOffset(offset)];
}
/**
* Get the first token representing the given node.
*
*/
getFirstToken(nodeOrToken) {
return this.findTokenByOffset(nodeOrToken.range[0]);
}
/**
* Get the last token representing the given node.
*
*/
getLastToken(nodeOrToken) {
return this.findTokenByOffset(nodeOrToken.range[1] - 1);
}
/**
* Get the first token before the given node or token.
*/
getTokenBefore(nodeOrToken, filter) {
const tokenIndex = this.findIndexByOffset(nodeOrToken.range[0]);
for (let index = tokenIndex - 1; index >= 0; index--) {
const token = this.tokens[index];
if (!filter || filter(token)) return token;
}
return null;
}
/**
* Get the first token after the given node or token.
*/
getTokenAfter(nodeOrToken, filter) {
const tokenIndex = this.findIndexByOffset(nodeOrToken.range[0]);
for (let index = tokenIndex + 1; index < this.tokens.length; index++) {
const token = this.tokens[index];
if (!filter || filter(token)) return token;
}
return null;
}
};
/**
* Checks if given token is comma
*/
function isComma(token) {
return token.type === "Punctuator" && token.value === ",";
}
//#endregion
//#region src/parser/validate.ts
const lineBreakPattern = /\r\n|[\n\r\u2028\u2029]/u;
const octalNumericLiteralPattern = /^0o/iu;
const legacyOctalNumericLiteralPattern = /^0\d/u;
const binaryNumericLiteralPattern = /^0b/iu;
const unicodeCodepointEscapePattern = /\\u\{[\dA-Fa-f]+\}/uy;
/**
* Check if given string has unicode codepoint escape
*/
function hasUnicodeCodepointEscapes(code) {
let escaped = false;
for (let index = 0; index < code.length - 4; index++) {
if (escaped) {
escaped = false;
continue;
}
if (code[index] === "\\") {
unicodeCodepointEscapePattern.lastIndex = index;
if (unicodeCodepointEscapePattern.test(code)) return true;
escaped = true;
}
}
return false;
}
/**
* Validate ES node
*/
function validateNode(node, tokens, ctx) {
if (node.type === "ObjectExpression") {
validateObjectExpressionNode(node, tokens, ctx);
return;
}
if (node.type === "Property") {
validatePropertyNode(node, tokens, ctx);
return;
}
if (node.type === "ArrayExpression") {
validateArrayExpressionNode(node, tokens, ctx);
return;
}
if (node.type === "Literal") {
validateLiteralNode(node, tokens, ctx);
return;
}
if (node.type === "UnaryExpression") {
validateUnaryExpressionNode(node, tokens, ctx);
return;
}
if (node.type === "Identifier") {
validateIdentifierNode(node, tokens, ctx);
return;
}
if (node.type === "TemplateLiteral") {
validateTemplateLiteralNode(node, tokens, ctx);
return;
}
if (node.type === "TemplateElement") {
validateTemplateElementNode(node, tokens);
return;
}
if (node.type === "BinaryExpression") {
validateBinaryExpressionNode(node, tokens, ctx);
return;
}
throw throwUnexpectedNodeError(node, tokens);
}
/**
* Validate ObjectExpression node
*/
function validateObjectExpressionNode(node, tokens, ctx) {
/* istanbul ignore next */
if (node.type !== "ObjectExpression") throw throwUnexpectedNodeError(node, tokens);
for (const prop of node.properties) setParent$1(prop, node);
if (!ctx.trailingCommas) {
const token = tokens.getTokenBefore(tokens.getLastToken(node));
if (token && isComma(token)) throw throwUnexpectedTokenError(",", token);
}
}
/**
* Validate Property node
*/
function validatePropertyNode(node, tokens, ctx) {
if (node.type !== "Property") throw throwUnexpectedNodeError(node, tokens);
setParent$1(node.key, node);
setParent$1(node.value, node);
if (node.computed) throw throwUnexpectedNodeError(node, tokens);
if (node.method) throw throwUnexpectedNodeError(node.value, tokens);
if (node.shorthand) throw throwExpectedTokenError(":", node);
if (node.kind !== "init") throw throwExpectedTokenError(":", tokens.getFirstToken(node));
if (node.key.type === "Literal") {
const keyValueType = typeof node.key.value;
if (keyValueType === "number") {
if (!ctx.numberProperties) throw throwUnexpectedNodeError(node.key, tokens);
} else if (keyValueType !== "string") throw throwUnexpectedNodeError(node.key, tokens);
} else if (node.key.type === "Identifier") {
if (!ctx.unquoteProperties) throw throwUnexpectedNodeError(node.key, tokens);
} else throw throwUnexpectedNodeError(node.key, tokens);
if (node.value.type === "Identifier") {
if (!isStaticValueIdentifier(node.value, ctx)) throw throwUnexpectedNodeError(node.value, tokens);
}
}
/**
* Validate ArrayExpression node
*/
function validateArrayExpressionNode(node, tokens, ctx) {
/* istanbul ignore next */
if (node.type !== "ArrayExpression") throw throwUnexpectedNodeError(node, tokens);
if (!ctx.trailingCommas) {
const token = tokens.getTokenBefore(tokens.getLastToken(node));
if (token && isComma(token)) throw throwUnexpectedTokenError(",", token);
}
node.elements.forEach((child, index) => {
if (!child) {
if (ctx.sparseArrays) return;
const beforeIndex = index - 1;
const before = beforeIndex >= 0 ? tokens.getLastToken(node.elements[beforeIndex]) : tokens.getFirstToken(node);
throw throwUnexpectedTokenError(",", tokens.getTokenAfter(before, isComma));
}
if (child.type === "Identifier") {
if (!isStaticValueIdentifier(child, ctx)) throw throwUnexpectedNodeError(child, tokens);
}
setParent$1(child, node);
});
}
/**
* Validate Literal node
*/
function validateLiteralNode(node, tokens, ctx) {
/* istanbul ignore next */
if (node.type !== "Literal") throw throwUnexpectedNodeError(node, tokens);
if (isRegExpLiteral(node)) {
if (!ctx.regExpLiterals) throw throwUnexpectedNodeError(node, tokens);
} else if (node.bigint) {
if (!ctx.bigintLiterals) throw throwUnexpectedNodeError(node, tokens);
} else validateLiteral(node, ctx);
}
/**
* Validate literal
*/
function validateLiteral(node, ctx) {
const value = node.value;
if ((!ctx.invalidJsonNumbers || !ctx.leadingOrTrailingDecimalPoints || !ctx.numericSeparators) && typeof value === "number") {
const text = node.raw;
if (!ctx.leadingOrTrailingDecimalPoints) {
if (text.startsWith(".")) throw throwUnexpectedTokenError(".", node);
if (text.endsWith(".")) throw throwUnexpectedTokenError(".", {
range: [node.range[1] - 1, node.range[1]],
loc: {
start: {
line: node.loc.end.line,
column: node.loc.end.column - 1
},
end: node.loc.end
}
});
}
if (!ctx.numericSeparators) {
if (text.includes("_")) {
const index = text.indexOf("_");
throw throwUnexpectedTokenError("_", {
range: [node.range[0] + index, node.range[0] + index + 1],
loc: {
start: {
line: node.loc.start.line,
column: node.loc.start.column + index
},
end: {
line: node.loc.start.line,
column: node.loc.start.column + index + 1
}
}
});
}
}
if (!ctx.octalNumericLiterals) {
if (octalNumericLiteralPattern.test(text)) throw throwUnexpectedError("octal numeric literal", node);
}
if (!ctx.legacyOctalNumericLiterals) {
if (legacyOctalNumericLiteralPattern.test(text)) throw throwUnexpectedError("legacy octal numeric literal", node);
}
if (!ctx.binaryNumericLiterals) {
if (binaryNumericLiteralPattern.test(text)) throw throwUnexpectedError("binary numeric literal", node);
}
if (!ctx.invalidJsonNumbers) try {
JSON.parse(text);
} catch {
throw throwInvalidNumberError(text, node);
}
}
if ((!ctx.multilineStrings || !ctx.singleQuotes || !ctx.unicodeCodepointEscapes) && typeof value === "string") {
if (!ctx.singleQuotes) {
if (node.raw.startsWith("'")) throw throwUnexpectedError("single quoted", node);
}
if (!ctx.multilineStrings) {
if (lineBreakPattern.test(node.raw)) throw throwUnexpectedError("multiline string", node);
}
if (!ctx.unicodeCodepointEscapes) {
if (hasUnicodeCodepointEscapes(node.raw)) throw throwUnexpectedError("unicode codepoint escape", node);
}
}
}
/**
* Validate UnaryExpression node
*/
function validateUnaryExpressionNode(node, tokens, ctx) {
/* istanbul ignore next */
if (node.type !== "UnaryExpression") throw throwUnexpectedNodeError(node, tokens);
const operator = node.operator;
if (operator === "+") {
if (!ctx.plusSigns) throw throwUnexpectedTokenError("+", node);
} else if (operator !== "-") throw throwUnexpectedNodeError(node, tokens);
const argument = node.argument;
if (argument.type === "Literal") {
if (typeof argument.value !== "number") throw throwUnexpectedNodeError(argument, tokens);
} else if (argument.type === "Identifier") {
if (!isNumberIdentifier$1(argument, ctx)) throw throwUnexpectedNodeError(argument, tokens);
} else throw throwUnexpectedNodeError(argument, tokens);
if (!ctx.spacedSigns) {
if (node.range[0] + 1 < argument.range[0]) throw throwUnexpectedSpaceError(tokens.getFirstToken(node));
}
setParent$1(argument, node);
}
/**
* Validate Identifier node
*/
function validateIdentifierNode(node, tokens, ctx) {
/* istanbul ignore next */
if (node.type !== "Identifier") throw throwUnexpectedNodeError(node, tokens);
if (!ctx.escapeSequenceInIdentifier) {
if (node.name.length < node.range[1] - node.range[0]) throw throwUnexpectedError("escape sequence", node);
}
}
/**
* Validate TemplateLiteral node
*/
function validateTemplateLiteralNode(node, tokens, ctx) {
/* istanbul ignore next */
if (node.type !== "TemplateLiteral") throw throwUnexpectedNodeError(node, tokens);
if (!ctx.templateLiterals) throw throwUnexpectedNodeError(node, tokens);
if (node.expressions.length) {
const token = tokens.getFirstToken(node.quasis[0]);
throw throwUnexpectedTokenError("$", {
loc: {
start: {
line: token.loc.end.line,
column: token.loc.end.column - 2
},
end: token.loc.end
},
range: [token.range[1] - 2, token.range[1]]
});
}
if (!ctx.unicodeCodepointEscapes) {
if (hasUnicodeCodepointEscapes(node.quasis[0].value.raw)) throw throwUnexpectedError("unicode codepoint escape", node);
}
for (const q of node.quasis) setParent$1(q, node);
}
/**
* Validate TemplateElement node
*/
function validateTemplateElementNode(node, tokens) {
/* istanbul ignore next */
if (node.type !== "TemplateElement") throw throwUnexpectedNodeError(node, tokens);
const { cooked } = node.value;
if (cooked == null) throw throwUnexpectedNodeError(node, tokens);
const startOffset = -1;
const endOffset = node.tail ? 1 : 2;
node.start += startOffset;
node.end += endOffset;
node.range[0] += startOffset;
node.range[1] += endOffset;
node.loc.start.column += startOffset;
node.loc.end.column += endOffset;
}
/**
* Validate BinaryExpression node
*/
function validateBinaryExpressionNode(node, tokens, ctx) {
/* istanbul ignore next */
if (node.type !== "BinaryExpression") throw throwUnexpectedNodeError(node, tokens);
if (!ctx.staticExpressions) throw throwUnexpectedNodeError(node, tokens);
const { operator, left, right } = node;
if (operator !== "+" && operator !== "-" && operator !== "*" && operator !== "/" && operator !== "%" && operator !== "**") throw throwOperatorError();
if (left.type === "PrivateIdentifier") throw throwUnexpectedNodeError(left, tokens);
validateExpr(left, throwOperatorError);
validateExpr(right, () => throwUnexpectedNodeError(right, tokens));
/**
* Validate Expression node
*/
function validateExpr(expr, throwError) {
if (expr.type === "Literal") {
if (typeof expr.value !== "number") throw throwError();
} else if (expr.type !== "BinaryExpression" && expr.type !== "UnaryExpression") throw throwError();
setParent$1(expr, node);
}
/**
* Throw error
*/
function throwOperatorError() {
throw throwUnexpectedTokenError(operator, tokens.getTokenAfter(tokens.getFirstToken(node), (t) => t.value === operator) || node);
}
}
/**
* Check if given node is NaN or Infinity or undefined
*/
function isStaticValueIdentifier(node, ctx) {
if (isNumberIdentifier$1(node, ctx)) return true;
return node.name === "undefined" && ctx.undefinedKeywords;
}
/**
* Check if given node is NaN or Infinity
*/
function isNumberIdentifier$1(node, ctx) {
if (node.name === "Infinity" && ctx.infinities) return true;
if (node.name === "NaN" && ctx.nans) return true;
return false;
}
/** Set parent node */
function setParent$1(prop, parent) {
prop.parent = parent;
}
//#endregion
//#region src/parser/modules/espree.ts
let espreeCache = null;
/**
* Get the path to the loaded `espree`'s package.json.
* If the loaded ESLint was not found, just returns `require.resolve("espree/package.json")`.
*/
function getEspreePath() {
const data = getEspreeData();
if (!data) return null;
return path.dirname(data.packageJsonPath);
}
/**
*
*/
function getEspreeData() {
if (!espreeCache) espreeCache = loadNewest([{
getPkg() {
return requireFromCwd("espree/package.json");
},
get() {
const packageJsonPath = resolveFromCwd("espree/package.json");
if (!packageJsonPath) return null;
return {
packageJsonPath,
kind: "cwd"
};
}
}, {
getPkg() {
return requireFromLinter("espree/package.json");
},
get() {
const packageJsonPath = resolveFromLinter("espree/package.json");
if (!packageJsonPath) return null;
return {
packageJsonPath,
kind: "linter"
};
}
}]);
return espreeCache;
}
//#endregion
//#region src/parser/modules/acorn.ts
let acornCache;
/**
* Load `acorn` from the loaded ESLint.
* If the loaded ESLint was not found, just returns `require("acorn")`.
*/
function getAcorn() {
if (!acornCache) acornCache = loadNewest([
{
getPkg() {
return requireFromCwd("acorn/package.json");
},
get() {
return requireFromCwd("acorn");
}
},
{
getPkg() {
return requireFromEspree("acorn/package.json");
},
get() {
return requireFromEspree("acorn");
}
},
{
getPkg() {
return acornPkg;
},
get() {
return acorn;
}
}
]);
return acornCache;
}
/**
* Get module from espree
*/
function requireFromEspree(module) {
try {
const espreePath = getEspreePath();
if (!espreePath) return null;
return createRequire(path.join(espreePath, "__placeholder__.js"))(module);
} catch {}
return null;
}
//#endregion
//#region src/parser/convert.ts
var TokenConvertor = class {
constructor(ctx, code) {
this.templateBuffer = [];
this.ctx = ctx;
this.code = code;
this.tokTypes = getAcorn().tokTypes;
}
convertToken(token) {
const { tokTypes } = this;
let type, value;
const additional = {};
if (token.type === tokTypes.eof) return null;
else if (token.type === tokTypes.string) {
type = "String";
value = this.code.slice(...token.range);
} else if (token.type === tokTypes.num) {
type = "Numeric";
value = this.code.slice(...token.range);
} else if (token.type.keyword) {
if (token.type.keyword === "true" || token.type.keyword === "false") type = "Boolean";
else if (token.type.keyword === "null") type = "Null";
else type = "Keyword";
value = token.value;
} else if (token.type === tokTypes.braceL || token.type === tokTypes.braceR || token.type === tokTypes.bracketL || token.type === tokTypes.bracketR || token.type === tokTypes.colon || token.type === tokTypes.comma || token.type === tokTypes.plusMin) {
type = "Punctuator";
value = this.code.slice(...token.range);
} else if (token.type === tokTypes.name) {
type = "Identifier";
value = token.value;
} else if (token.type === tokTypes.backQuote) {
if (this.templateBuffer.length > 0) {
const first = this.templateBuffer[0];
this.templateBuffer.length = 0;
return {
type: "Template",
value: this.code.slice(first.start, token.end),
range: [first.start, token.end],
loc: {
start: first.loc.start,
end: token.loc.end
}
};
}
this.templateBuffer.push(token);
return null;
} else if (token.type === tokTypes.template) {
if (this.templateBuffer.length === 0) return throwUnexpectedTokenError(this.code.slice(...token.range), token);
this.templateBuffer.push(token);
return null;
} else if (token.type === tokTypes.regexp) {
const reValue = token.value;
type = "RegularExpression";
additional.regex = {
flags: reValue.flags,
pattern: reValue.pattern
};
value = `/${reValue.pattern}/${reValue.flags}`;
} else if (this.ctx.parentheses && (token.type === tokTypes.parenL || token.type === tokTypes.parenR)) {
type = "Punctuator";
value = this.code.slice(...token.range);
} else if (this.ctx.staticExpressions && (token.type === tokTypes.star || token.type === tokTypes.slash || token.type === tokTypes.modulo || token.type === tokTypes.starstar)) {
type = "Punctuator";
value = this.code.slice(...token.range);
} else return throwUnexpectedTokenError(this.code.slice(...token.range), token);
token.type = type;
token.value = value;
for (const k in additional) token[k] = additional[k];
return token;
}
};
/**
* Convert root expression node to JSONProgram node
*/
function convertProgramNode(node, tokens, ctx, code) {
/* istanbul ignore next */
if (node.type !== "JSONObjectExpression" && node.type !== "JSONArrayExpression" && node.type !== "JSONLiteral" && node.type !== "JSONUnaryExpression" && node.type !== "JSONIdentifier" && node.type !== "JSONTemplateLiteral" && node.type !== "JSONBinaryExpression") return throwUnexpectedNodeError(node, tokens);
if (node.type === "JSONIdentifier") {
if (!isStaticValueIdentifier(node, ctx)) return throwUnexpectedNodeError(node, tokens);
}
const body = {
type: "JSONExpressionStatement",
expression: node,
...cloneLocation(node),
parent: null
};
setParent(node, body);
const end = code.length;
const endLoc = getAcorn().getLineInfo(code, end);
const nn = {
type: "Program",
body: [body],
comments: [],
tokens: [],
range: [0, end],
loc: {
start: {
line: 1,
column: 0
},
end: {
line: endLoc.line,
column: endLoc.column
}
},
parent: null
};
setParent(body, nn);
return nn;
}
/** Clone locations */
function cloneLocation(node) {
const range = node.range;
const loc = node.loc;
return {
range: [range[0], range[1]],
loc: {
start: {
line: loc.start.line,
column: loc.start.column
},
end: {
line: loc.end.line,
column: loc.end.column
}
}
};
}
/** Set parent node */
function setParent(prop, parent) {
prop.parent = parent;
}
//#endregion
//#region src/parser/extend-parser.ts
let parserCache;
const PRIVATE = Symbol("ExtendParser#private");
const PRIVATE_PROCESS_NODE = Symbol("ExtendParser#processNode");
/** Get extend parser */
function getParser() {
if (parserCache) return parserCache;
parserCache = class ExtendParser extends getAcorn().Parser {
constructor(options, code, pos) {
super((() => {
const tokenConvertor = new TokenConvertor(options.ctx, code);
const onToken = options.onToken || ((token) => {
const t = tokenConvertor.convertToken(token);
if (t) this[PRIVATE].tokenStore.add(t);
});
return {
ecmaVersion: options.ecmaVersion,
sourceType: options.sourceType,
ranges: true,
locations: true,
allowReserved: true,
onToken,
onComment: (block, text, start, end, startLoc, endLoc) => {
const comment = {
type: block ? "Block" : "Line",
value: text,
range: [start, end],
loc: {
start: startLoc,
end: endLoc
}
};
if (!this[PRIVATE].ctx.comments) throw throwUnexpectedCommentError(comment);
this[PRIVATE].comments.push(comment);
}
};
})(), code, pos);
this[PRIVATE] = {
code,
ctx: options.ctx,
tokenStore: options.tokenStore,
comments: options.comments,
nodes: options.nodes
};
}
/**
* Collect tokens.
*/
tokenize() {
const acornInstance = this;
const tokTypes = getAcorn().tokTypes;
do
acornInstance.next();
while (acornInstance.type !== tokTypes.eof);
acornInstance.next();
}
finishNode(...args) {
const result = super.finishNode(...args);
return this[PRIVATE_PROCESS_NODE](result);
}
finishNodeAt(...args) {
const result = super.finishNodeAt(...args);
return this[PRIVATE_PROCESS_NODE](result);
}
[PRIVATE_PROCESS_NODE](node) {
const { tokenStore, ctx, nodes } = this[PRIVATE];
validateNode(node, tokenStore, ctx);
nodes.push(node);
return node;
}
raise(pos, message) {
const loc = getAcorn().getLineInfo(this[PRIVATE].code, pos);
throw new ParseError(message, pos, loc.line, loc.column + 1);
}
raiseRecoverable(pos, message) {
this.raise(pos, message);
}
unexpected(pos) {
if (pos != null) {
this.raise(pos, "Unexpected token.");
return;
}
const start = this.start;
const end = this.end;
const token = this[PRIVATE].code.slice(start, end);
if (token) {
const message = `Unexpected token '${token}'.`;
this.raise(start, message);
} else {
if (!this[PRIVATE].nodes.length) this.raise(0, "Expected to be an expression, but got empty.");
if (this[PRIVATE].tokenStore.tokens.length) {
const last = this[PRIVATE].tokenStore.tokens[this[PRIVATE].tokenStore.tokens.length - 1];
this.raise(last.range[0], `Unexpected token '${last.value}'.`);
}
this.raise(start, "Unexpected token.");
}
}
};
return parserCache;
}
/** Get extend parser */
function getAnyTokenErrorParser() {
return class ExtendParser extends getParser() {
constructor(options, code, pos) {
super({
...options,
onToken: (token) => {
return throwUnexpectedTokenError(code.slice(...token.range), token);
}
}, code, pos);
}
};
}
//#endregion
//#region src/parser/syntax-context.ts
/**
* Normalize json syntax option
*/
function getJSONSyntaxContext(str) {
const upperCase = str?.toUpperCase();
if (upperCase === "JSON") return {
trailingCommas: false,
comments: false,
plusSigns: false,
spacedSigns: false,
leadingOrTrailingDecimalPoints: false,
infinities: false,
nans: false,
numericSeparators: false,
binaryNumericLiterals: false,
octalNumericLiterals: false,
legacyOctalNumericLiterals: false,
invalidJsonNumbers: false,
multilineStrings: false,
unquoteProperties: false,
singleQuotes: false,
numberProperties: false,
undefinedKeywords: false,
sparseArrays: false,
regExpLiterals: false,
templateLiterals: false,
bigintLiterals: false,
unicodeCodepointEscapes: false,
escapeSequenceInIdentifier: false,
parentheses: false,
staticExpressions: false
};
if (upperCase === "JSONC") return {
trailingCommas: true,
comments: true,
plusSigns: false,
spacedSigns: false,
leadingOrTrailingDecimalPoints: false,
infinities: false,
nans: false,
numericSeparators: false,
binaryNumericLiterals: false,
octalNumericLiterals: false,
legacyOctalNumericLiterals: false,
invalidJsonNumbers: false,
multilineStrings: false,
unquoteProperties: false,
singleQuotes: false,
numberProperties: false,
undefinedKeywords: false,
sparseArrays: false,
regExpLiterals: false,
templateLiterals: false,
bigintLiterals: false,
unicodeCodepointEscapes: false,
escapeSequenceInIdentifier: false,
parentheses: false,
staticExpressions: false
};
if (upperCase === "JSON5") return {
trailingCommas: true,
comments: true,
plusSigns: true,
spacedSigns: true,
leadingOrTrailingDecimalPoints: true,
infinities: true,
nans: true,
numericSeparators: false,
binaryNumericLiterals: false,
octalNumericLiterals: false,
legacyOctalNumericLiterals: false,
invalidJsonNumbers: true,
multilineStrings: true,
unquoteProperties: true,
singleQuotes: true,
numberProperties: false,
undefinedKeywords: false,
sparseArrays: false,
regExpLiterals: false,
templateLiterals: false,
bigintLiterals: false,
unicodeCodepointEscapes: false,
escapeSequenceInIdentifier: false,
parentheses: false,
staticExpressions: false
};
return {
trailingCommas: true,
comments: true,
plusSigns: true,
spacedSigns: true,
leadingOrTrailingDecimalPoints: true,
infinities: true,
nans: true,
numericSeparators: true,
binaryNumericLiterals: true,
octalNumericLiterals: true,
legacyOctalNumericLiterals: true,
invalidJsonNumbers: true,
multilineStrings: true,
unquoteProperties: true,
singleQuotes: true,
numberProperties: true,
undefinedKeywords: true,
sparseArrays: true,
regExpLiterals: true,
templateLiterals: true,
bigintLiterals: true,
unicodeCodepointEscapes: true,
escapeSequenceInIdentifier: true,
parentheses: true,
staticExpressions: true
};
}
//#endregion
//#region src/parser/parser.ts
/**
* Parse JSON source code
*/
function parseJSON(code, options) {
const parserOptions = Object.assign({ filePath: "<input>" }, options || {}, {
loc: true,
range: true,
raw: true,
tokens: true,
comment: true,
ecmaVersion: "latest"
});
const ctx = getJSONSyntaxContext(options?.jsonSyntax);
const tokens = [];
const comments = [];
const tokenStore = new TokenStore(tokens);
const nodes = [];
parserOptions.ctx = ctx;
parserOptions.tokenStore = tokenStore;
parserOptions.comments = comments;
parserOptions.nodes = nodes;
const baseAst = getParser().parseExpressionAt(code, 0, parserOptions);
for (const node of nodes) node.type = `JSON${node.type}`;
const ast = convertProgramNode(baseAst, tokenStore, ctx, code);
let lastIndex = Math.max(baseAst.range[1], tokens[tokens.length - 1]?.range[1] ?? 0, comments[comments.length - 1]?.range[1] ?? 0);
let lastChar = code[lastIndex];
while (lastChar === "\n" || lastChar === "\r" || lastChar === " " || lastChar === " ") {
lastIndex++;
lastChar = code[lastIndex];
}
if (lastIndex < code.length) getAnyTokenErrorParser().parseExpressionAt(code, lastIndex, parserOptions);
ast.tokens = tokens;
ast.comments = comments;
return ast;
}
/**
* Parse source code
*/
function parseForESLint(code, options) {
return {
ast: parseJSON(code, options),
visitorKeys: getVisitorKeys(),
services: { isJSON: true }
};
}
//#endregion
//#region src/parser/tokenizer.ts
/**
* Tokenizes the given code.
* @param code The code to tokenize.
* @param options The options to use for tokenization.
* @private
*/
function tokenize(code, options) {
const parserOptions = Object.assign({ filePath: "<input>" }, options || {}, {
loc: true,
range: true,
raw: true,
tokens: true,
comment: true,
ecmaVersion: "latest"
});
const ctx = getJSONSyntaxContext(options?.jsonSyntax);
const tokens = [];
const comments = [];
const tokenStore = new TokenStore(tokens);
parserOptions.ctx = ctx;
parserOptions.tokenStore = tokenStore;
parserOptions.comments = comments;
getParser().tokenizer(code, parserOptions).tokenize();
if (!options?.includeComments) return tokens;
const result = [];
let commentIndex = 0;
for (const token of tokens) {
while (commentIndex < comments.length && comments[commentIndex].range[0] < token.range[0]) {
result.push(comments[commentIndex]);
commentIndex++;
}
result.push(token);
}
while (commentIndex < comments.length) {
result.push(comments[commentIndex]);
commentIndex++;
}
return result;
}
//#endregion
//#region src/parser/traverse.ts
/**
* Check that the given key should be traversed or not.
* @this {Traversable}
* @param key The key to check.
* @returns `true` if the key should be traversed.
*/
function fallbackKeysFilter(key) {
let value = null;
return key !== "comments" && key !== "leadingComments" && key !== "loc" && key !== "parent" && key !== "range" && key !== "tokens" && key !== "trailingComments" && (value = this[key]) !== null && typeof value === "object" && (typeof value.type === "string" || Array.isArray(value));
}
/**
* Get the keys of the given node to traverse it.
* @param node The node to get.
* @returns The keys to traverse.
*/
function getFallbackKeys(node) {
return Object.keys(node).filter(fallbackKeysFilter, node);
}
/**
* Get the keys of the given node to traverse it.
* @param node The node to get.
* @returns The keys to traverse.
*/
function getKeys(node, visitorKeys) {
return ((visitorKeys || getVisitorKeys())[node.type] || getFallbackKeys(node)).filter((key) => !getNodes(node, key).next().done);
}
/**
* Get the nodes of the given node.
* @param node The node to get.
*/
function* getNodes(node, key) {
const child = node[key];
if (Array.isArray(child)) {
for (const c of child) if (isNode(c)) yield c;
} else if (isNode(child)) yield child;
}
/**
* Check whether a given value is a node.
* @param x The value to check.
* @returns `true` if the value is a node.
*/
function isNode(x) {
return x !== null && typeof x === "object" && typeof x.type === "string";
}
/**
* Traverse the given node.
* @param node The node to traverse.
* @param parent The parent node.
* @param visitor The node visitor.
*/
function traverse(node, parent, visitor) {
visitor.enterNode(node, parent);
const keys = getKeys(node, visitor.visitorKeys);
for (const key of keys) for (const child of getNodes(node, key)) traverse(child, node, visitor);
visitor.leaveNode(node, parent);
}
/**
* Traverse the given AST tree.
* @param node Root node to traverse.
* @param visitor Visitor.
*/
function traverseNodes(node, visitor) {
traverse(node, null, visitor);
}
//#endregion
//#region src/utils/ast.ts
/**
* Checks if given node is JSONExpression
*/
function isExpression(node) {
if (node.type === "JSONIdentifier" || node.type === "JSONLiteral") {
const parent = node.parent;
if (parent.type === "JSONProperty" && parent.key === node) return false;
return true;
}
if (node.type === "JSONObjectExpression" || node.type === "JSONArrayExpression" || node.type === "JSONUnaryExpression" || node.type === "JSONTemplateLiteral" || node.type === "JSONBinaryExpression") return true;
return false;
}
/**
* Checks if given node is JSONNumberIdentifier
*/
function isNumberIdentifier(node) {
return isExpression(node) && (node.name === "Infinity" || node.name === "NaN");
}
/**
* Checks if given node is JSONUndefinedIdentifier
*/
function isUndefinedIdentifier(node) {
return isExpression(node) && node.name === "undefined";
}
const resolver = {
Program(node) {
if (node.body.length !== 1 || node.body[0].type !== "JSONExpressionStatement") throw new Error("Illegal argument");
return getStaticJSONValue(node.body[0]);
},
JSONExpressionStatement(node) {
return getStaticJSONValue(node.expression);
},
JSONObjectExpression(node) {
const object = {};
for (const prop of node.properties) Object.assign(object, getStaticJSONValue(prop));
return object;
},
JSONProperty(node) {
return { [node.key.type === "JSONLiteral" ? `${node.key.value}` : node.key.name]: getStaticJSONValue(node.value) };
},
JSONArrayExpression(node) {
const array = [];
for (let index = 0; index < node.elements.length; index++) {
const element = node.elements[index];
if (element) array[index] = getStaticJSONValue(element);
}
return array;
},
JSONLiteral(node) {
if (node.regex) try {
return new RegExp(node.regex.pattern, node.regex.flags);
} catch {
return `/${node.regex.pattern}/${node.regex.flags}`;
}
if (node.bigint != null) try {
return BigInt(node.bigint);
} catch {
return `${node.bigint}`;
}
return node.value;
},
JSONUnaryExpression(node) {
const value = getStaticJSONValue(node.argument);
return node.operator === "-" ? -value : value;
},
JSONBinaryExpression(node) {
const left = getStaticJSONValue(node.left);
const right = getStaticJSONValue(node.right);
return node.operator === "+" ? left + right : node.operator === "-" ? left - right : node.operator === "*" ? left * right : node.operator === "/" ? left / right : node.operator === "%" ? left % right : node.operator === "**" ? left ** right : (() => {
throw new Error(`Unknown operator: ${node.operator}`);
})();
},
JSONIdentifier(node) {
if (node.name === "Infinity") return Infinity;
if (node.name === "NaN") return NaN;
if (node.name === "undefined") return;
throw new Error("Illegal argument");
},
JSONTemplateLiteral(node) {
return getStaticJSONValue(node.quasis[0]);
},
JSONTemplateElement(node) {
return node.value.cooked;
}
};
/**
* Gets the static value for the given node.
*/
function getStaticJSONValue(node) {
return resolver[node.type](node);
}
//#endregion
//#region package.json
var name$1 = "jsonc-eslint-parser";
var version$1 = "3.1.0";
//#endregion
//#region src/meta.ts
var meta_exports = /* @__PURE__ */ __exportAll({
name: () => name,
version: () => version
});
const name = name$1;
const version = version$1;
//#endregion
//#region src/index.ts
const VisitorKeys = getVisitorKeys();
//#endregion
export { VisitorKeys, getStaticJSONValue, isExpression, isNumberIdentifier, isUndefinedIdentifier, meta_exports as meta, name, parseForESLint, parseJSON, tokenize, traverseNodes };