@primer/stylelint-config 13.0.0-rc.6ef7302 → 13.0.0-rc.77d8c5f

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.
@@ -1,98 +0,0 @@
1
- import stylelint from 'stylelint'
2
- import primerJson from '@primer/css/dist/stats/primer.json' with {type: 'json'}
3
-
4
- const ruleName = 'primer/no-override'
5
- const CLASS_PATTERN = /(\.[-\w]+)/
6
- const CLASS_PATTERN_ALL = new RegExp(CLASS_PATTERN, 'g')
7
- const CLASS_PATTERN_ONLY = /^\.[-\w]+(:{1,2}[-\w]+)?$/
8
-
9
- export default stylelint.createPlugin(ruleName, (enabled, options = {}) => {
10
- if (!enabled) {
11
- return noop
12
- }
13
-
14
- const {ignoreSelectors = []} = options
15
-
16
- const isSelectorIgnored =
17
- typeof ignoreSelectors === 'function'
18
- ? ignoreSelectors
19
- : selector => {
20
- return ignoreSelectors.some(pattern => {
21
- return pattern instanceof RegExp ? pattern.test(selector) : selector.includes(pattern)
22
- })
23
- }
24
-
25
- // These map selectors to the bundle in which they're defined.
26
- // If there's no entry for a given selector, it means that it's not defined
27
- // in one of the *specified* bundles, since we're iterating over the list of
28
- // bundle names in the options.
29
- const immutableSelectors = new Set()
30
- const immutableClassSelectors = new Set()
31
-
32
- const selectors = primerJson.selectors.values
33
- for (const selector of selectors) {
34
- immutableSelectors.add(selector)
35
- for (const classSelector of getClassSelectors(selector)) {
36
- immutableClassSelectors.add(classSelector)
37
- }
38
- }
39
-
40
- const messages = stylelint.utils.ruleMessages(ruleName, {
41
- rejected: ({rule, selector}) => {
42
- const ruleSelector = collapseWhitespace(rule.selector)
43
- const context = selector === rule.selector ? '' : ` in "${ruleSelector}"`
44
- return `Primer CSS class "${collapseWhitespace(selector)}" should not be overridden${context}.`
45
- },
46
- })
47
-
48
- return (root, result) => {
49
- const report = subject =>
50
- stylelint.utils.report({
51
- message: messages.rejected(subject),
52
- node: subject.rule,
53
- result,
54
- ruleName,
55
- })
56
-
57
- root.walkRules(rule => {
58
- const {selector} = rule
59
- if (immutableSelectors.has(selector)) {
60
- if (isClassSelector(selector)) {
61
- if (!isSelectorIgnored(selector)) {
62
- return report({
63
- rule,
64
- selector,
65
- })
66
- }
67
- } else {
68
- // console.log(`not a class selector: "${selector}"`)
69
- }
70
- }
71
- for (const classSelector of getClassSelectors(selector)) {
72
- if (immutableClassSelectors.has(classSelector)) {
73
- if (!isSelectorIgnored(classSelector)) {
74
- return report({
75
- rule,
76
- selector: classSelector,
77
- })
78
- }
79
- }
80
- }
81
- })
82
- }
83
- })
84
-
85
- function getClassSelectors(selector) {
86
- const match = selector.match(CLASS_PATTERN_ALL)
87
- return match ? [...match] : []
88
- }
89
-
90
- function isClassSelector(selector) {
91
- return CLASS_PATTERN_ONLY.test(selector)
92
- }
93
-
94
- function collapseWhitespace(str) {
95
- return str.trim().replace(/\s+/g, ' ')
96
- }
97
-
98
- function noop() {}
@@ -1,51 +0,0 @@
1
- import stylelint from 'stylelint'
2
- import matchAll from 'string.prototype.matchall'
3
-
4
- export const ruleName = 'primer/no-scale-colors'
5
- export const messages = stylelint.utils.ruleMessages(ruleName, {
6
- rejected: varName =>
7
- `${varName} is a non-functional scale color and cannot be used without being wrapped in the color-variables mixin`,
8
- })
9
-
10
- // Match CSS variable references (e.g var(--color-text-primary))
11
- // eslint-disable-next-line no-useless-escape
12
- const variableReferenceRegex = /var\(([^\),]+)(,.*)?\)/g
13
-
14
- export default stylelint.createPlugin(ruleName, (enabled, options = {}) => {
15
- if (!enabled) {
16
- return noop
17
- }
18
-
19
- const {verbose = false} = options
20
- // eslint-disable-next-line no-console
21
- const log = verbose ? (...args) => console.warn(...args) : noop
22
-
23
- // Keep track of declarations we've already seen
24
- const seen = new WeakMap()
25
-
26
- return (root, result) => {
27
- root.walkRules(rule => {
28
- rule.walkDecls(decl => {
29
- if (seen.has(decl)) {
30
- return
31
- } else {
32
- seen.set(decl, true)
33
- }
34
-
35
- for (const [, variableName] of matchAll(decl.value, variableReferenceRegex)) {
36
- log(`Found variable reference ${variableName}`)
37
- if (variableName.match(/^--color-(scale|auto)-/)) {
38
- stylelint.utils.report({
39
- message: messages.rejected(variableName),
40
- node: decl,
41
- result,
42
- ruleName,
43
- })
44
- }
45
- }
46
- })
47
- })
48
- }
49
- })
50
-
51
- function noop() {}
@@ -1,118 +0,0 @@
1
- import fs from 'fs'
2
- import stylelint from 'stylelint'
3
- import matchAll from 'string.prototype.matchall'
4
- import globby from 'globby'
5
- import TapMap from 'tap-map'
6
-
7
- export const ruleName = 'primer/no-undefined-vars'
8
- export const messages = stylelint.utils.ruleMessages(ruleName, {
9
- rejected: varName => `${varName} is not defined`,
10
- })
11
-
12
- // Match CSS variable definitions (e.g. --color-text-primary:)
13
- const variableDefinitionRegex = /^\s*(--[\w|-]+):/gm
14
-
15
- // Match CSS variables defined with the color-variables mixin
16
- const colorModeVariableDefinitionRegex = /^[^/\n]*\(["']?([^'"\s,]+)["']?,\s*\(light|dark:/gm
17
-
18
- // Match CSS variable references (e.g var(--color-text-primary))
19
- // eslint-disable-next-line no-useless-escape
20
- const variableReferenceRegex = /var\(([^\),]+)(,.*)?\)/g
21
-
22
- export default stylelint.createPlugin(ruleName, (enabled, options = {}) => {
23
- if (!enabled) {
24
- return noop
25
- }
26
-
27
- const {files = ['**/*.scss', '!node_modules'], verbose = false} = options
28
- // eslint-disable-next-line no-console
29
- const log = verbose ? (...args) => console.warn(...args) : noop
30
- const globalDefinedVariables = getDefinedVariables(files, log)
31
- // Keep track of declarations we've already seen
32
- const seen = new WeakMap()
33
-
34
- return (root, result) => {
35
- const fileDefinedVariables = new Set()
36
-
37
- function checkVariable(variableName, node, allowed) {
38
- if (!allowed.has(variableName)) {
39
- stylelint.utils.report({
40
- message: messages.rejected(variableName),
41
- node,
42
- result,
43
- ruleName,
44
- })
45
- }
46
- }
47
-
48
- root.walkAtRules(rule => {
49
- if (rule.name === 'include' && rule.params.startsWith('color-variables')) {
50
- const innerMatch = [...matchAll(rule.params, variableReferenceRegex)]
51
- if (!innerMatch.length) {
52
- return
53
- }
54
-
55
- for (const [, variableName] of innerMatch) {
56
- checkVariable(variableName, rule, new Set([...globalDefinedVariables, ...fileDefinedVariables]))
57
- }
58
- }
59
- })
60
-
61
- root.walkRules(rule => {
62
- const scopeDefinedVaribles = new Set()
63
-
64
- rule.walkDecls(decl => {
65
- // Add CSS variable declarations within the source text to the list of allowed variables
66
- if (decl.prop.startsWith('--')) {
67
- scopeDefinedVaribles.add(decl.prop)
68
- if (decl.parent.selector === ':root' || decl.parent.selector === ':host') {
69
- fileDefinedVariables.add(decl.prop)
70
- }
71
- }
72
-
73
- if (seen.has(decl)) {
74
- return
75
- } else {
76
- seen.set(decl, true)
77
- }
78
-
79
- for (const [, variableName] of matchAll(decl.value, variableReferenceRegex)) {
80
- checkVariable(
81
- variableName,
82
- decl,
83
- new Set([...globalDefinedVariables, ...fileDefinedVariables, ...scopeDefinedVaribles]),
84
- )
85
- }
86
- })
87
- })
88
- }
89
- })
90
-
91
- const cwd = process.cwd()
92
- const cache = new TapMap()
93
-
94
- function getDefinedVariables(globs, log) {
95
- const cacheKey = JSON.stringify({globs, cwd})
96
- return cache.tap(cacheKey, () => {
97
- const definedVariables = new Set()
98
-
99
- const files = globby.sync(globs)
100
- log(`Scanning ${files.length} SCSS files for CSS variables`)
101
- for (const file of files) {
102
- log(`==========\nLooking for CSS variable definitions in ${file}`)
103
- const css = fs.readFileSync(file, 'utf-8')
104
- for (const [, variableName] of matchAll(css, variableDefinitionRegex)) {
105
- log(`${variableName} defined in ${file}`)
106
- definedVariables.add(variableName)
107
- }
108
- for (const [, variableName] of matchAll(css, colorModeVariableDefinitionRegex)) {
109
- log(`--color-${variableName} defined via color-variables in ${file}`)
110
- definedVariables.add(`--color-${variableName}`)
111
- }
112
- }
113
-
114
- return definedVariables
115
- })
116
- }
117
-
118
- function noop() {}
@@ -1,96 +0,0 @@
1
- import TapMap from 'tap-map'
2
- import globby from 'globby'
3
- import matchAll from 'string.prototype.matchall'
4
- import stylelint from 'stylelint'
5
- import {readFileSync} from 'fs'
6
-
7
- export const ruleName = 'primer/no-unused-vars'
8
-
9
- const cwd = process.cwd()
10
- const COLON = ':'
11
- const SCSS_VARIABLE_PATTERN = /(\$[-\w]+)/g
12
-
13
- export const messages = stylelint.utils.ruleMessages(ruleName, {
14
- rejected: name => `The variable "${name}" is not referenced.`,
15
- })
16
-
17
- const cache = new TapMap()
18
-
19
- export default stylelint.createPlugin(ruleName, (enabled, options = {}) => {
20
- if (!enabled) {
21
- return noop
22
- }
23
-
24
- const {files = ['**/*.scss', '!node_modules'], variablePattern = SCSS_VARIABLE_PATTERN, verbose = false} = options
25
- // eslint-disable-next-line no-console
26
- const log = verbose ? (...args) => console.warn(...args) : noop
27
- const cacheOptions = {files, variablePattern, cwd}
28
- const {refs} = getCachedVariables(cacheOptions, log)
29
-
30
- return (root, result) => {
31
- root.walkDecls(decl => {
32
- for (const [name] of matchAll(decl.prop, variablePattern)) {
33
- if (!refs.has(name)) {
34
- stylelint.utils.report({
35
- message: messages.rejected(name),
36
- node: decl,
37
- result,
38
- ruleName,
39
- })
40
- } else {
41
- const path = stripCwd(decl.source.input.file)
42
- log(`${name} declared in ${path} ref'd in ${pluralize(refs.get(name).size, 'file')}`)
43
- }
44
- }
45
- })
46
- }
47
- })
48
-
49
- function getCachedVariables(options, log) {
50
- const key = JSON.stringify(options)
51
- return cache.tap(key, () => {
52
- const {files, variablePattern} = options
53
- const decls = new TapMap()
54
- const refs = new TapMap()
55
-
56
- log(`Looking for variables in ${files} ...`)
57
- for (const file of globby.sync(files)) {
58
- const css = readFileSync(file, 'utf8')
59
- for (const match of matchAll(css, variablePattern)) {
60
- const after = css.substr(match.index + match[0].length)
61
- const name = match[0]
62
- if (after.startsWith(COLON)) {
63
- decls.tap(name, set).add(file)
64
- } else {
65
- refs.tap(name, set).add(file)
66
- }
67
- }
68
- }
69
- log(`Found ${decls.size} declarations, ${pluralize(refs.size, 'reference')}.`)
70
-
71
- for (const [name, filesList] of decls.entries()) {
72
- const fileRefs = refs.get(name)
73
- if (fileRefs) {
74
- log(`variable "${name}" declared in ${pluralize(filesList.size, 'file')}, ref'd in ${fileRefs.size}`)
75
- } else {
76
- log(`[!] variable "${name}" declared in ${Array.from(filesList)[0]} is not referenced`)
77
- }
78
- }
79
-
80
- return {decls, refs}
81
- })
82
- }
83
-
84
- function noop() {}
85
-
86
- function set() {
87
- return new Set()
88
- }
89
-
90
- function stripCwd(path) {
91
- return path.startsWith(cwd) ? path.substr(cwd.length + 1) : path
92
- }
93
-
94
- function pluralize(num, str, plural = `${str}s`) {
95
- return num === 1 ? `${num} ${str}` : `${num} ${plural}`
96
- }
@@ -1,52 +0,0 @@
1
- import stylelint from 'stylelint'
2
- import utilities from './lib/primer-utilities.js'
3
-
4
- export const ruleName = 'primer/utilities'
5
-
6
- export const messages = stylelint.utils.ruleMessages(ruleName, {
7
- rejected: (selector, utilityClass) => {
8
- return `Consider using the Primer utility '.${utilityClass}' instead of the selector '${selector}' in your html. https://primer.style/css/utilities`
9
- },
10
- })
11
-
12
- // eslint-disable-next-line no-unused-vars
13
- export default stylelint.createPlugin(ruleName, (enabled, options = {}, context) => {
14
- if (!enabled) {
15
- return noop
16
- }
17
-
18
- const utilityReplacement = (declaration, value) => {
19
- const declarationUtilities = utilities[declaration]
20
- if (declarationUtilities) {
21
- return declarationUtilities.find(utility => {
22
- return utility.value === value
23
- })
24
- }
25
- }
26
-
27
- const lintResult = (root, result) => {
28
- root.walkRules(rule => {
29
- if (!/^\.[\w\-_]+$/.exec(rule.selector)) {
30
- return
31
- }
32
- const decls = rule.nodes.filter(decl => decl.type === 'decl')
33
-
34
- if (decls.length === 1) {
35
- const replacement = utilityReplacement(decls[0].prop, decls[0].value)
36
- if (replacement) {
37
- stylelint.utils.report({
38
- index: rule.sourceIndex,
39
- message: messages.rejected(rule.selector, replacement.utilityClass),
40
- node: rule,
41
- result,
42
- ruleName,
43
- })
44
- }
45
- }
46
- })
47
- }
48
-
49
- return lintResult
50
- })
51
-
52
- function noop() {}