@primer/stylelint-config 12.9.2 → 13.0.0-rc.04ba287

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,27 +1,12 @@
1
- const stylelint = require('stylelint')
2
- const {requirePrimerFile} = require('./primer')
3
- const declarationValidator = require('./decl-validator')
1
+ import stylelint from 'stylelint'
2
+ import declarationValidator from './decl-validator.js'
3
+ import variables from '@primer/css/dist/variables.json' with {type: 'json'}
4
4
 
5
- const CSS_IMPORTANT = '!important'
6
- const CSS_DIRECTIONS = ['top', 'right', 'bottom', 'left']
7
- const CSS_CORNERS = ['top-right', 'bottom-right', 'bottom-left', 'top-left']
8
-
9
- module.exports = {
10
- createVariableRule,
11
- CSS_DIRECTIONS,
12
- CSS_CORNERS,
13
- CSS_IMPORTANT,
14
- }
15
-
16
- function createVariableRule(ruleName, rules, url) {
17
- let variables = {}
18
- try {
19
- variables = requirePrimerFile('dist/variables.json')
20
- } catch (error) {
21
- // eslint-disable-next-line no-console
22
- console.warn(`Unable to get variables.json from @primer/css. Replacements will need to be specified manually.`)
23
- }
5
+ export const CSS_IMPORTANT = '!important'
6
+ export const CSS_DIRECTIONS = ['top', 'right', 'bottom', 'left']
7
+ export const CSS_CORNERS = ['top-right', 'bottom-right', 'bottom-left', 'top-left']
24
8
 
9
+ export function createVariableRule(ruleName, rules, url) {
25
10
  const plugin = stylelint.createPlugin(ruleName, (enabled, options = {}, context) => {
26
11
  if (enabled === false) {
27
12
  return noop
@@ -1,8 +1,8 @@
1
- const stylelint = require('stylelint')
2
- const matchAll = require('string.prototype.matchall')
1
+ import stylelint from 'stylelint'
2
+ import matchAll from 'string.prototype.matchall'
3
3
 
4
- const ruleName = 'primer/no-display-colors'
5
- const messages = stylelint.utils.ruleMessages(ruleName, {
4
+ export const ruleName = 'primer/no-display-colors'
5
+ export const messages = stylelint.utils.ruleMessages(ruleName, {
6
6
  rejected: varName => `${varName} is in alpha and should be used with caution with approval from the Primer team`,
7
7
  })
8
8
 
@@ -10,7 +10,7 @@ const messages = stylelint.utils.ruleMessages(ruleName, {
10
10
  // eslint-disable-next-line no-useless-escape
11
11
  const variableReferenceRegex = /var\(([^\),]+)(,.*)?\)/g
12
12
 
13
- module.exports = stylelint.createPlugin(ruleName, (enabled, options = {}) => {
13
+ export default stylelint.createPlugin(ruleName, (enabled, options = {}) => {
14
14
  if (!enabled) {
15
15
  return noop
16
16
  }
@@ -48,6 +48,3 @@ module.exports = stylelint.createPlugin(ruleName, (enabled, options = {}) => {
48
48
  })
49
49
 
50
50
  function noop() {}
51
-
52
- module.exports.ruleName = ruleName
53
- module.exports.messages = messages
@@ -1,10 +1,10 @@
1
- const stylelint = require('stylelint')
2
- const declarationValueIndex = require('stylelint/lib/utils/declarationValueIndex')
3
- const valueParser = require('postcss-value-parser')
1
+ import stylelint from 'stylelint'
2
+ import declarationValueIndex from 'stylelint/lib/utils/declarationValueIndex.cjs'
3
+ import valueParser from 'postcss-value-parser'
4
4
 
5
- const ruleName = 'primer/responsive-widths'
5
+ export const ruleName = 'primer/responsive-widths'
6
6
 
7
- const messages = stylelint.utils.ruleMessages(ruleName, {
7
+ export const messages = stylelint.utils.ruleMessages(ruleName, {
8
8
  rejected: value => {
9
9
  return `A value larger than the smallest viewport could break responsive pages. Use a width value smaller than ${value}. https://primer.style/css/support/breakpoints`
10
10
  },
@@ -24,7 +24,7 @@ const walkGroups = (root, validate) => {
24
24
  }
25
25
 
26
26
  // eslint-disable-next-line no-unused-vars
27
- module.exports = stylelint.createPlugin(ruleName, (enabled, options = {}, context) => {
27
+ export default stylelint.createPlugin(ruleName, (enabled, options = {}, context) => {
28
28
  if (!enabled) {
29
29
  return noop
30
30
  }
@@ -93,6 +93,3 @@ module.exports = stylelint.createPlugin(ruleName, (enabled, options = {}, contex
93
93
  })
94
94
 
95
95
  function noop() {}
96
-
97
- module.exports.ruleName = ruleName
98
- module.exports.messages = messages
@@ -1,69 +1,60 @@
1
- const stylelint = require('stylelint')
2
- const declarationValueIndex = require('stylelint/lib/utils/declarationValueIndex')
3
- const valueParser = require('postcss-value-parser')
4
-
5
- // TODO: Pull this in from primer/primitives
6
- const spacerValues = {
7
- '$spacer-1': '4px',
8
- '$spacer-2': '8px',
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
- const ruleName = 'primer/spacing'
28
- const messages = stylelint.utils.ruleMessages(ruleName, {
1
+ import stylelint from 'stylelint'
2
+ import declarationValueIndex from 'stylelint/lib/utils/declarationValueIndex.cjs'
3
+ import valueParser from 'postcss-value-parser'
4
+ import {primitivesVariables, walkGroups} from './lib/utils.js'
5
+
6
+ const {
7
+ createPlugin,
8
+ utils: {report, ruleMessages, validateOptions},
9
+ } = stylelint
10
+
11
+ export const ruleName = 'primer/spacing'
12
+ export const messages = ruleMessages(ruleName, {
29
13
  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/support/spacing`
14
+ if (!replacement) {
15
+ return `Please use a primer size variable instead of '${value}'. Consult the primer docs for a suitable replacement. https://primer.style/foundations/primitives/size`
32
16
  }
33
17
 
34
- return `Please replace ${value} with spacing variable '${replacement}'.`
18
+ return `Please replace '${value}' with size variable '${replacement['name']}'. https://primer.style/foundations/primitives/size`
35
19
  },
36
20
  })
37
21
 
38
- const walkGroups = (root, validate) => {
39
- for (const node of root.nodes) {
40
- if (node.type === 'function') {
41
- walkGroups(node, validate)
42
- } else {
43
- validate(node)
44
- }
22
+ // Props that we want to check
23
+ const propList = ['padding', 'margin', 'top', 'right', 'bottom', 'left']
24
+ // Values that we want to ignore
25
+ const valueList = ['${']
26
+
27
+ const sizes = primitivesVariables('spacing')
28
+
29
+ // Add +-1px to each value
30
+ for (const size of sizes) {
31
+ const values = size['values']
32
+ const px = parseInt(values.find(value => value.includes('px')))
33
+ if (![2, 6].includes(px)) {
34
+ values.push(`${px + 1}px`)
35
+ values.push(`${px - 1}px`)
45
36
  }
46
- return root
47
37
  }
48
38
 
49
- // eslint-disable-next-line no-unused-vars
50
- module.exports = stylelint.createPlugin(ruleName, (enabled, options = {}, context) => {
51
- if (!enabled) {
52
- return noop
53
- }
39
+ /** @type {import('stylelint').Rule} */
40
+ const ruleFunction = (primary, secondaryOptions, context) => {
41
+ return (root, result) => {
42
+ const validOptions = validateOptions(result, ruleName, {
43
+ actual: primary,
44
+ possible: [true],
45
+ })
54
46
 
55
- const lintResult = (root, result) => {
56
- root.walk(decl => {
57
- if (decl.type !== 'decl' || !decl.prop.match(/^(padding|margin)/)) {
58
- return noop
59
- }
47
+ if (!validOptions) return
60
48
 
61
- const problems = []
49
+ root.walkDecls(declNode => {
50
+ const {prop, value} = declNode
62
51
 
63
- const parsedValue = walkGroups(valueParser(decl.value), node => {
64
- // Remove leading negative sign, if any.
65
- const cleanValue = node.value.replace(/^-/g, '')
52
+ if (!propList.some(spacingProp => prop.startsWith(spacingProp))) return
53
+ if (valueList.some(valueToIgnore => value.includes(valueToIgnore))) return
66
54
 
55
+ const problems = []
56
+
57
+ const parsedValue = walkGroups(valueParser(value), node => {
67
58
  // Only check word types. https://github.com/TrySound/postcss-value-parser#word
68
59
  if (node.type !== 'word') {
69
60
  return
@@ -74,30 +65,36 @@ module.exports = stylelint.createPlugin(ruleName, (enabled, options = {}, contex
74
65
  return
75
66
  }
76
67
 
77
- const valueUnit = valueParser.unit(cleanValue)
68
+ const valueUnit = valueParser.unit(node.value)
69
+
70
+ if (valueUnit && (valueUnit.unit === '' || !/^-?[0-9.]+$/.test(valueUnit.number))) {
71
+ return
72
+ }
78
73
 
79
- if (valueUnit && (valueUnit.unit === '' || !/^[0-9.]+$/.test(valueUnit.number))) {
74
+ // Skip if the value unit isn't a supported unit.
75
+ if (valueUnit && !['px', 'rem', 'em'].includes(valueUnit.unit)) {
80
76
  return
81
77
  }
82
78
 
83
- // If the a variable is found in the value, skip it.
79
+ // If the variable is found in the value, skip it.
84
80
  if (
85
- Object.keys(spacerValues).some(variable =>
86
- new RegExp(`${variable.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(cleanValue),
81
+ sizes.some(variable =>
82
+ new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(node.value),
87
83
  )
88
84
  ) {
89
85
  return
90
86
  }
91
87
 
92
- const replacement = Object.keys(spacerValues).find(spacer => spacerValues[spacer] === cleanValue) || null
93
- const valueMatch = replacement ? spacerValues[replacement] : node.value
88
+ const replacement = sizes.find(variable => variable.values.includes(node.value.replace('-', '')))
89
+ const fixable = replacement && valueUnit && !valueUnit.number.includes('-')
94
90
 
95
- if (replacement && context.fix) {
96
- node.value = node.value.replace(valueMatch, replacement)
91
+ if (fixable && context.fix) {
92
+ node.value = node.value.replace(node.value, `var(${replacement['name']})`)
97
93
  } else {
98
94
  problems.push({
99
- index: declarationValueIndex(decl) + node.sourceIndex,
100
- message: messages.rejected(valueMatch, replacement),
95
+ index: declarationValueIndex(declNode) + node.sourceIndex,
96
+ endIndex: declarationValueIndex(declNode) + node.sourceIndex + node.value.length,
97
+ message: messages.rejected(node.value, replacement),
101
98
  })
102
99
  }
103
100
 
@@ -105,15 +102,16 @@ module.exports = stylelint.createPlugin(ruleName, (enabled, options = {}, contex
105
102
  })
106
103
 
107
104
  if (context.fix) {
108
- decl.value = parsedValue.toString()
105
+ declNode.value = parsedValue.toString()
109
106
  }
110
107
 
111
108
  if (problems.length) {
112
109
  for (const err of problems) {
113
- stylelint.utils.report({
110
+ report({
114
111
  index: err.index,
112
+ endIndex: err.endIndex,
115
113
  message: err.message,
116
- node: decl,
114
+ node: declNode,
117
115
  result,
118
116
  ruleName,
119
117
  })
@@ -121,11 +119,12 @@ module.exports = stylelint.createPlugin(ruleName, (enabled, options = {}, contex
121
119
  }
122
120
  })
123
121
  }
122
+ }
124
123
 
125
- return lintResult
126
- })
127
-
128
- function noop() {}
124
+ ruleFunction.ruleName = ruleName
125
+ ruleFunction.messages = messages
126
+ ruleFunction.meta = {
127
+ fixable: true,
128
+ }
129
129
 
130
- module.exports.ruleName = ruleName
131
- module.exports.messages = messages
130
+ export default createPlugin(ruleName, ruleFunction)
@@ -1,6 +1,6 @@
1
- const {createVariableRule} = require('./lib/variable-rules')
1
+ import {createVariableRule} from './lib/variable-rules.js'
2
2
 
3
- module.exports = createVariableRule(
3
+ export default createVariableRule(
4
4
  'primer/typography',
5
5
  {
6
6
  'font-size': {
package/property-order.js CHANGED
@@ -1,4 +1,4 @@
1
- module.exports = [
1
+ export default [
2
2
  'all',
3
3
  'position',
4
4
  'top',
package/browsers.js DELETED
@@ -1,7 +0,0 @@
1
- module.exports = [
2
- '> 5%',
3
- 'last 2 firefox versions',
4
- 'last 2 chrome versions',
5
- 'last 2 safari versions',
6
- 'last 2 edge versions',
7
- ]
package/index.js DELETED
@@ -1,106 +0,0 @@
1
- const browsers = require('./browsers')
2
- const propertyOrder = require('./property-order')
3
-
4
- module.exports = {
5
- extends: ['stylelint-config-standard'],
6
- customSyntax: require('postcss-scss'),
7
- ignoreFiles: ['**/*.js', '**/*.cjs'],
8
- plugins: [
9
- 'stylelint-no-unsupported-browser-features',
10
- 'stylelint-order',
11
- 'stylelint-scss',
12
- './plugins/borders',
13
- './plugins/box-shadow',
14
- './plugins/colors',
15
- './plugins/no-deprecated-colors',
16
- './plugins/no-experimental-vars',
17
- './plugins/no-override',
18
- './plugins/no-scale-colors',
19
- './plugins/no-undefined-vars',
20
- './plugins/no-unused-vars',
21
- './plugins/responsive-widths',
22
- './plugins/spacing',
23
- './plugins/typography',
24
- './plugins/utilities',
25
- './plugins/new-color-vars-have-fallback',
26
- './plugins/no-display-colors',
27
- ],
28
- rules: {
29
- 'alpha-value-notation': 'number',
30
- 'at-rule-disallowed-list': ['extend'],
31
- 'at-rule-no-unknown': null,
32
- 'block-no-empty': true,
33
- 'color-function-notation': null,
34
- 'color-named': 'never',
35
- 'color-no-invalid-hex': true,
36
- 'comment-no-empty': null,
37
- 'custom-property-pattern': null,
38
- 'declaration-block-no-duplicate-properties': [true, {ignore: ['consecutive-duplicates']}],
39
- 'declaration-block-no-redundant-longhand-properties': null,
40
- 'declaration-block-no-shorthand-property-overrides': true,
41
- 'declaration-property-value-disallowed-list': {
42
- '/^transition/': ['/all/'],
43
- '/^background/': ['http:', 'https:'],
44
- '/^border/': ['none'],
45
- '/.+/': ['initial'],
46
- },
47
- 'function-calc-no-unspaced-operator': true,
48
- 'function-linear-gradient-no-nonstandard-direction': true,
49
- 'function-no-unknown': null,
50
- 'keyframes-name-pattern': null,
51
- 'max-nesting-depth': 3,
52
- 'media-feature-name-no-unknown': null,
53
- 'media-feature-name-no-vendor-prefix': null,
54
- 'no-descending-specificity': null,
55
- 'no-duplicate-selectors': true,
56
- 'no-invalid-position-at-import-rule': [true, {ignoreAtRules: ['use']}],
57
- 'number-max-precision': null,
58
- 'order/properties-order': propertyOrder,
59
- 'plugin/no-unsupported-browser-features': [true, {severity: 'warning', browsers}],
60
- 'primer/borders': true,
61
- 'primer/box-shadow': true,
62
- 'primer/colors': true,
63
- 'primer/no-deprecated-colors': true,
64
- 'primer/no-experimental-vars': [
65
- true,
66
- {
67
- designTokens: '@primer/primitives/tokens-v2-private/docs/docValues.json',
68
- },
69
- ],
70
- 'primer/no-override': true,
71
- 'primer/no-undefined-vars': [
72
- true,
73
- {severity: 'warning', files: 'node_modules/@primer/primitives/dist/scss/colors*/*.scss'},
74
- ],
75
- 'primer/no-unused-vars': [true, {severity: 'warning'}],
76
- 'primer/responsive-widths': true,
77
- 'primer/spacing': true,
78
- 'primer/typography': true,
79
- 'primer/utilities': null,
80
- 'primer/new-color-vars-have-fallback': [true, {severity: 'error'}],
81
- 'scss/at-extend-no-missing-placeholder': true,
82
- 'scss/at-rule-no-unknown': true,
83
- 'scss/declaration-nested-properties-no-divided-groups': true,
84
- 'scss/dollar-variable-no-missing-interpolation': true,
85
- 'scss/function-quote-no-quoted-strings-inside': true,
86
- 'scss/function-unquote-no-unquoted-strings-inside': true,
87
- 'scss/no-duplicate-mixins': true,
88
- 'scss/selector-no-redundant-nesting-selector': true,
89
- 'selector-class-pattern': null,
90
- 'selector-max-compound-selectors': 3,
91
- 'selector-max-id': 0,
92
- 'selector-max-specificity': '0,4,0',
93
- 'selector-max-type': 0,
94
- 'selector-no-qualifying-type': true,
95
- 'selector-pseudo-element-no-unknown': true,
96
- 'string-no-newline': true,
97
- 'unit-no-unknown': true,
98
- 'value-keyword-case': null,
99
- 'selector-not-notation': null,
100
- 'import-notation': ['string'],
101
- 'annotation-no-unknown': null,
102
- 'keyframe-selector-notation': ['percentage-unless-within-keyword-only-block'],
103
- 'media-query-no-invalid': null,
104
- 'media-feature-range-notation': ['prefix'],
105
- },
106
- }