@primer/stylelint-config 13.0.0-rc.f33e046 → 13.0.0-rc.fd47ce2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/dist/index.cjs +116 -658
- package/dist/index.mjs +116 -658
- package/package.json +9 -3
- package/plugins/README.md +0 -26
- package/plugins/lib/primitives.js +36 -0
- package/plugins/spacing.js +68 -63
- package/plugins/lib/primer-utilities.js +0 -526
- package/plugins/utilities.js +0 -52
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primer/stylelint-config",
|
|
3
|
-
"version": "13.0.0-rc.
|
|
3
|
+
"version": "13.0.0-rc.fd47ce2",
|
|
4
4
|
"description": "Sharable stylelint config used by GitHub's CSS",
|
|
5
5
|
"author": "GitHub, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@github/browserslist-config": "^1.0.0",
|
|
47
47
|
"@primer/css": "^21.0.8",
|
|
48
|
-
"@primer/primitives": "^
|
|
48
|
+
"@primer/primitives": "^8.2.0",
|
|
49
49
|
"anymatch": "^3.1.1",
|
|
50
50
|
"postcss-scss": "^4.0.2",
|
|
51
51
|
"postcss-styled-syntax": "^0.6.4",
|
|
@@ -53,16 +53,22 @@
|
|
|
53
53
|
"string.prototype.matchall": "^4.0.2",
|
|
54
54
|
"stylelint": "^16.3.1",
|
|
55
55
|
"stylelint-config-standard": "^36.0.0",
|
|
56
|
+
"stylelint-css-modules-no-global-scoped-selector": "^1.0.2",
|
|
56
57
|
"stylelint-no-unsupported-browser-features": "^8.0.0",
|
|
57
58
|
"stylelint-order": "^6.0.4",
|
|
58
59
|
"stylelint-scss": "^6.2.0",
|
|
59
60
|
"stylelint-value-no-unknown-custom-properties": "^6.0.1",
|
|
60
61
|
"tap-map": "^1.0.0"
|
|
61
62
|
},
|
|
63
|
+
"overrides": {
|
|
64
|
+
"stylelint-css-modules-no-global-scoped-selector": {
|
|
65
|
+
"stylelint": "$stylelint"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
62
68
|
"prettier": "@github/prettier-config",
|
|
63
69
|
"devDependencies": {
|
|
64
70
|
"@changesets/changelog-github": "^0.5.0",
|
|
65
|
-
"@changesets/cli": "2.
|
|
71
|
+
"@changesets/cli": "2.27.1",
|
|
66
72
|
"@github/prettier-config": "^0.0.6",
|
|
67
73
|
"@rollup/plugin-commonjs": "^25.0.7",
|
|
68
74
|
"@rollup/plugin-json": "^6.1.0",
|
package/plugins/README.md
CHANGED
|
@@ -13,7 +13,6 @@ This directory contains all of our custom stylelint plugins, each of which provi
|
|
|
13
13
|
- [`primer/borders`](#primerborders)
|
|
14
14
|
- [`primer/box-shadow`](#primerbox-shadow)
|
|
15
15
|
- [`primer/responsive-widths`](#primerresponsive-widths)
|
|
16
|
-
- [`primer/utilities`](#primerutilities)
|
|
17
16
|
- [Variable rules](#variable-rules)
|
|
18
17
|
- [Variable rule options](#variable-rule-options)
|
|
19
18
|
|
|
@@ -84,31 +83,6 @@ This [variable rule](#variable-rules) enforces the use of `$box-shadow*` variabl
|
|
|
84
83
|
|
|
85
84
|
This plugin checks for `width` and `min-width` declarations that use a value less than the minimum browser size. `320px`
|
|
86
85
|
|
|
87
|
-
## `primer/utilities`
|
|
88
|
-
|
|
89
|
-
Checks for selectors with single declarations that can be replaced with [primer/css utilities](https://primer.style/css/utilities/).
|
|
90
|
-
|
|
91
|
-
```scss
|
|
92
|
-
.foo {
|
|
93
|
-
color: var(--color-fg-default);
|
|
94
|
-
}
|
|
95
|
-
/** ↑
|
|
96
|
-
* FAIL: --color-fg-default can be replaced with .color-fg-default */
|
|
97
|
-
|
|
98
|
-
.foo {
|
|
99
|
-
color: #custom;
|
|
100
|
-
}
|
|
101
|
-
/** ↑
|
|
102
|
-
* OK: Color value doesn't match a utility. */
|
|
103
|
-
|
|
104
|
-
.foo {
|
|
105
|
-
color: var(--color-fg-default);
|
|
106
|
-
padding: 0;
|
|
107
|
-
}
|
|
108
|
-
/** ↑
|
|
109
|
-
* OK: Has more than one declaration, not considered */
|
|
110
|
-
```
|
|
111
|
-
|
|
112
86
|
## Variable rules
|
|
113
87
|
|
|
114
88
|
Variable rules are created using a general-purpose helper that can validate constraints for matching CSS properties and values. In general, the Primer CSS variable rules enforce two basic principles for custom CSS:
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {createRequire} from 'node:module'
|
|
2
|
+
|
|
3
|
+
const require = createRequire(import.meta.url)
|
|
4
|
+
|
|
5
|
+
export async function primitivesVariables(type) {
|
|
6
|
+
const variables = []
|
|
7
|
+
|
|
8
|
+
const files = []
|
|
9
|
+
switch (type) {
|
|
10
|
+
case 'size':
|
|
11
|
+
files.push('base/size/size.json')
|
|
12
|
+
break
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
for (const file of files) {
|
|
16
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
17
|
+
const data = require(`@primer/primitives/dist/styleLint/${file}`)
|
|
18
|
+
|
|
19
|
+
for (const key of Object.keys(data)) {
|
|
20
|
+
const size = data[key]
|
|
21
|
+
const values = size['value']
|
|
22
|
+
const intValue = parseInt(size['original']['value'])
|
|
23
|
+
if (![2, 6].includes(intValue)) {
|
|
24
|
+
values.push(`${intValue + 1}px`)
|
|
25
|
+
values.push(`${intValue - 1}px`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
variables.push({
|
|
29
|
+
name: `--${size['name']}`,
|
|
30
|
+
values,
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return variables
|
|
36
|
+
}
|
package/plugins/spacing.js
CHANGED
|
@@ -1,39 +1,12 @@
|
|
|
1
1
|
import stylelint from 'stylelint'
|
|
2
2
|
import declarationValueIndex from 'stylelint/lib/utils/declarationValueIndex.cjs'
|
|
3
3
|
import valueParser from 'postcss-value-parser'
|
|
4
|
+
import {primitivesVariables} from './lib/primitives.js'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
'$spacer-3': '16px',
|
|
10
|
-
'$spacer-4': '24px',
|
|
11
|
-
'$spacer-5': '32px',
|
|
12
|
-
'$spacer-6': '40px',
|
|
13
|
-
'$spacer-7': '48px',
|
|
14
|
-
'$spacer-8': '64px',
|
|
15
|
-
'$spacer-9': '80px',
|
|
16
|
-
'$spacer-10': '96px',
|
|
17
|
-
'$spacer-11': '112px',
|
|
18
|
-
'$spacer-12': '128px',
|
|
19
|
-
'$em-spacer-1': '0.0625em',
|
|
20
|
-
'$em-spacer-2': '0.125em',
|
|
21
|
-
'$em-spacer-3': '0.25em',
|
|
22
|
-
'$em-spacer-4': '0.375em',
|
|
23
|
-
'$em-spacer-5': '0.5em',
|
|
24
|
-
'$em-spacer-6': '0.75em',
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export const ruleName = 'primer/spacing'
|
|
28
|
-
export const messages = stylelint.utils.ruleMessages(ruleName, {
|
|
29
|
-
rejected: (value, replacement) => {
|
|
30
|
-
if (replacement === null) {
|
|
31
|
-
return `Please use a primer spacer variable instead of '${value}'. Consult the primer docs for a suitable replacement. https://primer.style/css/storybook/?path=/docs/support-spacing--docs`
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return `Please replace ${value} with spacing variable '${replacement}'.`
|
|
35
|
-
},
|
|
36
|
-
})
|
|
6
|
+
const {
|
|
7
|
+
createPlugin,
|
|
8
|
+
utils: {report, ruleMessages, validateOptions},
|
|
9
|
+
} = stylelint
|
|
37
10
|
|
|
38
11
|
const walkGroups = (root, validate) => {
|
|
39
12
|
for (const node of root.nodes) {
|
|
@@ -46,24 +19,47 @@ const walkGroups = (root, validate) => {
|
|
|
46
19
|
return root
|
|
47
20
|
}
|
|
48
21
|
|
|
49
|
-
|
|
50
|
-
export
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
22
|
+
export const ruleName = 'primer/spacing'
|
|
23
|
+
export const messages = ruleMessages(ruleName, {
|
|
24
|
+
rejected: (value, replacement) => {
|
|
25
|
+
if (!replacement) {
|
|
26
|
+
return `Please use a primer size variable instead of '${value}'. Consult the primer docs for a suitable replacement. https://primer.style/foundations/primitives/size`
|
|
27
|
+
}
|
|
54
28
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return noop
|
|
59
|
-
}
|
|
29
|
+
return `Please replace '${value}' with size variable '${replacement['name']}'. https://primer.style/foundations/primitives/size`
|
|
30
|
+
},
|
|
31
|
+
})
|
|
60
32
|
|
|
61
|
-
|
|
33
|
+
const meta = {
|
|
34
|
+
fixable: true,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** @type {import('stylelint').Rule} */
|
|
38
|
+
const ruleFunction = (primary, secondaryOptions, context) => {
|
|
39
|
+
return async (root, result) => {
|
|
40
|
+
// Props that we want to check
|
|
41
|
+
const propList = ['padding', 'margin', 'top', 'right', 'bottom', 'left']
|
|
42
|
+
// Values that we want to ignore
|
|
43
|
+
const valueList = ['${']
|
|
44
|
+
|
|
45
|
+
const sizes = await primitivesVariables('size')
|
|
46
|
+
|
|
47
|
+
const validOptions = validateOptions(result, ruleName, {
|
|
48
|
+
actual: primary,
|
|
49
|
+
possible: [true],
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
if (!validOptions) return
|
|
53
|
+
|
|
54
|
+
root.walkDecls(declNode => {
|
|
55
|
+
const {prop, value} = declNode
|
|
62
56
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const cleanValue = node.value.replace(/^-/g, '')
|
|
57
|
+
if (!propList.some(spacingProp => prop.startsWith(spacingProp))) return
|
|
58
|
+
if (valueList.some(valueToIgnore => value.includes(valueToIgnore))) return
|
|
66
59
|
|
|
60
|
+
const problems = []
|
|
61
|
+
|
|
62
|
+
const parsedValue = walkGroups(valueParser(value), node => {
|
|
67
63
|
// Only check word types. https://github.com/TrySound/postcss-value-parser#word
|
|
68
64
|
if (node.type !== 'word') {
|
|
69
65
|
return
|
|
@@ -74,30 +70,36 @@ export default stylelint.createPlugin(ruleName, (enabled, options = {}, context)
|
|
|
74
70
|
return
|
|
75
71
|
}
|
|
76
72
|
|
|
77
|
-
const valueUnit = valueParser.unit(
|
|
73
|
+
const valueUnit = valueParser.unit(node.value)
|
|
78
74
|
|
|
79
|
-
if (valueUnit && (valueUnit.unit === '' ||
|
|
75
|
+
if (valueUnit && (valueUnit.unit === '' || !/^-?[0-9.]+$/.test(valueUnit.number))) {
|
|
80
76
|
return
|
|
81
77
|
}
|
|
82
78
|
|
|
83
|
-
//
|
|
79
|
+
// Skip if the value unit isn't a supported unit.
|
|
80
|
+
if (valueUnit && !['px', 'rem', 'em'].includes(valueUnit.unit)) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// If the variable is found in the value, skip it.
|
|
84
85
|
if (
|
|
85
|
-
|
|
86
|
-
new RegExp(`${variable.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(
|
|
86
|
+
sizes.some(variable =>
|
|
87
|
+
new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(node.value),
|
|
87
88
|
)
|
|
88
89
|
) {
|
|
89
90
|
return
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
const replacement =
|
|
93
|
-
const
|
|
93
|
+
const replacement = sizes.find(variable => variable.values.includes(node.value.replace('-', '')))
|
|
94
|
+
const fixable = replacement && valueUnit && !valueUnit.number.includes('-')
|
|
94
95
|
|
|
95
|
-
if (
|
|
96
|
-
node.value = node.value.replace(
|
|
96
|
+
if (fixable && context.fix) {
|
|
97
|
+
node.value = node.value.replace(node.value, `var(${replacement['name']})`)
|
|
97
98
|
} else {
|
|
98
99
|
problems.push({
|
|
99
|
-
index: declarationValueIndex(
|
|
100
|
-
|
|
100
|
+
index: declarationValueIndex(declNode) + node.sourceIndex,
|
|
101
|
+
endIndex: declarationValueIndex(declNode) + node.sourceIndex + node.value.length,
|
|
102
|
+
message: messages.rejected(node.value, replacement),
|
|
101
103
|
})
|
|
102
104
|
}
|
|
103
105
|
|
|
@@ -105,15 +107,16 @@ export default stylelint.createPlugin(ruleName, (enabled, options = {}, context)
|
|
|
105
107
|
})
|
|
106
108
|
|
|
107
109
|
if (context.fix) {
|
|
108
|
-
|
|
110
|
+
declNode.value = parsedValue.toString()
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
if (problems.length) {
|
|
112
114
|
for (const err of problems) {
|
|
113
|
-
|
|
115
|
+
report({
|
|
114
116
|
index: err.index,
|
|
117
|
+
endIndex: err.endIndex,
|
|
115
118
|
message: err.message,
|
|
116
|
-
node:
|
|
119
|
+
node: declNode,
|
|
117
120
|
result,
|
|
118
121
|
ruleName,
|
|
119
122
|
})
|
|
@@ -121,8 +124,10 @@ export default stylelint.createPlugin(ruleName, (enabled, options = {}, context)
|
|
|
121
124
|
}
|
|
122
125
|
})
|
|
123
126
|
}
|
|
127
|
+
}
|
|
124
128
|
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
ruleFunction.ruleName = ruleName
|
|
130
|
+
ruleFunction.messages = messages
|
|
131
|
+
ruleFunction.meta = meta
|
|
127
132
|
|
|
128
|
-
|
|
133
|
+
export default createPlugin(ruleName, ruleFunction)
|