Add jsxA11yLang suggestions
MODIFY
packages/@romejs/cli-diagnostics/printAdvice.ts
packages/@romejs/cli-diagnostics/printAdvice.ts
@@ -228,7 +228,7 @@ function printList(
const {truncated} = opts.reporter.list(
item.list,
{
- truncate: opts.flags.verboseDiagnostics ? undefined : 20,
+ truncate: opts.flags.verboseDiagnostics ? undefined : 10,
reverse: item.reverse,
ordered: item.ordered,
},

MODIFY
packages/@romejs/diagnostics/descriptions.ts
packages/@romejs/diagnostics/descriptions.ts
@@ -300,10 +300,11 @@ export const descriptions = createMessages({
category: "lint/jsxA11yAnchorHasContent",
message: "Anchor must have content and the content must be accessible by a screen reader.",
},
- JSX_A11Y_HTML_INVALID_LANG: {
+ JSX_A11Y_HTML_INVALID_LANG: (value: string, suggestions: Array<string>) => ({
category: "lint/jsxA11yLang",
message: `The <emphasis>lang</emphasis> attribute must have a valid value.`,
- },
+ advice: buildSuggestionAdvice(value, suggestions),
+ }),
NO_DID_UPDATE_SET_STATE: {
category: "lint/noDidUpdateSetState",
message: "Avoid <emphasis>this.setState</emphasis> in <emphasis>componentDidUpdate</emphasis>",

MODIFY
packages/@romejs/js-compiler/lint/rules/react/jsxA11yLang.test.ts
packages/@romejs/js-compiler/lint/rules/react/jsxA11yLang.test.ts
@@ -13,6 +13,7 @@ test(
'<html lang="foo-bar"></html>',
'<html lang="aa-zz"></html>',
'<html lang="zz-AA"></html>',
+ '<html lang="en2></html>',
// VALID
'<html lang="en-US"></html>',
'<html lang="en"></html>',

MODIFY
packages/@romejs/js-compiler/lint/rules/react/jsxA11yLang.ts
packages/@romejs/js-compiler/lint/rules/react/jsxA11yLang.ts
@@ -397,30 +397,51 @@ const ISO = {
],
};
+// We lazily build this suggestions list as it is massive
+function getSuggestions() {
+ const suggestions = [...ISO.countries];
+
+ for (const language of ISO.languages) {
+ for (const country of ISO.countries) {
+ suggestions.push(`${language}-${country}`);
+ }
+ }
+
+ return suggestions;
+}
+
const COUNTRY_AND_REGION_REGEX = new RegExp(/([a-z]{2})-([A-Z]{2})/);
const COUNTRY_REGEX = new RegExp(/([a-z]{2})-([A-Z]{2})/);
-function jsxSupportedLang(node: JSXElement): boolean | undefined {
+// Will return the attribute value if invalid
+function jsxSupportedLang(node: JSXElement): undefined | string {
const attr = getJSXAttribute(node, "lang");
if (!attr || !attr.value) {
- return false;
+ return "undefined";
+ }
+
+ if (attr.value.type === "StringLiteral") {
+ const {value} = attr.value;
+ if (!langSupported(value)) {
+ return value;
}
- if (attr.value.type !== "StringLiteral") {
- return true;
}
- return langSupported(attr.value.value);
+
+ return undefined;
}
function langSupported(lang: string): boolean {
const countryAndRegionMatches = COUNTRY_AND_REGION_REGEX.exec(lang);
- const countryMatches = COUNTRY_REGEX.exec(lang);
if (countryAndRegionMatches && countryAndRegionMatches.length > 0) {
return (
ISO.languages.includes(countryAndRegionMatches[1]) &&
ISO.countries.includes(countryAndRegionMatches[2])
);
- } else if (countryMatches && countryMatches.length > 0) {
+ }
+
+ const countryMatches = COUNTRY_REGEX.exec(lang);
+ if (countryMatches && countryMatches.length > 0) {
return ISO.languages.includes(countryMatches[1]);
}
@@ -432,10 +453,15 @@ export default {
const {node} = path;
if (isJSXElement(node, "html") && hasJSXAttribute(node, "lang")) {
- if (jsxSupportedLang(node) === false) {
+ const invalidValue = jsxSupportedLang(node);
+ if (invalidValue !== undefined) {
+ // TODO add an autofix suggestion
path.context.addNodeDiagnostic(
node,
- descriptions.LINT.JSX_A11Y_HTML_INVALID_LANG,
+ descriptions.LINT.JSX_A11Y_HTML_INVALID_LANG(
+ invalidValue,
+ getSuggestions(),
+ ),
);
}
}

Add jsxA11yLang suggestions