@primer/stylelint-config 13.0.0-rc.980b8d0 → 13.0.0-rc.a78b8bb

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/dist/index.cjs ADDED
@@ -0,0 +1,1154 @@
1
+ 'use strict';
2
+
3
+ var browsers = require('@github/browserslist-config');
4
+ var stylelint = require('stylelint');
5
+ var anymatch = require('anymatch');
6
+ var valueParser = require('postcss-value-parser');
7
+ var TapMap = require('tap-map');
8
+ var variables = require('@primer/css/dist/variables.json');
9
+ var declarationValueIndex = require('stylelint/lib/utils/declarationValueIndex.cjs');
10
+ var node_module = require('node:module');
11
+ var matchAll = require('string.prototype.matchall');
12
+
13
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
14
+ var propertyOrder = [
15
+ 'all',
16
+ 'position',
17
+ 'top',
18
+ 'right',
19
+ 'bottom',
20
+ 'left',
21
+ 'z-index',
22
+ 'display',
23
+ 'float',
24
+ 'width',
25
+ 'min-width',
26
+ 'max-width',
27
+ 'height',
28
+ 'min-height',
29
+ 'max-height',
30
+ 'box-sizing',
31
+ 'padding',
32
+ 'padding-top',
33
+ 'padding-right',
34
+ 'padding-bottom',
35
+ 'padding-left',
36
+ 'margin',
37
+ 'margin-top',
38
+ 'margin-right',
39
+ 'margin-bottom',
40
+ 'margin-left',
41
+ 'overflow',
42
+ 'overflow-x',
43
+ 'overflow-y',
44
+ 'clip',
45
+ 'clear',
46
+ 'font',
47
+ 'font-family',
48
+ 'font-size',
49
+ 'font-style',
50
+ 'font-weight',
51
+ 'font-variant',
52
+ 'font-size-adjust',
53
+ 'font-stretch',
54
+ 'font-effect',
55
+ 'font-emphasize',
56
+ 'font-emphasize-position',
57
+ 'font-emphasize-style',
58
+ 'font-smooth',
59
+ 'hyphens',
60
+ 'line-height',
61
+ 'color',
62
+ 'text-align',
63
+ 'text-align-last',
64
+ 'text-emphasis',
65
+ 'text-emphasis-color',
66
+ 'text-emphasis-style',
67
+ 'text-emphasis-position',
68
+ 'text-decoration',
69
+ 'text-indent',
70
+ 'text-justify',
71
+ 'text-outline',
72
+ 'text-overflow',
73
+ 'text-overflow-ellipsis',
74
+ 'text-overflow-mode',
75
+ 'text-shadow',
76
+ 'text-transform',
77
+ 'text-wrap',
78
+ 'letter-spacing',
79
+ 'word-break',
80
+ 'word-spacing',
81
+ 'word-wrap',
82
+ 'tab-size',
83
+ 'white-space',
84
+ 'vertical-align',
85
+ 'list-style',
86
+ 'list-style-position',
87
+ 'list-style-type',
88
+ 'list-style-image',
89
+ 'pointer-events',
90
+ 'fill',
91
+ 'fill-opacity',
92
+ 'stroke',
93
+ 'stroke-opacity',
94
+ 'stroke-width',
95
+ 'shape-rendering',
96
+ 'cursor',
97
+ 'visibility',
98
+ 'zoom',
99
+ 'flex-direction',
100
+ 'flex-order',
101
+ 'flex-pack',
102
+ 'flex-align',
103
+ 'table-layout',
104
+ 'empty-cells',
105
+ 'caption-side',
106
+ 'border-spacing',
107
+ 'border-collapse',
108
+ 'content',
109
+ 'quotes',
110
+ 'counter-reset',
111
+ 'counter-increment',
112
+ 'resize',
113
+ 'user-select',
114
+ 'nav-index',
115
+ 'nav-up',
116
+ 'nav-right',
117
+ 'nav-down',
118
+ 'nav-left',
119
+ 'background',
120
+ 'background-color',
121
+ 'background-image',
122
+ 'filter',
123
+ 'background-repeat',
124
+ 'background-attachment',
125
+ 'background-position',
126
+ 'background-position-x',
127
+ 'background-position-y',
128
+ 'background-clip',
129
+ 'background-origin',
130
+ 'background-size',
131
+ 'border',
132
+ 'border-color',
133
+ 'border-style',
134
+ 'border-width',
135
+ 'border-top',
136
+ 'border-top-color',
137
+ 'border-top-style',
138
+ 'border-top-width',
139
+ 'border-right',
140
+ 'border-right-color',
141
+ 'border-right-style',
142
+ 'border-right-width',
143
+ 'border-bottom',
144
+ 'border-bottom-color',
145
+ 'border-bottom-style',
146
+ 'border-bottom-width',
147
+ 'border-left',
148
+ 'border-left-color',
149
+ 'border-left-style',
150
+ 'border-left-width',
151
+ 'border-radius',
152
+ 'border-top-left-radius',
153
+ 'border-top-right-radius',
154
+ 'border-bottom-right-radius',
155
+ 'border-bottom-left-radius',
156
+ 'border-image',
157
+ 'border-image-source',
158
+ 'border-image-slice',
159
+ 'border-image-width',
160
+ 'border-image-outset',
161
+ 'border-image-repeat',
162
+ 'outline',
163
+ 'outline-width',
164
+ 'outline-style',
165
+ 'outline-color',
166
+ 'outline-offset',
167
+ 'box-shadow',
168
+ 'opacity',
169
+ 'transition',
170
+ 'transition-delay',
171
+ 'transition-timing-function',
172
+ 'transition-duration',
173
+ 'transition-property',
174
+ 'transform',
175
+ 'transform-origin',
176
+ 'animation',
177
+ 'animation-name',
178
+ 'animation-duration',
179
+ 'animation-fill-mode',
180
+ 'animation-play-state',
181
+ 'animation-timing-function',
182
+ 'animation-delay',
183
+ 'animation-iteration-count',
184
+ 'animation-direction',
185
+ ];
186
+
187
+ const SKIP_VALUE_NODE_TYPES = new Set(['space', 'div']);
188
+ const SKIP_AT_RULE_NAMES = new Set(['each', 'for', 'function', 'mixin']);
189
+
190
+ function declarationValidator(rules, options = {}) {
191
+ const {formatMessage = defaultMessageFormatter, variables, verbose = false} = options;
192
+ const variableReplacements = new TapMap();
193
+ if (variables) {
194
+ for (const [name, {values}] of Object.entries(variables)) {
195
+ for (const value of values) {
196
+ variableReplacements.tap(value, () => []).push(name);
197
+ }
198
+ }
199
+ }
200
+
201
+ const validators = Object.entries(rules)
202
+ .map(([key, rule]) => {
203
+ if (rule === false) {
204
+ return false
205
+ }
206
+ const {name = key, props = name, expects = `a ${name} value`} = rule;
207
+ const replacements = Object.assign({}, rule.replacements, getVariableReplacements(rule.values));
208
+ // console.warn(`replacements for "${key}": ${JSON.stringify(replacements)}`)
209
+ Object.assign(rule, {name, props, expects, replacements});
210
+ return {
211
+ rule,
212
+ matchesProp: anymatch(props),
213
+ validate: Array.isArray(rule.components) ? componentValidator(rule) : valueValidator(rule),
214
+ }
215
+ })
216
+ .filter(Boolean);
217
+
218
+ const validatorsByProp = new TapMap();
219
+ const validatorsByReplacementValue = new Map();
220
+ for (const validator of validators) {
221
+ for (const value of Object.keys(validator.rule.replacements)) {
222
+ validatorsByReplacementValue.set(value, validator);
223
+ }
224
+ }
225
+
226
+ return decl => {
227
+ if (closest(decl, isSkippableAtRule)) {
228
+ if (verbose) {
229
+ // eslint-disable-next-line no-console
230
+ console.warn(`skipping declaration: ${decl.parent.toString()}`);
231
+ }
232
+ // As a general rule, any rule nested in an at-rule is ignored, since
233
+ // @for, @each, @mixin, and @function blocks can use whatever variables
234
+ // they want
235
+ return {valid: true}
236
+ }
237
+ const validator = getPropValidator(decl.prop);
238
+ if (validator) {
239
+ const result = validator.validate(decl);
240
+ result.errors = result.errors.map(formatMessage);
241
+ return result
242
+ } else {
243
+ return {valid: true}
244
+ }
245
+ }
246
+
247
+ function getVariableReplacements(values) {
248
+ const replacements = {};
249
+ const varValues = (Array.isArray(values) ? values : [values]).filter(v => typeof v === 'string' && v.includes('$'));
250
+ const matches = anymatch(varValues);
251
+ for (const [value, aliases] of variableReplacements.entries()) {
252
+ for (const alias of aliases) {
253
+ if (matches(alias)) {
254
+ replacements[value] = alias;
255
+ }
256
+ }
257
+ }
258
+ return replacements
259
+ }
260
+
261
+ function getPropValidator(prop) {
262
+ return validatorsByProp.tap(prop, () => validators.find(v => v.matchesProp(prop)))
263
+ }
264
+
265
+ function valueValidator({expects, values, replacements, singular = false}) {
266
+ const matches = anymatch(values);
267
+ return function validate({prop, value}, nested) {
268
+ if (matches(value)) {
269
+ return {
270
+ valid: true,
271
+ errors: [],
272
+ fixable: false,
273
+ replacement: undefined,
274
+ }
275
+ } else if (replacements[value]) {
276
+ let replacement = value;
277
+ do {
278
+ replacement = replacements[replacement];
279
+ } while (replacements[replacement])
280
+ return {
281
+ valid: false,
282
+ errors: [{expects, prop, value, replacement}],
283
+ fixable: true,
284
+ replacement,
285
+ }
286
+ } else {
287
+ if (nested || singular) {
288
+ return {
289
+ valid: false,
290
+ errors: [{expects, prop, value}],
291
+ fixable: false,
292
+ replacement: undefined,
293
+ }
294
+ }
295
+
296
+ const parsed = valueParser(value);
297
+ const validations = parsed.nodes
298
+ .map((node, index) => Object.assign(node, {index}))
299
+ .filter(node => !SKIP_VALUE_NODE_TYPES.has(node.type))
300
+ .map(node => {
301
+ const validation = validate({prop, value: valueParser.stringify(node)}, true);
302
+ validation.index = node.index;
303
+ return validation
304
+ });
305
+
306
+ const valid = validations.every(v => v.valid);
307
+ if (valid) {
308
+ return {valid, errors: [], fixable: false, replacement: undefined}
309
+ }
310
+
311
+ const fixable = validations.some(v => v.fixable);
312
+ const errors = validations.reduce((list, v) => list.concat(v.errors), []);
313
+
314
+ let replacement = undefined;
315
+ for (const validation of validations) {
316
+ if (fixable && validation.replacement) {
317
+ parsed.nodes[validation.index] = {type: 'word', value: validation.replacement};
318
+ }
319
+ }
320
+
321
+ if (fixable) {
322
+ replacement = valueParser.stringify(parsed);
323
+ }
324
+
325
+ return {
326
+ valid,
327
+ fixable,
328
+ errors,
329
+ replacement,
330
+ }
331
+ }
332
+ }
333
+ }
334
+
335
+ function componentValidator({expects, components, values, replacements}) {
336
+ const matchesCompoundValue = anymatch(values);
337
+ return decl => {
338
+ const {prop, value: compoundValue} = decl;
339
+ const parsed = valueParser(compoundValue);
340
+ if (parsed.nodes.length === 1 && matchesCompoundValue(compoundValue)) {
341
+ return {valid: true, errors: []}
342
+ }
343
+
344
+ const errors = [];
345
+
346
+ let fixable = false;
347
+ let componentIndex = 0;
348
+ for (const [index, node] of Object.entries(parsed.nodes)) {
349
+ if (SKIP_VALUE_NODE_TYPES.has(node.type)) {
350
+ continue
351
+ }
352
+
353
+ const value = valueParser.stringify(node);
354
+
355
+ let componentProp = components[componentIndex++];
356
+ let validator = getPropValidator(componentProp);
357
+ if (validatorsByReplacementValue.has(value)) {
358
+ validator = validatorsByReplacementValue.get(value);
359
+ componentProp = validator.rule.name;
360
+ }
361
+
362
+ const nestedProp = `${componentProp} (in ${prop})`;
363
+ if (validator) {
364
+ const result = validator.validate({prop: nestedProp, value}, true);
365
+ if (result.replacement) {
366
+ parsed.nodes[index] = {
367
+ type: 'word',
368
+ value: result.replacement,
369
+ };
370
+ fixable = true;
371
+ }
372
+ for (const error of result.errors) {
373
+ errors.push(error);
374
+ }
375
+ } else {
376
+ errors.push({expects, prop: nestedProp, value});
377
+ }
378
+ }
379
+
380
+ let replacement = fixable ? valueParser.stringify(parsed) : undefined;
381
+
382
+ // if a compound replacement exists, suggest *that* instead
383
+ if (replacement && replacements[replacement]) {
384
+ do {
385
+ replacement = replacements[replacement];
386
+ } while (replacements[replacement])
387
+ return {
388
+ valid: false,
389
+ errors: [{expects, prop, value: compoundValue, replacement}],
390
+ fixable: true,
391
+ replacement,
392
+ }
393
+ }
394
+
395
+ return {
396
+ valid: errors.length === 0,
397
+ errors,
398
+ fixable,
399
+ replacement,
400
+ }
401
+ }
402
+ }
403
+
404
+ function isSkippableAtRule(node) {
405
+ return node.type === 'atrule' && SKIP_AT_RULE_NAMES.has(node.name)
406
+ }
407
+ }
408
+
409
+ function defaultMessageFormatter(error) {
410
+ const {expects, value, replacement} = error;
411
+ const expected = replacement ? `"${replacement}"` : expects;
412
+ return `Please use ${expected} instead of "${value}"`
413
+ }
414
+
415
+ function closest(node, test) {
416
+ let ancestor = node;
417
+ do {
418
+ if (test(ancestor)) return ancestor
419
+ } while ((ancestor = ancestor.parent))
420
+ }
421
+
422
+ function createVariableRule(ruleName, rules, url) {
423
+ const plugin = stylelint.createPlugin(ruleName, (enabled, options = {}, context) => {
424
+ if (enabled === false) {
425
+ return noop$2
426
+ }
427
+
428
+ let actualRules = rules;
429
+ let overrides = options.rules;
430
+ if (typeof rules === 'function') {
431
+ actualRules = rules({variables, options, ruleName});
432
+ } else {
433
+ actualRules = Object.assign({}, rules);
434
+ }
435
+ if (typeof overrides === 'function') {
436
+ delete options.rules;
437
+ overrides = overrides({rules: actualRules, options, ruleName, variables});
438
+ }
439
+ if (overrides) {
440
+ Object.assign(actualRules, overrides);
441
+ }
442
+ const validate = declarationValidator(actualRules, {variables});
443
+
444
+ // The stylelint docs suggest respecting a "disableFix" rule option that
445
+ // overrides the "global" context.fix (--fix) linting option.
446
+ const {verbose = false, disableFix} = options;
447
+ const fixEnabled = context && context.fix && !disableFix;
448
+ const seen = new WeakMap();
449
+
450
+ return (root, result) => {
451
+ root.walkRules(rule => {
452
+ rule.walkDecls(decl => {
453
+ if (seen.has(decl)) {
454
+ return
455
+ } else {
456
+ seen.set(decl, true);
457
+ }
458
+
459
+ const validated = validate(decl);
460
+ const {valid, fixable, replacement, errors} = validated;
461
+ if (valid) {
462
+ // eslint-disable-next-line no-console
463
+ if (verbose) console.warn(`valid: "${decl.toString()}" in: "${rule.selector}"`);
464
+ return
465
+ } else if (fixEnabled && fixable) {
466
+ // eslint-disable-next-line no-console
467
+ if (verbose) console.warn(` fixed: ${replacement}`);
468
+ decl.value = replacement;
469
+ } else {
470
+ // eslint-disable-next-line no-console
471
+ if (verbose) console.warn(` ${errors.length} error(s)`);
472
+ for (const error of errors) {
473
+ const message = stylelint.utils
474
+ .ruleMessages(ruleName, {
475
+ rejected: m => {
476
+ if (url) {
477
+ return `${m}. See ${url}.`
478
+ }
479
+ return `${m}.`
480
+ },
481
+ })
482
+ .rejected(error);
483
+
484
+ stylelint.utils.report({
485
+ message,
486
+ node: decl,
487
+ result,
488
+ ruleName,
489
+ });
490
+ }
491
+ }
492
+ });
493
+ });
494
+ }
495
+ });
496
+
497
+ Object.assign(plugin, {rules});
498
+
499
+ return plugin
500
+ }
501
+
502
+ function noop$2() {}
503
+
504
+ var borders = createVariableRule(
505
+ 'primer/borders',
506
+ {
507
+ border: {
508
+ expects: 'a border variable',
509
+ props: 'border{,-top,-right,-bottom,-left}',
510
+ values: ['$border', 'none', '0'],
511
+ components: ['border-width', 'border-style', 'border-color'],
512
+ replacements: {
513
+ // because shorthand border properties ¯\_(ツ)_/¯
514
+ '$border-width $border-style $border-gray': '$border',
515
+ '$border-width $border-gray $border-style': '$border',
516
+ '$border-style $border-width $border-gray': '$border',
517
+ '$border-style $border-gray $border-width': '$border',
518
+ '$border-gray $border-width $border-style': '$border',
519
+ '$border-gray $border-style $border-width': '$border',
520
+ '$border-width $border-style $border-color': '$border',
521
+ '$border-width $border-color $border-style': '$border',
522
+ '$border-style $border-width $border-color': '$border',
523
+ '$border-style $border-color $border-width': '$border',
524
+ '$border-color $border-width $border-style': '$border',
525
+ '$border-color $border-style $border-width': '$border',
526
+ },
527
+ },
528
+ 'border color': {
529
+ expects: 'a border color variable',
530
+ props: 'border{,-top,-right,-bottom,-left}-color',
531
+ values: [
532
+ '$border-*',
533
+ 'transparent',
534
+ 'currentColor',
535
+ // Match variables in any of the following formats: --color-border-*, --color-*-border-*, --color-*-border, --borderColor-, *borderColor*
536
+ /var\(--color-(.+-)*border(-.+)*\)/,
537
+ /var\(--color-[^)]+\)/,
538
+ /var\(--borderColor-[^)]+\)/,
539
+ /var\((.+-)*borderColor(-.+)*\)/,
540
+ ],
541
+ replacements: {
542
+ '$border-gray': '$border-color',
543
+ },
544
+ },
545
+ 'border style': {
546
+ expects: 'a border style variable',
547
+ props: 'border{,-top,-right,-bottom,-left}-style',
548
+ values: ['$border-style', 'none'],
549
+ },
550
+ 'border width': {
551
+ expects: 'a border width variable',
552
+ props: 'border{,-top,-right,-bottom,-left}-width',
553
+ values: ['$border-width*', '0'],
554
+ },
555
+ 'border radius': {
556
+ expects: 'a border radius variable',
557
+ props: 'border{,-{top,bottom}-{left,right}}-radius',
558
+ values: ['$border-radius', '0', '50%', 'inherit'],
559
+ replacements: {
560
+ '100%': '50%',
561
+ },
562
+ },
563
+ },
564
+ 'https://primer.style/css/utilities/borders',
565
+ );
566
+
567
+ var boxShadow = createVariableRule(
568
+ 'primer/box-shadow',
569
+ {
570
+ 'box shadow': {
571
+ expects: 'a box-shadow variable',
572
+ props: 'box-shadow',
573
+ values: [
574
+ '$box-shadow*',
575
+ '$*-shadow',
576
+ 'none',
577
+ // Match variables in any of the following formats: --color-shadow-*, --color-*-shadow-*, --color-*-shadow, --shadow-*, *shadow*
578
+ /var\(--color-(.+-)*shadow(-.+)*\)/,
579
+ /var\(--shadow(-.+)*\)/,
580
+ /var\((.+-)*shadow(-.+)*\)/,
581
+ ],
582
+ singular: true,
583
+ },
584
+ },
585
+ 'https://primer.style/css/utilities/box-shadow',
586
+ );
587
+
588
+ const bgVars = [
589
+ '$bg-*',
590
+ '$tooltip-background-color',
591
+ // Match variables in any of the following formats: --color-bg-*, --color-*-bg-*, --color-*-bg, *bgColor*, *fgColor*, *borderColor*, *iconColor*
592
+ /var\(--color-(.+-)*bg(-.+)*\)/,
593
+ /var\(--color-[^)]+\)/,
594
+ /var\((.+-)*bgColor(-.+)*\)/,
595
+ /var\((.+-)*fgColor(-.+)*\)/,
596
+ /var\((.+-)*borderColor(-.+)*\)/,
597
+ /var\((.+-)*iconColor(-.+)*\)/,
598
+ ];
599
+
600
+ var colors = createVariableRule(
601
+ 'primer/colors',
602
+ {
603
+ 'background-color': {
604
+ expects: 'a background color variable',
605
+ values: bgVars.concat('none', 'transparent'),
606
+ },
607
+ background: {
608
+ expects: 'a background color variable',
609
+ values: bgVars.concat('none', 'transparent', 'top', 'right', 'bottom', 'left', 'center', '*px', 'url(*)'),
610
+ },
611
+ 'text color': {
612
+ expects: 'a text color variable',
613
+ props: 'color',
614
+ values: [
615
+ '$text-*',
616
+ '$tooltip-text-color',
617
+ 'inherit',
618
+ // Match variables in any of the following formats: --color-text-*, --color-*-text-*, --color-*-text, *fgColor*, *iconColor*
619
+ /var\(--color-(.+-)*text(-.+)*\)/,
620
+ /var\(--color-(.+-)*fg(-.+)*\)/,
621
+ /var\(--color-[^)]+\)/,
622
+ /var\((.+-)*fgColor(-.+)*\)/,
623
+ /var\((.+-)*iconColor(-.+)*\)/,
624
+ ],
625
+ },
626
+ },
627
+ 'https://primer.style/primitives/colors',
628
+ );
629
+
630
+ const ruleName$2 = 'primer/responsive-widths';
631
+
632
+ const messages$2 = stylelint.utils.ruleMessages(ruleName$2, {
633
+ rejected: value => {
634
+ 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`
635
+ },
636
+ });
637
+
638
+ // 320px is the smallest viewport size that we support
639
+
640
+ const walkGroups$1 = (root, validate) => {
641
+ for (const node of root.nodes) {
642
+ if (node.type === 'function') {
643
+ walkGroups$1(node, validate);
644
+ } else {
645
+ validate(node);
646
+ }
647
+ }
648
+ return root
649
+ };
650
+
651
+ // eslint-disable-next-line no-unused-vars
652
+ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}, context) => {
653
+ if (!enabled) {
654
+ return noop$1
655
+ }
656
+
657
+ const lintResult = (root, result) => {
658
+ root.walk(decl => {
659
+ // Ignore things inside of breakpoints
660
+ if (decl.type === 'atrule' && decl.name === 'include' && decl.params.includes('breakpoint')) {
661
+ return false
662
+ }
663
+
664
+ if (decl.type !== 'decl' || !decl.prop.match(/^(min-width|width)/)) {
665
+ return noop$1
666
+ }
667
+
668
+ const problems = [];
669
+
670
+ walkGroups$1(valueParser(decl.value), node => {
671
+ // Only check word types. https://github.com/TrySound/postcss-value-parser#word
672
+ if (node.type !== 'word') {
673
+ return
674
+ }
675
+
676
+ // Exact values to ignore.
677
+ if (['*', '+', '-', '/', '0', 'auto', 'inherit', 'initial'].includes(node.value)) {
678
+ return
679
+ }
680
+
681
+ const valueUnit = valueParser.unit(node.value);
682
+
683
+ switch (valueUnit.unit) {
684
+ case 'px':
685
+ if (parseInt(valueUnit.number) > 320) {
686
+ problems.push({
687
+ index: declarationValueIndex(decl) + node.sourceIndex,
688
+ message: messages$2.rejected(node.value),
689
+ });
690
+ }
691
+ break
692
+ case 'vw':
693
+ if (parseInt(valueUnit.number) > 100) {
694
+ problems.push({
695
+ index: declarationValueIndex(decl) + node.sourceIndex,
696
+ message: messages$2.rejected(node.value),
697
+ });
698
+ }
699
+ break
700
+ }
701
+ });
702
+
703
+ if (problems.length) {
704
+ for (const err of problems) {
705
+ stylelint.utils.report({
706
+ index: err.index,
707
+ message: err.message,
708
+ node: decl,
709
+ result,
710
+ ruleName: ruleName$2,
711
+ });
712
+ }
713
+ }
714
+ });
715
+ };
716
+
717
+ return lintResult
718
+ });
719
+
720
+ function noop$1() {}
721
+
722
+ const require$2 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
723
+
724
+ async function primitivesVariables(type) {
725
+ const variables = [];
726
+
727
+ const files = [];
728
+ switch (type) {
729
+ case 'size':
730
+ files.push('base/size/size.json');
731
+ break
732
+ }
733
+
734
+ for (const file of files) {
735
+ // eslint-disable-next-line import/no-dynamic-require
736
+ const data = require$2(`@primer/primitives/dist/styleLint/${file}`);
737
+
738
+ for (const key of Object.keys(data)) {
739
+ const size = data[key];
740
+ const values = size['value'];
741
+ const intValue = parseInt(size['original']['value']);
742
+ if (![2, 6].includes(intValue)) {
743
+ values.push(`${intValue + 1}px`);
744
+ values.push(`${intValue - 1}px`);
745
+ }
746
+
747
+ variables.push({
748
+ name: `--${size['name']}`,
749
+ values,
750
+ });
751
+ }
752
+ }
753
+
754
+ return variables
755
+ }
756
+
757
+ const {
758
+ createPlugin,
759
+ utils: {report, ruleMessages, validateOptions},
760
+ } = stylelint;
761
+
762
+ const walkGroups = (root, validate) => {
763
+ for (const node of root.nodes) {
764
+ if (node.type === 'function') {
765
+ walkGroups(node, validate);
766
+ } else {
767
+ validate(node);
768
+ }
769
+ }
770
+ return root
771
+ };
772
+
773
+ const ruleName$1 = 'primer/spacing';
774
+ const messages$1 = ruleMessages(ruleName$1, {
775
+ rejected: (value, replacement) => {
776
+ if (!replacement) {
777
+ return `Please use a primer size variable instead of '${value}'. Consult the primer docs for a suitable replacement. https://primer.style/foundations/primitives/size`
778
+ }
779
+
780
+ return `Please replace '${value}' with size variable '${replacement['name']}'. https://primer.style/foundations/primitives/size`
781
+ },
782
+ });
783
+
784
+ const meta = {
785
+ fixable: true,
786
+ };
787
+
788
+ /** @type {import('stylelint').Rule} */
789
+ const ruleFunction = (primary, secondaryOptions, context) => {
790
+ return async (root, result) => {
791
+ // Props that we want to check
792
+ const propList = ['padding', 'margin', 'top', 'right', 'bottom', 'left'];
793
+ // Values that we want to ignore
794
+ const valueList = ['${'];
795
+
796
+ const sizes = await primitivesVariables('size');
797
+
798
+ const validOptions = validateOptions(result, ruleName$1, {
799
+ actual: primary,
800
+ possible: [true],
801
+ });
802
+
803
+ if (!validOptions) return
804
+
805
+ root.walkDecls(declNode => {
806
+ const {prop, value} = declNode;
807
+
808
+ if (!propList.some(spacingProp => prop.startsWith(spacingProp))) return
809
+ if (valueList.some(valueToIgnore => value.includes(valueToIgnore))) return
810
+
811
+ const problems = [];
812
+
813
+ const parsedValue = walkGroups(valueParser(value), node => {
814
+ // Only check word types. https://github.com/TrySound/postcss-value-parser#word
815
+ if (node.type !== 'word') {
816
+ return
817
+ }
818
+
819
+ // Exact values to ignore.
820
+ if (['*', '+', '-', '/', '0', 'auto', 'inherit', 'initial'].includes(node.value)) {
821
+ return
822
+ }
823
+
824
+ const valueUnit = valueParser.unit(node.value);
825
+
826
+ if (valueUnit && (valueUnit.unit === '' || !/^-?[0-9.]+$/.test(valueUnit.number))) {
827
+ return
828
+ }
829
+
830
+ // Skip if the value unit isn't a supported unit.
831
+ if (valueUnit && !['px', 'rem', 'em'].includes(valueUnit.unit)) {
832
+ return
833
+ }
834
+
835
+ // If the variable is found in the value, skip it.
836
+ if (
837
+ sizes.some(variable =>
838
+ new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(node.value),
839
+ )
840
+ ) {
841
+ return
842
+ }
843
+
844
+ const replacement = sizes.find(variable => variable.values.includes(node.value.replace('-', '')));
845
+ const fixable = replacement && valueUnit && !valueUnit.number.includes('-');
846
+
847
+ if (fixable && context.fix) {
848
+ node.value = node.value.replace(node.value, `var(${replacement['name']})`);
849
+ } else {
850
+ problems.push({
851
+ index: declarationValueIndex(declNode) + node.sourceIndex,
852
+ endIndex: declarationValueIndex(declNode) + node.sourceIndex + node.value.length,
853
+ message: messages$1.rejected(node.value, replacement),
854
+ });
855
+ }
856
+
857
+ return
858
+ });
859
+
860
+ if (context.fix) {
861
+ declNode.value = parsedValue.toString();
862
+ }
863
+
864
+ if (problems.length) {
865
+ for (const err of problems) {
866
+ report({
867
+ index: err.index,
868
+ endIndex: err.endIndex,
869
+ message: err.message,
870
+ node: declNode,
871
+ result,
872
+ ruleName: ruleName$1,
873
+ });
874
+ }
875
+ }
876
+ });
877
+ }
878
+ };
879
+
880
+ ruleFunction.ruleName = ruleName$1;
881
+ ruleFunction.messages = messages$1;
882
+ ruleFunction.meta = meta;
883
+
884
+ var spacing = createPlugin(ruleName$1, ruleFunction);
885
+
886
+ var typography = createVariableRule(
887
+ 'primer/typography',
888
+ {
889
+ 'font-size': {
890
+ expects: 'a font-size variable',
891
+ values: ['$body-font-size', '$h{000,00,0,1,2,3,4,5,6}-size', '$font-size-*', '1', '1em', 'inherit'],
892
+ },
893
+ 'font-weight': {
894
+ props: 'font-weight',
895
+ values: ['$font-weight-*', 'inherit'],
896
+ replacements: {
897
+ bold: '$font-weight-bold',
898
+ normal: '$font-weight-normal',
899
+ },
900
+ },
901
+ 'line-height': {
902
+ props: 'line-height',
903
+ values: ['$body-line-height', '$lh-*', '0', '1', '1em', 'inherit'],
904
+ },
905
+ },
906
+ 'https://primer.style/css/utilities/typography',
907
+ );
908
+
909
+ const ruleName = 'primer/no-display-colors';
910
+ const messages = stylelint.utils.ruleMessages(ruleName, {
911
+ rejected: varName => `${varName} is in alpha and should be used with caution with approval from the Primer team`,
912
+ });
913
+
914
+ // Match CSS variable references (e.g var(--display-blue-fgColor))
915
+ // eslint-disable-next-line no-useless-escape
916
+ const variableReferenceRegex = /var\(([^\),]+)(,.*)?\)/g;
917
+
918
+ var noDisplayColors = stylelint.createPlugin(ruleName, (enabled, options = {}) => {
919
+ if (!enabled) {
920
+ return noop
921
+ }
922
+
923
+ const {verbose = false} = options;
924
+ // eslint-disable-next-line no-console
925
+ const log = verbose ? (...args) => console.warn(...args) : noop;
926
+
927
+ // Keep track of declarations we've already seen
928
+ const seen = new WeakMap();
929
+
930
+ return (root, result) => {
931
+ root.walkRules(rule => {
932
+ rule.walkDecls(decl => {
933
+ if (seen.has(decl)) {
934
+ return
935
+ } else {
936
+ seen.set(decl, true);
937
+ }
938
+
939
+ for (const [, variableName] of matchAll(decl.value, variableReferenceRegex)) {
940
+ log(`Found variable reference ${variableName}`);
941
+ if (variableName.match(/^--display-.*/)) {
942
+ stylelint.utils.report({
943
+ message: messages.rejected(variableName),
944
+ node: decl,
945
+ result,
946
+ ruleName,
947
+ });
948
+ }
949
+ }
950
+ });
951
+ });
952
+ }
953
+ });
954
+
955
+ function noop() {}
956
+
957
+ const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
958
+
959
+ /** @type {import('stylelint').Config} */
960
+ var index = {
961
+ extends: ['stylelint-config-standard'],
962
+ ignoreFiles: ['**/*.js', '**/*.cjs', '**/*.ts', '**/*.mjs'],
963
+ reportNeedlessDisables: true,
964
+ plugins: [
965
+ 'stylelint-value-no-unknown-custom-properties',
966
+ 'stylelint-no-unsupported-browser-features',
967
+ 'stylelint-order',
968
+ borders,
969
+ boxShadow,
970
+ colors,
971
+ responsiveWidths,
972
+ spacing,
973
+ typography,
974
+ noDisplayColors,
975
+ ],
976
+ rules: {
977
+ 'alpha-value-notation': 'number',
978
+ 'at-rule-disallowed-list': ['extend'],
979
+ 'at-rule-no-unknown': null,
980
+ 'block-no-empty': true,
981
+ 'color-function-notation': null,
982
+ 'color-named': 'never',
983
+ 'color-no-invalid-hex': true,
984
+ 'comment-no-empty': null,
985
+ 'csstools/value-no-unknown-custom-properties': [
986
+ true,
987
+ {
988
+ severity: 'warning',
989
+ importFrom: [
990
+ '@primer/primitives/dist/css/functional/size/size-coarse.css',
991
+ '@primer/primitives/dist/css/functional/size/border.css',
992
+ '@primer/primitives/dist/css/functional/size/size.css',
993
+ '@primer/primitives/dist/css/functional/size/size-fine.css',
994
+ '@primer/primitives/dist/css/functional/size/breakpoints.css',
995
+ '@primer/primitives/dist/css/functional/size/viewport.css',
996
+ '@primer/primitives/dist/css/functional/motion/motion.css',
997
+ '@primer/primitives/dist/css/functional/themes/light.css',
998
+ '@primer/primitives/dist/css/functional/typography/typography.css',
999
+ '@primer/primitives/dist/css/base/size/size.css',
1000
+ '@primer/primitives/dist/css/base/typography/typography.css',
1001
+ ].map(path => require$1.resolve(path)),
1002
+ },
1003
+ ],
1004
+ 'custom-property-pattern': null,
1005
+ 'declaration-block-no-duplicate-properties': [true, {ignore: ['consecutive-duplicates']}],
1006
+ 'declaration-block-no-redundant-longhand-properties': null,
1007
+ 'declaration-block-no-shorthand-property-overrides': true,
1008
+ 'declaration-property-value-disallowed-list': {
1009
+ '/^transition/': ['/all/'],
1010
+ '/^background/': ['http:', 'https:'],
1011
+ '/^border/': ['none'],
1012
+ '/.+/': ['initial'],
1013
+ },
1014
+ 'function-calc-no-unspaced-operator': true,
1015
+ 'function-linear-gradient-no-nonstandard-direction': true,
1016
+ 'function-no-unknown': null,
1017
+ 'keyframes-name-pattern': null,
1018
+ 'max-nesting-depth': 3,
1019
+ 'media-feature-name-no-unknown': null,
1020
+ 'media-feature-name-no-vendor-prefix': null,
1021
+ 'no-descending-specificity': null,
1022
+ 'no-duplicate-selectors': true,
1023
+ 'no-invalid-position-at-import-rule': [true, {ignoreAtRules: ['use']}],
1024
+ 'number-max-precision': null,
1025
+ 'order/properties-order': propertyOrder,
1026
+ 'plugin/no-unsupported-browser-features': [
1027
+ true,
1028
+ {
1029
+ severity: 'warning',
1030
+ ignore: ['css-nesting'],
1031
+ ignorePartialSupport: true,
1032
+ browsers,
1033
+ },
1034
+ ],
1035
+ 'primer/borders': true,
1036
+ 'primer/box-shadow': true,
1037
+ 'primer/colors': true,
1038
+ 'primer/responsive-widths': true,
1039
+ 'primer/spacing': true,
1040
+ 'primer/typography': true,
1041
+ 'primer/no-display-colors': true,
1042
+ 'property-no-unknown': [
1043
+ true,
1044
+ {
1045
+ ignoreProperties: ['@container', 'container-type'],
1046
+ },
1047
+ ],
1048
+ 'selector-class-pattern': null,
1049
+ 'selector-max-compound-selectors': 3,
1050
+ 'selector-max-id': 0,
1051
+ 'selector-max-specificity': '0,4,0',
1052
+ 'selector-max-type': 0,
1053
+ 'selector-no-qualifying-type': true,
1054
+ 'selector-pseudo-element-no-unknown': true,
1055
+ 'string-no-newline': true,
1056
+ 'unit-no-unknown': true,
1057
+ 'value-keyword-case': null,
1058
+ 'selector-not-notation': null,
1059
+ 'import-notation': ['string'],
1060
+ 'annotation-no-unknown': null,
1061
+ 'keyframe-selector-notation': ['percentage-unless-within-keyword-only-block'],
1062
+ 'media-query-no-invalid': null,
1063
+ 'media-feature-range-notation': ['prefix'],
1064
+ },
1065
+ overrides: [
1066
+ {
1067
+ files: ['**/*.scss'],
1068
+ customSyntax: 'postcss-scss',
1069
+ plugins: ['stylelint-scss'],
1070
+ rules: {
1071
+ 'scss/at-extend-no-missing-placeholder': true,
1072
+ 'scss/at-rule-no-unknown': true,
1073
+ 'scss/declaration-nested-properties-no-divided-groups': true,
1074
+ 'scss/dollar-variable-no-missing-interpolation': true,
1075
+ 'scss/function-quote-no-quoted-strings-inside': true,
1076
+ 'scss/function-unquote-no-unquoted-strings-inside': true,
1077
+ 'scss/no-duplicate-mixins': true,
1078
+ 'scss/selector-no-redundant-nesting-selector': true,
1079
+ },
1080
+ },
1081
+ {
1082
+ files: ['**/*.tsx'],
1083
+ customSyntax: 'postcss-styled-syntax',
1084
+ rules: {
1085
+ 'order/properties-order': null,
1086
+ 'rule-empty-line-before': null,
1087
+ 'declaration-empty-line-before': null,
1088
+ 'comment-empty-line-before': null,
1089
+ 'length-zero-no-unit': null,
1090
+ 'selector-max-type': null,
1091
+ 'primer/colors': null,
1092
+ 'primer/borders': null,
1093
+ 'primer/typography': null,
1094
+ 'primer/box-shadow': null,
1095
+ },
1096
+ },
1097
+ {
1098
+ files: ['**/*.pcss'],
1099
+ rules: {
1100
+ 'media-feature-range-notation': null,
1101
+ 'import-notation': null,
1102
+ 'custom-property-pattern': null,
1103
+ 'selector-class-pattern': null,
1104
+ 'keyframes-name-pattern': null,
1105
+ 'no-descending-specificity': null,
1106
+ 'declaration-block-no-redundant-longhand-properties': null,
1107
+ 'color-function-notation': 'legacy',
1108
+ 'selector-nested-pattern': '^&\\s?\\W',
1109
+ 'at-rule-no-unknown': [
1110
+ true,
1111
+ {
1112
+ ignoreAtRules: ['mixin', 'define-mixin'],
1113
+ },
1114
+ ],
1115
+ },
1116
+ },
1117
+ {
1118
+ files: ['**/*.module.css'],
1119
+ plugins: ['stylelint-css-modules-no-global-scoped-selector'],
1120
+ rules: {
1121
+ 'property-no-unknown': [
1122
+ true,
1123
+ {
1124
+ ignoreProperties: ['composes', 'compose-with'],
1125
+ ignoreSelectors: [':export', /^:import/],
1126
+ },
1127
+ ],
1128
+ 'selector-pseudo-class-no-unknown': [
1129
+ true,
1130
+ {ignorePseudoClasses: ['export', 'import', 'global', 'local', 'external']},
1131
+ ],
1132
+ 'selector-type-no-unknown': [
1133
+ true,
1134
+ {
1135
+ ignoreTypes: ['from'],
1136
+ },
1137
+ ],
1138
+ 'function-no-unknown': [
1139
+ true,
1140
+ {
1141
+ ignoreFunctions: ['global'],
1142
+ },
1143
+ ],
1144
+ 'css-modules/no-global-scoped-selector': true,
1145
+ // temporarily disabiling Primer plugins while we work on upgrades https://github.com/github/primer/issues/3165
1146
+ 'primer/borders': null,
1147
+ 'primer/typography': null,
1148
+ 'primer/box-shadow': null,
1149
+ },
1150
+ },
1151
+ ],
1152
+ };
1153
+
1154
+ module.exports = index;