eslint-plugin-yml
Version:
This ESLint plugin provides linting rules for YAML.
266 lines (265 loc) • 10.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.hasTabIndent = hasTabIndent;
exports.calcExpectIndentForPairs = calcExpectIndentForPairs;
exports.calcExpectIndentForEntries = calcExpectIndentForEntries;
exports.getActualIndent = getActualIndent;
exports.getActualIndentFromLine = getActualIndentFromLine;
exports.incIndent = incIndent;
exports.decIndent = decIndent;
exports.getNumOfIndent = getNumOfIndent;
exports.compareIndent = compareIndent;
exports.isKeyNode = isKeyNode;
exports.unwrapMeta = unwrapMeta;
exports.processIndentFix = processIndentFix;
exports.fixIndent = fixIndent;
const ast_utils_1 = require("./ast-utils");
const compat_1 = require("./compat");
function hasTabIndent(context) {
for (const line of (0, compat_1.getSourceCode)(context).getLines()) {
if (/^\s*\t/u.test(line)) {
return true;
}
if (/^\s*-\s*\t/u.test(line)) {
return true;
}
}
return false;
}
function calcExpectIndentForPairs(mapping, context) {
const sourceCode = (0, compat_1.getSourceCode)(context);
let parentNode = mapping.parent;
if (parentNode.type === "YAMLWithMeta") {
const before = sourceCode.getTokenBefore(parentNode);
if (before == null || before.loc.end.line < parentNode.loc.start.line) {
return calcExpectIndentFromBaseNode(parentNode, mapping.pairs[0], context);
}
parentNode = parentNode.parent;
}
if (parentNode.type === "YAMLDocument") {
const mappingIndent = getActualIndent(mapping, context);
const firstPairIndent = getActualIndent(mapping.pairs[0], context);
if (mappingIndent == null) {
return firstPairIndent;
}
if (firstPairIndent != null &&
compareIndent(mappingIndent, firstPairIndent) < 0) {
return firstPairIndent;
}
return mappingIndent;
}
if (parentNode.type === "YAMLSequence") {
const hyphen = sourceCode.getTokenBefore(mapping);
if (!(0, ast_utils_1.isHyphen)(hyphen)) {
return null;
}
if (hyphen.loc.start.line === mapping.loc.start.line) {
const hyphenIndent = getActualIndent(hyphen, context);
if (hyphenIndent == null) {
return null;
}
const offsetIndent = sourceCode.text.slice(hyphen.range[1], mapping.range[0]);
return `${hyphenIndent} ${offsetIndent}`;
}
return getActualIndent(mapping, context);
}
if (parentNode.type !== "YAMLPair") {
return null;
}
return calcExpectIndentFromBaseNode(parentNode, mapping.pairs[0], context);
}
function calcExpectIndentForEntries(sequence, context) {
const sourceCode = (0, compat_1.getSourceCode)(context);
let parentNode = sequence.parent;
if (parentNode.type === "YAMLWithMeta") {
const before = sourceCode.getTokenBefore(parentNode);
if (before == null || before.loc.end.line < parentNode.loc.start.line) {
return calcExpectIndentFromBaseNode(parentNode, sequence.entries[0], context);
}
parentNode = parentNode.parent;
}
if (parentNode.type === "YAMLDocument") {
const sequenceIndent = getActualIndent(sequence, context);
const firstPairIndent = getActualIndent(sequence.entries[0], context);
if (sequenceIndent == null) {
return firstPairIndent;
}
if (firstPairIndent != null &&
compareIndent(sequenceIndent, firstPairIndent) < 0) {
return firstPairIndent;
}
return sequenceIndent;
}
if (parentNode.type === "YAMLSequence") {
const hyphen = sourceCode.getTokenBefore(sequence);
if (!(0, ast_utils_1.isHyphen)(hyphen)) {
return null;
}
if (hyphen.loc.start.line === sequence.loc.start.line) {
const hyphenIndent = getActualIndent(hyphen, context);
if (hyphenIndent == null) {
return null;
}
const offsetIndent = sourceCode.text.slice(hyphen.range[1], sequence.range[0]);
return `${hyphenIndent} ${offsetIndent}`;
}
return getActualIndent(sequence, context);
}
if (parentNode.type !== "YAMLPair") {
return null;
}
return calcExpectIndentFromBaseNode(parentNode, sequence.entries[0], context);
}
function calcExpectIndentFromBaseNode(baseNode, node, context) {
const baseIndent = getActualIndent(baseNode, context);
if (baseIndent == null) {
return null;
}
const indent = getActualIndent(node, context);
if (indent != null && compareIndent(baseIndent, indent) < 0) {
return indent;
}
return incIndent(baseIndent, context);
}
function getActualIndent(node, context) {
const sourceCode = (0, compat_1.getSourceCode)(context);
const before = sourceCode.getTokenBefore(node, { includeComments: true });
if (!before || before.loc.end.line < node.loc.start.line) {
return getActualIndentFromLine(node.loc.start.line, context);
}
return null;
}
function getActualIndentFromLine(line, context) {
const sourceCode = (0, compat_1.getSourceCode)(context);
const lineText = sourceCode.getLines()[line - 1];
return /^[^\S\n\r\u2028\u2029]*/u.exec(lineText)[0];
}
function incIndent(indent, context) {
const numOfIndent = getNumOfIndent(context);
const add = numOfIndent === 2
? " "
: numOfIndent === 4
? " "
: " ".repeat(numOfIndent);
return `${indent}${add}`;
}
function decIndent(indent, context) {
const numOfIndent = getNumOfIndent(context);
return " ".repeat(indent.length - numOfIndent);
}
function getNumOfIndent(context, optionValue) {
var _a, _b;
const num = optionValue !== null && optionValue !== void 0 ? optionValue : (_b = (_a = context.settings) === null || _a === void 0 ? void 0 : _a.yml) === null || _b === void 0 ? void 0 : _b.indent;
return num == null || num < 2 ? 2 : num;
}
function compareIndent(a, b) {
const minLen = Math.min(a.length, b.length);
for (let index = 0; index < minLen; index++) {
if (a[index] !== b[index]) {
return NaN;
}
}
return a.length > b.length ? 1 : a.length < b.length ? -1 : 0;
}
function isKeyNode(node) {
if (node.parent.type === "YAMLWithMeta") {
return isKeyNode(node.parent);
}
return node.parent.type === "YAMLPair" && node.parent.key === node;
}
function unwrapMeta(node) {
if (!node) {
return node;
}
if (node.type === "YAMLWithMeta") {
return node.value;
}
return node;
}
function* processIndentFix(fixer, baseIndent, targetNode, context) {
const sourceCode = (0, compat_1.getSourceCode)(context);
if (targetNode.type === "YAMLWithMeta") {
yield* metaIndent(targetNode);
return;
}
if (targetNode.type === "YAMLPair") {
yield* pairIndent(targetNode);
return;
}
yield* contentIndent(targetNode);
function* contentIndent(contentNode) {
const actualIndent = getActualIndent(contentNode, context);
if (actualIndent != null && compareIndent(baseIndent, actualIndent) < 0) {
return;
}
let nextBaseIndent = baseIndent;
const expectValueIndent = incIndent(baseIndent, context);
if (actualIndent != null) {
yield fixIndent(fixer, sourceCode, expectValueIndent, contentNode);
nextBaseIndent = expectValueIndent;
}
if (contentNode.type === "YAMLMapping") {
for (const p of contentNode.pairs) {
yield* processIndentFix(fixer, nextBaseIndent, p, context);
}
if (contentNode.style === "flow") {
const close = sourceCode.getLastToken(contentNode);
if (close.value === "}") {
const closeActualIndent = getActualIndent(close, context);
if (closeActualIndent != null &&
compareIndent(closeActualIndent, nextBaseIndent) < 0) {
yield fixIndent(fixer, sourceCode, nextBaseIndent, close);
}
}
}
}
else if (contentNode.type === "YAMLSequence") {
for (const e of contentNode.entries) {
if (!e) {
continue;
}
yield* processIndentFix(fixer, nextBaseIndent, e, context);
}
}
}
function* metaIndent(metaNode) {
let nextBaseIndent = baseIndent;
const actualIndent = getActualIndent(metaNode, context);
if (actualIndent != null) {
if (compareIndent(baseIndent, actualIndent) < 0) {
nextBaseIndent = actualIndent;
}
else {
const expectMetaIndent = incIndent(baseIndent, context);
yield fixIndent(fixer, sourceCode, expectMetaIndent, metaNode);
nextBaseIndent = expectMetaIndent;
}
}
if (metaNode.value) {
yield* processIndentFix(fixer, nextBaseIndent, metaNode.value, context);
}
}
function* pairIndent(pairNode) {
let nextBaseIndent = baseIndent;
const actualIndent = getActualIndent(pairNode, context);
if (actualIndent != null) {
if (compareIndent(baseIndent, actualIndent) < 0) {
nextBaseIndent = actualIndent;
}
else {
const expectKeyIndent = incIndent(baseIndent, context);
yield fixIndent(fixer, sourceCode, expectKeyIndent, pairNode);
nextBaseIndent = expectKeyIndent;
}
}
if (pairNode.value) {
yield* processIndentFix(fixer, nextBaseIndent, pairNode.value, context);
}
}
}
function fixIndent(fixer, sourceCode, indent, node) {
const prevToken = sourceCode.getTokenBefore(node, {
includeComments: true,
});
return fixer.replaceTextRange([prevToken.range[1], node.range[0]], `\n${indent}`);
}