@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.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  import browsers from '@github/browserslist-config';
2
2
  import stylelint from 'stylelint';
3
- import anymatch from 'anymatch';
4
- import valueParser from 'postcss-value-parser';
5
- import TapMap from 'tap-map';
6
- import variables from '@primer/css/dist/variables.json' assert { type: 'json' };
7
3
  import declarationValueIndex from 'stylelint/lib/utils/declarationValueIndex.cjs';
4
+ import valueParser from 'postcss-value-parser';
8
5
  import { createRequire } from 'node:module';
6
+ import anymatch from 'anymatch';
7
+ import TapMap from 'tap-map';
8
+ import variables$1 from '@primer/css/dist/variables.json' assert { type: 'json' };
9
9
  import matchAll from 'string.prototype.matchall';
10
10
 
11
11
  var propertyOrder = [
@@ -181,6 +181,243 @@ var propertyOrder = [
181
181
  'animation-direction',
182
182
  ];
183
183
 
184
+ const require$1 = createRequire(import.meta.url);
185
+
186
+ function primitivesVariables(type) {
187
+ const variables = [];
188
+
189
+ const files = [];
190
+ switch (type) {
191
+ case 'spacing':
192
+ files.push('base/size/size.json');
193
+ break
194
+ case 'border':
195
+ files.push('functional/size/border.json');
196
+ break
197
+ }
198
+
199
+ for (const file of files) {
200
+ // eslint-disable-next-line import/no-dynamic-require
201
+ const data = require$1(`@primer/primitives/dist/styleLint/${file}`);
202
+
203
+ for (const key of Object.keys(data)) {
204
+ const size = data[key];
205
+ const values = typeof size['value'] === 'string' ? [size['value']] : size['value'];
206
+
207
+ variables.push({
208
+ name: `--${size['name']}`,
209
+ values,
210
+ });
211
+ }
212
+ }
213
+
214
+ return variables
215
+ }
216
+
217
+ function walkGroups$1(root, validate) {
218
+ for (const node of root.nodes) {
219
+ if (node.type === 'function') {
220
+ walkGroups$1(node, validate);
221
+ } else {
222
+ validate(node);
223
+ }
224
+ }
225
+ return root
226
+ }
227
+
228
+ const {
229
+ createPlugin: createPlugin$1,
230
+ utils: {report: report$1, ruleMessages: ruleMessages$1, validateOptions: validateOptions$1},
231
+ } = stylelint;
232
+
233
+ const ruleName$3 = 'primer/borders';
234
+ const messages$3 = ruleMessages$1(ruleName$3, {
235
+ rejected: (value, replacement, propName) => {
236
+ if (propName && propName.includes('radius') && value.includes('borderWidth')) {
237
+ return `Border radius variables can not be used for border widths`
238
+ }
239
+
240
+ if ((propName && propName.includes('width')) || (borderShorthand(propName) && value.includes('borderRadius'))) {
241
+ return `Border width variables can not be used for border radii`
242
+ }
243
+
244
+ if (!replacement) {
245
+ 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`
246
+ }
247
+
248
+ return `Please replace '${value}' with a Primer border variable '${replacement['name']}'. https://primer.style/foundations/primitives/size#border`
249
+ },
250
+ });
251
+
252
+ const variables = primitivesVariables('border');
253
+ const sizes$1 = [];
254
+ const radii = [];
255
+
256
+ // Props that we want to check
257
+ const propList$1 = ['border', 'border-width', 'border-radius'];
258
+ // Values that we want to ignore
259
+ const valueList$1 = ['${'];
260
+
261
+ const borderShorthand = prop =>
262
+ /^border(-(top|right|bottom|left|block-start|block-end|inline-start|inline-end))?$/.test(prop);
263
+
264
+ for (const variable of variables) {
265
+ const name = variable['name'];
266
+
267
+ if (name.includes('borderWidth')) {
268
+ const value = variable['values']
269
+ .pop()
270
+ .replace(/max|\(|\)/g, '')
271
+ .split(',')[0];
272
+ sizes$1.push({
273
+ name,
274
+ values: [value],
275
+ });
276
+ }
277
+
278
+ if (name.includes('borderRadius')) {
279
+ radii.push(variable);
280
+ }
281
+ }
282
+
283
+ /** @type {import('stylelint').Rule} */
284
+ const ruleFunction$1 = (primary, secondaryOptions, context) => {
285
+ return (root, result) => {
286
+ const validOptions = validateOptions$1(result, ruleName$3, {
287
+ actual: primary,
288
+ possible: [true],
289
+ });
290
+
291
+ if (!validOptions) return
292
+
293
+ root.walkDecls(declNode => {
294
+ const {prop, value} = declNode;
295
+
296
+ if (!propList$1.some(borderProp => prop.startsWith(borderProp))) return
297
+ if (/^border(-(top|right|bottom|left|block-start|block-end|inline-start|inline-end))?-color$/.test(prop)) return
298
+ if (valueList$1.some(valueToIgnore => value.includes(valueToIgnore))) return
299
+
300
+ const problems = [];
301
+
302
+ const parsedValue = walkGroups$1(valueParser(value), node => {
303
+ const checkForVariable = (vars, nodeValue) =>
304
+ vars.some(variable =>
305
+ new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(nodeValue),
306
+ );
307
+
308
+ // Only check word types. https://github.com/TrySound/postcss-value-parser#word
309
+ if (node.type !== 'word') {
310
+ return
311
+ }
312
+
313
+ // Exact values to ignore.
314
+ if (
315
+ [
316
+ '*',
317
+ '+',
318
+ '-',
319
+ '/',
320
+ '0',
321
+ 'none',
322
+ 'inherit',
323
+ 'initial',
324
+ 'revert',
325
+ 'revert-layer',
326
+ 'unset',
327
+ 'solid',
328
+ 'dashed',
329
+ 'dotted',
330
+ 'transparent',
331
+ ].includes(node.value)
332
+ ) {
333
+ return
334
+ }
335
+
336
+ const valueUnit = valueParser.unit(node.value);
337
+
338
+ if (valueUnit && (valueUnit.unit === '' || !/^-?[0-9.]+$/.test(valueUnit.number))) {
339
+ return
340
+ }
341
+
342
+ // Skip if the value unit isn't a supported unit.
343
+ if (valueUnit && !['px', 'rem', 'em'].includes(valueUnit.unit)) {
344
+ return
345
+ }
346
+
347
+ // if we're looking at the border property that sets color in shorthand, don't bother checking the color
348
+ if (
349
+ // using border shorthand
350
+ borderShorthand(prop) &&
351
+ // includes a color as a third space-separated value
352
+ value.split(' ').length > 2 &&
353
+ // the color in the third space-separated value includes `node.value`
354
+ value
355
+ .split(' ')
356
+ .slice(2)
357
+ .some(color => color.includes(node.value))
358
+ ) {
359
+ return
360
+ }
361
+
362
+ // If the variable is found in the value, skip it.
363
+ if (prop.includes('width') || borderShorthand(prop)) {
364
+ if (checkForVariable(sizes$1, node.value)) {
365
+ return
366
+ }
367
+ }
368
+
369
+ if (prop.includes('radius')) {
370
+ if (checkForVariable(radii, node.value)) {
371
+ return
372
+ }
373
+ }
374
+
375
+ const replacement = (prop.includes('radius') ? radii : sizes$1).find(variable =>
376
+ variable.values.includes(node.value.replace('-', '')),
377
+ );
378
+ const fixable = replacement && valueUnit && !valueUnit.number.includes('-');
379
+
380
+ if (fixable && context.fix) {
381
+ node.value = node.value.replace(node.value, `var(${replacement['name']})`);
382
+ } else {
383
+ problems.push({
384
+ index: declarationValueIndex(declNode) + node.sourceIndex,
385
+ endIndex: declarationValueIndex(declNode) + node.sourceIndex + node.value.length,
386
+ message: messages$3.rejected(node.value, replacement, prop),
387
+ });
388
+ }
389
+
390
+ return
391
+ });
392
+
393
+ if (context.fix) {
394
+ declNode.value = parsedValue.toString();
395
+ }
396
+
397
+ if (problems.length) {
398
+ for (const err of problems) {
399
+ report$1({
400
+ index: err.index,
401
+ endIndex: err.endIndex,
402
+ message: err.message,
403
+ node: declNode,
404
+ result,
405
+ ruleName: ruleName$3,
406
+ });
407
+ }
408
+ }
409
+ });
410
+ }
411
+ };
412
+
413
+ ruleFunction$1.ruleName = ruleName$3;
414
+ ruleFunction$1.messages = messages$3;
415
+ ruleFunction$1.meta = {
416
+ fixable: true,
417
+ };
418
+
419
+ var borders = createPlugin$1(ruleName$3, ruleFunction$1);
420
+
184
421
  const SKIP_VALUE_NODE_TYPES = new Set(['space', 'div']);
185
422
  const SKIP_AT_RULE_NAMES = new Set(['each', 'for', 'function', 'mixin']);
186
423
 
@@ -425,18 +662,18 @@ function createVariableRule(ruleName, rules, url) {
425
662
  let actualRules = rules;
426
663
  let overrides = options.rules;
427
664
  if (typeof rules === 'function') {
428
- actualRules = rules({variables, options, ruleName});
665
+ actualRules = rules({variables: variables$1, options, ruleName});
429
666
  } else {
430
667
  actualRules = Object.assign({}, rules);
431
668
  }
432
669
  if (typeof overrides === 'function') {
433
670
  delete options.rules;
434
- overrides = overrides({rules: actualRules, options, ruleName, variables});
671
+ overrides = overrides({rules: actualRules, options, ruleName, variables: variables$1});
435
672
  }
436
673
  if (overrides) {
437
674
  Object.assign(actualRules, overrides);
438
675
  }
439
- const validate = declarationValidator(actualRules, {variables});
676
+ const validate = declarationValidator(actualRules, {variables: variables$1});
440
677
 
441
678
  // The stylelint docs suggest respecting a "disableFix" rule option that
442
679
  // overrides the "global" context.fix (--fix) linting option.
@@ -498,69 +735,6 @@ function createVariableRule(ruleName, rules, url) {
498
735
 
499
736
  function noop$2() {}
500
737
 
501
- var borders = createVariableRule(
502
- 'primer/borders',
503
- {
504
- border: {
505
- expects: 'a border variable',
506
- props: 'border{,-top,-right,-bottom,-left}',
507
- values: ['$border', 'none', '0'],
508
- components: ['border-width', 'border-style', 'border-color'],
509
- replacements: {
510
- // because shorthand border properties ¯\_(ツ)_/¯
511
- '$border-width $border-style $border-gray': '$border',
512
- '$border-width $border-gray $border-style': '$border',
513
- '$border-style $border-width $border-gray': '$border',
514
- '$border-style $border-gray $border-width': '$border',
515
- '$border-gray $border-width $border-style': '$border',
516
- '$border-gray $border-style $border-width': '$border',
517
- '$border-width $border-style $border-color': '$border',
518
- '$border-width $border-color $border-style': '$border',
519
- '$border-style $border-width $border-color': '$border',
520
- '$border-style $border-color $border-width': '$border',
521
- '$border-color $border-width $border-style': '$border',
522
- '$border-color $border-style $border-width': '$border',
523
- },
524
- },
525
- 'border color': {
526
- expects: 'a border color variable',
527
- props: 'border{,-top,-right,-bottom,-left}-color',
528
- values: [
529
- '$border-*',
530
- 'transparent',
531
- 'currentColor',
532
- // Match variables in any of the following formats: --color-border-*, --color-*-border-*, --color-*-border, --borderColor-, *borderColor*
533
- /var\(--color-(.+-)*border(-.+)*\)/,
534
- /var\(--color-[^)]+\)/,
535
- /var\(--borderColor-[^)]+\)/,
536
- /var\((.+-)*borderColor(-.+)*\)/,
537
- ],
538
- replacements: {
539
- '$border-gray': '$border-color',
540
- },
541
- },
542
- 'border style': {
543
- expects: 'a border style variable',
544
- props: 'border{,-top,-right,-bottom,-left}-style',
545
- values: ['$border-style', 'none'],
546
- },
547
- 'border width': {
548
- expects: 'a border width variable',
549
- props: 'border{,-top,-right,-bottom,-left}-width',
550
- values: ['$border-width*', '0'],
551
- },
552
- 'border radius': {
553
- expects: 'a border radius variable',
554
- props: 'border{,-{top,bottom}-{left,right}}-radius',
555
- values: ['$border-radius', '0', '50%', 'inherit'],
556
- replacements: {
557
- '100%': '50%',
558
- },
559
- },
560
- },
561
- 'https://primer.style/css/utilities/borders',
562
- );
563
-
564
738
  var boxShadow = createVariableRule(
565
739
  'primer/box-shadow',
566
740
  {
@@ -634,10 +808,10 @@ const messages$2 = stylelint.utils.ruleMessages(ruleName$2, {
634
808
 
635
809
  // 320px is the smallest viewport size that we support
636
810
 
637
- const walkGroups$1 = (root, validate) => {
811
+ const walkGroups = (root, validate) => {
638
812
  for (const node of root.nodes) {
639
813
  if (node.type === 'function') {
640
- walkGroups$1(node, validate);
814
+ walkGroups(node, validate);
641
815
  } else {
642
816
  validate(node);
643
817
  }
@@ -664,7 +838,7 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
664
838
 
665
839
  const problems = [];
666
840
 
667
- walkGroups$1(valueParser(decl.value), node => {
841
+ walkGroups(valueParser(decl.value), node => {
668
842
  // Only check word types. https://github.com/TrySound/postcss-value-parser#word
669
843
  if (node.type !== 'word') {
670
844
  return
@@ -716,57 +890,11 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
716
890
 
717
891
  function noop$1() {}
718
892
 
719
- const require$1 = createRequire(import.meta.url);
720
-
721
- async function primitivesVariables(type) {
722
- const variables = [];
723
-
724
- const files = [];
725
- switch (type) {
726
- case 'size':
727
- files.push('base/size/size.json');
728
- break
729
- }
730
-
731
- for (const file of files) {
732
- // eslint-disable-next-line import/no-dynamic-require
733
- const data = require$1(`@primer/primitives/dist/styleLint/${file}`);
734
-
735
- for (const key of Object.keys(data)) {
736
- const size = data[key];
737
- const values = size['value'];
738
- const intValue = parseInt(size['original']['value']);
739
- if (![2, 6].includes(intValue)) {
740
- values.push(`${intValue + 1}px`);
741
- values.push(`${intValue - 1}px`);
742
- }
743
-
744
- variables.push({
745
- name: `--${size['name']}`,
746
- values,
747
- });
748
- }
749
- }
750
-
751
- return variables
752
- }
753
-
754
893
  const {
755
894
  createPlugin,
756
895
  utils: {report, ruleMessages, validateOptions},
757
896
  } = stylelint;
758
897
 
759
- const walkGroups = (root, validate) => {
760
- for (const node of root.nodes) {
761
- if (node.type === 'function') {
762
- walkGroups(node, validate);
763
- } else {
764
- validate(node);
765
- }
766
- }
767
- return root
768
- };
769
-
770
898
  const ruleName$1 = 'primer/spacing';
771
899
  const messages$1 = ruleMessages(ruleName$1, {
772
900
  rejected: (value, replacement) => {
@@ -778,20 +906,26 @@ const messages$1 = ruleMessages(ruleName$1, {
778
906
  },
779
907
  });
780
908
 
781
- const meta = {
782
- fixable: true,
783
- };
909
+ // Props that we want to check
910
+ const propList = ['padding', 'margin', 'top', 'right', 'bottom', 'left'];
911
+ // Values that we want to ignore
912
+ const valueList = ['${'];
784
913
 
785
- /** @type {import('stylelint').Rule} */
786
- const ruleFunction = (primary, secondaryOptions, context) => {
787
- return async (root, result) => {
788
- // Props that we want to check
789
- const propList = ['padding', 'margin', 'top', 'right', 'bottom', 'left'];
790
- // Values that we want to ignore
791
- const valueList = ['${'];
914
+ const sizes = primitivesVariables('spacing');
792
915
 
793
- const sizes = await primitivesVariables('size');
916
+ // Add +-1px to each value
917
+ for (const size of sizes) {
918
+ const values = size['values'];
919
+ const px = parseInt(values.find(value => value.includes('px')));
920
+ if (![2, 6].includes(px)) {
921
+ values.push(`${px + 1}px`);
922
+ values.push(`${px - 1}px`);
923
+ }
924
+ }
794
925
 
926
+ /** @type {import('stylelint').Rule} */
927
+ const ruleFunction = (primary, secondaryOptions, context) => {
928
+ return (root, result) => {
795
929
  const validOptions = validateOptions(result, ruleName$1, {
796
930
  actual: primary,
797
931
  possible: [true],
@@ -807,7 +941,7 @@ const ruleFunction = (primary, secondaryOptions, context) => {
807
941
 
808
942
  const problems = [];
809
943
 
810
- const parsedValue = walkGroups(valueParser(value), node => {
944
+ const parsedValue = walkGroups$1(valueParser(value), node => {
811
945
  // Only check word types. https://github.com/TrySound/postcss-value-parser#word
812
946
  if (node.type !== 'word') {
813
947
  return
@@ -876,7 +1010,9 @@ const ruleFunction = (primary, secondaryOptions, context) => {
876
1010
 
877
1011
  ruleFunction.ruleName = ruleName$1;
878
1012
  ruleFunction.messages = messages$1;
879
- ruleFunction.meta = meta;
1013
+ ruleFunction.meta = {
1014
+ fixable: true,
1015
+ };
880
1016
 
881
1017
  var spacing = createPlugin(ruleName$1, ruleFunction);
882
1018
 
@@ -1086,7 +1222,6 @@ var index = {
1086
1222
  'length-zero-no-unit': null,
1087
1223
  'selector-max-type': null,
1088
1224
  'primer/colors': null,
1089
- 'primer/borders': null,
1090
1225
  'primer/typography': null,
1091
1226
  'primer/box-shadow': null,
1092
1227
  },
@@ -1140,7 +1275,6 @@ var index = {
1140
1275
  ],
1141
1276
  'css-modules/no-global-scoped-selector': true,
1142
1277
  // temporarily disabiling Primer plugins while we work on upgrades https://github.com/github/primer/issues/3165
1143
- 'primer/borders': null,
1144
1278
  'primer/typography': null,
1145
1279
  'primer/box-shadow': null,
1146
1280
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primer/stylelint-config",
3
- "version": "13.0.0-rc.ead57f2",
3
+ "version": "13.0.0-rc.f7b4bdd",
4
4
  "description": "Sharable stylelint config used by GitHub's CSS",
5
5
  "author": "GitHub, Inc.",
6
6
  "license": "MIT",
@@ -62,27 +62,29 @@
62
62
  },
63
63
  "overrides": {
64
64
  "stylelint-css-modules-no-global-scoped-selector": {
65
- "stylelint": "$stylelint"
65
+ "stylelint": "$stylelint",
66
+ "postcss-modules-local-by-default": "^4.0.0"
66
67
  }
67
68
  },
68
69
  "prettier": "@github/prettier-config",
69
70
  "devDependencies": {
70
71
  "@changesets/changelog-github": "^0.5.0",
71
- "@changesets/cli": "2.27.1",
72
+ "@changesets/cli": "2.27.7",
72
73
  "@github/prettier-config": "^0.0.6",
73
- "@rollup/plugin-commonjs": "^25.0.7",
74
+ "@rollup/plugin-commonjs": "^26.0.1",
74
75
  "@rollup/plugin-json": "^6.1.0",
75
76
  "@rollup/plugin-node-resolve": "^15.2.3",
76
- "@typescript-eslint/parser": "^7.7.0",
77
+ "@typescript-eslint/parser": "^8.0.1",
77
78
  "dedent": "^1.5.3",
78
- "eslint": "^8.0.1",
79
- "eslint-plugin-github": "^4.10.2",
79
+ "eslint": "^8.57.0",
80
+ "eslint-plugin-github": "^5.0.1",
81
+ "eslint-plugin-import": "^2.29.1",
80
82
  "eslint-plugin-jest": "^28.2.0",
81
83
  "eslint-plugin-prettier": "^5.1.3",
82
84
  "jest": "^29.7.0",
83
85
  "jest-preset-stylelint": "^7.0.0",
84
86
  "prettier": "^3.2.5",
85
- "rimraf": "^5.0.5",
87
+ "rimraf": "^6.0.1",
86
88
  "rollup": "^4.14.3"
87
89
  },
88
90
  "jest": {