Original address series article address
At Facebook, we have thousands of engineers working across different product lines to provide reliable and high-quality services to users around the world, and we face unique challenges in code quality management. Not only are we dealing with a large codebase base, but there are a growing number of features, and sometimes if you want to refactor and improve one module, it will affect many others. In the case of CSS, we have to deal with thousands of CSS files that are constantly changing. In the past, we focused on working together to ensure Code quality through Code Review, Code style specification, and refactoring, but a lot of errors still slipped under the radar and were submitted to the Code base. We’ve been using our own CSS Linter to detect basic code errors and ensure a consistent coding style, and although it has mostly met our goals, there are still a lot of problems, so I’d like to discuss how to ensure CSS code quality in this article as well.
Regex is not Enough: Regex is not Enough
The old Linter used to extract syntax from CSS based on a number of regular expressions, something like this:
preg_match_all( // This pattern matches [attr] selectors with no preceding selector. '/\/\*.*? \*\/|\{[^}]*\}|\s(\[[^\]]+\])/s', $data, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); foreach ($matches as $match) { if (isset($match[1])) { raiseError(...) ; }Copy the code
Basically, a detection rule needs to add a special matching rule, which is very difficult to maintain and has a big problem in performance. We need to traverse the entire file for each rule, which is very poor performance.
Abstract Syntax Tree
Fed up with regular expressions, we wanted a more usable and nuanced CSS interpreter. CSS is a language itself, and it’s not good to treat it as a plain text file, so we’re going to build a parser using AST, which is an abstract syntax tree. This new approach has a nice performance boost. For example, we have this CSS code in our code base:
{ display: none: background-color: #8B1D3; Padding: 10 px, px 10, 0, 0. Opacity: 1.0 f; }Copy the code
It takes a good look to spot problems in the code snippet, such as a wrong property name, a wrong hexadecimal color code, a wrong delimiter, and so on. Browsers don’t automatically report errors to you, making it harder for developers to find errors themselves. In terms of implementation, we found PostCSS to be a good tool, so we chose Stylelint as our new Linter tool, which is built on PostCSS and is very flexible and has a good community.
Like Esprima and ESLint in JavaScript, Stylelint provides access to the entire AST, making it easier and faster to access specific code nodes depending on the situation. For example, our detection rules now look like this:
root.walkDecls(node => { if (node.prop === 'text-transform' && node.value === 'uppercase') { report({ ... }); }});Copy the code
We can also pass in some basic functions, such as Linear-gradient, like this:
// disallow things like linear-gradient(top, blue, green) w. incorrect first valueroot.walkDecls(node => { const parsedValue = styleParser(node.value); parsedValue.walk(valueNode => { if (valueNode.type === 'function' && valueNode.value === 'linear-gradient') { const firstValueInGradient = styleParser.stringify(valueNode.nodes[0]); if (disallowedFirstValuesInGradient.indexOf(firstValueInGradient) > -1) { report({ ... }); }}}); });Copy the code
The test rules written this way are more readable, easier to understand and maintain, and work this way regardless of CSS formatting and where rules and declarations are placed.
Custom rules: user-defined rules
By default, we use some of the rules built into Stylelint, such as declaration-no-important,selector-no-universal, and selector-class-pattern. How to add custom rules can refer to the built-in plugin mechanism, and we use examples such as:
-
Slow-css-properties alerts any properties with poor performance, such as opacity or box-shadow
-
Filters-with-svg-files alerts Edge that SVG filtering is not supported
-
Use-variables alerts variables that can be replaced by built-in constants
-
Common-properties-whitelist checks for miswritten properties that don’t actually exist
-
Mobile-flexbox to detect properties that are not supported by older mobile browsers
-
Text-transform-uppercase Alerts “text-transform: uppercase”, which is considered unfriendly in some languages
We also contributed some rules as well as AD plugin itions to the Stylelint.
Automatic replacement: indicates Automatic replacement
An important part of our inspection process is automatic formatting. Linter will ask you if you need to replace it according to the rules if you find a problem. This feature will save you a lot of manual correction time. In general, we look at errors reported by Linter before submitting code and fix them. Unfortunately Stylelint doesn’t have built-in automatic formatting and repair mechanisms, so we rewrote part of the Stylelint rules to add an automatic replacement and repair feature.
Test all the things
Another problem with our old Linter was that there was no unit testing, which was as unreliable as not testing the code before it went live. We can process text in any format, so we also want to make sure that our detection rules are applicable to a real-world environment. Here we choose Jest, which Stylelint supports well, and a unit test looks something like this:
test.ok('div { background-image: linear-gradient( 0deg, blue, green 40%, red ); }',
'linear gradient with valid syntax');
test.notOk('a { background: linear-gradient(top, blue, green); }',
message,
'linear-gradient with invalid syntax');
Copy the code
What ‘s next
Switching to a reliable CSS Linter tool is only the first step in ensuring high-quality CSS code. We are also going to add a lot of custom detection rules to catch common errors, ensure that prescribed best practices and common code conventions are used. We’ve already done this in JavaScript validation.
In addition, CSS Linter is also a challenge to csS-in-JS in the React community. Currently, most linters focus on handling traditional CSS files. JSX processing specifications will be added in the future.