SourceTermAnalysisSystem_vue/node_modules/eslint-plugin-toml/lib/rules/key-spacing.js
2026-05-15 10:22:44 +08:00

397 lines
14 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../utils");
const ast_utils_1 = require("../utils/ast-utils");
const compat_1 = require("../utils/compat");
function containsLineTerminator(str) {
return /[\n\r\u2028\u2029]/u.test(str);
}
function last(arr) {
return arr[arr.length - 1];
}
function isSingleLine(node) {
return node.loc.end.line === node.loc.start.line;
}
function isSingleLineProperties(properties) {
const [firstProp] = properties;
const lastProp = last(properties);
return firstProp.loc.start.line === lastProp.loc.end.line;
}
function initOptionProperty(fromOptions) {
const mode = fromOptions.mode || "strict";
let beforeEqual, afterEqual;
if (typeof fromOptions.beforeEqual !== "undefined") {
beforeEqual = fromOptions.beforeEqual;
}
else {
beforeEqual = true;
}
if (typeof fromOptions.afterEqual !== "undefined") {
afterEqual = fromOptions.afterEqual;
}
else {
afterEqual = true;
}
let align = undefined;
if (typeof fromOptions.align !== "undefined") {
if (typeof fromOptions.align === "object") {
align = fromOptions.align;
}
else {
align = {
on: fromOptions.align,
mode,
beforeEqual,
afterEqual,
};
}
}
return {
mode,
beforeEqual,
afterEqual,
align,
};
}
function initOptions(fromOptions) {
let align, multiLine, singleLine;
if (typeof fromOptions.align === "object") {
align = {
...initOptionProperty(fromOptions.align),
on: fromOptions.align.on || "equal",
mode: fromOptions.align.mode || "strict",
};
multiLine = initOptionProperty(fromOptions.multiLine || fromOptions);
singleLine = initOptionProperty(fromOptions.singleLine || fromOptions);
}
else {
multiLine = initOptionProperty(fromOptions.multiLine || fromOptions);
singleLine = initOptionProperty(fromOptions.singleLine || fromOptions);
if (multiLine.align) {
align = {
on: multiLine.align.on,
mode: multiLine.align.mode || multiLine.mode,
beforeEqual: multiLine.align.beforeEqual,
afterEqual: multiLine.align.afterEqual,
};
}
}
return {
align,
multiLine,
singleLine,
};
}
const ON_SCHEMA = {
enum: ["equal", "value"],
};
const OBJECT_WITHOUT_ON_SCHEMA = {
type: "object",
properties: {
mode: {
enum: ["strict", "minimum"],
},
beforeEqual: {
type: "boolean",
},
afterEqual: {
type: "boolean",
},
},
additionalProperties: false,
};
const ALIGN_OBJECT_SCHEMA = {
type: "object",
properties: {
on: ON_SCHEMA,
...OBJECT_WITHOUT_ON_SCHEMA.properties,
},
additionalProperties: false,
};
exports.default = (0, utils_1.createRule)("key-spacing", {
meta: {
docs: {
description: "enforce consistent spacing between keys and values in key/value pairs",
categories: ["standard"],
extensionRule: "key-spacing",
},
fixable: "whitespace",
schema: [
{
anyOf: [
{
type: "object",
properties: {
align: {
anyOf: [ON_SCHEMA, ALIGN_OBJECT_SCHEMA],
},
...OBJECT_WITHOUT_ON_SCHEMA.properties,
},
additionalProperties: false,
},
{
type: "object",
properties: {
singleLine: OBJECT_WITHOUT_ON_SCHEMA,
multiLine: {
type: "object",
properties: {
align: {
anyOf: [ON_SCHEMA, ALIGN_OBJECT_SCHEMA],
},
...OBJECT_WITHOUT_ON_SCHEMA.properties,
},
additionalProperties: false,
},
},
additionalProperties: false,
},
{
type: "object",
properties: {
singleLine: OBJECT_WITHOUT_ON_SCHEMA,
multiLine: OBJECT_WITHOUT_ON_SCHEMA,
align: ALIGN_OBJECT_SCHEMA,
},
additionalProperties: false,
},
],
},
],
messages: {
extraKey: "Extra space after key '{{key}}'.",
extraValue: "Extra space before value for key '{{key}}'.",
missingKey: "Missing space after key '{{key}}'.",
missingValue: "Missing space before value for key '{{key}}'.",
},
type: "layout",
},
create,
});
function create(context) {
const sourceCode = (0, compat_1.getSourceCode)(context);
if (!sourceCode.parserServices?.isTOML) {
return {};
}
const options = context.options[0] || {};
const { multiLine: multiLineOptions, singleLine: singleLineOptions, align: alignmentOptions, } = initOptions(options);
function isKeyValueProperty(property) {
return property.type === "TOMLKeyValue";
}
function getLastTokenBeforeEqual(node) {
const equalToken = sourceCode.getTokenAfter(node, ast_utils_1.isEqualSign);
return sourceCode.getTokenBefore(equalToken);
}
function getNextEqual(node) {
return sourceCode.getTokenAfter(node, ast_utils_1.isEqualSign);
}
function getKey(property) {
const keys = property.key.keys;
return keys
.map((key) => sourceCode.getText().slice(key.range[0], key.range[1]))
.join(".");
}
function report(property, side, whitespace, expected, mode) {
const diff = whitespace.length - expected;
const nextEqual = getNextEqual(property.key);
const tokenBeforeEqual = sourceCode.getTokenBefore(nextEqual, {
includeComments: true,
});
const tokenAfterEqual = sourceCode.getTokenAfter(nextEqual, {
includeComments: true,
});
const invalid = (mode === "strict"
? diff !== 0
:
diff < 0 || (diff > 0 && expected === 0)) &&
!(expected && containsLineTerminator(whitespace));
if (!invalid) {
return;
}
const { locStart, locEnd, missingLoc } = side === "key"
? {
locStart: tokenBeforeEqual.loc.end,
locEnd: nextEqual.loc.start,
missingLoc: tokenBeforeEqual.loc,
}
: {
locStart: nextEqual.loc.start,
locEnd: tokenAfterEqual.loc.start,
missingLoc: tokenAfterEqual.loc,
};
const { loc, messageId } = diff > 0
? {
loc: { start: locStart, end: locEnd },
messageId: side === "key" ? "extraKey" : "extraValue",
}
: {
loc: missingLoc,
messageId: side === "key" ? "missingKey" : "missingValue",
};
context.report({
node: property[side],
loc,
messageId,
data: {
key: getKey(property),
},
fix(fixer) {
if (diff > 0) {
if (side === "key") {
return fixer.removeRange([
tokenBeforeEqual.range[1],
tokenBeforeEqual.range[1] + diff,
]);
}
return fixer.removeRange([
tokenAfterEqual.range[0] - diff,
tokenAfterEqual.range[0],
]);
}
const spaces = " ".repeat(-diff);
if (side === "key") {
return fixer.insertTextAfter(tokenBeforeEqual, spaces);
}
return fixer.insertTextBefore(tokenAfterEqual, spaces);
},
});
}
function getKeyWidth(pair) {
const startToken = sourceCode.getFirstToken(pair);
const endToken = getLastTokenBeforeEqual(pair.key);
return endToken.range[1] - startToken.range[0];
}
function getPropertyWhitespace(pair) {
const whitespace = /(\s*)=(\s*)/u.exec(sourceCode.getText().slice(pair.key.range[1], pair.value.range[0]));
if (whitespace) {
return {
beforeEqual: whitespace[1],
afterEqual: whitespace[2],
};
}
return null;
}
function verifySpacing(node, lineOptions) {
const actual = getPropertyWhitespace(node);
if (actual) {
report(node, "key", actual.beforeEqual, lineOptions.beforeEqual ? 1 : 0, lineOptions.mode);
report(node, "value", actual.afterEqual, lineOptions.afterEqual ? 1 : 0, lineOptions.mode);
}
}
function verifyListSpacing(properties, lineOptions) {
const length = properties.length;
for (let i = 0; i < length; i++) {
verifySpacing(properties[i], lineOptions);
}
}
if (alignmentOptions) {
return defineAlignmentVisitor(alignmentOptions);
}
return defineSpacingVisitor();
function defineAlignmentVisitor(alignmentOptions) {
return {
"TOMLTopLevelTable, TOMLTable, TOMLInlineTable"(node) {
if (isSingleLine(node)) {
const body = node.body;
verifyListSpacing(body.filter(isKeyValueProperty), singleLineOptions);
}
else {
verifyAlignment(node);
}
},
};
function verifyGroupAlignment(properties) {
const length = properties.length;
const widths = properties.map(getKeyWidth);
const align = alignmentOptions.on;
let targetWidth = Math.max(...widths);
let beforeEqual, afterEqual, mode;
if (alignmentOptions && length > 1) {
beforeEqual = alignmentOptions.beforeEqual ? 1 : 0;
afterEqual = alignmentOptions.afterEqual ? 1 : 0;
mode = alignmentOptions.mode;
}
else {
beforeEqual = multiLineOptions.beforeEqual ? 1 : 0;
afterEqual = multiLineOptions.afterEqual ? 1 : 0;
mode = alignmentOptions.mode;
}
targetWidth += align === "equal" ? beforeEqual : afterEqual;
for (let i = 0; i < length; i++) {
const property = properties[i];
const whitespace = getPropertyWhitespace(property);
if (whitespace) {
const width = widths[i];
if (align === "value") {
report(property, "key", whitespace.beforeEqual, beforeEqual, mode);
report(property, "value", whitespace.afterEqual, targetWidth - width, mode);
}
else {
report(property, "key", whitespace.beforeEqual, targetWidth - width, mode);
report(property, "value", whitespace.afterEqual, afterEqual, mode);
}
}
}
}
function continuesPropertyGroup(lastMember, candidate) {
const groupEndLine = lastMember.loc.start.line;
const candidateStartLine = candidate.loc.start.line;
if (candidateStartLine - groupEndLine <= 1) {
return true;
}
const leadingComments = sourceCode.getCommentsBefore(candidate);
if (leadingComments.length &&
leadingComments[0].loc.start.line - groupEndLine <= 1 &&
candidateStartLine - last(leadingComments).loc.end.line <= 1) {
for (let i = 1; i < leadingComments.length; i++) {
if (leadingComments[i].loc.start.line -
leadingComments[i - 1].loc.end.line >
1) {
return false;
}
}
return true;
}
return false;
}
function createGroups(node) {
const body = node.body;
const pairs = body.filter(isKeyValueProperty);
if (pairs.length === 1) {
return [pairs];
}
return pairs.reduce((groups, property) => {
const currentGroup = last(groups);
const prev = last(currentGroup);
if (!prev || continuesPropertyGroup(prev, property)) {
currentGroup.push(property);
}
else {
groups.push([property]);
}
return groups;
}, [[]]);
}
function verifyAlignment(node) {
createGroups(node).forEach((group) => {
const properties = group;
if (properties.length > 0 && isSingleLineProperties(properties)) {
verifyListSpacing(properties, multiLineOptions);
}
else {
verifyGroupAlignment(properties);
}
});
}
}
function defineSpacingVisitor() {
return {
TOMLKeyValue(node) {
if (!isKeyValueProperty(node))
return;
verifySpacing(node, isSingleLine(node.parent) ? singleLineOptions : multiLineOptions);
},
};
}
}