Skip to content

Commit

Permalink
chore: enforce title case for device descriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
AlCalzone committed Sep 29, 2023
1 parent c0bc439 commit 29b4c1f
Showing 1 changed file with 96 additions and 5 deletions.
101 changes: 96 additions & 5 deletions packages/eslint-plugin/src/rules/consistent-config-labels.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,89 @@
import { type AST } from "jsonc-eslint-parser";
import { type JSONCRule } from "../utils";

// const ROOT = "Program > JSONObjectExpression";
const ROOT = "Program > JSONExpressionStatement > JSONObjectExpression";

function isSurroundedByWhitespace(str: string) {
return /^\s/.test(str) || /\s$/.test(str);
}

const titleCaseExceptions = [
"with",
"in",
"of",
"by",
"for",
"the",
"and",
"kW",
"kWh",
"RFID",
];

const titleCaseIgnored: RegExp[] = [
/^v\d+$/i, // Versions
/^x$/i, // x
/^[a-z]+[A-Z]/, // fancY mArketing nAmEs
/[®™]$/i, // Trademarks etc.
];

const alwaysUppercase: RegExp[] = [
/^\d+\w$/i,
];

// TODO: Additional fixes:
// Plug-In, In-Wall
// remove Z-Wave and all its variants

function isTitleCase(str: string) {
const words = str.split(" ");
return words.every((word, i) => {
if (word.length === 0) return true;
if (i > 0) {
// Exceptions don't apply for the first word
const exception = titleCaseExceptions.find(
(ex) => ex.toLowerCase() === word.toLowerCase(),
);
if (exception) return word === exception;
}
if (titleCaseIgnored.some((re) => re.test(word))) return true;
if (alwaysUppercase.some((re) => re.test(word))) {
return word === word.toUpperCase();
}

return word[0] === word[0].toUpperCase();
});
}

function toTitleCase(str: string) {
const words = str.split(" ");
return words.map((word, i) => {
if (word.length === 0) return word;
// Return some exceptions as they are defined
if (i > 0) {
// Exceptions don't apply for the first word
const exception = titleCaseExceptions.find(
(ex) => ex.toLowerCase() === word.toLowerCase(),
);
if (exception) return exception;
}
if (titleCaseIgnored.some((re) => re.test(word))) return word;
if (alwaysUppercase.some((re) => re.test(word))) {
return word.toUpperCase();
}
// Title case the rest
return word[0].toUpperCase() + word.slice(1);
}).join(" ");
}

export const consistentConfigLabels: JSONCRule.RuleModule = {
create(context) {
if (!context.parserServices.isJSON) {
return {};
}

return {
// Disallow surrounding whitespace
JSONLiteral(node: AST.JSONLiteral) {
if (typeof node.value !== "string") return;
if (!isSurroundedByWhitespace(node.value)) return;
Expand All @@ -28,11 +98,31 @@ export const consistentConfigLabels: JSONCRule.RuleModule = {
),
});
},
// [`${ROOT} > JSONProperty[key.value='description']`](
// node: AST.JSONProperty,
// ) {
// },

// Enforce title case for device descriptions
[`${ROOT} > JSONProperty[key.value='description']`](
node: AST.JSONProperty,
) {
if (
node.value.type !== "JSONLiteral"
|| typeof node.value.value !== "string"
) return;
const value = node.value;
if (isTitleCase(value.value)) return;

context.report({
loc: node.loc,
messageId: "must-be-title-case",
data: {
what: "Device descriptions",
},
fix: (fixer) =>
fixer.replaceTextRange(
value.range,
`"${toTitleCase(value.raw.slice(1, -1))}"`,
),
});
},
// "JSONProperty[key.value='paramInformation'] > JSONArrayExpression > JSONObjectExpression"(
// node: AST.JSONObjectExpression,
// ) {
Expand All @@ -50,6 +140,7 @@ export const consistentConfigLabels: JSONCRule.RuleModule = {
messages: {
"no-surrounding-whitespace":
"Leading and trailing whitespace is not allowed",
"must-be-title-case": "{{what}} must be in Title Case",
},
type: "problem",
},
Expand Down

0 comments on commit 29b4c1f

Please sign in to comment.