Skip to content

Commit bb8895d

Browse files
authored
Upgrade stylelint config and adding stylelint scripts (#1934)
* Upgrading to @primer/stylelint-config@12.3.1 * Adding scripts for automating stylelint tasks * Disabling violations after upgrade
1 parent 0fb6789 commit bb8895d

File tree

8 files changed

+158
-17
lines changed

8 files changed

+158
-17
lines changed

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
"scripts": {
2626
"dist": "script/dist.js",
2727
"dist:watch": "chokidar \"src/**/*.scss\" -c \"script/dist.js\"",
28-
"stylelint": "stylelint --quiet 'src/**/*.scss'",
28+
"stylelint": "stylelint --quiet --rd 'src/**/*.scss'",
29+
"stylelint:fix": "yarn stylelint -- --fix",
30+
"stylelint:remove-disables": "script/stylelint-remove-disables.js 'src/**/*.scss'",
31+
"stylelint:add-disables": "script/stylelint-add-disables.js 'src/**/*.scss'",
2932
"eslint": "eslint deprecations.js __tests__ script",
3033
"prepublishOnly": "script/prepublish",
3134
"start": "yarn dev",
@@ -43,7 +46,7 @@
4346
"@changesets/cli": "2.20.0",
4447
"@github/prettier-config": "0.0.4",
4548
"@koddsson/postcss-sass": "5.0.1",
46-
"@primer/stylelint-config": "^12.3.0",
49+
"@primer/stylelint-config": "^12.3.1",
4750
"autoprefixer": "10.4.2",
4851
"chokidar-cli": "3.0.0",
4952
"cssstats": "4.0.2",

script/remove-needless-disables

Lines changed: 0 additions & 11 deletions
This file was deleted.

script/stylelint-add-disables.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/usr/bin/env node
2+
// Disables stylelint rules in SASS/CSS files with next-line comments. This is
3+
// useful when introducing a new rule that causes many failures. The comments
4+
// can be fixed and removed at while updating the file later.
5+
//
6+
// Usage:
7+
//
8+
// script/stylelint-add-disables.js src/**/*.scss
9+
10+
import fs from 'fs'
11+
import {execFile} from 'child_process'
12+
13+
execFile('stylelint', ['--quiet', '--formatter', 'json', process.argv[2]], (error, stdout) => {
14+
for (const result of JSON.parse(stdout)) {
15+
const filename = result.source
16+
const jsLines = fs.readFileSync(filename, 'utf8').split('\n')
17+
const offensesByLine = {}
18+
let addedLines = 0
19+
20+
// Produces {47: ['github/no-d-none', 'github/no-blur'], 83: ['github/no-blur']}
21+
for (const message of result.warnings) {
22+
if (offensesByLine[message.lin] && offensesByLine[message.line].includes(message.rule)) {
23+
continue
24+
} else if (offensesByLine[message.line]) {
25+
offensesByLine[message.line].push(message.rule)
26+
} else {
27+
offensesByLine[message.line] = [message.rule]
28+
}
29+
}
30+
31+
for (const line of Object.keys(offensesByLine)) {
32+
const lineIndex = Number(line) - 1 + addedLines
33+
const previousLine = jsLines[lineIndex - 1]
34+
const ruleIds = Array.from(new Set(offensesByLine[line])).join(', ')
35+
if (isDisableComment(previousLine)) {
36+
if (previousLine.match(/\s?\*\/$/)) {
37+
jsLines[lineIndex - 1] = previousLine.replace(/\s?\*\/$/, `, ${ruleIds} */`)
38+
} else {
39+
jsLines[lineIndex - 1] = `${jsLines[lineIndex - 1]}, ${ruleIds}`
40+
}
41+
} else {
42+
const leftPad = ' '.repeat(jsLines[lineIndex].match(/^\s*/g)[0].length)
43+
jsLines.splice(lineIndex, 0, `${leftPad}// stylelint-disable-next-line ${ruleIds}`)
44+
addedLines += 1
45+
}
46+
}
47+
48+
if (result.warnings.length !== 0) {
49+
fs.writeFileSync(filename, jsLines.join('\n'), 'utf8')
50+
}
51+
}
52+
})
53+
54+
function isDisableComment(line) {
55+
return line && line.match(/\/(\*|\/) stylelint-disable(-next-line)? .+(\*\/)?/)
56+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/usr/bin/env node
2+
import {execFile} from 'child_process'
3+
import {readFileSync, writeFileSync} from 'fs'
4+
5+
const files = process.argv.slice(2)
6+
if (files.length === 0) {
7+
files.push('app/assets/stylesheets')
8+
}
9+
10+
// we use an empty "marker" to delineate removed lines
11+
const REMOVED = `===REMOVED@${Date.now()}===`
12+
13+
execFile('stylelint', ['--rd', '--quiet', '--formatter', 'json', process.argv[2]], (error, stdout) => {
14+
15+
// Filter to only needless diables comments
16+
const results = JSON.parse(stdout)
17+
.filter(result => result.warnings.length > 0)
18+
.map(({source, warnings}) => {
19+
return {
20+
source,
21+
warnings: warnings.filter(warning => warning.rule === '--report-needless-disables')
22+
}
23+
})
24+
25+
for (const {source, warnings} of results) {
26+
console.log(`--- ${source}\n+++ ${source}`)
27+
const lines = readFileSync(source, 'utf8').split(/\n/)
28+
for (const {text, line: lineNum} of warnings) {
29+
const ruleName = text.match(/^Needless disable for "(.+)"$/)[1]
30+
const line = lines[lineNum - 1]
31+
let disableComment = parseDisableComment(line)
32+
33+
if (disableComment) {
34+
const rules = new Set(disableComment.rules)
35+
rules.delete(ruleName)
36+
let replacementLine = line
37+
if (rules.size === 0) {
38+
console.log(`@@ ${lineNum},${lineNum - 1} @@`)
39+
replacementLine = line.replace(`// ${disableComment.content}`, REMOVED)
40+
} else {
41+
console.log(`@@ ${lineNum},${lineNum} @@`)
42+
replacementLine = line.replace(disableComment.content, `${disableComment.type} ${Array.from(rules).join(', ')}`)
43+
}
44+
lines[lineNum - 1] = replacementLine
45+
46+
if (lines[lineNum - 2]) {
47+
console.log(`${lines[lineNum - 2]}`)
48+
}
49+
console.log(`- ${line}`)
50+
if (!replacementLine.includes(REMOVED)) {
51+
console.log(`+ ${replacementLine}`)
52+
}
53+
if (lines[lineNum]) {
54+
console.log(`${lines[lineNum]}`)
55+
}
56+
}
57+
}
58+
console.log('')
59+
const output = lines.map(line => {
60+
if (line.trim() === REMOVED) {
61+
return null
62+
} else if (line.includes(REMOVED)) {
63+
line = line.replace(REMOVED, '').trimEnd()
64+
}
65+
66+
return line
67+
}).filter(line => line !== null).join('\n')
68+
69+
writeFileSync(source, output, 'utf8')
70+
}
71+
})
72+
73+
function parseDisableComment(str) {
74+
const match = str.match(/(stylelint-disable((-next)?-line)?)\s+(.+)$/)
75+
return match
76+
? {
77+
content: match[0],
78+
type: match[1],
79+
rules: match[4].split(/,\s+/)
80+
}
81+
: false
82+
}

src/actionlist/action-list-item.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@
389389

390390
// if nested list exists, remove default padding
391391
.ActionList {
392+
// stylelint-disable-next-line primer/spacing
392393
padding: unset;
393394
}
394395
}
@@ -397,6 +398,7 @@
397398
.ActionList-content {
398399
position: relative;
399400
display: grid;
401+
// stylelint-disable-next-line primer/spacing
400402
padding: $actionList-item-padding-vertical $actionList-item-padding-horizontal;
401403
font-size: $body-font-size;
402404
font-weight: $font-weight-normal;
@@ -423,11 +425,13 @@
423425

424426
&.ActionList-content--sizeMedium {
425427
// 44px total height
428+
// stylelint-disable-next-line primer/spacing
426429
padding: $actionList-item-padding-vertical-md $actionList-item-padding-horizontal;
427430
}
428431

429432
&.ActionList-content--sizeLarge {
430433
// 48px total height
434+
// stylelint-disable-next-line primer/spacing
431435
padding: $actionList-item-padding-vertical-lg $actionList-item-padding-horizontal;
432436
}
433437

@@ -437,6 +441,7 @@
437441

438442
// On pointer:coarse (mobile), all list items are large
439443
@media (pointer: coarse) {
444+
// stylelint-disable-next-line primer/spacing
440445
padding: $actionList-item-padding-vertical-lg $actionList-item-padding-horizontal;
441446
}
442447

@@ -493,6 +498,7 @@
493498
align-items: baseline;
494499

495500
.ActionList-item-description {
501+
// stylelint-disable-next-line primer/spacing
496502
margin-left: $actionList-item-padding-horizontal;
497503
}
498504
}

src/forms/radio-group.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
.radio-label {
88
float: left;
99
padding: 6px $spacer-3 6px ($spacer-3 + 12px + $spacer-2); // 12px is the size of the radio-input
10+
// stylelint-disable-next-line primer/spacing
1011
margin-left: -1px;
1112
font-size: $body-font-size;
1213
// stylelint-disable-next-line primer/typography
@@ -41,6 +42,7 @@
4142
.radio-input {
4243
z-index: 3;
4344
float: left;
45+
// stylelint-disable-next-line primer/spacing
4446
margin: 10px (-$spacer-5) 0 $spacer-3;
4547

4648
&:disabled {

src/layout/page-layout.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,11 +361,14 @@ $Layout-responsive-variant-max-breakpoint: 'md' !default;
361361
.PageLayout-content,
362362
.PageLayout-pane,
363363
.PageLayout-footer {
364+
// stylelint-disable-next-line primer/spacing
364365
padding: var(--Layout-inner-spacing-min);
365366
}
366367

367368
.PageLayout-content {
369+
// stylelint-disable-next-line primer/spacing
368370
padding-right: var(--Layout-inner-spacing-max);
371+
// stylelint-disable-next-line primer/spacing
369372
padding-left: var(--Layout-inner-spacing-max);
370373
grid-area: content;
371374
}

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,10 +1153,10 @@
11531153
resolved "https://registry.yarnpkg.com/@primer/primitives/-/primitives-7.4.0.tgz#75df54a80233a432b687c0e3010e4be6bd60a82d"
11541154
integrity sha512-gD6yHXN7YKox/bdUNgxhoSS/WXZVaORK1r4dOAyTrdoPrLV/ucIfRInPyVcTF+Mqr0zcTFJtiMtuA5Y8CSyOEg==
11551155

1156-
"@primer/stylelint-config@^12.3.0":
1157-
version "12.3.0"
1158-
resolved "https://registry.yarnpkg.com/@primer/stylelint-config/-/stylelint-config-12.3.0.tgz#de45e16f21f245f31a634ab76a5fcdef796495ac"
1159-
integrity sha512-BZG1ARFaWLQP8avLObAtpSj88z/CLAvhSND+Ts2H1KEYnKNmwkgqwc6CW5OVpLD5BUKqDwun+8OVa37+ueeSQA==
1156+
"@primer/stylelint-config@^12.3.1":
1157+
version "12.3.1"
1158+
resolved "https://registry.yarnpkg.com/@primer/stylelint-config/-/stylelint-config-12.3.1.tgz#96905721dd15ff52ed26a97e81c165a189d44eaf"
1159+
integrity sha512-Bii+wGxPmXf23sfStuYPXD/os5wKPgnnUZLv0U+SiYGJTlVFTtdcxz1lk2xEI56cCRMjvTOgUWDVeGy3TeW+/w==
11601160
dependencies:
11611161
anymatch "^3.1.1"
11621162
globby "^11.0.1"

0 commit comments

Comments
 (0)