@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.
package/dist/index.cjs ADDED
@@ -0,0 +1,1288 @@
1
+ 'use strict';
2
+
3
+ var browsers = require('@github/browserslist-config');
4
+ var stylelint = require('stylelint');
5
+ var declarationValueIndex = require('stylelint/lib/utils/declarationValueIndex.cjs');
6
+ var valueParser = require('postcss-value-parser');
7
+ var node_module = require('node:module');
8
+ var anymatch = require('anymatch');
9
+ var TapMap = require('tap-map');
10
+ var variables$1 = require('@primer/css/dist/variables.json');
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 require$2 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
188
+
189
+ function primitivesVariables(type) {
190
+ const variables = [];
191
+
192
+ const files = [];
193
+ switch (type) {
194
+ case 'spacing':
195
+ files.push('base/size/size.json');
196
+ break
197
+ case 'border':
198
+ files.push('functional/size/border.json');
199
+ break
200
+ }
201
+
202
+ for (const file of files) {
203
+ // eslint-disable-next-line import/no-dynamic-require
204
+ const data = require$2(`@primer/primitives/dist/styleLint/${file}`);
205
+
206
+ for (const key of Object.keys(data)) {
207
+ const size = data[key];
208
+ const values = typeof size['value'] === 'string' ? [size['value']] : size['value'];
209
+
210
+ variables.push({
211
+ name: `--${size['name']}`,
212
+ values,
213
+ });
214
+ }
215
+ }
216
+
217
+ return variables
218
+ }
219
+
220
+ function walkGroups$1(root, validate) {
221
+ for (const node of root.nodes) {
222
+ if (node.type === 'function') {
223
+ walkGroups$1(node, validate);
224
+ } else {
225
+ validate(node);
226
+ }
227
+ }
228
+ return root
229
+ }
230
+
231
+ const {
232
+ createPlugin: createPlugin$1,
233
+ utils: {report: report$1, ruleMessages: ruleMessages$1, validateOptions: validateOptions$1},
234
+ } = stylelint;
235
+
236
+ const ruleName$3 = 'primer/borders';
237
+ const messages$3 = ruleMessages$1(ruleName$3, {
238
+ rejected: (value, replacement, propName) => {
239
+ if (propName && propName.includes('radius') && value.includes('borderWidth')) {
240
+ return `Border radius variables can not be used for border widths`
241
+ }
242
+
243
+ if ((propName && propName.includes('width')) || (borderShorthand(propName) && value.includes('borderRadius'))) {
244
+ return `Border width variables can not be used for border radii`
245
+ }
246
+
247
+ if (!replacement) {
248
+ return `Please use a Primer border variable instead of '${value}'. Consult the primer docs for a suitable replacement. https://primer.style/foundations/primitives/size#border`
249
+ }
250
+
251
+ return `Please replace '${value}' with a Primer border variable '${replacement['name']}'. https://primer.style/foundations/primitives/size#border`
252
+ },
253
+ });
254
+
255
+ const variables = primitivesVariables('border');
256
+ const sizes$1 = [];
257
+ const radii = [];
258
+
259
+ // Props that we want to check
260
+ const propList$1 = ['border', 'border-width', 'border-radius'];
261
+ // Values that we want to ignore
262
+ const valueList$1 = ['${'];
263
+
264
+ const borderShorthand = prop =>
265
+ /^border(-(top|right|bottom|left|block-start|block-end|inline-start|inline-end))?$/.test(prop);
266
+
267
+ for (const variable of variables) {
268
+ const name = variable['name'];
269
+
270
+ if (name.includes('borderWidth')) {
271
+ const value = variable['values']
272
+ .pop()
273
+ .replace(/max|\(|\)/g, '')
274
+ .split(',')[0];
275
+ sizes$1.push({
276
+ name,
277
+ values: [value],
278
+ });
279
+ }
280
+
281
+ if (name.includes('borderRadius')) {
282
+ radii.push(variable);
283
+ }
284
+ }
285
+
286
+ /** @type {import('stylelint').Rule} */
287
+ const ruleFunction$1 = (primary, secondaryOptions, context) => {
288
+ return (root, result) => {
289
+ const validOptions = validateOptions$1(result, ruleName$3, {
290
+ actual: primary,
291
+ possible: [true],
292
+ });
293
+
294
+ if (!validOptions) return
295
+
296
+ root.walkDecls(declNode => {
297
+ const {prop, value} = declNode;
298
+
299
+ if (!propList$1.some(borderProp => prop.startsWith(borderProp))) return
300
+ if (/^border(-(top|right|bottom|left|block-start|block-end|inline-start|inline-end))?-color$/.test(prop)) return
301
+ if (valueList$1.some(valueToIgnore => value.includes(valueToIgnore))) return
302
+
303
+ const problems = [];
304
+
305
+ const parsedValue = walkGroups$1(valueParser(value), node => {
306
+ const checkForVariable = (vars, nodeValue) =>
307
+ vars.some(variable =>
308
+ new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(nodeValue),
309
+ );
310
+
311
+ // Only check word types. https://github.com/TrySound/postcss-value-parser#word
312
+ if (node.type !== 'word') {
313
+ return
314
+ }
315
+
316
+ // Exact values to ignore.
317
+ if (
318
+ [
319
+ '*',
320
+ '+',
321
+ '-',
322
+ '/',
323
+ '0',
324
+ 'none',
325
+ 'inherit',
326
+ 'initial',
327
+ 'revert',
328
+ 'revert-layer',
329
+ 'unset',
330
+ 'solid',
331
+ 'dashed',
332
+ 'dotted',
333
+ 'transparent',
334
+ ].includes(node.value)
335
+ ) {
336
+ return
337
+ }
338
+
339
+ const valueUnit = valueParser.unit(node.value);
340
+
341
+ if (valueUnit && (valueUnit.unit === '' || !/^-?[0-9.]+$/.test(valueUnit.number))) {
342
+ return
343
+ }
344
+
345
+ // Skip if the value unit isn't a supported unit.
346
+ if (valueUnit && !['px', 'rem', 'em'].includes(valueUnit.unit)) {
347
+ return
348
+ }
349
+
350
+ // if we're looking at the border property that sets color in shorthand, don't bother checking the color
351
+ if (
352
+ // using border shorthand
353
+ borderShorthand(prop) &&
354
+ // includes a color as a third space-separated value
355
+ value.split(' ').length > 2 &&
356
+ // the color in the third space-separated value includes `node.value`
357
+ value
358
+ .split(' ')
359
+ .slice(2)
360
+ .some(color => color.includes(node.value))
361
+ ) {
362
+ return
363
+ }
364
+
365
+ // If the variable is found in the value, skip it.
366
+ if (prop.includes('width') || borderShorthand(prop)) {
367
+ if (checkForVariable(sizes$1, node.value)) {
368
+ return
369
+ }
370
+ }
371
+
372
+ if (prop.includes('radius')) {
373
+ if (checkForVariable(radii, node.value)) {
374
+ return
375
+ }
376
+ }
377
+
378
+ const replacement = (prop.includes('radius') ? radii : sizes$1).find(variable =>
379
+ variable.values.includes(node.value.replace('-', '')),
380
+ );
381
+ const fixable = replacement && valueUnit && !valueUnit.number.includes('-');
382
+
383
+ if (fixable && context.fix) {
384
+ node.value = node.value.replace(node.value, `var(${replacement['name']})`);
385
+ } else {
386
+ problems.push({
387
+ index: declarationValueIndex(declNode) + node.sourceIndex,
388
+ endIndex: declarationValueIndex(declNode) + node.sourceIndex + node.value.length,
389
+ message: messages$3.rejected(node.value, replacement, prop),
390
+ });
391
+ }
392
+
393
+ return
394
+ });
395
+
396
+ if (context.fix) {
397
+ declNode.value = parsedValue.toString();
398
+ }
399
+
400
+ if (problems.length) {
401
+ for (const err of problems) {
402
+ report$1({
403
+ index: err.index,
404
+ endIndex: err.endIndex,
405
+ message: err.message,
406
+ node: declNode,
407
+ result,
408
+ ruleName: ruleName$3,
409
+ });
410
+ }
411
+ }
412
+ });
413
+ }
414
+ };
415
+
416
+ ruleFunction$1.ruleName = ruleName$3;
417
+ ruleFunction$1.messages = messages$3;
418
+ ruleFunction$1.meta = {
419
+ fixable: true,
420
+ };
421
+
422
+ var borders = createPlugin$1(ruleName$3, ruleFunction$1);
423
+
424
+ const SKIP_VALUE_NODE_TYPES = new Set(['space', 'div']);
425
+ const SKIP_AT_RULE_NAMES = new Set(['each', 'for', 'function', 'mixin']);
426
+
427
+ function declarationValidator(rules, options = {}) {
428
+ const {formatMessage = defaultMessageFormatter, variables, verbose = false} = options;
429
+ const variableReplacements = new TapMap();
430
+ if (variables) {
431
+ for (const [name, {values}] of Object.entries(variables)) {
432
+ for (const value of values) {
433
+ variableReplacements.tap(value, () => []).push(name);
434
+ }
435
+ }
436
+ }
437
+
438
+ const validators = Object.entries(rules)
439
+ .map(([key, rule]) => {
440
+ if (rule === false) {
441
+ return false
442
+ }
443
+ const {name = key, props = name, expects = `a ${name} value`} = rule;
444
+ const replacements = Object.assign({}, rule.replacements, getVariableReplacements(rule.values));
445
+ // console.warn(`replacements for "${key}": ${JSON.stringify(replacements)}`)
446
+ Object.assign(rule, {name, props, expects, replacements});
447
+ return {
448
+ rule,
449
+ matchesProp: anymatch(props),
450
+ validate: Array.isArray(rule.components) ? componentValidator(rule) : valueValidator(rule),
451
+ }
452
+ })
453
+ .filter(Boolean);
454
+
455
+ const validatorsByProp = new TapMap();
456
+ const validatorsByReplacementValue = new Map();
457
+ for (const validator of validators) {
458
+ for (const value of Object.keys(validator.rule.replacements)) {
459
+ validatorsByReplacementValue.set(value, validator);
460
+ }
461
+ }
462
+
463
+ return decl => {
464
+ if (closest(decl, isSkippableAtRule)) {
465
+ if (verbose) {
466
+ // eslint-disable-next-line no-console
467
+ console.warn(`skipping declaration: ${decl.parent.toString()}`);
468
+ }
469
+ // As a general rule, any rule nested in an at-rule is ignored, since
470
+ // @for, @each, @mixin, and @function blocks can use whatever variables
471
+ // they want
472
+ return {valid: true}
473
+ }
474
+ const validator = getPropValidator(decl.prop);
475
+ if (validator) {
476
+ const result = validator.validate(decl);
477
+ result.errors = result.errors.map(formatMessage);
478
+ return result
479
+ } else {
480
+ return {valid: true}
481
+ }
482
+ }
483
+
484
+ function getVariableReplacements(values) {
485
+ const replacements = {};
486
+ const varValues = (Array.isArray(values) ? values : [values]).filter(v => typeof v === 'string' && v.includes('$'));
487
+ const matches = anymatch(varValues);
488
+ for (const [value, aliases] of variableReplacements.entries()) {
489
+ for (const alias of aliases) {
490
+ if (matches(alias)) {
491
+ replacements[value] = alias;
492
+ }
493
+ }
494
+ }
495
+ return replacements
496
+ }
497
+
498
+ function getPropValidator(prop) {
499
+ return validatorsByProp.tap(prop, () => validators.find(v => v.matchesProp(prop)))
500
+ }
501
+
502
+ function valueValidator({expects, values, replacements, singular = false}) {
503
+ const matches = anymatch(values);
504
+ return function validate({prop, value}, nested) {
505
+ if (matches(value)) {
506
+ return {
507
+ valid: true,
508
+ errors: [],
509
+ fixable: false,
510
+ replacement: undefined,
511
+ }
512
+ } else if (replacements[value]) {
513
+ let replacement = value;
514
+ do {
515
+ replacement = replacements[replacement];
516
+ } while (replacements[replacement])
517
+ return {
518
+ valid: false,
519
+ errors: [{expects, prop, value, replacement}],
520
+ fixable: true,
521
+ replacement,
522
+ }
523
+ } else {
524
+ if (nested || singular) {
525
+ return {
526
+ valid: false,
527
+ errors: [{expects, prop, value}],
528
+ fixable: false,
529
+ replacement: undefined,
530
+ }
531
+ }
532
+
533
+ const parsed = valueParser(value);
534
+ const validations = parsed.nodes
535
+ .map((node, index) => Object.assign(node, {index}))
536
+ .filter(node => !SKIP_VALUE_NODE_TYPES.has(node.type))
537
+ .map(node => {
538
+ const validation = validate({prop, value: valueParser.stringify(node)}, true);
539
+ validation.index = node.index;
540
+ return validation
541
+ });
542
+
543
+ const valid = validations.every(v => v.valid);
544
+ if (valid) {
545
+ return {valid, errors: [], fixable: false, replacement: undefined}
546
+ }
547
+
548
+ const fixable = validations.some(v => v.fixable);
549
+ const errors = validations.reduce((list, v) => list.concat(v.errors), []);
550
+
551
+ let replacement = undefined;
552
+ for (const validation of validations) {
553
+ if (fixable && validation.replacement) {
554
+ parsed.nodes[validation.index] = {type: 'word', value: validation.replacement};
555
+ }
556
+ }
557
+
558
+ if (fixable) {
559
+ replacement = valueParser.stringify(parsed);
560
+ }
561
+
562
+ return {
563
+ valid,
564
+ fixable,
565
+ errors,
566
+ replacement,
567
+ }
568
+ }
569
+ }
570
+ }
571
+
572
+ function componentValidator({expects, components, values, replacements}) {
573
+ const matchesCompoundValue = anymatch(values);
574
+ return decl => {
575
+ const {prop, value: compoundValue} = decl;
576
+ const parsed = valueParser(compoundValue);
577
+ if (parsed.nodes.length === 1 && matchesCompoundValue(compoundValue)) {
578
+ return {valid: true, errors: []}
579
+ }
580
+
581
+ const errors = [];
582
+
583
+ let fixable = false;
584
+ let componentIndex = 0;
585
+ for (const [index, node] of Object.entries(parsed.nodes)) {
586
+ if (SKIP_VALUE_NODE_TYPES.has(node.type)) {
587
+ continue
588
+ }
589
+
590
+ const value = valueParser.stringify(node);
591
+
592
+ let componentProp = components[componentIndex++];
593
+ let validator = getPropValidator(componentProp);
594
+ if (validatorsByReplacementValue.has(value)) {
595
+ validator = validatorsByReplacementValue.get(value);
596
+ componentProp = validator.rule.name;
597
+ }
598
+
599
+ const nestedProp = `${componentProp} (in ${prop})`;
600
+ if (validator) {
601
+ const result = validator.validate({prop: nestedProp, value}, true);
602
+ if (result.replacement) {
603
+ parsed.nodes[index] = {
604
+ type: 'word',
605
+ value: result.replacement,
606
+ };
607
+ fixable = true;
608
+ }
609
+ for (const error of result.errors) {
610
+ errors.push(error);
611
+ }
612
+ } else {
613
+ errors.push({expects, prop: nestedProp, value});
614
+ }
615
+ }
616
+
617
+ let replacement = fixable ? valueParser.stringify(parsed) : undefined;
618
+
619
+ // if a compound replacement exists, suggest *that* instead
620
+ if (replacement && replacements[replacement]) {
621
+ do {
622
+ replacement = replacements[replacement];
623
+ } while (replacements[replacement])
624
+ return {
625
+ valid: false,
626
+ errors: [{expects, prop, value: compoundValue, replacement}],
627
+ fixable: true,
628
+ replacement,
629
+ }
630
+ }
631
+
632
+ return {
633
+ valid: errors.length === 0,
634
+ errors,
635
+ fixable,
636
+ replacement,
637
+ }
638
+ }
639
+ }
640
+
641
+ function isSkippableAtRule(node) {
642
+ return node.type === 'atrule' && SKIP_AT_RULE_NAMES.has(node.name)
643
+ }
644
+ }
645
+
646
+ function defaultMessageFormatter(error) {
647
+ const {expects, value, replacement} = error;
648
+ const expected = replacement ? `"${replacement}"` : expects;
649
+ return `Please use ${expected} instead of "${value}"`
650
+ }
651
+
652
+ function closest(node, test) {
653
+ let ancestor = node;
654
+ do {
655
+ if (test(ancestor)) return ancestor
656
+ } while ((ancestor = ancestor.parent))
657
+ }
658
+
659
+ function createVariableRule(ruleName, rules, url) {
660
+ const plugin = stylelint.createPlugin(ruleName, (enabled, options = {}, context) => {
661
+ if (enabled === false) {
662
+ return noop$2
663
+ }
664
+
665
+ let actualRules = rules;
666
+ let overrides = options.rules;
667
+ if (typeof rules === 'function') {
668
+ actualRules = rules({variables: variables$1, options, ruleName});
669
+ } else {
670
+ actualRules = Object.assign({}, rules);
671
+ }
672
+ if (typeof overrides === 'function') {
673
+ delete options.rules;
674
+ overrides = overrides({rules: actualRules, options, ruleName, variables: variables$1});
675
+ }
676
+ if (overrides) {
677
+ Object.assign(actualRules, overrides);
678
+ }
679
+ const validate = declarationValidator(actualRules, {variables: variables$1});
680
+
681
+ // The stylelint docs suggest respecting a "disableFix" rule option that
682
+ // overrides the "global" context.fix (--fix) linting option.
683
+ const {verbose = false, disableFix} = options;
684
+ const fixEnabled = context && context.fix && !disableFix;
685
+ const seen = new WeakMap();
686
+
687
+ return (root, result) => {
688
+ root.walkRules(rule => {
689
+ rule.walkDecls(decl => {
690
+ if (seen.has(decl)) {
691
+ return
692
+ } else {
693
+ seen.set(decl, true);
694
+ }
695
+
696
+ const validated = validate(decl);
697
+ const {valid, fixable, replacement, errors} = validated;
698
+ if (valid) {
699
+ // eslint-disable-next-line no-console
700
+ if (verbose) console.warn(`valid: "${decl.toString()}" in: "${rule.selector}"`);
701
+ return
702
+ } else if (fixEnabled && fixable) {
703
+ // eslint-disable-next-line no-console
704
+ if (verbose) console.warn(` fixed: ${replacement}`);
705
+ decl.value = replacement;
706
+ } else {
707
+ // eslint-disable-next-line no-console
708
+ if (verbose) console.warn(` ${errors.length} error(s)`);
709
+ for (const error of errors) {
710
+ const message = stylelint.utils
711
+ .ruleMessages(ruleName, {
712
+ rejected: m => {
713
+ if (url) {
714
+ return `${m}. See ${url}.`
715
+ }
716
+ return `${m}.`
717
+ },
718
+ })
719
+ .rejected(error);
720
+
721
+ stylelint.utils.report({
722
+ message,
723
+ node: decl,
724
+ result,
725
+ ruleName,
726
+ });
727
+ }
728
+ }
729
+ });
730
+ });
731
+ }
732
+ });
733
+
734
+ Object.assign(plugin, {rules});
735
+
736
+ return plugin
737
+ }
738
+
739
+ function noop$2() {}
740
+
741
+ var boxShadow = createVariableRule(
742
+ 'primer/box-shadow',
743
+ {
744
+ 'box shadow': {
745
+ expects: 'a box-shadow variable',
746
+ props: 'box-shadow',
747
+ values: [
748
+ '$box-shadow*',
749
+ '$*-shadow',
750
+ 'none',
751
+ // Match variables in any of the following formats: --color-shadow-*, --color-*-shadow-*, --color-*-shadow, --shadow-*, *shadow*
752
+ /var\(--color-(.+-)*shadow(-.+)*\)/,
753
+ /var\(--shadow(-.+)*\)/,
754
+ /var\((.+-)*shadow(-.+)*\)/,
755
+ ],
756
+ singular: true,
757
+ },
758
+ },
759
+ 'https://primer.style/css/utilities/box-shadow',
760
+ );
761
+
762
+ const bgVars = [
763
+ '$bg-*',
764
+ '$tooltip-background-color',
765
+ // Match variables in any of the following formats: --color-bg-*, --color-*-bg-*, --color-*-bg, *bgColor*, *fgColor*, *borderColor*, *iconColor*
766
+ /var\(--color-(.+-)*bg(-.+)*\)/,
767
+ /var\(--color-[^)]+\)/,
768
+ /var\((.+-)*bgColor(-.+)*\)/,
769
+ /var\((.+-)*fgColor(-.+)*\)/,
770
+ /var\((.+-)*borderColor(-.+)*\)/,
771
+ /var\((.+-)*iconColor(-.+)*\)/,
772
+ ];
773
+
774
+ var colors = createVariableRule(
775
+ 'primer/colors',
776
+ {
777
+ 'background-color': {
778
+ expects: 'a background color variable',
779
+ values: bgVars.concat('none', 'transparent'),
780
+ },
781
+ background: {
782
+ expects: 'a background color variable',
783
+ values: bgVars.concat('none', 'transparent', 'top', 'right', 'bottom', 'left', 'center', '*px', 'url(*)'),
784
+ },
785
+ 'text color': {
786
+ expects: 'a text color variable',
787
+ props: 'color',
788
+ values: [
789
+ '$text-*',
790
+ '$tooltip-text-color',
791
+ 'inherit',
792
+ // Match variables in any of the following formats: --color-text-*, --color-*-text-*, --color-*-text, *fgColor*, *iconColor*
793
+ /var\(--color-(.+-)*text(-.+)*\)/,
794
+ /var\(--color-(.+-)*fg(-.+)*\)/,
795
+ /var\(--color-[^)]+\)/,
796
+ /var\((.+-)*fgColor(-.+)*\)/,
797
+ /var\((.+-)*iconColor(-.+)*\)/,
798
+ ],
799
+ },
800
+ },
801
+ 'https://primer.style/primitives/colors',
802
+ );
803
+
804
+ const ruleName$2 = 'primer/responsive-widths';
805
+
806
+ const messages$2 = stylelint.utils.ruleMessages(ruleName$2, {
807
+ rejected: value => {
808
+ 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`
809
+ },
810
+ });
811
+
812
+ // 320px is the smallest viewport size that we support
813
+
814
+ const walkGroups = (root, validate) => {
815
+ for (const node of root.nodes) {
816
+ if (node.type === 'function') {
817
+ walkGroups(node, validate);
818
+ } else {
819
+ validate(node);
820
+ }
821
+ }
822
+ return root
823
+ };
824
+
825
+ // eslint-disable-next-line no-unused-vars
826
+ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}, context) => {
827
+ if (!enabled) {
828
+ return noop$1
829
+ }
830
+
831
+ const lintResult = (root, result) => {
832
+ root.walk(decl => {
833
+ // Ignore things inside of breakpoints
834
+ if (decl.type === 'atrule' && decl.name === 'include' && decl.params.includes('breakpoint')) {
835
+ return false
836
+ }
837
+
838
+ if (decl.type !== 'decl' || !decl.prop.match(/^(min-width|width)/)) {
839
+ return noop$1
840
+ }
841
+
842
+ const problems = [];
843
+
844
+ walkGroups(valueParser(decl.value), node => {
845
+ // Only check word types. https://github.com/TrySound/postcss-value-parser#word
846
+ if (node.type !== 'word') {
847
+ return
848
+ }
849
+
850
+ // Exact values to ignore.
851
+ if (['*', '+', '-', '/', '0', 'auto', 'inherit', 'initial'].includes(node.value)) {
852
+ return
853
+ }
854
+
855
+ const valueUnit = valueParser.unit(node.value);
856
+
857
+ switch (valueUnit.unit) {
858
+ case 'px':
859
+ if (parseInt(valueUnit.number) > 320) {
860
+ problems.push({
861
+ index: declarationValueIndex(decl) + node.sourceIndex,
862
+ message: messages$2.rejected(node.value),
863
+ });
864
+ }
865
+ break
866
+ case 'vw':
867
+ if (parseInt(valueUnit.number) > 100) {
868
+ problems.push({
869
+ index: declarationValueIndex(decl) + node.sourceIndex,
870
+ message: messages$2.rejected(node.value),
871
+ });
872
+ }
873
+ break
874
+ }
875
+ });
876
+
877
+ if (problems.length) {
878
+ for (const err of problems) {
879
+ stylelint.utils.report({
880
+ index: err.index,
881
+ message: err.message,
882
+ node: decl,
883
+ result,
884
+ ruleName: ruleName$2,
885
+ });
886
+ }
887
+ }
888
+ });
889
+ };
890
+
891
+ return lintResult
892
+ });
893
+
894
+ function noop$1() {}
895
+
896
+ const {
897
+ createPlugin,
898
+ utils: {report, ruleMessages, validateOptions},
899
+ } = stylelint;
900
+
901
+ const ruleName$1 = 'primer/spacing';
902
+ const messages$1 = ruleMessages(ruleName$1, {
903
+ rejected: (value, replacement) => {
904
+ if (!replacement) {
905
+ return `Please use a primer size variable instead of '${value}'. Consult the primer docs for a suitable replacement. https://primer.style/foundations/primitives/size`
906
+ }
907
+
908
+ return `Please replace '${value}' with size variable '${replacement['name']}'. https://primer.style/foundations/primitives/size`
909
+ },
910
+ });
911
+
912
+ // Props that we want to check
913
+ const propList = ['padding', 'margin', 'top', 'right', 'bottom', 'left'];
914
+ // Values that we want to ignore
915
+ const valueList = ['${'];
916
+
917
+ const sizes = primitivesVariables('spacing');
918
+
919
+ // Add +-1px to each value
920
+ for (const size of sizes) {
921
+ const values = size['values'];
922
+ const px = parseInt(values.find(value => value.includes('px')));
923
+ if (![2, 6].includes(px)) {
924
+ values.push(`${px + 1}px`);
925
+ values.push(`${px - 1}px`);
926
+ }
927
+ }
928
+
929
+ /** @type {import('stylelint').Rule} */
930
+ const ruleFunction = (primary, secondaryOptions, context) => {
931
+ return (root, result) => {
932
+ const validOptions = validateOptions(result, ruleName$1, {
933
+ actual: primary,
934
+ possible: [true],
935
+ });
936
+
937
+ if (!validOptions) return
938
+
939
+ root.walkDecls(declNode => {
940
+ const {prop, value} = declNode;
941
+
942
+ if (!propList.some(spacingProp => prop.startsWith(spacingProp))) return
943
+ if (valueList.some(valueToIgnore => value.includes(valueToIgnore))) return
944
+
945
+ const problems = [];
946
+
947
+ const parsedValue = walkGroups$1(valueParser(value), node => {
948
+ // Only check word types. https://github.com/TrySound/postcss-value-parser#word
949
+ if (node.type !== 'word') {
950
+ return
951
+ }
952
+
953
+ // Exact values to ignore.
954
+ if (['*', '+', '-', '/', '0', 'auto', 'inherit', 'initial'].includes(node.value)) {
955
+ return
956
+ }
957
+
958
+ const valueUnit = valueParser.unit(node.value);
959
+
960
+ if (valueUnit && (valueUnit.unit === '' || !/^-?[0-9.]+$/.test(valueUnit.number))) {
961
+ return
962
+ }
963
+
964
+ // Skip if the value unit isn't a supported unit.
965
+ if (valueUnit && !['px', 'rem', 'em'].includes(valueUnit.unit)) {
966
+ return
967
+ }
968
+
969
+ // If the variable is found in the value, skip it.
970
+ if (
971
+ sizes.some(variable =>
972
+ new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(node.value),
973
+ )
974
+ ) {
975
+ return
976
+ }
977
+
978
+ const replacement = sizes.find(variable => variable.values.includes(node.value.replace('-', '')));
979
+ const fixable = replacement && valueUnit && !valueUnit.number.includes('-');
980
+
981
+ if (fixable && context.fix) {
982
+ node.value = node.value.replace(node.value, `var(${replacement['name']})`);
983
+ } else {
984
+ problems.push({
985
+ index: declarationValueIndex(declNode) + node.sourceIndex,
986
+ endIndex: declarationValueIndex(declNode) + node.sourceIndex + node.value.length,
987
+ message: messages$1.rejected(node.value, replacement),
988
+ });
989
+ }
990
+
991
+ return
992
+ });
993
+
994
+ if (context.fix) {
995
+ declNode.value = parsedValue.toString();
996
+ }
997
+
998
+ if (problems.length) {
999
+ for (const err of problems) {
1000
+ report({
1001
+ index: err.index,
1002
+ endIndex: err.endIndex,
1003
+ message: err.message,
1004
+ node: declNode,
1005
+ result,
1006
+ ruleName: ruleName$1,
1007
+ });
1008
+ }
1009
+ }
1010
+ });
1011
+ }
1012
+ };
1013
+
1014
+ ruleFunction.ruleName = ruleName$1;
1015
+ ruleFunction.messages = messages$1;
1016
+ ruleFunction.meta = {
1017
+ fixable: true,
1018
+ };
1019
+
1020
+ var spacing = createPlugin(ruleName$1, ruleFunction);
1021
+
1022
+ var typography = createVariableRule(
1023
+ 'primer/typography',
1024
+ {
1025
+ 'font-size': {
1026
+ expects: 'a font-size variable',
1027
+ values: ['$body-font-size', '$h{000,00,0,1,2,3,4,5,6}-size', '$font-size-*', '1', '1em', 'inherit'],
1028
+ },
1029
+ 'font-weight': {
1030
+ props: 'font-weight',
1031
+ values: ['$font-weight-*', 'inherit'],
1032
+ replacements: {
1033
+ bold: '$font-weight-bold',
1034
+ normal: '$font-weight-normal',
1035
+ },
1036
+ },
1037
+ 'line-height': {
1038
+ props: 'line-height',
1039
+ values: ['$body-line-height', '$lh-*', '0', '1', '1em', 'inherit'],
1040
+ },
1041
+ },
1042
+ 'https://primer.style/css/utilities/typography',
1043
+ );
1044
+
1045
+ const ruleName = 'primer/no-display-colors';
1046
+ const messages = stylelint.utils.ruleMessages(ruleName, {
1047
+ rejected: varName => `${varName} is in alpha and should be used with caution with approval from the Primer team`,
1048
+ });
1049
+
1050
+ // Match CSS variable references (e.g var(--display-blue-fgColor))
1051
+ // eslint-disable-next-line no-useless-escape
1052
+ const variableReferenceRegex = /var\(([^\),]+)(,.*)?\)/g;
1053
+
1054
+ var noDisplayColors = stylelint.createPlugin(ruleName, (enabled, options = {}) => {
1055
+ if (!enabled) {
1056
+ return noop
1057
+ }
1058
+
1059
+ const {verbose = false} = options;
1060
+ // eslint-disable-next-line no-console
1061
+ const log = verbose ? (...args) => console.warn(...args) : noop;
1062
+
1063
+ // Keep track of declarations we've already seen
1064
+ const seen = new WeakMap();
1065
+
1066
+ return (root, result) => {
1067
+ root.walkRules(rule => {
1068
+ rule.walkDecls(decl => {
1069
+ if (seen.has(decl)) {
1070
+ return
1071
+ } else {
1072
+ seen.set(decl, true);
1073
+ }
1074
+
1075
+ for (const [, variableName] of matchAll(decl.value, variableReferenceRegex)) {
1076
+ log(`Found variable reference ${variableName}`);
1077
+ if (variableName.match(/^--display-.*/)) {
1078
+ stylelint.utils.report({
1079
+ message: messages.rejected(variableName),
1080
+ node: decl,
1081
+ result,
1082
+ ruleName,
1083
+ });
1084
+ }
1085
+ }
1086
+ });
1087
+ });
1088
+ }
1089
+ });
1090
+
1091
+ function noop() {}
1092
+
1093
+ 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)));
1094
+
1095
+ /** @type {import('stylelint').Config} */
1096
+ var index = {
1097
+ extends: ['stylelint-config-standard'],
1098
+ ignoreFiles: ['**/*.js', '**/*.cjs', '**/*.ts', '**/*.mjs'],
1099
+ reportNeedlessDisables: true,
1100
+ plugins: [
1101
+ 'stylelint-value-no-unknown-custom-properties',
1102
+ 'stylelint-no-unsupported-browser-features',
1103
+ 'stylelint-order',
1104
+ borders,
1105
+ boxShadow,
1106
+ colors,
1107
+ responsiveWidths,
1108
+ spacing,
1109
+ typography,
1110
+ noDisplayColors,
1111
+ ],
1112
+ rules: {
1113
+ 'alpha-value-notation': 'number',
1114
+ 'at-rule-disallowed-list': ['extend'],
1115
+ 'at-rule-no-unknown': null,
1116
+ 'block-no-empty': true,
1117
+ 'color-function-notation': null,
1118
+ 'color-named': 'never',
1119
+ 'color-no-invalid-hex': true,
1120
+ 'comment-no-empty': null,
1121
+ 'csstools/value-no-unknown-custom-properties': [
1122
+ true,
1123
+ {
1124
+ severity: 'warning',
1125
+ importFrom: [
1126
+ '@primer/primitives/dist/css/functional/size/size-coarse.css',
1127
+ '@primer/primitives/dist/css/functional/size/border.css',
1128
+ '@primer/primitives/dist/css/functional/size/size.css',
1129
+ '@primer/primitives/dist/css/functional/size/size-fine.css',
1130
+ '@primer/primitives/dist/css/functional/size/breakpoints.css',
1131
+ '@primer/primitives/dist/css/functional/size/viewport.css',
1132
+ '@primer/primitives/dist/css/functional/motion/motion.css',
1133
+ '@primer/primitives/dist/css/functional/themes/light.css',
1134
+ '@primer/primitives/dist/css/functional/typography/typography.css',
1135
+ '@primer/primitives/dist/css/base/size/size.css',
1136
+ '@primer/primitives/dist/css/base/typography/typography.css',
1137
+ ].map(path => require$1.resolve(path)),
1138
+ },
1139
+ ],
1140
+ 'custom-property-pattern': null,
1141
+ 'declaration-block-no-duplicate-properties': [true, {ignore: ['consecutive-duplicates']}],
1142
+ 'declaration-block-no-redundant-longhand-properties': null,
1143
+ 'declaration-block-no-shorthand-property-overrides': true,
1144
+ 'declaration-property-value-disallowed-list': {
1145
+ '/^transition/': ['/all/'],
1146
+ '/^background/': ['http:', 'https:'],
1147
+ '/^border/': ['none'],
1148
+ '/.+/': ['initial'],
1149
+ },
1150
+ 'function-calc-no-unspaced-operator': true,
1151
+ 'function-linear-gradient-no-nonstandard-direction': true,
1152
+ 'function-no-unknown': null,
1153
+ 'keyframes-name-pattern': null,
1154
+ 'max-nesting-depth': 3,
1155
+ 'media-feature-name-no-unknown': null,
1156
+ 'media-feature-name-no-vendor-prefix': null,
1157
+ 'no-descending-specificity': null,
1158
+ 'no-duplicate-selectors': true,
1159
+ 'no-invalid-position-at-import-rule': [true, {ignoreAtRules: ['use']}],
1160
+ 'number-max-precision': null,
1161
+ 'order/properties-order': propertyOrder,
1162
+ 'plugin/no-unsupported-browser-features': [
1163
+ true,
1164
+ {
1165
+ severity: 'warning',
1166
+ ignore: ['css-nesting'],
1167
+ ignorePartialSupport: true,
1168
+ browsers,
1169
+ },
1170
+ ],
1171
+ 'primer/borders': true,
1172
+ 'primer/box-shadow': true,
1173
+ 'primer/colors': true,
1174
+ 'primer/responsive-widths': true,
1175
+ 'primer/spacing': true,
1176
+ 'primer/typography': true,
1177
+ 'primer/no-display-colors': true,
1178
+ 'property-no-unknown': [
1179
+ true,
1180
+ {
1181
+ ignoreProperties: ['@container', 'container-type'],
1182
+ },
1183
+ ],
1184
+ 'selector-class-pattern': null,
1185
+ 'selector-max-compound-selectors': 3,
1186
+ 'selector-max-id': 0,
1187
+ 'selector-max-specificity': '0,4,0',
1188
+ 'selector-max-type': 0,
1189
+ 'selector-no-qualifying-type': true,
1190
+ 'selector-pseudo-element-no-unknown': true,
1191
+ 'string-no-newline': true,
1192
+ 'unit-no-unknown': true,
1193
+ 'value-keyword-case': null,
1194
+ 'selector-not-notation': null,
1195
+ 'import-notation': ['string'],
1196
+ 'annotation-no-unknown': null,
1197
+ 'keyframe-selector-notation': ['percentage-unless-within-keyword-only-block'],
1198
+ 'media-query-no-invalid': null,
1199
+ 'media-feature-range-notation': ['prefix'],
1200
+ },
1201
+ overrides: [
1202
+ {
1203
+ files: ['**/*.scss'],
1204
+ customSyntax: 'postcss-scss',
1205
+ plugins: ['stylelint-scss'],
1206
+ rules: {
1207
+ 'scss/at-extend-no-missing-placeholder': true,
1208
+ 'scss/at-rule-no-unknown': true,
1209
+ 'scss/declaration-nested-properties-no-divided-groups': true,
1210
+ 'scss/dollar-variable-no-missing-interpolation': true,
1211
+ 'scss/function-quote-no-quoted-strings-inside': true,
1212
+ 'scss/function-unquote-no-unquoted-strings-inside': true,
1213
+ 'scss/no-duplicate-mixins': true,
1214
+ 'scss/selector-no-redundant-nesting-selector': true,
1215
+ },
1216
+ },
1217
+ {
1218
+ files: ['**/*.tsx'],
1219
+ customSyntax: 'postcss-styled-syntax',
1220
+ rules: {
1221
+ 'order/properties-order': null,
1222
+ 'rule-empty-line-before': null,
1223
+ 'declaration-empty-line-before': null,
1224
+ 'comment-empty-line-before': null,
1225
+ 'length-zero-no-unit': null,
1226
+ 'selector-max-type': null,
1227
+ 'primer/colors': null,
1228
+ 'primer/typography': null,
1229
+ 'primer/box-shadow': null,
1230
+ },
1231
+ },
1232
+ {
1233
+ files: ['**/*.pcss'],
1234
+ rules: {
1235
+ 'media-feature-range-notation': null,
1236
+ 'import-notation': null,
1237
+ 'custom-property-pattern': null,
1238
+ 'selector-class-pattern': null,
1239
+ 'keyframes-name-pattern': null,
1240
+ 'no-descending-specificity': null,
1241
+ 'declaration-block-no-redundant-longhand-properties': null,
1242
+ 'color-function-notation': 'legacy',
1243
+ 'selector-nested-pattern': '^&\\s?\\W',
1244
+ 'at-rule-no-unknown': [
1245
+ true,
1246
+ {
1247
+ ignoreAtRules: ['mixin', 'define-mixin'],
1248
+ },
1249
+ ],
1250
+ },
1251
+ },
1252
+ {
1253
+ files: ['**/*.module.css'],
1254
+ plugins: ['stylelint-css-modules-no-global-scoped-selector'],
1255
+ rules: {
1256
+ 'property-no-unknown': [
1257
+ true,
1258
+ {
1259
+ ignoreProperties: ['composes', 'compose-with'],
1260
+ ignoreSelectors: [':export', /^:import/],
1261
+ },
1262
+ ],
1263
+ 'selector-pseudo-class-no-unknown': [
1264
+ true,
1265
+ {ignorePseudoClasses: ['export', 'import', 'global', 'local', 'external']},
1266
+ ],
1267
+ 'selector-type-no-unknown': [
1268
+ true,
1269
+ {
1270
+ ignoreTypes: ['from'],
1271
+ },
1272
+ ],
1273
+ 'function-no-unknown': [
1274
+ true,
1275
+ {
1276
+ ignoreFunctions: ['global'],
1277
+ },
1278
+ ],
1279
+ 'css-modules/no-global-scoped-selector': true,
1280
+ // temporarily disabiling Primer plugins while we work on upgrades https://github.com/github/primer/issues/3165
1281
+ 'primer/typography': null,
1282
+ 'primer/box-shadow': null,
1283
+ },
1284
+ },
1285
+ ],
1286
+ };
1287
+
1288
+ module.exports = index;