-
-
Notifications
You must be signed in to change notification settings - Fork 404
docs: Add Custom Rules info #1674
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This comment was marked as resolved.
This comment was marked as resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request adds comprehensive documentation for creating and using custom rules in HTMLHint, including a new documentation page, examples, and updates to CLI and options documentation. The documentation is well-structured and detailed.
My review focuses on improving the correctness and robustness of the provided code examples. The key suggestions are:
- Using arrow functions in event listeners to ensure the correct
this
context. - Handling rule options more safely, especially for falsy values like
0
. - Improving performance and robustness in the example rule by caching RegExp objects and handling potential errors.
These changes will ensure the examples follow best practices and are more reliable for users to copy and adapt.
init: function (parser, reporter, options) { | ||
// Listen for start tags | ||
parser.addListener('tagstart', function (event) { | ||
const tagName = event.tagName.toLowerCase() | ||
const mapAttrs = parser.getMapAttrs(event.attrs) | ||
|
||
// Example: Check if elements have a title attribute when they should | ||
if (tagName === 'img' && !mapAttrs.title && !mapAttrs.alt) { | ||
reporter.warn( | ||
'Images should have either a title or alt attribute for accessibility', | ||
event.line, | ||
event.col, | ||
this, | ||
event.raw | ||
) | ||
} | ||
|
||
// Example: Check for specific class naming convention | ||
if (mapAttrs.class && options && options.classPattern) { | ||
const classPattern = new RegExp(options.classPattern) | ||
const classes = mapAttrs.class.split(/\s+/) | ||
|
||
for (const className of classes) { | ||
if (!classPattern.test(className)) { | ||
reporter.warn( | ||
`Class "${className}" does not match the required pattern: ${options.classPattern}`, | ||
event.line, | ||
event.col, | ||
this, | ||
event.raw | ||
) | ||
} | ||
} | ||
} | ||
}) | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example rule can be improved to demonstrate best practices for writing custom rules:
this
context: Thethis
context within theaddListener
callback is not guaranteed to be the rule instance when using afunction
expression. It's safer to use an arrow function(event) => { ... }
to lexically bindthis
to the rule instance from theinit
function's scope.- Performance: The
RegExp
forclassPattern
is created on everytagstart
event. For better performance, it should be created only once during theinit
phase. - Robustness: The
classPattern
option comes from user configuration and could be an invalid regular expression. This would causenew RegExp()
to throw an error and crash the linting process. It's good practice to wrap this in atry...catch
block.
Here's a suggested revision that addresses these points.
init: function (parser, reporter, options) {
let classPattern;
if (options && options.classPattern) {
try {
classPattern = new RegExp(options.classPattern);
} catch (e) {
// Invalid RegExp pattern from user config, so we can't validate class names.
}
}
// Listen for start tags
parser.addListener('tagstart', (event) => {
const tagName = event.tagName.toLowerCase()
const mapAttrs = parser.getMapAttrs(event.attrs)
// Example: Check if elements have a title attribute when they should
if (tagName === 'img' && !mapAttrs.title && !mapAttrs.alt) {
reporter.warn(
'Images should have either a title or alt attribute for accessibility',
event.line,
event.col,
this,
event.raw
)
}
// Example: Check for specific class naming convention
if (mapAttrs.class && classPattern) {
const classes = mapAttrs.class.split(/\s+/)
for (const className of classes) {
if (!classPattern.test(className)) {
reporter.warn(
`Class "${className}" does not match the required pattern: ${options.classPattern}`,
event.line,
event.col,
this,
event.raw
)
}
}
}
})
},
id: 'max-attributes', | ||
description: 'Elements should not have more than the specified number of attributes', | ||
init: function(parser, reporter, options) { | ||
const maxAttrs = options || 5; // Default to 5 if no options provided |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The line const maxAttrs = options || 5;
is not robust for handling options. If a user configures "max-attributes": 0
in their .htmlhintrc
, options
will be 0
, and 0 || 5
will evaluate to 5
. This is likely not the user's intent.
A safer way to provide a default value is to check the type of options
or check specifically for undefined
.
const maxAttrs = typeof options === 'number' ? options : 5; // Default to 5 if no options provided
This pull request introduces comprehensive support for custom HTMLHint rules, including examples, documentation, and CLI enhancements. The changes aim to make it easier for users to create, configure, and use custom rules to extend HTMLHint's functionality.
Custom Rule Examples and Implementation:
examples/custom-rules/example-rule.js
: Added an example custom rule that checks for accessibility attributes on images and validates class names against a configurable pattern. The rule demonstrates integration with the HTML parser and reporter.examples/custom-rules/README.md
: Documented the example custom rule, including its purpose, usage, configuration, and testing instructions.Documentation Updates:
website/src/content/docs/usage/custom-rules.mdx
: Created a detailed guide on creating, using, and organizing custom rules, including best practices, examples, and troubleshooting tips.website/src/content/docs/usage/cli.md
: Expanded the CLI documentation for the--rulesdir
flag, showing how to load custom rules from files or directories.website/src/content/docs/usage/options.md
: Added documentation for therulesdir
option, with usage examples and details on directory loading behavior.