If you haven’t used the esLint comment configuration method:
/* eslint-disable no-alert, no-console */
alert('foo');
console.log('bar');
/* eslint-enable no-alert, no-console */
// eslint-disable-next-line
alert('foo');
Copy the code
Eslint supports inline config that specifies whether a rule takes effect, such as ESlint-disable, eslint-enable, and eslint-disable-next-line. This configuration is called inline config.
This configuration is also available in Webpack, where you can configure code splitting when dynamically introducing a module, called Magic Comment.
import(
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
/* webpackExports: ["default", "named"] */
'module'
);
Copy the code
Similarly, Terser has this mechanism, called annotations, that specifies whether an API is pure or not, and if it’s pure you can delete it if you don’t use it.
var a = /*#__PURE__*/React.createElement("div".null);
Copy the code
As you can see, many libraries use this annotation configuration approach, whether it’s called annotation, magic Comment, or inline config, it all refers to the same thing.
Since this is such a common way to configure, how do they implement it?
The implementation principle of the configuration in the annotation
Let’s take a look at the implementation of ESLint’s Inline config.
Eslint will parse the source code into an AST, then pass the AST as a set of rules to check, and output the results as formatter.
Where does the annotation configuration take effect?
I simplified the source code like this:
verify(text) {
/ / parse the source code
const ast = parse(text);
// Call rule to get lint's problem
const lintingProblems = runRules(ast);
// Get the configuration in the comment through the AST
const commentDirectives = getDirectiveComments(ast);
// Filter problems according to the configuration in the comment
return applyDisableDirectives(lintingProblems, commentDirectives);
}
Copy the code
As you can see, the overall flow is:
- Parse the source code into an AST
- Call rule to check the AST and get problems for Lint
- Get diectives in comments by AST
- And filter problems through directives that are ultimately reported
That is, ESLint inline config takes effect after the AST is finished, and problems are filtered once.
How are the directives removed from the AST? How do you filter problems?
Let’s take a look at each.
The simplified source code for fetching directives from the AST looks like this:
function getDirectiveComments(ast){
const directives = [];
ast.comments.forEach(comment= > {
const match = /^[#@](eslint(? :-env|-enable|-disable(? : (? :-next)? -line)?) ? |exported|globals?) (? :\s|$)/u.exec(comment.trim());
if (match) {
const directiveText = match[1]; . directives.push({type: xxx, line: loc.start.line, column: loc.start.column + 1, ruleId }); }}return directives;
}
Copy the code
This is to match all comments in the AST with regular comments. If a directive is supported, collect it and record the corresponding column and line numbers.
Next comes the filtering of problems.
The simplified source code looks like this:
function applyDisableDirectives(problems, disableDirectives) {
const filteredProblems = [];
const disabledRuleMap = new Map(a);let nextIndex = 0;
for (const problem of problems) {
// For each Probelm, the currently disabled rule is found
while (
nextIndex < disableDirectives.length &&
compareLocations(disableDirectives[nextIndex], problem) <= 0
) {
const directive = disableDirectives[nextIndex++];
switch (directive.type) {
case "disable":
disabledRuleMap.set(directive.ruleId, directive);
break;
case "enable":
disabledRuleMap.delete(directive.ruleId);
break; }}// Return if the rule corresponding to problem is not disabled
if (!disabledRuleMap.has(problem.ruleId)) {
filteredProblems.push(problem);
}
}
return filteredProblems;
}
function compareLocations(itemA, itemB) {
return itemA.line - itemB.line || itemA.column - itemB.column;
}
Copy the code
Let’s think about this:
We need to filter out the problems reported by the disabled rule and return the filtered problems.
You can maintain a disabledRuleMap that indicates the disabled rule.
For each problem, fetch the directive information from the disableDirectives according to the row and column number and put the corresponding rule into the disabledRuleMap.
Then check to see if the rule of the problem is disabled, that is, in disabledRuleMap. If so, filter it out.
Once this is done, the returned problem is displayable.
This is how comments such as ESlint-disable, eslint-enable, and eslint-disable-next-line for ESLint can configure rules to take effect.
Eslint finds the corresponding comment based on the column number. In fact, many asTs record the comment associated with each node.
For example, Babel’s AST:
This allows you to retrieve comments based on the AST and then use the re to determine if it is directive.
Finding comments by column number and finding associated comments by AST are two ways to find comments.
conclusion
The configuration in comments is used in tools like ESLint, Webpack, terser, etc., called inline Config, Magic Comment, annotation, respectively, but they all refer to the same thing.
They all find comments in the AST, match with the regular to see if it is a supported directive, and then extract the corresponding information.
After finding directive, we need to find the place where directive takes effect. We can find it in two ways: one is based on the comparison of column and column numbers, the other is based on the associated AST.
Once you have found the directive and where it is in effect, you can do whatever you want with the information in the directive.
Eslint gets all the problems after calling rule, matches directive with row and column numbers, and filters out some problems based on their disabled rules.
The configuration in comments is a common configuration method suitable for some local configurations. Understanding how they work gives us a better grasp of the mechanics.