.stylelintrc.js (15008B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* eslint-env node */ 6 7 "use strict"; 8 9 const fs = require("fs"); 10 const path = require("path"); 11 const rollouts = require("./stylelint-rollouts.config"); 12 13 function readFile(filePath) { 14 return fs 15 .readFileSync(filePath, { encoding: "utf-8" }) 16 .split("\n") 17 .filter(p => p && !p.startsWith("#")); 18 } 19 20 const ignoreFiles = [ 21 ...readFile( 22 path.join(__dirname, "tools", "rewriting", "ThirdPartyPaths.txt") 23 ), 24 ...readFile(path.join(__dirname, "tools", "rewriting", "Generated.txt")), 25 ]; 26 27 module.exports = { 28 extends: ["stylelint-config-recommended"], 29 plugins: [ 30 "./tools/lint/stylelint/stylelint-plugin-mozilla/index.mjs", 31 "@stylistic/stylelint-plugin", 32 "stylelint-use-logical", 33 ], 34 ignoreFiles, 35 rules: { 36 /* Disabled because of `-moz-element(#foo)` which gets misparsed. */ 37 "color-no-invalid-hex": null, 38 "font-family-no-missing-generic-family-keyword": [ 39 true, 40 { 41 ignoreFontFamilies: [ 42 "-moz-button", 43 "-moz-field", 44 "-moz-fixed", 45 "-moz-list", 46 "caption", 47 ], 48 }, 49 ], 50 51 "function-no-unknown": [ 52 true, 53 { 54 ignoreFunctions: [ 55 "light-dark" /* Used for color-scheme dependent colors */, 56 "add" /* Used in mathml.css */, 57 "-moz-symbolic-icon" /* Used for GTK icons */, 58 ], 59 }, 60 ], 61 62 "length-zero-no-unit": [ 63 true, 64 { 65 ignore: ["custom-properties"], 66 }, 67 ], 68 69 "max-nesting-depth": [ 70 3, 71 { 72 ignore: ["blockless-at-rules"], 73 }, 74 ], 75 76 "no-descending-specificity": null, 77 "no-duplicate-selectors": null, 78 79 "property-no-unknown": [ 80 true, 81 { 82 ignoreProperties: [ 83 // overflow-clip-box is Gecko-specific and not exposed to web 84 // content. Might be replaced with overflow-clip-margin, see: 85 // https://github.com/w3c/csswg-drafts/issues/10745 86 "overflow-clip-box", 87 "overflow-clip-box-block", 88 "overflow-clip-box-inline", 89 ], 90 }, 91 ], 92 93 /* 94 * XXXgijs: we would like to enable this, but we can't right now. 95 * This is because Gecko uses a number of custom pseudoclasses, 96 * and stylelint assumes that for `:unknown-pseudoclass(foo)`, 97 * `foo` should be a known type. 98 * This is tedious but workable for things like `-moz-locale-dir` where 99 * the set of acceptable values (ltr/rtl) is small. 100 * However, for tree cells, the set of values is unlimited (ie 101 * user-defined, based on atoms sent by the JS tree view APIs). 102 * There does not appear to be a way to exempt the contents of these 103 * unknown pseudoclasses, and as a result, this rule is not 104 * usable for us. The 'type' only includes the contents of the 105 * pseudoclass, not the pseudo itself, so we can't filter based on the 106 * pseudoclass either. 107 * Ideally, we would either create an option to the builtin rule 108 * in stylelint itself, or mimic the rule but exempt these, or 109 * add parser support for our custom pseudoclasses. 110 * 111 * For now, we just disable this rule. 112 */ 113 "selector-type-no-unknown": null, 114 /* 115 * See above - if we enabled this rule, we'd have to allow for a number 116 * of custom elements we use, which are listed here: 117 "selector-type-no-unknown": [ 118 true, 119 { 120 ignore: ["custom-elements"], 121 ignoreTypes: [ 122 // Modern custom element / storybooked components: 123 /^moz-/, 124 // moz-locale-dir trips this rule for some reason: 125 "rtl", 126 "ltr", 127 // Migrated XBL elements not part of core XUL that we use at the moment: 128 "findbar", 129 "panelmultiview", 130 "panelview", 131 "popupnotification", 132 "popupnotificationcontent", 133 // Legacy XUL elements: 134 // (the commented out ones used to be a thing and aren't used in-tree anymore) 135 "arrowscrollbox", 136 "box", 137 // "broadcaster", 138 // "broadcasterset", 139 "button", 140 "browser", 141 "checkbox", 142 "caption", 143 // clicktoscroll 144 // colorpicker 145 // column 146 // columns 147 "commandset", 148 "command", 149 // conditions 150 // content 151 // datepicker 152 "deck", 153 "description", 154 "dialog", 155 // dialogheader 156 "dropmarker", 157 "editor", 158 // grid 159 // grippy 160 "groupbox", 161 "hbox", 162 // iframe 163 // image 164 "key", 165 "keyset", 166 // label 167 "listbox", 168 // listcell 169 // listcol 170 // listcols 171 // listhead 172 // listheader 173 "listitem", 174 // member 175 "menu", 176 "menubar", 177 "menucaption", 178 "menuitem", 179 "menulist", 180 "menupopup", 181 "menuseparator", 182 "notification", 183 "notificationbox", 184 "observes", 185 // overlay 186 // page 187 "panel", 188 // param 189 "popupset", 190 // preference 191 // preferences 192 // prefpane 193 // prefwindow 194 // progressmeter 195 // query 196 // queryset 197 "radio", 198 "radiogroup", 199 // resizer 200 "richlistbox", 201 "richlistitem", 202 // row 203 // rows 204 // rule 205 // scale 206 // script 207 "scrollbar", 208 "scrollbox", 209 "scrollcorner", 210 "separator", 211 "spacer", 212 // spinbuttons 213 "splitter", 214 "stack", 215 // statusbar 216 // statusbarpanel 217 "stringbundle", 218 "stringbundleset", 219 "tab", 220 "tabbox", 221 "tabpanel", 222 "tabpanels", 223 "tabs", 224 // template 225 // textnode 226 "textbox", 227 // timepicker 228 "titlebar", 229 "toolbar", 230 "toolbarbutton", 231 // toolbargrippy 232 "toolbaritem", 233 "toolbarpalette", 234 "toolbarpaletteitem", 235 "toolbarseparator", 236 "toolbarset", 237 "toolbarspacer", 238 "toolbarspring", 239 "toolbartabstop", 240 "toolbox", 241 "tooltip", 242 "tree", 243 "treecell", 244 "treechildren", 245 "treecol", 246 "treecols", 247 "treeitem", 248 "treerow", 249 "treeseparator", 250 // triple 251 "vbox", 252 // where 253 "window", 254 "wizard", 255 "wizardpage", 256 ], 257 }, 258 ], 259 */ 260 261 "selector-pseudo-class-no-unknown": [ 262 true, 263 { 264 ignorePseudoClasses: ["popover-open"], 265 }, 266 ], 267 "selector-pseudo-element-no-unknown": [ 268 true, 269 { 270 ignorePseudoElements: ["slider-track", "slider-fill", "slider-thumb"], 271 }, 272 ], 273 // stylelint fixes for the use-logical rule will be addressed in Bug 1996168 274 // Remove this line setting `csscontrols/use-logical` to null after implementing fixes 275 "csstools/use-logical": null, 276 "stylelint-plugin-mozilla/no-base-design-tokens": true, 277 "stylelint-plugin-mozilla/use-design-tokens": true, 278 "stylelint-plugin-mozilla/no-non-semantic-token-usage": true, 279 "stylelint-plugin-mozilla/use-size-tokens": true, 280 }, 281 282 overrides: [ 283 { 284 files: "*.scss", 285 customSyntax: "postcss-scss", 286 extends: "stylelint-config-recommended-scss", 287 }, 288 { 289 files: [ 290 "browser/components/aboutwelcome/**", 291 "browser/components/asrouter/**", 292 "browser/extensions/newtab/**", 293 ], 294 customSyntax: "postcss-scss", 295 extends: "stylelint-config-standard-scss", 296 rules: { 297 "@stylistic/color-hex-case": "upper", 298 "@stylistic/indentation": 2, 299 "@stylistic/no-eol-whitespace": true, 300 "@stylistic/no-missing-end-of-source-newline": true, 301 "@stylistic/number-leading-zero": "always", 302 "@stylistic/number-no-trailing-zeros": true, 303 "@stylistic/string-quotes": [ 304 "single", 305 { 306 avoidEscape: true, 307 }, 308 ], 309 "at-rule-disallowed-list": [ 310 ["debug", "warn", "error"], 311 { 312 message: "Clean up %s directives before committing", 313 }, 314 ], 315 "at-rule-no-vendor-prefix": null, 316 "color-function-notation": null, 317 "comment-empty-line-before": [ 318 "always", 319 { 320 except: ["first-nested"], 321 ignore: ["after-comment", "stylelint-commands"], 322 }, 323 ], 324 "custom-property-empty-line-before": null, 325 "custom-property-pattern": null, 326 "declaration-block-no-duplicate-properties": true, 327 "declaration-block-no-redundant-longhand-properties": null, 328 "declaration-no-important": true, 329 "function-no-unknown": [ 330 true, 331 { 332 ignoreFunctions: ["div"], 333 }, 334 ], 335 "function-url-no-scheme-relative": true, 336 "keyframes-name-pattern": null, 337 "media-feature-name-no-vendor-prefix": null, 338 "no-descending-specificity": null, 339 "property-disallowed-list": [ 340 ["margin-left", "margin-right"], 341 { 342 message: "Use margin-inline instead of %s", 343 }, 344 ], 345 "property-no-unknown": true, 346 "property-no-vendor-prefix": null, 347 "scss/dollar-variable-empty-line-before": null, 348 "scss/double-slash-comment-empty-line-before": [ 349 "always", 350 { 351 except: ["first-nested"], 352 ignore: ["between-comments", "stylelint-commands", "inside-block"], 353 }, 354 ], 355 "selector-class-pattern": null, 356 "selector-no-vendor-prefix": null, 357 "value-keyword-case": null, 358 "value-no-vendor-prefix": null, 359 }, 360 }, 361 { 362 files: ["browser/extensions/newtab/**"], 363 rules: { 364 "declaration-property-value-disallowed-list": [ 365 { 366 "font-size": [ 367 "/^[0-9.]+(px|em|rem|%)$/", 368 "/^[0-9.]+$/", 369 "/^(small|medium|large|x-large|xx-large)$/", 370 ], 371 "border-radius": [ 372 "/^[0-9.]+(px|em|rem|%)$/", 373 "/^(small|medium|large|x-large|xx-large)$/", 374 ], 375 // Validate to only allow variables and global values 376 "font-weight": [ 377 "/^(?!var\\(|inherit$|initial$|unset$|revert$|revert-layer$).+$/", 378 ], 379 [/^(margin|padding|inset|gap|row-gap|column-gap|grid-row-gap|grid-column-gap|top|right|bottom|left)($|-)/]: 380 ["/[0-9.]+(px|em|rem)|\\$/"], 381 }, 382 { 383 message: 384 "Avoid literal values. Use variables (e.g. var(--font-size-small)) or inherit/unset/etc.", 385 }, 386 ], 387 "csstools/use-logical": [ 388 "always", 389 { 390 // Bug 2003301: Do not enforce logical properties for any height/width properties 391 except: [/^(min-|max-)?width/i, /^(min-|max-)?height/i], 392 severity: "error", 393 }, 394 ], 395 }, 396 }, 397 { 398 name: "design-token-rules-off", 399 files: [ 400 // CSS files under browser/branding do not use design tokens 401 "browser/branding/**", 402 // CSS files under browser/components/extensions are not using design tokens 403 "browser/components/extensions/**", 404 // Webcompat interventions are not expected to use design tokens 405 // They are intended to override existing styles for specific extension 406 "browser/extensions/webcompat/injections/css/**", 407 // Most of devtools does not use design tokens, so turn the appropriate rules off for devtools. 408 // Stylelint does not support negating the file glob within an overrides section, 409 // so these rules get re-enabled for some devtools files in the section below 410 "devtools/**", 411 // FXR is no longer maintained and is not expected to use design tokens 412 "browser/fxr/**", 413 // Android does not use design tokens 414 "mobile/android/**", 415 // Docs do not use design tokens 416 "docs/**", 417 // DOM does not use design tokens 418 "dom/**", 419 // Layouts do not use design tokens 420 "layout/**", 421 // Testing does not use design tokens 422 "testing/**", 423 // UA Widgets should not use design tokens 424 "toolkit/themes/shared/colorpicker-common.css", 425 "toolkit/themes/shared/colorpicker.css", 426 "toolkit/themes/shared/media/pipToggle.css", 427 "toolkit/themes/shared/media/videocontrols.css", 428 "toolkit/content/widgets/datetimebox.css", 429 "toolkit/content/widgets/marquee.css", 430 "toolkit/themes/shared/media/textrecognition.css", 431 // The contents of backup/content/archive.css are injected as inline CSS 432 // into the HTML backup archive files that exist on a user's file system 433 // and can be opened in any browser. 434 "browser/components/backup/content/archive.css", 435 // Bug 2003877 - this is a centralization of a bunch of rules that had 436 // been spread across about:newtab and about:privatebrowsing. We'll 437 // fix these design tokens issues in a follow-up (presuming the 438 // replacement of the handoff bar doesn't land and remove this first). 439 "browser/components/search/content/contentSearchHandoffUI.css", 440 ], 441 rules: { 442 "stylelint-plugin-mozilla/use-design-tokens": null, 443 "stylelint-plugin-mozilla/no-non-semantic-token-usage": null, 444 "stylelint-plugin-mozilla/use-size-tokens": null, 445 }, 446 }, 447 { 448 name: "design-token-rules-on", 449 files: [ 450 // Enable design token related rules only on the parts of devtools that can use them 451 "devtools/client/aboutdebugging/src/**", 452 ], 453 rules: { 454 "stylelint-plugin-mozilla/use-design-tokens": true, 455 "stylelint-plugin-mozilla/no-non-semantic-token-usage": true, 456 "stylelint-plugin-mozilla/use-size-tokens": true, 457 }, 458 }, 459 { 460 files: ["toolkit/**/*.css", "toolkit/**/*.scss"], 461 rules: { 462 "stylelint-plugin-mozilla/no-browser-refs-in-toolkit": true, 463 }, 464 }, 465 { 466 // non-logical properties make sense in devtools/ where physical positioning always makes sense 467 name: "logical-properties-rule-off", 468 files: ["devtools/**"], 469 rules: { 470 "csstools/use-logical": null, 471 }, 472 }, 473 // Rollouts should always be applied last in the overrides section 474 // to ensure that they take precedence over other overrides. 475 ...rollouts, 476 ], 477 };