@primer/stylelint-config 13.0.0-rc.ead57f2 → 13.0.0-rc.f7b4bdd

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$1 = 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,243 @@ 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
+ }
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
+
187
424
  const SKIP_VALUE_NODE_TYPES = new Set(['space', 'div']);
188
425
  const SKIP_AT_RULE_NAMES = new Set(['each', 'for', 'function', 'mixin']);
189
426
 
@@ -428,18 +665,18 @@ function createVariableRule(ruleName, rules, url) {
428
665
  let actualRules = rules;
429
666
  let overrides = options.rules;
430
667
  if (typeof rules === 'function') {
431
- actualRules = rules({variables, options, ruleName});
668
+ actualRules = rules({variables: variables$1, options, ruleName});
432
669
  } else {
433
670
  actualRules = Object.assign({}, rules);
434
671
  }
435
672
  if (typeof overrides === 'function') {
436
673
  delete options.rules;
437
- overrides = overrides({rules: actualRules, options, ruleName, variables});
674
+ overrides = overrides({rules: actualRules, options, ruleName, variables: variables$1});
438
675
  }
439
676
  if (overrides) {
440
677
  Object.assign(actualRules, overrides);
441
678
  }
442
- const validate = declarationValidator(actualRules, {variables});
679
+ const validate = declarationValidator(actualRules, {variables: variables$1});
443
680
 
444
681
  // The stylelint docs suggest respecting a "disableFix" rule option that
445
682
  // overrides the "global" context.fix (--fix) linting option.
@@ -501,69 +738,6 @@ function createVariableRule(ruleName, rules, url) {
501
738
 
502
739
  function noop$2() {}
503
740
 
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
741
  var boxShadow = createVariableRule(
568
742
  'primer/box-shadow',
569
743
  {
@@ -637,10 +811,10 @@ const messages$2 = stylelint.utils.ruleMessages(ruleName$2, {
637
811
 
638
812
  // 320px is the smallest viewport size that we support
639
813
 
640
- const walkGroups$1 = (root, validate) => {
814
+ const walkGroups = (root, validate) => {
641
815
  for (const node of root.nodes) {
642
816
  if (node.type === 'function') {
643
- walkGroups$1(node, validate);
817
+ walkGroups(node, validate);
644
818
  } else {
645
819
  validate(node);
646
820
  }
@@ -667,7 +841,7 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
667
841
 
668
842
  const problems = [];
669
843
 
670
- walkGroups$1(valueParser(decl.value), node => {
844
+ walkGroups(valueParser(decl.value), node => {
671
845
  // Only check word types. https://github.com/TrySound/postcss-value-parser#word
672
846
  if (node.type !== 'word') {
673
847
  return
@@ -719,57 +893,11 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
719
893
 
720
894
  function noop$1() {}
721
895
 
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
896
  const {
758
897
  createPlugin,
759
898
  utils: {report, ruleMessages, validateOptions},
760
899
  } = stylelint;
761
900
 
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
901
  const ruleName$1 = 'primer/spacing';
774
902
  const messages$1 = ruleMessages(ruleName$1, {
775
903
  rejected: (value, replacement) => {
@@ -781,20 +909,26 @@ const messages$1 = ruleMessages(ruleName$1, {
781
909
  },
782
910
  });
783
911
 
784
- const meta = {
785
- fixable: true,
786
- };
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 = ['${'];
787
916
 
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 = ['${'];
917
+ const sizes = primitivesVariables('spacing');
795
918
 
796
- const sizes = await primitivesVariables('size');
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
+ }
797
928
 
929
+ /** @type {import('stylelint').Rule} */
930
+ const ruleFunction = (primary, secondaryOptions, context) => {
931
+ return (root, result) => {
798
932
  const validOptions = validateOptions(result, ruleName$1, {
799
933
  actual: primary,
800
934
  possible: [true],
@@ -810,7 +944,7 @@ const ruleFunction = (primary, secondaryOptions, context) => {
810
944
 
811
945
  const problems = [];
812
946
 
813
- const parsedValue = walkGroups(valueParser(value), node => {
947
+ const parsedValue = walkGroups$1(valueParser(value), node => {
814
948
  // Only check word types. https://github.com/TrySound/postcss-value-parser#word
815
949
  if (node.type !== 'word') {
816
950
  return
@@ -879,7 +1013,9 @@ const ruleFunction = (primary, secondaryOptions, context) => {
879
1013
 
880
1014
  ruleFunction.ruleName = ruleName$1;
881
1015
  ruleFunction.messages = messages$1;
882
- ruleFunction.meta = meta;
1016
+ ruleFunction.meta = {
1017
+ fixable: true,
1018
+ };
883
1019
 
884
1020
  var spacing = createPlugin(ruleName$1, ruleFunction);
885
1021
 
@@ -1089,7 +1225,6 @@ var index = {
1089
1225
  'length-zero-no-unit': null,
1090
1226
  'selector-max-type': null,
1091
1227
  'primer/colors': null,
1092
- 'primer/borders': null,
1093
1228
  'primer/typography': null,
1094
1229
  'primer/box-shadow': null,
1095
1230
  },
@@ -1143,7 +1278,6 @@ var index = {
1143
1278
  ],
1144
1279
  'css-modules/no-global-scoped-selector': true,
1145
1280
  // temporarily disabiling Primer plugins while we work on upgrades https://github.com/github/primer/issues/3165
1146
- 'primer/borders': null,
1147
1281
  'primer/typography': null,
1148
1282
  'primer/box-shadow': null,
1149
1283
  },