@primer/stylelint-config 13.0.0-rc.fd47ce2 → 13.0.0

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 CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  var browsers = require('@github/browserslist-config');
4
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
5
  var declarationValueIndex = require('stylelint/lib/utils/declarationValueIndex.cjs');
6
+ var valueParser = require('postcss-value-parser');
10
7
  var node_module = require('node:module');
8
+ var anymatch = require('anymatch');
9
+ var TapMap = require('tap-map');
10
+ var variables$3 = require('@primer/css/dist/variables.json');
11
11
  var matchAll = require('string.prototype.matchall');
12
12
 
13
13
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
@@ -184,6 +184,346 @@ var propertyOrder = [
184
184
  'animation-direction',
185
185
  ];
186
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
+ case 'typography':
201
+ files.push('base/typography/typography.json');
202
+ files.push('functional/typography/typography.json');
203
+ break
204
+ case 'box-shadow':
205
+ files.push('functional/themes/light.json');
206
+ files.push('functional/size/border.json');
207
+ break
208
+ }
209
+
210
+ for (const file of files) {
211
+ // eslint-disable-next-line import/no-dynamic-require
212
+ const data = require$2(`@primer/primitives/dist/styleLint/${file}`);
213
+
214
+ for (const key of Object.keys(data)) {
215
+ const size = data[key];
216
+ const values = typeof size['value'] === 'string' ? [size['value']] : size['value'];
217
+
218
+ variables.push({
219
+ name: `--${size['name']}`,
220
+ values,
221
+ });
222
+ }
223
+ }
224
+
225
+ return variables
226
+ }
227
+
228
+ function walkGroups$1(root, validate) {
229
+ for (const node of root.nodes) {
230
+ if (node.type === 'function') {
231
+ walkGroups$1(node, validate);
232
+ } else {
233
+ validate(node);
234
+ }
235
+ }
236
+ return root
237
+ }
238
+
239
+ const {
240
+ createPlugin: createPlugin$3,
241
+ utils: {report: report$3, ruleMessages: ruleMessages$3, validateOptions: validateOptions$3},
242
+ } = stylelint;
243
+
244
+ const ruleName$5 = 'primer/borders';
245
+ const messages$5 = ruleMessages$3(ruleName$5, {
246
+ rejected: (value, replacement, propName) => {
247
+ if (propName && propName.includes('radius') && value.includes('borderWidth')) {
248
+ return `Border radius variables can not be used for border widths`
249
+ }
250
+
251
+ if ((propName && propName.includes('width')) || (borderShorthand(propName) && value.includes('borderRadius'))) {
252
+ return `Border width variables can not be used for border radii`
253
+ }
254
+
255
+ if (!replacement) {
256
+ 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`
257
+ }
258
+
259
+ return `Please replace '${value}' with a Primer border variable '${replacement['name']}'. https://primer.style/foundations/primitives/size#border`
260
+ },
261
+ });
262
+
263
+ const variables$2 = primitivesVariables('border');
264
+ const sizes$1 = [];
265
+ const radii = [];
266
+
267
+ // Props that we want to check
268
+ const propList$2 = ['border', 'border-width', 'border-radius'];
269
+ // Values that we want to ignore
270
+ const valueList$1 = ['${'];
271
+
272
+ const borderShorthand = prop =>
273
+ /^border(-(top|right|bottom|left|block-start|block-end|inline-start|inline-end))?$/.test(prop);
274
+
275
+ for (const variable of variables$2) {
276
+ const name = variable['name'];
277
+
278
+ if (name.includes('borderWidth')) {
279
+ const value = variable['values']
280
+ .pop()
281
+ .replace(/max|\(|\)/g, '')
282
+ .split(',')[0];
283
+ sizes$1.push({
284
+ name,
285
+ values: [value],
286
+ });
287
+ }
288
+
289
+ if (name.includes('borderRadius')) {
290
+ radii.push(variable);
291
+ }
292
+ }
293
+
294
+ /** @type {import('stylelint').Rule} */
295
+ const ruleFunction$3 = (primary, secondaryOptions, context) => {
296
+ return (root, result) => {
297
+ const validOptions = validateOptions$3(result, ruleName$5, {
298
+ actual: primary,
299
+ possible: [true],
300
+ });
301
+
302
+ if (!validOptions) return
303
+
304
+ root.walkDecls(declNode => {
305
+ const {prop, value} = declNode;
306
+
307
+ if (!propList$2.some(borderProp => prop.startsWith(borderProp))) return
308
+ if (/^border(-(top|right|bottom|left|block-start|block-end|inline-start|inline-end))?-color$/.test(prop)) return
309
+ if (valueList$1.some(valueToIgnore => value.includes(valueToIgnore))) return
310
+
311
+ const problems = [];
312
+
313
+ const parsedValue = walkGroups$1(valueParser(value), node => {
314
+ const checkForVariable = (vars, nodeValue) =>
315
+ vars.some(variable =>
316
+ new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(nodeValue),
317
+ );
318
+
319
+ // Only check word types. https://github.com/TrySound/postcss-value-parser#word
320
+ if (node.type !== 'word') {
321
+ return
322
+ }
323
+
324
+ // Exact values to ignore.
325
+ if (
326
+ [
327
+ '*',
328
+ '+',
329
+ '-',
330
+ '/',
331
+ '0',
332
+ 'none',
333
+ 'inherit',
334
+ 'initial',
335
+ 'revert',
336
+ 'revert-layer',
337
+ 'unset',
338
+ 'solid',
339
+ 'dashed',
340
+ 'dotted',
341
+ 'transparent',
342
+ ].includes(node.value)
343
+ ) {
344
+ return
345
+ }
346
+
347
+ const valueUnit = valueParser.unit(node.value);
348
+
349
+ if (valueUnit && (valueUnit.unit === '' || !/^-?[0-9.]+$/.test(valueUnit.number))) {
350
+ return
351
+ }
352
+
353
+ // Skip if the value unit isn't a supported unit.
354
+ if (valueUnit && !['px', 'rem', 'em'].includes(valueUnit.unit)) {
355
+ return
356
+ }
357
+
358
+ // if we're looking at the border property that sets color in shorthand, don't bother checking the color
359
+ if (
360
+ // using border shorthand
361
+ borderShorthand(prop) &&
362
+ // includes a color as a third space-separated value
363
+ value.split(' ').length > 2 &&
364
+ // the color in the third space-separated value includes `node.value`
365
+ value
366
+ .split(' ')
367
+ .slice(2)
368
+ .some(color => color.includes(node.value))
369
+ ) {
370
+ return
371
+ }
372
+
373
+ // If the variable is found in the value, skip it.
374
+ if (prop.includes('width') || borderShorthand(prop)) {
375
+ if (checkForVariable(sizes$1, node.value)) {
376
+ return
377
+ }
378
+ }
379
+
380
+ if (prop.includes('radius')) {
381
+ if (checkForVariable(radii, node.value)) {
382
+ return
383
+ }
384
+ }
385
+
386
+ const replacement = (prop.includes('radius') ? radii : sizes$1).find(variable =>
387
+ variable.values.includes(node.value.replace('-', '')),
388
+ );
389
+ const fixable = replacement && valueUnit && !valueUnit.number.includes('-');
390
+
391
+ if (fixable && context.fix) {
392
+ node.value = node.value.replace(node.value, `var(${replacement['name']})`);
393
+ } else {
394
+ problems.push({
395
+ index: declarationValueIndex(declNode) + node.sourceIndex,
396
+ endIndex: declarationValueIndex(declNode) + node.sourceIndex + node.value.length,
397
+ message: messages$5.rejected(node.value, replacement, prop),
398
+ });
399
+ }
400
+
401
+ return
402
+ });
403
+
404
+ if (context.fix) {
405
+ declNode.value = parsedValue.toString();
406
+ }
407
+
408
+ if (problems.length) {
409
+ for (const err of problems) {
410
+ report$3({
411
+ index: err.index,
412
+ endIndex: err.endIndex,
413
+ message: err.message,
414
+ node: declNode,
415
+ result,
416
+ ruleName: ruleName$5,
417
+ });
418
+ }
419
+ }
420
+ });
421
+ }
422
+ };
423
+
424
+ ruleFunction$3.ruleName = ruleName$5;
425
+ ruleFunction$3.messages = messages$5;
426
+ ruleFunction$3.meta = {
427
+ fixable: true,
428
+ };
429
+
430
+ var borders = createPlugin$3(ruleName$5, ruleFunction$3);
431
+
432
+ const {
433
+ createPlugin: createPlugin$2,
434
+ utils: {report: report$2, ruleMessages: ruleMessages$2, validateOptions: validateOptions$2},
435
+ } = stylelint;
436
+
437
+ const ruleName$4 = 'primer/box-shadow';
438
+ const messages$4 = ruleMessages$2(ruleName$4, {
439
+ rejected: (value, replacement) => {
440
+ if (!replacement) {
441
+ return `Please use a Primer box-shadow variable instead of '${value}'. Consult the primer docs for a suitable replacement. https://primer.style/foundations/primitives/color#shadow or https://primer.style/foundations/primitives/size#border-size`
442
+ }
443
+
444
+ return `Please replace '${value}' with a Primer box-shadow variable '${replacement['name']}'. https://primer.style/foundations/primitives/color#shadow or https://primer.style/foundations/primitives/size#border-size`
445
+ },
446
+ });
447
+
448
+ const variables$1 = primitivesVariables('box-shadow');
449
+ const shadows = [];
450
+
451
+ for (const variable of variables$1) {
452
+ const name = variable['name'];
453
+
454
+ // TODO: Decide if this is safe. Someday we might have variables that
455
+ // have 'shadow' in the name but aren't full box-shadows.
456
+ if (name.includes('shadow') || name.includes('boxShadow')) {
457
+ shadows.push(variable);
458
+ }
459
+ }
460
+
461
+ /** @type {import('stylelint').Rule} */
462
+ const ruleFunction$2 = (primary, secondaryOptions, context) => {
463
+ return (root, result) => {
464
+ const validOptions = validateOptions$2(result, ruleName$4, {
465
+ actual: primary,
466
+ possible: [true],
467
+ });
468
+ const validValues = shadows;
469
+
470
+ if (!validOptions) return
471
+
472
+ root.walkDecls(declNode => {
473
+ const {prop, value} = declNode;
474
+
475
+ if (prop !== 'box-shadow') return
476
+
477
+ if (value === 'none') return
478
+
479
+ const problems = [];
480
+
481
+ const checkForVariable = (vars, nodeValue) => {
482
+ return vars.some(variable =>
483
+ new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(nodeValue),
484
+ )
485
+ };
486
+
487
+ if (checkForVariable(validValues, value)) {
488
+ return
489
+ }
490
+
491
+ const replacement = validValues.find(variable => variable.values.includes(value));
492
+
493
+ if (replacement && context.fix) {
494
+ declNode.value = value.replace(value, `var(${replacement['name']})`);
495
+ } else {
496
+ problems.push({
497
+ index: declarationValueIndex(declNode),
498
+ endIndex: declarationValueIndex(declNode) + value.length,
499
+ message: messages$4.rejected(value, replacement),
500
+ });
501
+ }
502
+
503
+ if (problems.length) {
504
+ for (const err of problems) {
505
+ report$2({
506
+ index: err.index,
507
+ endIndex: err.endIndex,
508
+ message: err.message,
509
+ node: declNode,
510
+ result,
511
+ ruleName: ruleName$4,
512
+ });
513
+ }
514
+ }
515
+ });
516
+ }
517
+ };
518
+
519
+ ruleFunction$2.ruleName = ruleName$4;
520
+ ruleFunction$2.messages = messages$4;
521
+ ruleFunction$2.meta = {
522
+ fixable: true,
523
+ };
524
+
525
+ var boxShadow = createPlugin$2(ruleName$4, ruleFunction$2);
526
+
187
527
  const SKIP_VALUE_NODE_TYPES = new Set(['space', 'div']);
188
528
  const SKIP_AT_RULE_NAMES = new Set(['each', 'for', 'function', 'mixin']);
189
529
 
@@ -428,18 +768,18 @@ function createVariableRule(ruleName, rules, url) {
428
768
  let actualRules = rules;
429
769
  let overrides = options.rules;
430
770
  if (typeof rules === 'function') {
431
- actualRules = rules({variables, options, ruleName});
771
+ actualRules = rules({variables: variables$3, options, ruleName});
432
772
  } else {
433
773
  actualRules = Object.assign({}, rules);
434
774
  }
435
775
  if (typeof overrides === 'function') {
436
776
  delete options.rules;
437
- overrides = overrides({rules: actualRules, options, ruleName, variables});
777
+ overrides = overrides({rules: actualRules, options, ruleName, variables: variables$3});
438
778
  }
439
779
  if (overrides) {
440
780
  Object.assign(actualRules, overrides);
441
781
  }
442
- const validate = declarationValidator(actualRules, {variables});
782
+ const validate = declarationValidator(actualRules, {variables: variables$3});
443
783
 
444
784
  // The stylelint docs suggest respecting a "disableFix" rule option that
445
785
  // overrides the "global" context.fix (--fix) linting option.
@@ -473,10 +813,9 @@ function createVariableRule(ruleName, rules, url) {
473
813
  const message = stylelint.utils
474
814
  .ruleMessages(ruleName, {
475
815
  rejected: m => {
476
- if (url) {
816
+ {
477
817
  return `${m}. See ${url}.`
478
818
  }
479
- return `${m}.`
480
819
  },
481
820
  })
482
821
  .rejected(error);
@@ -501,90 +840,6 @@ function createVariableRule(ruleName, rules, url) {
501
840
 
502
841
  function noop$2() {}
503
842
 
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
843
  const bgVars = [
589
844
  '$bg-*',
590
845
  '$tooltip-background-color',
@@ -627,9 +882,9 @@ var colors = createVariableRule(
627
882
  'https://primer.style/primitives/colors',
628
883
  );
629
884
 
630
- const ruleName$2 = 'primer/responsive-widths';
885
+ const ruleName$3 = 'primer/responsive-widths';
631
886
 
632
- const messages$2 = stylelint.utils.ruleMessages(ruleName$2, {
887
+ const messages$3 = stylelint.utils.ruleMessages(ruleName$3, {
633
888
  rejected: value => {
634
889
  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
890
  },
@@ -637,10 +892,10 @@ const messages$2 = stylelint.utils.ruleMessages(ruleName$2, {
637
892
 
638
893
  // 320px is the smallest viewport size that we support
639
894
 
640
- const walkGroups$1 = (root, validate) => {
895
+ const walkGroups = (root, validate) => {
641
896
  for (const node of root.nodes) {
642
897
  if (node.type === 'function') {
643
- walkGroups$1(node, validate);
898
+ walkGroups(node, validate);
644
899
  } else {
645
900
  validate(node);
646
901
  }
@@ -649,7 +904,7 @@ const walkGroups$1 = (root, validate) => {
649
904
  };
650
905
 
651
906
  // eslint-disable-next-line no-unused-vars
652
- var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}, context) => {
907
+ var responsiveWidths = stylelint.createPlugin(ruleName$3, (enabled, options = {}, context) => {
653
908
  if (!enabled) {
654
909
  return noop$1
655
910
  }
@@ -667,7 +922,7 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
667
922
 
668
923
  const problems = [];
669
924
 
670
- walkGroups$1(valueParser(decl.value), node => {
925
+ walkGroups(valueParser(decl.value), node => {
671
926
  // Only check word types. https://github.com/TrySound/postcss-value-parser#word
672
927
  if (node.type !== 'word') {
673
928
  return
@@ -685,7 +940,7 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
685
940
  if (parseInt(valueUnit.number) > 320) {
686
941
  problems.push({
687
942
  index: declarationValueIndex(decl) + node.sourceIndex,
688
- message: messages$2.rejected(node.value),
943
+ message: messages$3.rejected(node.value),
689
944
  });
690
945
  }
691
946
  break
@@ -693,7 +948,7 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
693
948
  if (parseInt(valueUnit.number) > 100) {
694
949
  problems.push({
695
950
  index: declarationValueIndex(decl) + node.sourceIndex,
696
- message: messages$2.rejected(node.value),
951
+ message: messages$3.rejected(node.value),
697
952
  });
698
953
  }
699
954
  break
@@ -707,7 +962,7 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
707
962
  message: err.message,
708
963
  node: decl,
709
964
  result,
710
- ruleName: ruleName$2,
965
+ ruleName: ruleName$3,
711
966
  });
712
967
  }
713
968
  }
@@ -719,59 +974,13 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
719
974
 
720
975
  function noop$1() {}
721
976
 
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
977
  const {
758
- createPlugin,
759
- utils: {report, ruleMessages, validateOptions},
978
+ createPlugin: createPlugin$1,
979
+ utils: {report: report$1, ruleMessages: ruleMessages$1, validateOptions: validateOptions$1},
760
980
  } = stylelint;
761
981
 
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, {
982
+ const ruleName$2 = 'primer/spacing';
983
+ const messages$2 = ruleMessages$1(ruleName$2, {
775
984
  rejected: (value, replacement) => {
776
985
  if (!replacement) {
777
986
  return `Please use a primer size variable instead of '${value}'. Consult the primer docs for a suitable replacement. https://primer.style/foundations/primitives/size`
@@ -781,21 +990,27 @@ const messages$1 = ruleMessages(ruleName$1, {
781
990
  },
782
991
  });
783
992
 
784
- const meta = {
785
- fixable: true,
786
- };
993
+ // Props that we want to check
994
+ const propList$1 = ['padding', 'margin', 'top', 'right', 'bottom', 'left'];
995
+ // Values that we want to ignore
996
+ const valueList = ['${'];
787
997
 
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 = ['${'];
998
+ const sizes = primitivesVariables('spacing');
795
999
 
796
- const sizes = await primitivesVariables('size');
1000
+ // Add +-1px to each value
1001
+ for (const size of sizes) {
1002
+ const values = size['values'];
1003
+ const px = parseInt(values.find(value => value.includes('px')));
1004
+ if (![2, 6].includes(px)) {
1005
+ values.push(`${px + 1}px`);
1006
+ values.push(`${px - 1}px`);
1007
+ }
1008
+ }
797
1009
 
798
- const validOptions = validateOptions(result, ruleName$1, {
1010
+ /** @type {import('stylelint').Rule} */
1011
+ const ruleFunction$1 = (primary, secondaryOptions, context) => {
1012
+ return (root, result) => {
1013
+ const validOptions = validateOptions$1(result, ruleName$2, {
799
1014
  actual: primary,
800
1015
  possible: [true],
801
1016
  });
@@ -805,12 +1020,12 @@ const ruleFunction = (primary, secondaryOptions, context) => {
805
1020
  root.walkDecls(declNode => {
806
1021
  const {prop, value} = declNode;
807
1022
 
808
- if (!propList.some(spacingProp => prop.startsWith(spacingProp))) return
1023
+ if (!propList$1.some(spacingProp => prop.startsWith(spacingProp))) return
809
1024
  if (valueList.some(valueToIgnore => value.includes(valueToIgnore))) return
810
1025
 
811
1026
  const problems = [];
812
1027
 
813
- const parsedValue = walkGroups(valueParser(value), node => {
1028
+ const parsedValue = walkGroups$1(valueParser(value), node => {
814
1029
  // Only check word types. https://github.com/TrySound/postcss-value-parser#word
815
1030
  if (node.type !== 'word') {
816
1031
  return
@@ -850,7 +1065,7 @@ const ruleFunction = (primary, secondaryOptions, context) => {
850
1065
  problems.push({
851
1066
  index: declarationValueIndex(declNode) + node.sourceIndex,
852
1067
  endIndex: declarationValueIndex(declNode) + node.sourceIndex + node.value.length,
853
- message: messages$1.rejected(node.value, replacement),
1068
+ message: messages$2.rejected(node.value, replacement),
854
1069
  });
855
1070
  }
856
1071
 
@@ -861,6 +1076,189 @@ const ruleFunction = (primary, secondaryOptions, context) => {
861
1076
  declNode.value = parsedValue.toString();
862
1077
  }
863
1078
 
1079
+ if (problems.length) {
1080
+ for (const err of problems) {
1081
+ report$1({
1082
+ index: err.index,
1083
+ endIndex: err.endIndex,
1084
+ message: err.message,
1085
+ node: declNode,
1086
+ result,
1087
+ ruleName: ruleName$2,
1088
+ });
1089
+ }
1090
+ }
1091
+ });
1092
+ }
1093
+ };
1094
+
1095
+ ruleFunction$1.ruleName = ruleName$2;
1096
+ ruleFunction$1.messages = messages$2;
1097
+ ruleFunction$1.meta = {
1098
+ fixable: true,
1099
+ };
1100
+
1101
+ var spacing = createPlugin$1(ruleName$2, ruleFunction$1);
1102
+
1103
+ const {
1104
+ createPlugin,
1105
+ utils: {report, ruleMessages, validateOptions},
1106
+ } = stylelint;
1107
+
1108
+ const ruleName$1 = 'primer/typography';
1109
+ const messages$1 = ruleMessages(ruleName$1, {
1110
+ rejected: (value, replacement) => {
1111
+ // no possible replacement
1112
+ if (!replacement) {
1113
+ return `Please use a Primer typography variable instead of '${value}'. Consult the primer docs for a suitable replacement. https://primer.style/foundations/primitives/typography`
1114
+ }
1115
+
1116
+ // multiple possible replacements
1117
+ if (replacement.length) {
1118
+ return `Please use one of the following Primer typography variables instead of '${value}': ${replacement.map(replacementObj => `'${replacementObj.name}'`).join(', ')}. https://primer.style/foundations/primitives/typography`
1119
+ }
1120
+
1121
+ // one possible replacement
1122
+ return `Please replace '${value}' with Primer typography variable '${replacement['name']}'. https://primer.style/foundations/primitives/typography`
1123
+ },
1124
+ });
1125
+
1126
+ const fontWeightKeywordMap = {
1127
+ normal: 400,
1128
+ bold: 600,
1129
+ bolder: 600,
1130
+ lighter: 300,
1131
+ };
1132
+ const getClosestFontWeight = (goalWeightNumber, fontWeightsTokens) => {
1133
+ return fontWeightsTokens.reduce((prev, curr) =>
1134
+ Math.abs(curr.values - goalWeightNumber) < Math.abs(prev.values - goalWeightNumber) ? curr : prev,
1135
+ ).values
1136
+ };
1137
+
1138
+ const variables = primitivesVariables('typography');
1139
+ const fontSizes = [];
1140
+ const fontWeights = [];
1141
+ const lineHeights = [];
1142
+ const fontStacks = [];
1143
+ const fontShorthands = [];
1144
+
1145
+ // Props that we want to check for typography variables
1146
+ const propList = ['font-size', 'font-weight', 'line-height', 'font-family', 'font'];
1147
+
1148
+ for (const variable of variables) {
1149
+ const name = variable['name'];
1150
+
1151
+ if (name.includes('size')) {
1152
+ fontSizes.push(variable);
1153
+ }
1154
+
1155
+ if (name.includes('weight')) {
1156
+ fontWeights.push(variable);
1157
+ }
1158
+
1159
+ if (name.includes('lineHeight')) {
1160
+ lineHeights.push(variable);
1161
+ }
1162
+
1163
+ if (name.includes('fontStack')) {
1164
+ fontStacks.push(variable);
1165
+ }
1166
+
1167
+ if (name.includes('shorthand')) {
1168
+ fontShorthands.push(variable);
1169
+ }
1170
+ }
1171
+
1172
+ /** @type {import('stylelint').Rule} */
1173
+ const ruleFunction = (primary, secondaryOptions, context) => {
1174
+ return (root, result) => {
1175
+ const validOptions = validateOptions(result, ruleName$1, {
1176
+ actual: primary,
1177
+ possible: [true],
1178
+ });
1179
+ let validValues = [];
1180
+
1181
+ if (!validOptions) return
1182
+
1183
+ root.walkDecls(declNode => {
1184
+ const {prop, value} = declNode;
1185
+
1186
+ if (!propList.some(typographyProp => prop.startsWith(typographyProp))) return
1187
+
1188
+ const problems = [];
1189
+
1190
+ const checkForVariable = (vars, nodeValue) =>
1191
+ vars.some(variable =>
1192
+ new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(nodeValue),
1193
+ );
1194
+
1195
+ // Exact values to ignore.
1196
+ if (value === 'inherit') {
1197
+ return
1198
+ }
1199
+
1200
+ switch (prop) {
1201
+ case 'font-size':
1202
+ validValues = fontSizes;
1203
+ break
1204
+ case 'font-weight':
1205
+ validValues = fontWeights;
1206
+ break
1207
+ case 'line-height':
1208
+ validValues = lineHeights;
1209
+ break
1210
+ case 'font-family':
1211
+ validValues = fontStacks;
1212
+ break
1213
+ case 'font':
1214
+ validValues = fontShorthands;
1215
+ break
1216
+ default:
1217
+ validValues = [];
1218
+ }
1219
+
1220
+ if (checkForVariable(validValues, value)) {
1221
+ return
1222
+ }
1223
+
1224
+ const getReplacements = () => {
1225
+ const replacementTokens = validValues.filter(variable => {
1226
+ if (!(variable.values instanceof Array)) {
1227
+ let nodeValue = value;
1228
+
1229
+ if (prop === 'font-weight') {
1230
+ nodeValue = getClosestFontWeight(fontWeightKeywordMap[value] || value, fontWeights);
1231
+ }
1232
+
1233
+ return variable.values.toString() === nodeValue.toString()
1234
+ }
1235
+
1236
+ return variable.values.includes(value.replace('-', ''))
1237
+ });
1238
+
1239
+ if (!replacementTokens.length) {
1240
+ return
1241
+ }
1242
+
1243
+ if (replacementTokens.length > 1) {
1244
+ return replacementTokens
1245
+ }
1246
+
1247
+ return replacementTokens[0]
1248
+ };
1249
+ const replacement = getReplacements();
1250
+ const fixable = replacement && !replacement.length;
1251
+
1252
+ if (fixable && context.fix) {
1253
+ declNode.value = value.replace(value, `var(${replacement['name']})`);
1254
+ } else {
1255
+ problems.push({
1256
+ index: declarationValueIndex(declNode),
1257
+ endIndex: declarationValueIndex(declNode) + value.length,
1258
+ message: messages$1.rejected(value, replacement, prop),
1259
+ });
1260
+ }
1261
+
864
1262
  if (problems.length) {
865
1263
  for (const err of problems) {
866
1264
  report({
@@ -879,32 +1277,11 @@ const ruleFunction = (primary, secondaryOptions, context) => {
879
1277
 
880
1278
  ruleFunction.ruleName = ruleName$1;
881
1279
  ruleFunction.messages = messages$1;
882
- ruleFunction.meta = meta;
883
-
884
- var spacing = createPlugin(ruleName$1, ruleFunction);
1280
+ ruleFunction.meta = {
1281
+ fixable: true,
1282
+ };
885
1283
 
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
- );
1284
+ var typography = createPlugin(ruleName$1, ruleFunction);
908
1285
 
909
1286
  const ruleName = 'primer/no-display-colors';
910
1287
  const messages = stylelint.utils.ruleMessages(ruleName, {
@@ -1089,9 +1466,6 @@ var index = {
1089
1466
  'length-zero-no-unit': null,
1090
1467
  'selector-max-type': null,
1091
1468
  'primer/colors': null,
1092
- 'primer/borders': null,
1093
- 'primer/typography': null,
1094
- 'primer/box-shadow': null,
1095
1469
  },
1096
1470
  },
1097
1471
  {
@@ -1116,7 +1490,6 @@ var index = {
1116
1490
  },
1117
1491
  {
1118
1492
  files: ['**/*.module.css'],
1119
- plugins: ['stylelint-css-modules-no-global-scoped-selector'],
1120
1493
  rules: {
1121
1494
  'property-no-unknown': [
1122
1495
  true,
@@ -1141,11 +1514,6 @@ var index = {
1141
1514
  ignoreFunctions: ['global'],
1142
1515
  },
1143
1516
  ],
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
1517
  },
1150
1518
  },
1151
1519
  ],