eslint-plugin-yml
Version:
This ESLint plugin provides linting rules for YAML.
209 lines (208 loc) • 7.31 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const yaml_eslint_parser_1 = require("yaml-eslint-parser");
const index_1 = require("../utils/index");
const compat_1 = require("../utils/compat");
const SYMBOLS = new Set([
":",
"{",
"}",
"[",
"]",
",",
"&",
"*",
"#",
"|",
"+",
"%",
'"',
"'",
"\\",
]);
function toRegExps(patterns) {
return patterns.map((p) => new RegExp(p, "u"));
}
function isStringScalar(node) {
return typeof node.value === "string";
}
exports.default = (0, index_1.createRule)("plain-scalar", {
meta: {
docs: {
description: "require or disallow plain style scalar.",
categories: ["standard"],
extensionRule: false,
layout: false,
},
fixable: "code",
schema: [
{ enum: ["always", "never"] },
{
type: "object",
properties: {
ignorePatterns: {
type: "array",
items: { type: "string" },
},
overrides: {
type: "object",
properties: {
mappingKey: { enum: ["always", "never", null] },
},
additionalProperties: false,
},
},
additionalProperties: false,
},
],
messages: {
required: "Must use plain style scalar.",
disallow: "Must use quoted style scalar.",
},
type: "layout",
},
create(context) {
var _a, _b, _c, _d, _e;
const sourceCode = (0, compat_1.getSourceCode)(context);
if (!((_a = sourceCode.parserServices) === null || _a === void 0 ? void 0 : _a.isYAML)) {
return {};
}
const valueOption = {
prefer: context.options[0] || "always",
ignorePatterns: [],
};
const overridesMappingKey = (_c = (_b = context.options[1]) === null || _b === void 0 ? void 0 : _b.overrides) === null || _c === void 0 ? void 0 : _c.mappingKey;
const keyOption = overridesMappingKey
? {
prefer: overridesMappingKey,
ignorePatterns: [],
}
: valueOption;
if ((_d = context.options[1]) === null || _d === void 0 ? void 0 : _d.ignorePatterns) {
valueOption.ignorePatterns = toRegExps((_e = context.options[1]) === null || _e === void 0 ? void 0 : _e.ignorePatterns);
}
else {
if (valueOption.prefer === "always") {
valueOption.ignorePatterns = toRegExps([
String.raw `[\v\f\u0085\u00a0\u1680\u180e\u2000-\u200b\u2028\u2029\u202f\u205f\u3000\ufeff]`,
]);
}
if (overridesMappingKey && keyOption.prefer === "always") {
keyOption.ignorePatterns = toRegExps([
String.raw `[\v\f\u0085\u00a0\u1680\u180e\u2000-\u200b\u2028\u2029\u202f\u205f\u3000\ufeff]`,
]);
}
}
let currentDocument;
function canToPlain(node) {
if (node.value !== node.value.trim()) {
return false;
}
for (let index = 0; index < node.value.length; index++) {
const char = node.value[index];
if (SYMBOLS.has(char)) {
return false;
}
if (index === 0) {
if (char === "-" || char === "?") {
const next = node.value[index + 1];
if (next && !next.trim()) {
return false;
}
}
else if (char === "!") {
const next = node.value[index + 1];
if (next && (!next.trim() || next === "!" || next === "<")) {
return false;
}
}
}
}
const parent = node.parent.type === "YAMLWithMeta" ? node.parent.parent : node.parent;
if (parent.type === "YAMLPair") {
if (parent.key === node) {
const colon = sourceCode.getTokenAfter(node);
if (colon && colon.value === ":") {
const next = sourceCode.getTokenAfter(colon);
if (colon.range[1] === (next === null || next === void 0 ? void 0 : next.range[0])) {
return false;
}
}
}
}
return true;
}
function verifyAlways(node) {
if (node.style !== "double-quoted" && node.style !== "single-quoted") {
return;
}
if (!canToPlain(node)) {
return;
}
try {
const result = (0, yaml_eslint_parser_1.parseForESLint)(node.value, {
defaultYAMLVersion: currentDocument === null || currentDocument === void 0 ? void 0 : currentDocument.version,
});
if ((0, yaml_eslint_parser_1.getStaticYAMLValue)(result.ast) !== node.value) {
return;
}
}
catch (_a) {
return;
}
context.report({
node,
messageId: "required",
fix(fixer) {
return fixer.replaceText(node, node.value);
},
});
}
function verifyNever(node) {
if (node.style !== "plain") {
return;
}
const text = node.value;
context.report({
node,
messageId: "disallow",
fix(fixer) {
return fixer.replaceText(node, `"${text
.replace(/(["\\])/gu, "\\$1")
.replace(/\r?\n|[\u2028\u2029]/gu, "\\n")}"`);
},
});
}
function withinKey(node) {
const parent = node.parent;
if (parent.type === "YAMLPair" && parent.key === node) {
return true;
}
const grandParent = parent.parent;
if (grandParent.type === "YAMLWithMeta") {
return withinKey(grandParent);
}
return false;
}
return {
YAMLDocument(node) {
currentDocument = node;
},
YAMLScalar(node) {
if (!isStringScalar(node)) {
return;
}
const option = withinKey(node) ? keyOption : valueOption;
if (option.ignorePatterns.some((p) => p.test(node.value))) {
return;
}
if (option.prefer === "always") {
verifyAlways(node);
}
else {
verifyNever(node);
}
},
};
},
});