587 lines
16 KiB
JavaScript
587 lines
16 KiB
JavaScript
import { t as __exportAll } from "./chunk-DQk6qfdC.mjs";
|
|
import "./no-parsing-error-B9_Ixkn3.mjs";
|
|
import { getRules } from "./rules.mjs";
|
|
import { VisitorKeys, parseForESLint, traverseNodes } from "jsonc-eslint-parser";
|
|
import { CallMethodStep, ConfigCommentParser, Directive, TextSourceCodeBase, VisitNodeStep } from "@eslint/plugin-kit";
|
|
import { TokenStore } from "@ota-meshi/ast-token-store";
|
|
|
|
//#region lib/configs/flat/base.ts
|
|
var base_default = [{ plugins: { get jsonc() {
|
|
return lib_default;
|
|
} } }, {
|
|
files: [
|
|
"*.json",
|
|
"**/*.json",
|
|
"*.json5",
|
|
"**/*.json5",
|
|
"*.jsonc",
|
|
"**/*.jsonc"
|
|
],
|
|
language: "jsonc/x",
|
|
rules: {
|
|
strict: "off",
|
|
"no-unused-expressions": "off",
|
|
"no-unused-vars": "off"
|
|
}
|
|
}];
|
|
|
|
//#endregion
|
|
//#region lib/configs/flat/recommended-with-json.ts
|
|
var recommended_with_json_default = [...base_default, { rules: {
|
|
"jsonc/comma-dangle": "error",
|
|
"jsonc/no-bigint-literals": "error",
|
|
"jsonc/no-binary-expression": "error",
|
|
"jsonc/no-binary-numeric-literals": "error",
|
|
"jsonc/no-comments": "error",
|
|
"jsonc/no-dupe-keys": "error",
|
|
"jsonc/no-escape-sequence-in-identifier": "error",
|
|
"jsonc/no-floating-decimal": "error",
|
|
"jsonc/no-hexadecimal-numeric-literals": "error",
|
|
"jsonc/no-infinity": "error",
|
|
"jsonc/no-irregular-whitespace": "error",
|
|
"jsonc/no-multi-str": "error",
|
|
"jsonc/no-nan": "error",
|
|
"jsonc/no-number-props": "error",
|
|
"jsonc/no-numeric-separators": "error",
|
|
"jsonc/no-octal-numeric-literals": "error",
|
|
"jsonc/no-octal": "error",
|
|
"jsonc/no-parenthesized": "error",
|
|
"jsonc/no-plus-sign": "error",
|
|
"jsonc/no-regexp-literals": "error",
|
|
"jsonc/no-sparse-arrays": "error",
|
|
"jsonc/no-template-literals": "error",
|
|
"jsonc/no-undefined-value": "error",
|
|
"jsonc/no-unicode-codepoint-escapes": "error",
|
|
"jsonc/no-useless-escape": "error",
|
|
"jsonc/quote-props": "error",
|
|
"jsonc/quotes": "error",
|
|
"jsonc/space-unary-ops": "error",
|
|
"jsonc/valid-json-number": "error",
|
|
"jsonc/vue-custom-block/no-parsing-error": "error"
|
|
} }];
|
|
|
|
//#endregion
|
|
//#region lib/configs/flat/recommended-with-jsonc.ts
|
|
var recommended_with_jsonc_default = [...base_default, { rules: {
|
|
"jsonc/no-bigint-literals": "error",
|
|
"jsonc/no-binary-expression": "error",
|
|
"jsonc/no-binary-numeric-literals": "error",
|
|
"jsonc/no-dupe-keys": "error",
|
|
"jsonc/no-escape-sequence-in-identifier": "error",
|
|
"jsonc/no-floating-decimal": "error",
|
|
"jsonc/no-hexadecimal-numeric-literals": "error",
|
|
"jsonc/no-infinity": "error",
|
|
"jsonc/no-irregular-whitespace": "error",
|
|
"jsonc/no-multi-str": "error",
|
|
"jsonc/no-nan": "error",
|
|
"jsonc/no-number-props": "error",
|
|
"jsonc/no-numeric-separators": "error",
|
|
"jsonc/no-octal-numeric-literals": "error",
|
|
"jsonc/no-octal": "error",
|
|
"jsonc/no-parenthesized": "error",
|
|
"jsonc/no-plus-sign": "error",
|
|
"jsonc/no-regexp-literals": "error",
|
|
"jsonc/no-sparse-arrays": "error",
|
|
"jsonc/no-template-literals": "error",
|
|
"jsonc/no-undefined-value": "error",
|
|
"jsonc/no-unicode-codepoint-escapes": "error",
|
|
"jsonc/no-useless-escape": "error",
|
|
"jsonc/quote-props": "error",
|
|
"jsonc/quotes": "error",
|
|
"jsonc/space-unary-ops": "error",
|
|
"jsonc/valid-json-number": "error",
|
|
"jsonc/vue-custom-block/no-parsing-error": "error"
|
|
} }];
|
|
|
|
//#endregion
|
|
//#region lib/configs/flat/recommended-with-json5.ts
|
|
var recommended_with_json5_default = [...base_default, { rules: {
|
|
"jsonc/no-bigint-literals": "error",
|
|
"jsonc/no-binary-expression": "error",
|
|
"jsonc/no-binary-numeric-literals": "error",
|
|
"jsonc/no-dupe-keys": "error",
|
|
"jsonc/no-escape-sequence-in-identifier": "error",
|
|
"jsonc/no-irregular-whitespace": "error",
|
|
"jsonc/no-number-props": "error",
|
|
"jsonc/no-numeric-separators": "error",
|
|
"jsonc/no-octal-numeric-literals": "error",
|
|
"jsonc/no-octal": "error",
|
|
"jsonc/no-parenthesized": "error",
|
|
"jsonc/no-regexp-literals": "error",
|
|
"jsonc/no-sparse-arrays": "error",
|
|
"jsonc/no-template-literals": "error",
|
|
"jsonc/no-undefined-value": "error",
|
|
"jsonc/no-unicode-codepoint-escapes": "error",
|
|
"jsonc/no-useless-escape": "error",
|
|
"jsonc/space-unary-ops": "error",
|
|
"jsonc/vue-custom-block/no-parsing-error": "error"
|
|
} }];
|
|
|
|
//#endregion
|
|
//#region lib/configs/flat/prettier.ts
|
|
var prettier_default = [...base_default, { rules: {
|
|
"jsonc/array-bracket-newline": "off",
|
|
"jsonc/array-bracket-spacing": "off",
|
|
"jsonc/array-element-newline": "off",
|
|
"jsonc/comma-dangle": "off",
|
|
"jsonc/comma-style": "off",
|
|
"jsonc/indent": "off",
|
|
"jsonc/key-spacing": "off",
|
|
"jsonc/no-floating-decimal": "off",
|
|
"jsonc/object-curly-newline": "off",
|
|
"jsonc/object-curly-spacing": "off",
|
|
"jsonc/object-property-newline": "off",
|
|
"jsonc/quote-props": "off",
|
|
"jsonc/quotes": "off",
|
|
"jsonc/space-unary-ops": "off"
|
|
} }];
|
|
|
|
//#endregion
|
|
//#region lib/configs/flat/all.ts
|
|
const all = {};
|
|
for (const rule of getRules()) {
|
|
if (rule.meta.docs.ruleId === "jsonc/sort-array-values") continue;
|
|
all[rule.meta.docs.ruleId] = "error";
|
|
}
|
|
const config = [...base_default, { rules: { ...all } }];
|
|
|
|
//#endregion
|
|
//#region lib/meta.ts
|
|
var meta_exports = /* @__PURE__ */ __exportAll({
|
|
name: () => name,
|
|
version: () => version
|
|
});
|
|
const name = "eslint-plugin-jsonc";
|
|
const version = "3.1.2";
|
|
|
|
//#endregion
|
|
//#region lib/language/jsonc-source-code.ts
|
|
/**
|
|
* @fileoverview The JSONCSourceCode class.
|
|
*/
|
|
const commentParser = new ConfigCommentParser();
|
|
/**
|
|
* Pattern to match ESLint inline configuration comments in JSONC.
|
|
* Matches: eslint, eslint-disable, eslint-enable, eslint-disable-line, eslint-disable-next-line
|
|
*/
|
|
const INLINE_CONFIG = /^\s*eslint(?:-enable|-disable(?:(?:-next)?-line)?)?(?:\s|$)/u;
|
|
/**
|
|
* JSONC Source Code Object
|
|
*/
|
|
var JSONCSourceCode = class extends TextSourceCodeBase {
|
|
hasBOM;
|
|
parserServices;
|
|
visitorKeys;
|
|
tokenStore;
|
|
#steps = null;
|
|
#cacheTokensAndComments = null;
|
|
#inlineConfigComments = null;
|
|
/**
|
|
* Creates a new instance.
|
|
*/
|
|
constructor(config) {
|
|
super({
|
|
ast: config.ast,
|
|
text: config.text
|
|
});
|
|
this.hasBOM = Boolean(config.hasBOM);
|
|
this.parserServices = config.parserServices;
|
|
this.visitorKeys = config.visitorKeys || VisitorKeys;
|
|
this.tokenStore = new TokenStore({
|
|
tokens: [...config.ast.tokens, ...config.ast.comments],
|
|
isComment: (token) => token.type === "Block" || token.type === "Line"
|
|
});
|
|
}
|
|
traverse() {
|
|
if (this.#steps != null) return this.#steps;
|
|
const steps = [];
|
|
this.#steps = steps;
|
|
const root = this.ast;
|
|
steps.push(new CallMethodStep({
|
|
target: "onCodePathStart",
|
|
args: [{}, root]
|
|
}));
|
|
traverseNodes(root, {
|
|
enterNode(n) {
|
|
steps.push(new VisitNodeStep({
|
|
target: n,
|
|
phase: 1,
|
|
args: [n]
|
|
}));
|
|
},
|
|
leaveNode(n) {
|
|
steps.push(new VisitNodeStep({
|
|
target: n,
|
|
phase: 2,
|
|
args: [n]
|
|
}));
|
|
}
|
|
});
|
|
steps.push(new CallMethodStep({
|
|
target: "onCodePathEnd",
|
|
args: [{}, root]
|
|
}));
|
|
return steps;
|
|
}
|
|
/**
|
|
* Gets all tokens and comments.
|
|
*/
|
|
get tokensAndComments() {
|
|
return this.#cacheTokensAndComments ??= [...this.ast.tokens, ...this.ast.comments].sort((a, b) => a.range[0] - b.range[0]);
|
|
}
|
|
getLines() {
|
|
return this.lines;
|
|
}
|
|
getAllComments() {
|
|
return this.tokenStore.getAllComments();
|
|
}
|
|
/**
|
|
* Returns an array of all inline configuration nodes found in the source code.
|
|
* This includes eslint-disable, eslint-enable, eslint-disable-line,
|
|
* eslint-disable-next-line, and eslint (for inline config) comments.
|
|
*/
|
|
getInlineConfigNodes() {
|
|
if (!this.#inlineConfigComments) this.#inlineConfigComments = this.ast.comments.filter((comment) => INLINE_CONFIG.test(comment.value));
|
|
return this.#inlineConfigComments;
|
|
}
|
|
/**
|
|
* Returns directives that enable or disable rules along with any problems
|
|
* encountered while parsing the directives.
|
|
*/
|
|
getDisableDirectives() {
|
|
const problems = [];
|
|
const directives = [];
|
|
this.getInlineConfigNodes().forEach((comment) => {
|
|
const directive = commentParser.parseDirective(comment.value);
|
|
if (!directive) return;
|
|
const { label, value, justification } = directive;
|
|
if (label === "eslint-disable-line" && comment.loc.start.line !== comment.loc.end.line) {
|
|
const message = `${label} comment should not span multiple lines.`;
|
|
problems.push({
|
|
ruleId: null,
|
|
message,
|
|
loc: comment.loc
|
|
});
|
|
return;
|
|
}
|
|
switch (label) {
|
|
case "eslint-disable":
|
|
case "eslint-enable":
|
|
case "eslint-disable-next-line":
|
|
case "eslint-disable-line": {
|
|
const directiveType = label.slice(7);
|
|
directives.push(new Directive({
|
|
type: directiveType,
|
|
node: comment,
|
|
value,
|
|
justification
|
|
}));
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
return {
|
|
problems,
|
|
directives
|
|
};
|
|
}
|
|
/**
|
|
* Returns inline rule configurations along with any problems
|
|
* encountered while parsing the configurations.
|
|
*/
|
|
applyInlineConfig() {
|
|
const problems = [];
|
|
const configs = [];
|
|
this.getInlineConfigNodes().forEach((comment) => {
|
|
const directive = commentParser.parseDirective(comment.value);
|
|
if (!directive) return;
|
|
const { label, value } = directive;
|
|
if (label === "eslint") {
|
|
const parseResult = commentParser.parseJSONLikeConfig(value);
|
|
if (parseResult.ok) configs.push({
|
|
config: { rules: parseResult.config },
|
|
loc: comment.loc
|
|
});
|
|
else problems.push({
|
|
ruleId: null,
|
|
message: parseResult.error.message,
|
|
loc: comment.loc
|
|
});
|
|
}
|
|
});
|
|
return {
|
|
configs,
|
|
problems
|
|
};
|
|
}
|
|
/**
|
|
* Gets the source text for the given node or the entire source if no node is provided.
|
|
*/
|
|
getText(node, beforeCount, afterCount) {
|
|
if (!node) return this.text;
|
|
const range = node.range;
|
|
const start = range[0] - (beforeCount ?? 0);
|
|
const end = range[1] + (afterCount ?? 0);
|
|
return this.text.slice(Math.max(0, start), Math.min(this.text.length, end));
|
|
}
|
|
getNodeByRangeIndex(index) {
|
|
let node = find([this.ast]);
|
|
if (!node) return null;
|
|
while (true) {
|
|
const child = find(this._getChildren(node));
|
|
if (!child) return node;
|
|
node = child;
|
|
}
|
|
/**
|
|
* Finds a node that contains the given index.
|
|
*/
|
|
function find(nodes) {
|
|
for (const node of nodes) if (node.range[0] <= index && index < node.range[1]) return node;
|
|
return null;
|
|
}
|
|
}
|
|
getFirstToken(node, options) {
|
|
return this.tokenStore.getFirstToken(node, options);
|
|
}
|
|
getFirstTokens(node, options) {
|
|
return this.tokenStore.getFirstTokens(node, options);
|
|
}
|
|
getLastToken(node, options) {
|
|
return this.tokenStore.getLastToken(node, options);
|
|
}
|
|
getLastTokens(node, options) {
|
|
return this.tokenStore.getLastTokens(node, options);
|
|
}
|
|
getTokenBefore(node, options) {
|
|
return this.tokenStore.getTokenBefore(node, options);
|
|
}
|
|
getTokensBefore(node, options) {
|
|
return this.tokenStore.getTokensBefore(node, options);
|
|
}
|
|
getTokenAfter(node, options) {
|
|
return this.tokenStore.getTokenAfter(node, options);
|
|
}
|
|
getTokensAfter(node, options) {
|
|
return this.tokenStore.getTokensAfter(node, options);
|
|
}
|
|
getFirstTokenBetween(left, right, options) {
|
|
return this.tokenStore.getFirstTokenBetween(left, right, options);
|
|
}
|
|
getFirstTokensBetween(left, right, options) {
|
|
return this.tokenStore.getFirstTokensBetween(left, right, options);
|
|
}
|
|
getLastTokenBetween(left, right, options) {
|
|
return this.tokenStore.getLastTokenBetween(left, right, options);
|
|
}
|
|
getLastTokensBetween(left, right, options) {
|
|
return this.tokenStore.getLastTokensBetween(left, right, options);
|
|
}
|
|
getTokens(node, options) {
|
|
return this.tokenStore.getTokens(node, options);
|
|
}
|
|
getTokensBetween(left, right, options) {
|
|
return this.tokenStore.getTokensBetween(left, right, options);
|
|
}
|
|
getCommentsInside(nodeOrToken) {
|
|
return this.tokenStore.getCommentsInside(nodeOrToken);
|
|
}
|
|
getCommentsBefore(nodeOrToken) {
|
|
return this.tokenStore.getCommentsBefore(nodeOrToken);
|
|
}
|
|
getCommentsAfter(nodeOrToken) {
|
|
return this.tokenStore.getCommentsAfter(nodeOrToken);
|
|
}
|
|
commentsExistBetween(first, second) {
|
|
return this.tokenStore.commentsExistBetween(first, second);
|
|
}
|
|
isSpaceBetween(first, second) {
|
|
const [left, right] = first.range[1] <= second.range[0] ? [first, second] : [second, first];
|
|
return this.tokenStore.isSpaceBetween(left, right);
|
|
}
|
|
/**
|
|
* Compatibility for ESLint's SourceCode API
|
|
* @deprecated JSONC does not have scopes
|
|
*/
|
|
getScope(node) {
|
|
if (node?.type !== "Program") return null;
|
|
return createFakeGlobalScope(this.ast);
|
|
}
|
|
/**
|
|
* Compatibility for ESLint's SourceCode API
|
|
* @deprecated JSONC does not have scopes
|
|
*/
|
|
get scopeManager() {
|
|
return {
|
|
scopes: [],
|
|
globalScope: createFakeGlobalScope(this.ast),
|
|
acquire: (node) => {
|
|
if (node.type === "Program") return createFakeGlobalScope(this.ast);
|
|
return null;
|
|
},
|
|
getDeclaredVariables: () => [],
|
|
addGlobals: () => {}
|
|
};
|
|
}
|
|
/**
|
|
* Compatibility for ESLint's SourceCode API
|
|
* @deprecated
|
|
*/
|
|
isSpaceBetweenTokens(first, second) {
|
|
return this.isSpaceBetween(first, second);
|
|
}
|
|
_getChildren(node) {
|
|
const keys = this.visitorKeys[node.type] || [];
|
|
const children = [];
|
|
for (const key of keys) {
|
|
const value = node[key];
|
|
if (Array.isArray(value)) {
|
|
for (const element of value) if (isNode(element)) children.push(element);
|
|
} else if (isNode(value)) children.push(value);
|
|
}
|
|
return children;
|
|
}
|
|
};
|
|
/**
|
|
* Determines whether the given value is a JSONC AST node.
|
|
*/
|
|
function isNode(value) {
|
|
return typeof value === "object" && value !== null && typeof value.type === "string" && Array.isArray(value.range) && Boolean(value.loc) && typeof value.loc === "object";
|
|
}
|
|
/**
|
|
* Creates a fake global scope for JSONC files.
|
|
* @deprecated JSONC does not have scopes
|
|
*/
|
|
function createFakeGlobalScope(node) {
|
|
const fakeGlobalScope = {
|
|
type: "global",
|
|
block: node,
|
|
set: /* @__PURE__ */ new Map(),
|
|
through: [],
|
|
childScopes: [],
|
|
variableScope: null,
|
|
variables: [],
|
|
references: [],
|
|
functionExpressionScope: false,
|
|
isStrict: false,
|
|
upper: null,
|
|
implicit: {
|
|
variables: [],
|
|
set: /* @__PURE__ */ new Map()
|
|
}
|
|
};
|
|
fakeGlobalScope.variableScope = fakeGlobalScope;
|
|
return fakeGlobalScope;
|
|
}
|
|
|
|
//#endregion
|
|
//#region lib/language/jsonc-language.ts
|
|
/**
|
|
* The JSONC language implementation for ESLint.
|
|
*/
|
|
var JSONCLanguage = class {
|
|
/**
|
|
* The type of file to read.
|
|
*/
|
|
fileType = "text";
|
|
/**
|
|
* The line number at which the parser starts counting.
|
|
*/
|
|
lineStart = 1;
|
|
/**
|
|
* The column number at which the parser starts counting.
|
|
*/
|
|
columnStart = 0;
|
|
/**
|
|
* The name of the key that holds the type of the node.
|
|
*/
|
|
nodeTypeKey = "type";
|
|
_mode;
|
|
constructor(options) {
|
|
this._mode = options?.mode ?? "EXTENDED";
|
|
}
|
|
/**
|
|
* Validates the language options.
|
|
*/
|
|
validateLanguageOptions(_languageOptions) {}
|
|
normalizeLanguageOptions(languageOptions) {
|
|
return {
|
|
ecmaVersion: "latest",
|
|
...languageOptions,
|
|
parserOptions: {
|
|
...this._mode !== "EXTENDED" ? { jsonSyntax: this._mode } : {},
|
|
...languageOptions.parserOptions
|
|
}
|
|
};
|
|
}
|
|
/**
|
|
* Parses the given file into an AST.
|
|
*/
|
|
parse(file, context) {
|
|
const text = file.body;
|
|
try {
|
|
return {
|
|
ok: true,
|
|
ast: parseForESLint(text, { jsonSyntax: context.languageOptions?.parserOptions?.jsonSyntax ?? (this._mode !== "EXTENDED" ? this._mode : void 0) }).ast
|
|
};
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
const parseError = error;
|
|
return {
|
|
ok: false,
|
|
errors: [{
|
|
message,
|
|
line: parseError.lineNumber ?? 1,
|
|
column: parseError.column ?? 1
|
|
}]
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Creates a new SourceCode object for the given file and parse result.
|
|
*/
|
|
createSourceCode(file, parseResult) {
|
|
return new JSONCSourceCode({
|
|
text: file.body,
|
|
ast: parseResult.ast,
|
|
hasBOM: file.bom,
|
|
parserServices: { isJSON: true },
|
|
visitorKeys: VisitorKeys
|
|
});
|
|
}
|
|
};
|
|
|
|
//#endregion
|
|
//#region lib/index.ts
|
|
const configs = {
|
|
base: base_default,
|
|
"recommended-with-json": recommended_with_json_default,
|
|
"recommended-with-jsonc": recommended_with_jsonc_default,
|
|
"recommended-with-json5": recommended_with_json5_default,
|
|
prettier: prettier_default,
|
|
all: config,
|
|
"flat/base": base_default,
|
|
"flat/recommended-with-json": recommended_with_json_default,
|
|
"flat/recommended-with-jsonc": recommended_with_jsonc_default,
|
|
"flat/recommended-with-json5": recommended_with_json5_default,
|
|
"flat/prettier": prettier_default,
|
|
"flat/all": config
|
|
};
|
|
const rules = getRules().reduce((obj, r) => {
|
|
obj[r.meta.docs.ruleName] = r;
|
|
return obj;
|
|
}, {});
|
|
const languages = {
|
|
json: new JSONCLanguage({ mode: "JSON" }),
|
|
jsonc: new JSONCLanguage({ mode: "JSONC" }),
|
|
json5: new JSONCLanguage({ mode: "JSON5" }),
|
|
x: new JSONCLanguage({ mode: "EXTENDED" })
|
|
};
|
|
var lib_default = {
|
|
meta: meta_exports,
|
|
configs,
|
|
rules,
|
|
languages
|
|
};
|
|
|
|
//#endregion
|
|
export { configs, lib_default as default, languages, meta_exports as meta, rules }; |