159 lines
6.6 KiB
JavaScript
159 lines
6.6 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const utils_1 = require("../utils");
|
|
const regex_syntax_1 = require("../utils/regex-syntax");
|
|
const CASE_SCHEMA = ["lowercase", "uppercase", "ignore"];
|
|
const DEFAULTS = {
|
|
caseInsensitive: "lowercase",
|
|
unicodeEscape: "lowercase",
|
|
hexadecimalEscape: "lowercase",
|
|
controlEscape: "uppercase",
|
|
};
|
|
function parseOptions(option) {
|
|
if (!option) {
|
|
return DEFAULTS;
|
|
}
|
|
return {
|
|
caseInsensitive: option.caseInsensitive || DEFAULTS.caseInsensitive,
|
|
unicodeEscape: option.unicodeEscape || DEFAULTS.unicodeEscape,
|
|
hexadecimalEscape: option.hexadecimalEscape || DEFAULTS.hexadecimalEscape,
|
|
controlEscape: option.controlEscape || DEFAULTS.controlEscape,
|
|
};
|
|
}
|
|
const CODE_POINT_CASE_CHECKER = {
|
|
lowercase: utils_1.isLowercaseLetter,
|
|
uppercase: utils_1.isUppercaseLetter,
|
|
};
|
|
const STRING_CASE_CHECKER = {
|
|
lowercase: (s) => s.toLowerCase() === s,
|
|
uppercase: (s) => s.toUpperCase() === s,
|
|
};
|
|
const CONVERTER = {
|
|
lowercase: (s) => s.toLowerCase(),
|
|
uppercase: (s) => s.toUpperCase(),
|
|
};
|
|
exports.default = (0, utils_1.createRule)("letter-case", {
|
|
meta: {
|
|
docs: {
|
|
description: "enforce into your favorite case",
|
|
category: "Stylistic Issues",
|
|
recommended: false,
|
|
},
|
|
fixable: "code",
|
|
schema: [
|
|
{
|
|
type: "object",
|
|
properties: {
|
|
caseInsensitive: { enum: CASE_SCHEMA },
|
|
unicodeEscape: { enum: CASE_SCHEMA },
|
|
hexadecimalEscape: { enum: CASE_SCHEMA },
|
|
controlEscape: { enum: CASE_SCHEMA },
|
|
},
|
|
additionalProperties: false,
|
|
},
|
|
],
|
|
messages: {
|
|
unexpected: "'{{char}}' is not in {{case}}",
|
|
},
|
|
type: "layout",
|
|
},
|
|
create(context) {
|
|
const options = parseOptions(context.options[0]);
|
|
function report({ node, getRegexpLocation, fixReplaceNode }, reportNode, letterCase, convertText) {
|
|
context.report({
|
|
node,
|
|
loc: getRegexpLocation(reportNode),
|
|
messageId: "unexpected",
|
|
data: {
|
|
char: reportNode.raw,
|
|
case: letterCase,
|
|
},
|
|
fix: fixReplaceNode(reportNode, () => convertText(CONVERTER[letterCase])),
|
|
});
|
|
}
|
|
function verifyCharacterInCaseInsensitive(regexpContext, cNode) {
|
|
if (cNode.parent.type === "CharacterClassRange" ||
|
|
options.caseInsensitive === "ignore") {
|
|
return;
|
|
}
|
|
if (CODE_POINT_CASE_CHECKER[options.caseInsensitive](cNode.value) ||
|
|
!(0, utils_1.isLetter)(cNode.value)) {
|
|
return;
|
|
}
|
|
report(regexpContext, cNode, options.caseInsensitive, (converter) => converter(String.fromCodePoint(cNode.value)));
|
|
}
|
|
function verifyCharacterClassRangeInCaseInsensitive(regexpContext, ccrNode) {
|
|
if (options.caseInsensitive === "ignore") {
|
|
return;
|
|
}
|
|
if (CODE_POINT_CASE_CHECKER[options.caseInsensitive](ccrNode.min.value) ||
|
|
!(0, utils_1.isLetter)(ccrNode.min.value) ||
|
|
CODE_POINT_CASE_CHECKER[options.caseInsensitive](ccrNode.max.value) ||
|
|
!(0, utils_1.isLetter)(ccrNode.max.value)) {
|
|
return;
|
|
}
|
|
report(regexpContext, ccrNode, options.caseInsensitive, (converter) => `${converter(String.fromCodePoint(ccrNode.min.value))}-${converter(String.fromCodePoint(ccrNode.max.value))}`);
|
|
}
|
|
function verifyCharacterInUnicodeEscape(regexpContext, cNode) {
|
|
if (options.unicodeEscape === "ignore") {
|
|
return;
|
|
}
|
|
const parts = /^(?<prefix>\\u\{?)(?<code>.*?)(?<suffix>\}?)$/u.exec(cNode.raw);
|
|
if (STRING_CASE_CHECKER[options.unicodeEscape](parts.groups.code)) {
|
|
return;
|
|
}
|
|
report(regexpContext, cNode, options.unicodeEscape, (converter) => `${parts.groups.prefix}${converter(parts.groups.code)}${parts.groups.suffix}`);
|
|
}
|
|
function verifyCharacterInHexadecimalEscape(regexpContext, cNode) {
|
|
if (options.hexadecimalEscape === "ignore") {
|
|
return;
|
|
}
|
|
const parts = /^\\x(?<code>.*)$/u.exec(cNode.raw);
|
|
if (STRING_CASE_CHECKER[options.hexadecimalEscape](parts.groups.code)) {
|
|
return;
|
|
}
|
|
report(regexpContext, cNode, options.hexadecimalEscape, (converter) => `\\x${converter(parts.groups.code)}`);
|
|
}
|
|
function verifyCharacterInControl(regexpContext, cNode) {
|
|
if (options.controlEscape === "ignore") {
|
|
return;
|
|
}
|
|
const parts = /^\\c(?<code>.*)$/u.exec(cNode.raw);
|
|
if (STRING_CASE_CHECKER[options.controlEscape](parts.groups.code)) {
|
|
return;
|
|
}
|
|
report(regexpContext, cNode, options.controlEscape, (converter) => `\\c${converter(parts.groups.code)}`);
|
|
}
|
|
function createVisitor(regexpContext) {
|
|
const { flags } = regexpContext;
|
|
return {
|
|
onCharacterEnter(cNode) {
|
|
if (flags.ignoreCase) {
|
|
verifyCharacterInCaseInsensitive(regexpContext, cNode);
|
|
}
|
|
const escapeKind = (0, regex_syntax_1.getEscapeSequenceKind)(cNode.raw);
|
|
if (escapeKind === regex_syntax_1.EscapeSequenceKind.unicode ||
|
|
escapeKind === regex_syntax_1.EscapeSequenceKind.unicodeCodePoint) {
|
|
verifyCharacterInUnicodeEscape(regexpContext, cNode);
|
|
}
|
|
if (escapeKind === regex_syntax_1.EscapeSequenceKind.hexadecimal) {
|
|
verifyCharacterInHexadecimalEscape(regexpContext, cNode);
|
|
}
|
|
if (escapeKind === regex_syntax_1.EscapeSequenceKind.control) {
|
|
verifyCharacterInControl(regexpContext, cNode);
|
|
}
|
|
},
|
|
...(flags.ignoreCase
|
|
? {
|
|
onCharacterClassRangeEnter(ccrNode) {
|
|
verifyCharacterClassRangeInCaseInsensitive(regexpContext, ccrNode);
|
|
},
|
|
}
|
|
: {}),
|
|
};
|
|
}
|
|
return (0, utils_1.defineRegexpVisitor)(context, {
|
|
createVisitor,
|
|
});
|
|
},
|
|
});
|