To use our custom cops, you have to inherit our gem configuration in
You can also modify that configuration enabling/disabling the cops you want:
To get access to our ERB linters, create a
.erb-linters/primer.rb file with:
Then, you can enable them in
If you also want to add our cops when linting ERB, you can modify your
.erb-lint.yml to have:
linters:Rubocop:enabled: truerubocop_config:inherit_gem:primer_view_components: lib/rubocop/config/default.yml
Linters have been extremely helpful tools to migrate code from old HTML to components. It keeps human errors like typos out of the migrations and can surface many cases at once. We want to build linters for all of our components, so it's a good idea to explain how to do it.
Before building a new linter, we need to understand their structure and how they work.
This class holds the main linter logic, interpreting the AST and building a "tag tree", linking each tag with its closing tag. Knowing where each block starts and ends allows us to apply autocorrections using
This module has the autocorrection logic, which will transform HTML attributes into component arguments. It will also build the replacement code for said HTML.
These are classes that define which attributes and classes should be converted into arguments. They all should inherit
ERBLint::Linters::ArgumentMappers::Base, which provides an interface to return the arguments as a hash or string.
Base class will also make sure that all autocorrections take SystemArguments into consideration.
Since all the logic matching is extracted to
BaseLinter, we need to set some parameters in our class to create a linter.
To help keep arguments in sync, we provide a
Primer::ViewComponents::Constants.get helper, which can get constant values from our components. This will guarantee that when we update our components, it will also update the linters.
If a linter does not provide autocorrection, you only need to inherit from
BaseLinter and set the following constants:
TAGS constant holds an array of all the valid HTML tags for this component. This will be used by
BaseLinter to check if a node is a candidate for a component.
button, summary, a for a
This is the message that will be displayed when there is an offense. Most of them follow the same template but can be customized.
CLASSES constant will have a list of classes that may indicate that an HTML node corresponds to a component. This will only be checked if the node passed the
btn, btn-link for a
A list of arguments that are required for a component. Each item in the list can be either a string or a regex.
/for|value/, "aria-label" for
To enable autocorrection in a linter, make sure to include the
Autocorrectable module and set the following constants:
The class responsible for transforming classes and attributes into arguments for the component. See ArgumentMapper section on how to create a mapper.
The component name for the linter. It will be used on autocorrection to set
COMPONENT.new(arguments) as a suggestion.
All mappers follow the same interface provided by
The base logic will transform:
If the linter needs to transform more attributes or classes into specific values, you'll need to create a new class that inherits
Custom mappers have two methods:
If you need to transform attributes into arguments, you'll need to set a
ATTRIBUTES constant with the list you want to convert and then implement the
That method will be called for each attribute in the constant and must return a hash with the arguments you want.
This method will receive a list of all the classes and must return a Hash with all the arguments. The hash must also have
classes key with all the classes that couldn't be mapped (or an empty array).