Collection of 17 custom ESLint rules covering accessibility, security, performance, and coding best practices specifically designed for GitHub's development standards.
Rules that enforce web accessibility standards and ARIA best practices.
/**
* Disallow interactive elements with visually hidden styling
*/
'a11y-no-visually-hidden-interactive-element': ESLintRule;
/**
* Disallow generic link text like "click here" or "read more"
*/
'a11y-no-generic-link-text': ESLintRule;
/**
* Disallow title attributes on elements
*/
'a11y-no-title-attribute': ESLintRule;
/**
* Require ARIA labels to be well-formatted
*/
'a11y-aria-label-is-well-formatted': ESLintRule;
/**
* Ensure ARIA roles support the specified ARIA properties
*/
'a11y-role-supports-aria-props': ESLintRule;
/**
* Require SVG elements to have an accessible name
*/
'a11y-svg-has-accessible-name': ESLintRule;Usage Examples:
// .eslintrc.js
module.exports = {
plugins: ['github'],
rules: {
'github/a11y-svg-has-accessible-name': 'error',
'github/a11y-no-generic-link-text': 'error',
'github/a11y-role-supports-aria-props': 'error'
}
};
// Examples that would trigger these rules:
// ❌ Bad: <svg></svg>
// ✅ Good: <svg aria-label="Chart showing sales data"></svg>
// ❌ Bad: <a href="/help">Click here</a>
// ✅ Good: <a href="/help">View help documentation</a>Rules that improve code performance and enforce coding best practices.
/**
* Disallow Array.prototype.forEach in favor of for loops
*/
'array-foreach': ESLintRule;
/**
* Disallow accessing currentTarget in async functions
*/
'async-currenttarget': ESLintRule;
/**
* Disallow calling preventDefault in async functions
*/
'async-preventdefault': ESLintRule;
/**
* Disallow Element.prototype.blur() usage
*/
'no-blur': ESLintRule;
/**
* Disallow Promise.prototype.then() in favor of async/await
*/
'no-then': ESLintRule;
/**
* Prefer observers over polling or manual checks
*/
'prefer-observers': ESLintRule;
/**
* Disallow useless passive event listeners
*/
'no-useless-passive': ESLintRule;
/**
* Require passive event listeners where appropriate
*/
'require-passive-events': ESLintRule;Usage Examples:
// Examples that would trigger these rules:
// ❌ Bad: array-foreach
users.forEach(user => processUser(user));
// ✅ Good:
for (const user of users) processUser(user);
// ❌ Bad: no-blur
element.blur();
// ✅ Good: restore focus to previous element
// ❌ Bad: no-then
fetch('/api/data').then(response => response.json());
// ✅ Good:
const response = await fetch('/api/data');
const data = await response.json();Rules that prevent common security vulnerabilities and enforce secure coding practices.
/**
* Disallow usage of CSRF tokens in JavaScript
*/
'authenticity-token': ESLintRule;
/**
* Disallow dynamic script tag creation
*/
'no-dynamic-script-tag': ESLintRule;
/**
* Disallow innerHTML usage
*/
'no-inner-html': ESLintRule;
/**
* Disallow unescaped HTML literals
*/
'unescaped-html-literal': ESLintRule;Usage Examples:
// Examples that would trigger security rules:
// ❌ Bad: authenticity-token
const token = document.querySelector('[name="authenticity_token"]').value;
// ❌ Bad: no-dynamic-script-tag
const script = document.createElement('script');
script.src = userInput;
document.head.appendChild(script);
// ❌ Bad: no-inner-html
element.innerHTML = userContent;
// ✅ Good:
element.textContent = userContent;Rules for DOM manipulation best practices and utility functions.
/**
* Require filenames to match a specific regex pattern
*/
'filenames-match-regex': ESLintRule;
/**
* Enforce specific patterns for getAttribute usage
*/
'get-attribute': ESLintRule;
/**
* Enforce JavaScript class naming conventions
*/
'js-class-name': ESLintRule;
/**
* Disallow Bootstrap's d-none class usage
*/
'no-d-none': ESLintRule;
/**
* Disallow dataset API usage
*/
'no-dataset': ESLintRule;
/**
* Disallow implicit buggy global variables
*/
'no-implicit-buggy-globals': ESLintRule;
/**
* Disallow innerText usage in favor of textContent
*/
'no-innerText': ESLintRule;Usage Examples:
// Examples for DOM rules:
// ❌ Bad: no-dataset
element.dataset.userId = '123';
// ✅ Good:
element.setAttribute('data-user-id', '123');
// ❌ Bad: no-innerText
element.innerText = 'Hello World';
// ✅ Good:
element.textContent = 'Hello World';
// ❌ Bad: get-attribute
const id = element.getAttribute('id');
// ✅ Good: (depending on rule configuration)
const id = element.id;All rules can be configured with ESLint severity levels and options.
/**
* ESLint rule severity levels
*/
type ESLintSeverity = 'off' | 'warn' | 'error' | 0 | 1 | 2;
/**
* Rule configuration format
*/
type RuleConfig = ESLintSeverity | [ESLintSeverity, RuleOptions];
interface RuleOptions {
[key: string]: any;
}
/**
* ESLint rule structure
*/
interface ESLintRule {
meta: {
type: 'problem' | 'suggestion' | 'layout';
docs: {
description: string;
url: string;
recommended: boolean;
};
schema: any[];
messages: Record<string, string>;
};
create(context: ESLintContext): Record<string, Function>;
}Configuration Examples:
// Individual rule configuration
module.exports = {
plugins: ['github'],
rules: {
'github/no-blur': 'error',
'github/no-inner-html': 'warn',
'github/a11y-svg-has-accessible-name': ['error', { /* options */ }]
}
};
// Flat configuration
export default [
{
plugins: { github: eslintPluginGithub },
rules: {
'github/authenticity-token': 'error',
'github/no-dynamic-script-tag': 'error',
'github/prefer-observers': 'warn'
}
}
];interface ESLintContext {
/** Report a linting violation */
report(descriptor: {
node: any;
messageId: string;
data?: Record<string, any>;
fix?: (fixer: any) => any;
}): void;
/** Get the source code being linted */
getSourceCode(): {
getText(node?: any): string;
getAllComments(): any[];
getCommentsBefore(node: any): any[];
getCommentsAfter(node: any): any[];
};
/** Get the filename being linted */
getFilename(): string;
/** Get rule options */
options: any[];
}
interface ESLintMessage {
line: number;
column: number;
endLine?: number;
endColumn?: number;
ruleId: string;
message: string;
severity: 1 | 2;
fix?: ESLintFix;
}
interface ESLintFix {
range: [number, number];
text: string;
}