.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / docs / developer-guide / rules.md
1 # Working on rules
2
3 **Please help us create, enhance, and debug stylelint rules!**
4
5 There are well over a hundred rules already, so stylelint *needs* community contributions to continue to improve.
6
7 If you like stylelint and open source software (since you're reading this, you almost certainly do), please consider taking some time to pitch in. Not only will you help stylelint thrive, you will also learn a thing or two — about CSS, PostCSS, Node, ES2015, unit testing, open source software, and more.
8
9 **We want to do everything we can to encourage contributions!** So if you want to participate but don't end up doing it for one reason or another, please file an issue and give us feedback about what we could do to better encourage you.
10
11 Also: we hope that your participation in the project isn't a one-off. *We'd love to add more members to the organization and see more regulars pop up in issues and pull requests!*
12
13 ## Creating a new rule
14
15 First, open [an issue](https://github.com/stylelint/stylelint/issues/new) with your idea for the new rule.
16
17 Usually we have some discussion about the rule's purpose, name, options, and suitability as a rule.
18
19 ### Criteria for inclusion
20
21 We discuss whether the rule meets the following criteria for inclusion in stylelint:
22
23 -   Applicable to standard CSS syntax only.
24 -   Generally useful; not tied to idiosyncratic patterns.
25 -   Has a clear and unambiguous finished state.
26 -   Has a singular purpose.
27 -   Is standalone, and doesn't rely on another rule.
28 -   Does not contain functionality that overlaps with another rule.
29
30 Otherwise, it should be a plugin. However, plugins should also try to adhere to the latter three criteria.
31
32 ### Naming a rule
33
34 Have a look at the [rules user guide](../user-guide/about-rules.md) to familiarize yourself the rule naming conventions.
35
36 We take care to ensure that all the rules are named accurately and consistently. Our goals in that effort are to ensure that rules are easy to find and understand, and to prevent us from wanting to change the name later.
37
38 *Rules are named to encourage explicit, rather than implicit, options.* For example, `color-hex-case: "upper"|"lower"` rather than `color-hex-uppercase: "always"|"never"`. As `color-hex-uppercase: "never"` *implies* always lowercase, whereas `color-hex-case: "lower"` makes it *explicit*.
39
40 ### Determining options
41
42 #### Primary
43
44 Every rule *must have* a **primary option**.
45
46 -   In `"color-hex-case": "upper"`, the primary option is `"upper"`.
47 -   In `"indentation": [2, { "except": ["block"] }]`, the primary option is `2`.
48
49 If your rule can accept an array as its primary option, you must designate this by setting the property `primaryOptionArray = true` on your rule function. For example:
50
51 ```js
52 function rule(primary, secondary) {
53   return (root, result) => {..}
54 }
55 rule.primaryOptionArray = true
56 export default rule
57 // or, for plugins: stylelint.createPlugin(ruleName, rule)
58 ```
59
60 There is one caveat here: If your rule accepts a primary option array, it cannot also accept a primary option object. Whenever possible, if you want your rule to accept a primary option array, you should just make an array the only possibility, instead of allowing for various data structures.
61
62 #### Secondary
63
64 Some rules require extra flexibility to address a variety of use-cases. These can use an **optional secondary options object**.
65
66 -   In `"color-hex-case": "upper"`, there is no secondary options object.
67 -   In `"indentation": [2, { "except": ["block"] }]`, the secondary options object is `{ "except": ["block"] }`.
68
69 The most typical secondary options are `"ignore": []` and `"except": []`; but anything is possible.
70
71 A rule's secondary option can be anything if you're not ignoring or making exceptions. As an example, `resolveNestedSelectors: true|false` is used within some `selector-*` rules to change how the rule processes nested selectors.
72
73 ##### Keyword `"ignore"` and `"except"`
74
75 `"ignore"` and `"except"` accept an array of predefined keyword options e.g. `["relative", "first-nested", "descendant"]`.
76
77 -   Use `"ignore"` when you want the rule to simply skip-over a particular pattern.
78 -   Use `"except"` when you want to invert the primary option for a particular pattern.
79
80 ##### User-defined `"ignore*"`
81
82 Use a more specific secondary option name when accepting a *user-defined* list of things to ignore. This takes the form of `"ignore<Things>": []` e.g. use `"ignoreAtRules": []` if a rule checks at-rules and you want to allow a user to specify which particular at-rule types to ignore.
83
84 ### Determine violation messages
85
86 Messages take one of these forms:
87
88 -   "Expected \[something\] \[in some context\]".
89 -   "Unexpected \[something\] \[in some context\]."
90
91 Look at the messages of other rules to glean more conventions and patterns.
92
93 ### Write the rule
94
95 *When writing the rule, always look to other similar rules for conventions and patterns to start from and mimic.*
96
97 You will use the simple [PostCSS API](http://api.postcss.org/) to navigate and analyze the CSS syntax tree. We recommend using the `walk` iterators (e.g. `walkDecls`), rather than using `forEach` to loop through the nodes.
98
99 Depending on the rule, we also recommend using [postcss-value-parser](https://github.com/TrySound/postcss-value-parser) and [postcss-selector-parser](https://github.com/postcss/postcss-selector-parser). There are significant benefits to using these parsers instead of regular expressions or `indexOf` searches (even if they aren't always the most performant method).
100
101 stylelint has a number of [utility functions](https://github.com/stylelint/stylelint/tree/master/lib/utils) that are used in existing rules and might prove useful to you, as well. Please look through those so that you know what's available. (And if you have a new function that you think might prove generally helpful, let's add it to the list!). You will definitely want to use `validateOptions()` util so that users are warned about invalid options. (Looking at other rules for examples of options validation will help a lot.). You should also make use of the `isStandardSyntax*` utilities to ignore non-standard syntax.
102
103 The rule should be strict *by default*. The user can make the rule more permissive by using the `"ignore*:"` secondary options.
104
105 The rule should not include code for methodologies or language extensions. Instead, provide generic secondary options so that the user can ignore these at the *configuration level*. For example, when dealing with specificity, a rule should not account for the `:global` and `:local` psuedo-classes (introduced in the CSS Modules language extension), instead the rule should provide a `ignorePsuedoClasses: []` secondary option. Methodologies come and go quickly, and this approach ensures the code base does not become littered with code for obsolete things.
106
107 Only add an option to a rule if it addresses a *requested* use case. Do not add an option to a rule, even for the sake of consistency, if there has been no request. This is to avoid polluting the tool with unused features.
108
109 ### Adding autofixing
110
111 Depending on the rule, it might be possible to automatically fix the rule's violations by mutating the PostCSS AST (Abstract Syntax Tree) using the [PostCSS API](http://api.postcss.org/).
112
113 Add `context` variable to rule parameters:
114
115 ```js
116 function rule(primary, secondary, context) {
117   return (root, result) => {..}
118 }
119 ```
120
121 `context` is an object which could have two properties:
122
123 -   `fix`(boolean): If `true`, your rule can apply autofixes.
124 -   `newline`(string): Line-ending used in current linted file.
125
126 If `context.fix` is `true`, then change `root` using PostCSS API and return early before `report()` is called.
127
128 ```js
129 if (context.fix) {
130   // Apply fixes using PostCSS API
131   return // Return and don't report a problem
132 }
133
134 report(...)
135 ```
136
137 ### Write tests
138
139 Each rule must be accompanied by tests that contain:
140
141 -   All patterns that are considered violations.
142 -   All patterns that should *not* be considered violations.
143
144 It is easy to write stylelint tests, so *write as many as you can stand to*.
145
146 #### Checklist
147
148 Please run through this checklist and ensure each point is covered by your tests. Especially *consider the edge-cases*. These are where the bugs and shortcomings of rules always arise.
149
150 ##### Best practices
151
152 -   Ensure you are testing errors in multiple positions, not the same place every time.
153 -   Ensure you use realistic (if simple) CSS, and avoid the use of ellipses.
154 -   Ensure you use standard CSS syntax by default, and only swap parsers when testing a specific piece of non-standard syntax.
155 -   When accessing raw strings from the PostCSS AST, use `node.raws` instead of `node.raw()`. This will ensure string corresponds exactly to the original.
156
157 ##### Commonly overlooked edge-cases
158
159 -   How does your rule handle variables (`$sass`, `@less`, or `var(--custom-property)`)?
160 -   How does your rule handle CSS strings (e.g. `content: "anything goes";`)?
161 -   How does your rule handle CSS comments (e.g. `/* anything goes */`)?
162 -   How does your rule handle `url()` functions, including data URIs (e.g. `url(anything/goes.jpg)`)?
163 -   How does your rule handle vendor prefixes (e.g. `@-webkit-keyframes name {}`)?
164 -   How does your rule handle case sensitivity (e.g. `@KEYFRAMES name {}`)?
165 -   How does your rule handle a pseudo-class *combined* with a pseudo-element (e.g. `a:hover::before`)?
166 -   How does your rule handle nesting (e.g. do you resolve `& a {}`, or check it as is?)?
167 -   How does your rule handle whitespace and punctuation (e.g. comparing `rgb(0,0,0)` with `rgb(0, 0, 0)`)?
168
169 #### Running tests
170
171 You can run the tests via:
172
173 ```console
174 npm test
175 ```
176
177 However, this runs all 25,000+ unit tests and also linting.
178
179 You can use the interactive testing prompt to run tests for just a chosen set of rules (which you'll want to do during development). For example, to run the tests for just the `color-hex-case` and `color-hex-length` rules:
180
181 1.  Use `npm run watch` to start the prompt.
182 2.  Press `p` to filter by a filename regex pattern.
183 3.  Enter `color-hex-case|color-hex-length` i.e. each rule name separated by the pipe symbol (`|`).
184
185 ### Write the README
186
187 Each rule must be accompanied by a README, fitting the following format:
188
189 1.  Rule name.
190 2.  Single line description.
191 3.  Prototypical code example.
192 4.  Expanded description (if necessary).
193 5.  Options.
194 6.  Example patterns that are considered violations (for each option value).
195 7.  Example patterns that are *not* considered violations (for each option value).
196 8.  Optional options (if applicable).
197
198 Look at the READMEs of other rules to glean more conventional patterns.
199
200 #### Single line descriptions
201
202 Take the form of:
203
204 -   "Disallow ..." (for `no` rules).
205 -   "Limit ..." (for `max` rules).
206 -   "Require ..." (for rules that accept `"always"` and `"never"` options).
207 -   "Specify ..." (for everything else).
208
209 #### Example patterns
210
211 -   Use complete CSS patterns i.e. avoid ellipses (`...`)
212 -   Use standard CSS syntax (and use `css` code fences) by default.
213 -   Use the minimum amount of code possible to communicate the pattern e.g. if the rule targets selectors then use an empty rule e.g. `{}`.
214 -   Use `{}`, rather than `{ }` for empty rules.
215 -   Use the `a` type selector by default.
216 -   Use the `@media` at-rules by default.
217 -   Use the `color` property by default.
218 -   Use *foo*, *bar* and *baz* for names e.g. `.foo`, `#bar` `--baz`
219
220 ### Wire up the rule
221
222 The final step is to add references to the new rule in the following places:
223
224 -   [The rules `index.js` file](https://github.com/stylelint/stylelint/blob/master/lib/rules/index.js)
225 -   [The list of rules](../user-guide/rules.md)
226 -   [The example config](../user-guide/example-config.md)
227
228 Once you have something to show, you'll create a [pull request](https://github.com/stylelint/stylelint/compare) to continue the conversation.
229
230 ## Adding an option to an existing rule
231
232 First, open [an issue](https://github.com/stylelint/stylelint/issues/new) about the option you wish to add. We'll discuss its functionality and name there.
233
234 Once we've agreed on the direction, you can work on a pull request. Here are the steps you'll need to take:
235
236 1.  Run `npm run watch` to start the interactive testing prompt.
237 2.  Use the `p` command to filter the active tests to just the rule you're working on.
238 2.  Change the rule's validation to allow for the new option.
239 3.  Add to the rule some logic (as little as possible) to make the option work.
240 4.  Add new unit tests to test the option.
241 5.  Add documentation about the new option.
242
243 ## Fixing a bug in an existing rule
244
245 Fixing bugs is usually very easy. Here is a process that works:
246
247 1.  Run `npm run watch` to start the interactive testing prompt.
248 2.  Use the `p` command to filter the active tests to just the rule you're working on.
249 3.  Write failing unit tests that exemplify the bug.
250 4.  Fiddle with the rule until those new tests pass.
251
252 That's it! **If you are unable to figure out how to fix the bug yourself, it is still helpful to submit a pull request with your failing test cases.** It means that somebody else can jump right in and help out with the rule's logic.
253
254 ## Deprecating a rule
255
256 Deprecating rules doesn't happen very often. However, these two steps are important to do:
257
258 1.  Point the `stylelintReference` link to the specific version of the rule README on the GitHub website, so that it is always accessible.
259 2.  Add the appropriate meta data to mark the rule as deprecated.
260
261 ## Improving the performance of a new or an existing rule
262
263 There's a simple way to run benchmarks on any given rule with any valid config for it:
264
265 ```shell
266 npm run benchmark-rule -- [rule-name] [config]
267 ```
268
269 If the `config` argument is anything other than a string or a boolean, it must be valid JSON wrapped in quotation marks.
270
271 ```shell
272 npm run benchmark-rule -- selector-combinator-space-after never
273 ```
274
275 ```shell
276 npm run benchmark-rule -- selector-combinator-space-after always
277 ```
278
279 ```shell
280 npm run benchmark-rule -- selector-no-combinator true
281 ```
282
283 ```shell
284 npm run benchmark-rule -- block-opening-brace-space-before "[\"always\", {\"ignoreAtRules\": [\"else\"]}]"
285 ```
286
287 The script loads Bootstrap's CSS (from its CDN) and runs it through the configured rule.
288
289 It will end up printing some simple stats like this:
290
291 ```shell
292 Warnings: 1441
293 Mean: 74.17598357142856 ms
294 Deviation: 16.63969674310928 ms
295 ```
296
297 What can you do with this? **When writing new rules or refactoring existing rules, use these measurements to determine the efficiency of your code.**
298
299 A stylelint rule can repeat it's core logic many, many times (e.g. checking every value node of every declaration in a vast CSS codebase). So it's worth paying attention to performance and doing what we can to improve it!
300
301 **This is a great way to contribute if you just want a quick little project.** Try picking a rule and seeing if there's anything you can do to speed it up.
302
303 Make sure to include benchmark measurements in your PR's!