@primer/stylelint-config 13.0.0-rc.ee7c4cb → 13.0.0-rc.f49bf6d

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
@@ -7,7 +7,7 @@ var valueParser = require('postcss-value-parser');
7
7
  var node_module = require('node:module');
8
8
  var anymatch = require('anymatch');
9
9
  var TapMap = require('tap-map');
10
- var variables$1 = require('@primer/css/dist/variables.json');
10
+ var variables$2 = 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;
@@ -197,6 +197,10 @@ function primitivesVariables(type) {
197
197
  case 'border':
198
198
  files.push('functional/size/border.json');
199
199
  break
200
+ case 'typography':
201
+ files.push('base/typography/typography.json');
202
+ files.push('functional/typography/typography.json');
203
+ break
200
204
  }
201
205
 
202
206
  for (const file of files) {
@@ -229,12 +233,12 @@ function walkGroups$1(root, validate) {
229
233
  }
230
234
 
231
235
  const {
232
- createPlugin: createPlugin$1,
233
- utils: {report: report$1, ruleMessages: ruleMessages$1, validateOptions: validateOptions$1},
236
+ createPlugin: createPlugin$2,
237
+ utils: {report: report$2, ruleMessages: ruleMessages$2, validateOptions: validateOptions$2},
234
238
  } = stylelint;
235
239
 
236
- const ruleName$3 = 'primer/borders';
237
- const messages$3 = ruleMessages$1(ruleName$3, {
240
+ const ruleName$4 = 'primer/borders';
241
+ const messages$4 = ruleMessages$2(ruleName$4, {
238
242
  rejected: (value, replacement, propName) => {
239
243
  if (propName && propName.includes('radius') && value.includes('borderWidth')) {
240
244
  return `Border radius variables can not be used for border widths`
@@ -252,19 +256,19 @@ const messages$3 = ruleMessages$1(ruleName$3, {
252
256
  },
253
257
  });
254
258
 
255
- const variables = primitivesVariables('border');
259
+ const variables$1 = primitivesVariables('border');
256
260
  const sizes$1 = [];
257
261
  const radii = [];
258
262
 
259
263
  // Props that we want to check
260
- const propList$1 = ['border', 'border-width', 'border-radius'];
264
+ const propList$2 = ['border', 'border-width', 'border-radius'];
261
265
  // Values that we want to ignore
262
266
  const valueList$1 = ['${'];
263
267
 
264
268
  const borderShorthand = prop =>
265
269
  /^border(-(top|right|bottom|left|block-start|block-end|inline-start|inline-end))?$/.test(prop);
266
270
 
267
- for (const variable of variables) {
271
+ for (const variable of variables$1) {
268
272
  const name = variable['name'];
269
273
 
270
274
  if (name.includes('borderWidth')) {
@@ -284,9 +288,9 @@ for (const variable of variables) {
284
288
  }
285
289
 
286
290
  /** @type {import('stylelint').Rule} */
287
- const ruleFunction$1 = (primary, secondaryOptions, context) => {
291
+ const ruleFunction$2 = (primary, secondaryOptions, context) => {
288
292
  return (root, result) => {
289
- const validOptions = validateOptions$1(result, ruleName$3, {
293
+ const validOptions = validateOptions$2(result, ruleName$4, {
290
294
  actual: primary,
291
295
  possible: [true],
292
296
  });
@@ -296,7 +300,7 @@ const ruleFunction$1 = (primary, secondaryOptions, context) => {
296
300
  root.walkDecls(declNode => {
297
301
  const {prop, value} = declNode;
298
302
 
299
- if (!propList$1.some(borderProp => prop.startsWith(borderProp))) return
303
+ if (!propList$2.some(borderProp => prop.startsWith(borderProp))) return
300
304
  if (/^border(-(top|right|bottom|left|block-start|block-end|inline-start|inline-end))?-color$/.test(prop)) return
301
305
  if (valueList$1.some(valueToIgnore => value.includes(valueToIgnore))) return
302
306
 
@@ -386,7 +390,7 @@ const ruleFunction$1 = (primary, secondaryOptions, context) => {
386
390
  problems.push({
387
391
  index: declarationValueIndex(declNode) + node.sourceIndex,
388
392
  endIndex: declarationValueIndex(declNode) + node.sourceIndex + node.value.length,
389
- message: messages$3.rejected(node.value, replacement, prop),
393
+ message: messages$4.rejected(node.value, replacement, prop),
390
394
  });
391
395
  }
392
396
 
@@ -399,13 +403,13 @@ const ruleFunction$1 = (primary, secondaryOptions, context) => {
399
403
 
400
404
  if (problems.length) {
401
405
  for (const err of problems) {
402
- report$1({
406
+ report$2({
403
407
  index: err.index,
404
408
  endIndex: err.endIndex,
405
409
  message: err.message,
406
410
  node: declNode,
407
411
  result,
408
- ruleName: ruleName$3,
412
+ ruleName: ruleName$4,
409
413
  });
410
414
  }
411
415
  }
@@ -413,13 +417,13 @@ const ruleFunction$1 = (primary, secondaryOptions, context) => {
413
417
  }
414
418
  };
415
419
 
416
- ruleFunction$1.ruleName = ruleName$3;
417
- ruleFunction$1.messages = messages$3;
418
- ruleFunction$1.meta = {
420
+ ruleFunction$2.ruleName = ruleName$4;
421
+ ruleFunction$2.messages = messages$4;
422
+ ruleFunction$2.meta = {
419
423
  fixable: true,
420
424
  };
421
425
 
422
- var borders = createPlugin$1(ruleName$3, ruleFunction$1);
426
+ var borders = createPlugin$2(ruleName$4, ruleFunction$2);
423
427
 
424
428
  const SKIP_VALUE_NODE_TYPES = new Set(['space', 'div']);
425
429
  const SKIP_AT_RULE_NAMES = new Set(['each', 'for', 'function', 'mixin']);
@@ -665,18 +669,18 @@ function createVariableRule(ruleName, rules, url) {
665
669
  let actualRules = rules;
666
670
  let overrides = options.rules;
667
671
  if (typeof rules === 'function') {
668
- actualRules = rules({variables: variables$1, options, ruleName});
672
+ actualRules = rules({variables: variables$2, options, ruleName});
669
673
  } else {
670
674
  actualRules = Object.assign({}, rules);
671
675
  }
672
676
  if (typeof overrides === 'function') {
673
677
  delete options.rules;
674
- overrides = overrides({rules: actualRules, options, ruleName, variables: variables$1});
678
+ overrides = overrides({rules: actualRules, options, ruleName, variables: variables$2});
675
679
  }
676
680
  if (overrides) {
677
681
  Object.assign(actualRules, overrides);
678
682
  }
679
- const validate = declarationValidator(actualRules, {variables: variables$1});
683
+ const validate = declarationValidator(actualRules, {variables: variables$2});
680
684
 
681
685
  // The stylelint docs suggest respecting a "disableFix" rule option that
682
686
  // overrides the "global" context.fix (--fix) linting option.
@@ -801,9 +805,9 @@ var colors = createVariableRule(
801
805
  'https://primer.style/primitives/colors',
802
806
  );
803
807
 
804
- const ruleName$2 = 'primer/responsive-widths';
808
+ const ruleName$3 = 'primer/responsive-widths';
805
809
 
806
- const messages$2 = stylelint.utils.ruleMessages(ruleName$2, {
810
+ const messages$3 = stylelint.utils.ruleMessages(ruleName$3, {
807
811
  rejected: value => {
808
812
  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
813
  },
@@ -823,7 +827,7 @@ const walkGroups = (root, validate) => {
823
827
  };
824
828
 
825
829
  // eslint-disable-next-line no-unused-vars
826
- var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}, context) => {
830
+ var responsiveWidths = stylelint.createPlugin(ruleName$3, (enabled, options = {}, context) => {
827
831
  if (!enabled) {
828
832
  return noop$1
829
833
  }
@@ -859,7 +863,7 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
859
863
  if (parseInt(valueUnit.number) > 320) {
860
864
  problems.push({
861
865
  index: declarationValueIndex(decl) + node.sourceIndex,
862
- message: messages$2.rejected(node.value),
866
+ message: messages$3.rejected(node.value),
863
867
  });
864
868
  }
865
869
  break
@@ -867,7 +871,7 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
867
871
  if (parseInt(valueUnit.number) > 100) {
868
872
  problems.push({
869
873
  index: declarationValueIndex(decl) + node.sourceIndex,
870
- message: messages$2.rejected(node.value),
874
+ message: messages$3.rejected(node.value),
871
875
  });
872
876
  }
873
877
  break
@@ -881,7 +885,7 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
881
885
  message: err.message,
882
886
  node: decl,
883
887
  result,
884
- ruleName: ruleName$2,
888
+ ruleName: ruleName$3,
885
889
  });
886
890
  }
887
891
  }
@@ -894,12 +898,12 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
894
898
  function noop$1() {}
895
899
 
896
900
  const {
897
- createPlugin,
898
- utils: {report, ruleMessages, validateOptions},
901
+ createPlugin: createPlugin$1,
902
+ utils: {report: report$1, ruleMessages: ruleMessages$1, validateOptions: validateOptions$1},
899
903
  } = stylelint;
900
904
 
901
- const ruleName$1 = 'primer/spacing';
902
- const messages$1 = ruleMessages(ruleName$1, {
905
+ const ruleName$2 = 'primer/spacing';
906
+ const messages$2 = ruleMessages$1(ruleName$2, {
903
907
  rejected: (value, replacement) => {
904
908
  if (!replacement) {
905
909
  return `Please use a primer size variable instead of '${value}'. Consult the primer docs for a suitable replacement. https://primer.style/foundations/primitives/size`
@@ -910,7 +914,7 @@ const messages$1 = ruleMessages(ruleName$1, {
910
914
  });
911
915
 
912
916
  // Props that we want to check
913
- const propList = ['padding', 'margin', 'top', 'right', 'bottom', 'left'];
917
+ const propList$1 = ['padding', 'margin', 'top', 'right', 'bottom', 'left'];
914
918
  // Values that we want to ignore
915
919
  const valueList = ['${'];
916
920
 
@@ -927,9 +931,9 @@ for (const size of sizes) {
927
931
  }
928
932
 
929
933
  /** @type {import('stylelint').Rule} */
930
- const ruleFunction = (primary, secondaryOptions, context) => {
934
+ const ruleFunction$1 = (primary, secondaryOptions, context) => {
931
935
  return (root, result) => {
932
- const validOptions = validateOptions(result, ruleName$1, {
936
+ const validOptions = validateOptions$1(result, ruleName$2, {
933
937
  actual: primary,
934
938
  possible: [true],
935
939
  });
@@ -939,7 +943,7 @@ const ruleFunction = (primary, secondaryOptions, context) => {
939
943
  root.walkDecls(declNode => {
940
944
  const {prop, value} = declNode;
941
945
 
942
- if (!propList.some(spacingProp => prop.startsWith(spacingProp))) return
946
+ if (!propList$1.some(spacingProp => prop.startsWith(spacingProp))) return
943
947
  if (valueList.some(valueToIgnore => value.includes(valueToIgnore))) return
944
948
 
945
949
  const problems = [];
@@ -984,7 +988,7 @@ const ruleFunction = (primary, secondaryOptions, context) => {
984
988
  problems.push({
985
989
  index: declarationValueIndex(declNode) + node.sourceIndex,
986
990
  endIndex: declarationValueIndex(declNode) + node.sourceIndex + node.value.length,
987
- message: messages$1.rejected(node.value, replacement),
991
+ message: messages$2.rejected(node.value, replacement),
988
992
  });
989
993
  }
990
994
 
@@ -995,6 +999,189 @@ const ruleFunction = (primary, secondaryOptions, context) => {
995
999
  declNode.value = parsedValue.toString();
996
1000
  }
997
1001
 
1002
+ if (problems.length) {
1003
+ for (const err of problems) {
1004
+ report$1({
1005
+ index: err.index,
1006
+ endIndex: err.endIndex,
1007
+ message: err.message,
1008
+ node: declNode,
1009
+ result,
1010
+ ruleName: ruleName$2,
1011
+ });
1012
+ }
1013
+ }
1014
+ });
1015
+ }
1016
+ };
1017
+
1018
+ ruleFunction$1.ruleName = ruleName$2;
1019
+ ruleFunction$1.messages = messages$2;
1020
+ ruleFunction$1.meta = {
1021
+ fixable: true,
1022
+ };
1023
+
1024
+ var spacing = createPlugin$1(ruleName$2, ruleFunction$1);
1025
+
1026
+ const {
1027
+ createPlugin,
1028
+ utils: {report, ruleMessages, validateOptions},
1029
+ } = stylelint;
1030
+
1031
+ const ruleName$1 = 'primer/typography';
1032
+ const messages$1 = ruleMessages(ruleName$1, {
1033
+ rejected: (value, replacement) => {
1034
+ // no possible replacement
1035
+ if (!replacement) {
1036
+ return `Please use a Primer typography variable instead of '${value}'. Consult the primer docs for a suitable replacement. https://primer.style/foundations/primitives/typography`
1037
+ }
1038
+
1039
+ // multiple possible replacements
1040
+ if (replacement.length) {
1041
+ 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`
1042
+ }
1043
+
1044
+ // one possible replacement
1045
+ return `Please replace '${value}' with Primer typography variable '${replacement['name']}'. https://primer.style/foundations/primitives/typography`
1046
+ },
1047
+ });
1048
+
1049
+ const fontWeightKeywordMap = {
1050
+ normal: 400,
1051
+ bold: 600,
1052
+ bolder: 600,
1053
+ lighter: 300,
1054
+ };
1055
+ const getClosestFontWeight = (goalWeightNumber, fontWeightsTokens) => {
1056
+ return fontWeightsTokens.reduce((prev, curr) =>
1057
+ Math.abs(curr.values - goalWeightNumber) < Math.abs(prev.values - goalWeightNumber) ? curr : prev,
1058
+ ).values
1059
+ };
1060
+
1061
+ const variables = primitivesVariables('typography');
1062
+ const fontSizes = [];
1063
+ const fontWeights = [];
1064
+ const lineHeights = [];
1065
+ const fontStacks = [];
1066
+ const fontShorthands = [];
1067
+
1068
+ // Props that we want to check for typography variables
1069
+ const propList = ['font-size', 'font-weight', 'line-height', 'font-family', 'font'];
1070
+
1071
+ for (const variable of variables) {
1072
+ const name = variable['name'];
1073
+
1074
+ if (name.includes('size')) {
1075
+ fontSizes.push(variable);
1076
+ }
1077
+
1078
+ if (name.includes('weight')) {
1079
+ fontWeights.push(variable);
1080
+ }
1081
+
1082
+ if (name.includes('lineHeight')) {
1083
+ lineHeights.push(variable);
1084
+ }
1085
+
1086
+ if (name.includes('fontStack')) {
1087
+ fontStacks.push(variable);
1088
+ }
1089
+
1090
+ if (name.includes('shorthand')) {
1091
+ fontShorthands.push(variable);
1092
+ }
1093
+ }
1094
+
1095
+ /** @type {import('stylelint').Rule} */
1096
+ const ruleFunction = (primary, secondaryOptions, context) => {
1097
+ return (root, result) => {
1098
+ const validOptions = validateOptions(result, ruleName$1, {
1099
+ actual: primary,
1100
+ possible: [true],
1101
+ });
1102
+ let validValues = [];
1103
+
1104
+ if (!validOptions) return
1105
+
1106
+ root.walkDecls(declNode => {
1107
+ const {prop, value} = declNode;
1108
+
1109
+ if (!propList.some(typographyProp => prop.startsWith(typographyProp))) return
1110
+
1111
+ const problems = [];
1112
+
1113
+ const checkForVariable = (vars, nodeValue) =>
1114
+ vars.some(variable =>
1115
+ new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(nodeValue),
1116
+ );
1117
+
1118
+ // Exact values to ignore.
1119
+ if (value === 'inherit') {
1120
+ return
1121
+ }
1122
+
1123
+ switch (prop) {
1124
+ case 'font-size':
1125
+ validValues = fontSizes;
1126
+ break
1127
+ case 'font-weight':
1128
+ validValues = fontWeights;
1129
+ break
1130
+ case 'line-height':
1131
+ validValues = lineHeights;
1132
+ break
1133
+ case 'font-family':
1134
+ validValues = fontStacks;
1135
+ break
1136
+ case 'font':
1137
+ validValues = fontShorthands;
1138
+ break
1139
+ default:
1140
+ validValues = [];
1141
+ }
1142
+
1143
+ if (checkForVariable(validValues, value)) {
1144
+ return
1145
+ }
1146
+
1147
+ const getReplacements = () => {
1148
+ const replacementTokens = validValues.filter(variable => {
1149
+ if (!(variable.values instanceof Array)) {
1150
+ let nodeValue = value;
1151
+
1152
+ if (prop === 'font-weight') {
1153
+ nodeValue = getClosestFontWeight(fontWeightKeywordMap[value] || value, fontWeights);
1154
+ }
1155
+
1156
+ return variable.values.toString() === nodeValue.toString()
1157
+ }
1158
+
1159
+ return variable.values.includes(value.replace('-', ''))
1160
+ });
1161
+
1162
+ if (!replacementTokens.length) {
1163
+ return
1164
+ }
1165
+
1166
+ if (replacementTokens.length > 1) {
1167
+ return replacementTokens
1168
+ }
1169
+
1170
+ return replacementTokens[0]
1171
+ };
1172
+ const replacement = getReplacements();
1173
+ const fixable = replacement && !replacement.length;
1174
+
1175
+ if (fixable && context.fix) {
1176
+ declNode.value = value.replace(value, `var(${replacement['name']})`);
1177
+ } else {
1178
+ problems.push({
1179
+ index: declarationValueIndex(declNode),
1180
+ endIndex: declarationValueIndex(declNode) + value.length,
1181
+ message: messages$1.rejected(value, replacement, prop),
1182
+ });
1183
+ }
1184
+
998
1185
  if (problems.length) {
999
1186
  for (const err of problems) {
1000
1187
  report({
@@ -1017,30 +1204,7 @@ ruleFunction.meta = {
1017
1204
  fixable: true,
1018
1205
  };
1019
1206
 
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
- );
1207
+ var typography = createPlugin(ruleName$1, ruleFunction);
1044
1208
 
1045
1209
  const ruleName = 'primer/no-display-colors';
1046
1210
  const messages = stylelint.utils.ruleMessages(ruleName, {
@@ -1225,7 +1389,6 @@ var index = {
1225
1389
  'length-zero-no-unit': null,
1226
1390
  'selector-max-type': null,
1227
1391
  'primer/colors': null,
1228
- 'primer/typography': null,
1229
1392
  'primer/box-shadow': null,
1230
1393
  },
1231
1394
  },
@@ -1276,7 +1439,6 @@ var index = {
1276
1439
  },
1277
1440
  ],
1278
1441
  // temporarily disabiling Primer plugins while we work on upgrades https://github.com/github/primer/issues/3165
1279
- 'primer/typography': null,
1280
1442
  'primer/box-shadow': null,
1281
1443
  },
1282
1444
  },
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ import valueParser from 'postcss-value-parser';
5
5
  import { createRequire } from 'node:module';
6
6
  import anymatch from 'anymatch';
7
7
  import TapMap from 'tap-map';
8
- import variables$1 from '@primer/css/dist/variables.json' with { type: 'json' };
8
+ import variables$2 from '@primer/css/dist/variables.json' with { type: 'json' };
9
9
  import matchAll from 'string.prototype.matchall';
10
10
 
11
11
  var propertyOrder = [
@@ -194,6 +194,10 @@ function primitivesVariables(type) {
194
194
  case 'border':
195
195
  files.push('functional/size/border.json');
196
196
  break
197
+ case 'typography':
198
+ files.push('base/typography/typography.json');
199
+ files.push('functional/typography/typography.json');
200
+ break
197
201
  }
198
202
 
199
203
  for (const file of files) {
@@ -226,12 +230,12 @@ function walkGroups$1(root, validate) {
226
230
  }
227
231
 
228
232
  const {
229
- createPlugin: createPlugin$1,
230
- utils: {report: report$1, ruleMessages: ruleMessages$1, validateOptions: validateOptions$1},
233
+ createPlugin: createPlugin$2,
234
+ utils: {report: report$2, ruleMessages: ruleMessages$2, validateOptions: validateOptions$2},
231
235
  } = stylelint;
232
236
 
233
- const ruleName$3 = 'primer/borders';
234
- const messages$3 = ruleMessages$1(ruleName$3, {
237
+ const ruleName$4 = 'primer/borders';
238
+ const messages$4 = ruleMessages$2(ruleName$4, {
235
239
  rejected: (value, replacement, propName) => {
236
240
  if (propName && propName.includes('radius') && value.includes('borderWidth')) {
237
241
  return `Border radius variables can not be used for border widths`
@@ -249,19 +253,19 @@ const messages$3 = ruleMessages$1(ruleName$3, {
249
253
  },
250
254
  });
251
255
 
252
- const variables = primitivesVariables('border');
256
+ const variables$1 = primitivesVariables('border');
253
257
  const sizes$1 = [];
254
258
  const radii = [];
255
259
 
256
260
  // Props that we want to check
257
- const propList$1 = ['border', 'border-width', 'border-radius'];
261
+ const propList$2 = ['border', 'border-width', 'border-radius'];
258
262
  // Values that we want to ignore
259
263
  const valueList$1 = ['${'];
260
264
 
261
265
  const borderShorthand = prop =>
262
266
  /^border(-(top|right|bottom|left|block-start|block-end|inline-start|inline-end))?$/.test(prop);
263
267
 
264
- for (const variable of variables) {
268
+ for (const variable of variables$1) {
265
269
  const name = variable['name'];
266
270
 
267
271
  if (name.includes('borderWidth')) {
@@ -281,9 +285,9 @@ for (const variable of variables) {
281
285
  }
282
286
 
283
287
  /** @type {import('stylelint').Rule} */
284
- const ruleFunction$1 = (primary, secondaryOptions, context) => {
288
+ const ruleFunction$2 = (primary, secondaryOptions, context) => {
285
289
  return (root, result) => {
286
- const validOptions = validateOptions$1(result, ruleName$3, {
290
+ const validOptions = validateOptions$2(result, ruleName$4, {
287
291
  actual: primary,
288
292
  possible: [true],
289
293
  });
@@ -293,7 +297,7 @@ const ruleFunction$1 = (primary, secondaryOptions, context) => {
293
297
  root.walkDecls(declNode => {
294
298
  const {prop, value} = declNode;
295
299
 
296
- if (!propList$1.some(borderProp => prop.startsWith(borderProp))) return
300
+ if (!propList$2.some(borderProp => prop.startsWith(borderProp))) return
297
301
  if (/^border(-(top|right|bottom|left|block-start|block-end|inline-start|inline-end))?-color$/.test(prop)) return
298
302
  if (valueList$1.some(valueToIgnore => value.includes(valueToIgnore))) return
299
303
 
@@ -383,7 +387,7 @@ const ruleFunction$1 = (primary, secondaryOptions, context) => {
383
387
  problems.push({
384
388
  index: declarationValueIndex(declNode) + node.sourceIndex,
385
389
  endIndex: declarationValueIndex(declNode) + node.sourceIndex + node.value.length,
386
- message: messages$3.rejected(node.value, replacement, prop),
390
+ message: messages$4.rejected(node.value, replacement, prop),
387
391
  });
388
392
  }
389
393
 
@@ -396,13 +400,13 @@ const ruleFunction$1 = (primary, secondaryOptions, context) => {
396
400
 
397
401
  if (problems.length) {
398
402
  for (const err of problems) {
399
- report$1({
403
+ report$2({
400
404
  index: err.index,
401
405
  endIndex: err.endIndex,
402
406
  message: err.message,
403
407
  node: declNode,
404
408
  result,
405
- ruleName: ruleName$3,
409
+ ruleName: ruleName$4,
406
410
  });
407
411
  }
408
412
  }
@@ -410,13 +414,13 @@ const ruleFunction$1 = (primary, secondaryOptions, context) => {
410
414
  }
411
415
  };
412
416
 
413
- ruleFunction$1.ruleName = ruleName$3;
414
- ruleFunction$1.messages = messages$3;
415
- ruleFunction$1.meta = {
417
+ ruleFunction$2.ruleName = ruleName$4;
418
+ ruleFunction$2.messages = messages$4;
419
+ ruleFunction$2.meta = {
416
420
  fixable: true,
417
421
  };
418
422
 
419
- var borders = createPlugin$1(ruleName$3, ruleFunction$1);
423
+ var borders = createPlugin$2(ruleName$4, ruleFunction$2);
420
424
 
421
425
  const SKIP_VALUE_NODE_TYPES = new Set(['space', 'div']);
422
426
  const SKIP_AT_RULE_NAMES = new Set(['each', 'for', 'function', 'mixin']);
@@ -662,18 +666,18 @@ function createVariableRule(ruleName, rules, url) {
662
666
  let actualRules = rules;
663
667
  let overrides = options.rules;
664
668
  if (typeof rules === 'function') {
665
- actualRules = rules({variables: variables$1, options, ruleName});
669
+ actualRules = rules({variables: variables$2, options, ruleName});
666
670
  } else {
667
671
  actualRules = Object.assign({}, rules);
668
672
  }
669
673
  if (typeof overrides === 'function') {
670
674
  delete options.rules;
671
- overrides = overrides({rules: actualRules, options, ruleName, variables: variables$1});
675
+ overrides = overrides({rules: actualRules, options, ruleName, variables: variables$2});
672
676
  }
673
677
  if (overrides) {
674
678
  Object.assign(actualRules, overrides);
675
679
  }
676
- const validate = declarationValidator(actualRules, {variables: variables$1});
680
+ const validate = declarationValidator(actualRules, {variables: variables$2});
677
681
 
678
682
  // The stylelint docs suggest respecting a "disableFix" rule option that
679
683
  // overrides the "global" context.fix (--fix) linting option.
@@ -798,9 +802,9 @@ var colors = createVariableRule(
798
802
  'https://primer.style/primitives/colors',
799
803
  );
800
804
 
801
- const ruleName$2 = 'primer/responsive-widths';
805
+ const ruleName$3 = 'primer/responsive-widths';
802
806
 
803
- const messages$2 = stylelint.utils.ruleMessages(ruleName$2, {
807
+ const messages$3 = stylelint.utils.ruleMessages(ruleName$3, {
804
808
  rejected: value => {
805
809
  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`
806
810
  },
@@ -820,7 +824,7 @@ const walkGroups = (root, validate) => {
820
824
  };
821
825
 
822
826
  // eslint-disable-next-line no-unused-vars
823
- var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}, context) => {
827
+ var responsiveWidths = stylelint.createPlugin(ruleName$3, (enabled, options = {}, context) => {
824
828
  if (!enabled) {
825
829
  return noop$1
826
830
  }
@@ -856,7 +860,7 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
856
860
  if (parseInt(valueUnit.number) > 320) {
857
861
  problems.push({
858
862
  index: declarationValueIndex(decl) + node.sourceIndex,
859
- message: messages$2.rejected(node.value),
863
+ message: messages$3.rejected(node.value),
860
864
  });
861
865
  }
862
866
  break
@@ -864,7 +868,7 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
864
868
  if (parseInt(valueUnit.number) > 100) {
865
869
  problems.push({
866
870
  index: declarationValueIndex(decl) + node.sourceIndex,
867
- message: messages$2.rejected(node.value),
871
+ message: messages$3.rejected(node.value),
868
872
  });
869
873
  }
870
874
  break
@@ -878,7 +882,7 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
878
882
  message: err.message,
879
883
  node: decl,
880
884
  result,
881
- ruleName: ruleName$2,
885
+ ruleName: ruleName$3,
882
886
  });
883
887
  }
884
888
  }
@@ -891,12 +895,12 @@ var responsiveWidths = stylelint.createPlugin(ruleName$2, (enabled, options = {}
891
895
  function noop$1() {}
892
896
 
893
897
  const {
894
- createPlugin,
895
- utils: {report, ruleMessages, validateOptions},
898
+ createPlugin: createPlugin$1,
899
+ utils: {report: report$1, ruleMessages: ruleMessages$1, validateOptions: validateOptions$1},
896
900
  } = stylelint;
897
901
 
898
- const ruleName$1 = 'primer/spacing';
899
- const messages$1 = ruleMessages(ruleName$1, {
902
+ const ruleName$2 = 'primer/spacing';
903
+ const messages$2 = ruleMessages$1(ruleName$2, {
900
904
  rejected: (value, replacement) => {
901
905
  if (!replacement) {
902
906
  return `Please use a primer size variable instead of '${value}'. Consult the primer docs for a suitable replacement. https://primer.style/foundations/primitives/size`
@@ -907,7 +911,7 @@ const messages$1 = ruleMessages(ruleName$1, {
907
911
  });
908
912
 
909
913
  // Props that we want to check
910
- const propList = ['padding', 'margin', 'top', 'right', 'bottom', 'left'];
914
+ const propList$1 = ['padding', 'margin', 'top', 'right', 'bottom', 'left'];
911
915
  // Values that we want to ignore
912
916
  const valueList = ['${'];
913
917
 
@@ -924,9 +928,9 @@ for (const size of sizes) {
924
928
  }
925
929
 
926
930
  /** @type {import('stylelint').Rule} */
927
- const ruleFunction = (primary, secondaryOptions, context) => {
931
+ const ruleFunction$1 = (primary, secondaryOptions, context) => {
928
932
  return (root, result) => {
929
- const validOptions = validateOptions(result, ruleName$1, {
933
+ const validOptions = validateOptions$1(result, ruleName$2, {
930
934
  actual: primary,
931
935
  possible: [true],
932
936
  });
@@ -936,7 +940,7 @@ const ruleFunction = (primary, secondaryOptions, context) => {
936
940
  root.walkDecls(declNode => {
937
941
  const {prop, value} = declNode;
938
942
 
939
- if (!propList.some(spacingProp => prop.startsWith(spacingProp))) return
943
+ if (!propList$1.some(spacingProp => prop.startsWith(spacingProp))) return
940
944
  if (valueList.some(valueToIgnore => value.includes(valueToIgnore))) return
941
945
 
942
946
  const problems = [];
@@ -981,7 +985,7 @@ const ruleFunction = (primary, secondaryOptions, context) => {
981
985
  problems.push({
982
986
  index: declarationValueIndex(declNode) + node.sourceIndex,
983
987
  endIndex: declarationValueIndex(declNode) + node.sourceIndex + node.value.length,
984
- message: messages$1.rejected(node.value, replacement),
988
+ message: messages$2.rejected(node.value, replacement),
985
989
  });
986
990
  }
987
991
 
@@ -992,6 +996,189 @@ const ruleFunction = (primary, secondaryOptions, context) => {
992
996
  declNode.value = parsedValue.toString();
993
997
  }
994
998
 
999
+ if (problems.length) {
1000
+ for (const err of problems) {
1001
+ report$1({
1002
+ index: err.index,
1003
+ endIndex: err.endIndex,
1004
+ message: err.message,
1005
+ node: declNode,
1006
+ result,
1007
+ ruleName: ruleName$2,
1008
+ });
1009
+ }
1010
+ }
1011
+ });
1012
+ }
1013
+ };
1014
+
1015
+ ruleFunction$1.ruleName = ruleName$2;
1016
+ ruleFunction$1.messages = messages$2;
1017
+ ruleFunction$1.meta = {
1018
+ fixable: true,
1019
+ };
1020
+
1021
+ var spacing = createPlugin$1(ruleName$2, ruleFunction$1);
1022
+
1023
+ const {
1024
+ createPlugin,
1025
+ utils: {report, ruleMessages, validateOptions},
1026
+ } = stylelint;
1027
+
1028
+ const ruleName$1 = 'primer/typography';
1029
+ const messages$1 = ruleMessages(ruleName$1, {
1030
+ rejected: (value, replacement) => {
1031
+ // no possible replacement
1032
+ if (!replacement) {
1033
+ return `Please use a Primer typography variable instead of '${value}'. Consult the primer docs for a suitable replacement. https://primer.style/foundations/primitives/typography`
1034
+ }
1035
+
1036
+ // multiple possible replacements
1037
+ if (replacement.length) {
1038
+ 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`
1039
+ }
1040
+
1041
+ // one possible replacement
1042
+ return `Please replace '${value}' with Primer typography variable '${replacement['name']}'. https://primer.style/foundations/primitives/typography`
1043
+ },
1044
+ });
1045
+
1046
+ const fontWeightKeywordMap = {
1047
+ normal: 400,
1048
+ bold: 600,
1049
+ bolder: 600,
1050
+ lighter: 300,
1051
+ };
1052
+ const getClosestFontWeight = (goalWeightNumber, fontWeightsTokens) => {
1053
+ return fontWeightsTokens.reduce((prev, curr) =>
1054
+ Math.abs(curr.values - goalWeightNumber) < Math.abs(prev.values - goalWeightNumber) ? curr : prev,
1055
+ ).values
1056
+ };
1057
+
1058
+ const variables = primitivesVariables('typography');
1059
+ const fontSizes = [];
1060
+ const fontWeights = [];
1061
+ const lineHeights = [];
1062
+ const fontStacks = [];
1063
+ const fontShorthands = [];
1064
+
1065
+ // Props that we want to check for typography variables
1066
+ const propList = ['font-size', 'font-weight', 'line-height', 'font-family', 'font'];
1067
+
1068
+ for (const variable of variables) {
1069
+ const name = variable['name'];
1070
+
1071
+ if (name.includes('size')) {
1072
+ fontSizes.push(variable);
1073
+ }
1074
+
1075
+ if (name.includes('weight')) {
1076
+ fontWeights.push(variable);
1077
+ }
1078
+
1079
+ if (name.includes('lineHeight')) {
1080
+ lineHeights.push(variable);
1081
+ }
1082
+
1083
+ if (name.includes('fontStack')) {
1084
+ fontStacks.push(variable);
1085
+ }
1086
+
1087
+ if (name.includes('shorthand')) {
1088
+ fontShorthands.push(variable);
1089
+ }
1090
+ }
1091
+
1092
+ /** @type {import('stylelint').Rule} */
1093
+ const ruleFunction = (primary, secondaryOptions, context) => {
1094
+ return (root, result) => {
1095
+ const validOptions = validateOptions(result, ruleName$1, {
1096
+ actual: primary,
1097
+ possible: [true],
1098
+ });
1099
+ let validValues = [];
1100
+
1101
+ if (!validOptions) return
1102
+
1103
+ root.walkDecls(declNode => {
1104
+ const {prop, value} = declNode;
1105
+
1106
+ if (!propList.some(typographyProp => prop.startsWith(typographyProp))) return
1107
+
1108
+ const problems = [];
1109
+
1110
+ const checkForVariable = (vars, nodeValue) =>
1111
+ vars.some(variable =>
1112
+ new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(nodeValue),
1113
+ );
1114
+
1115
+ // Exact values to ignore.
1116
+ if (value === 'inherit') {
1117
+ return
1118
+ }
1119
+
1120
+ switch (prop) {
1121
+ case 'font-size':
1122
+ validValues = fontSizes;
1123
+ break
1124
+ case 'font-weight':
1125
+ validValues = fontWeights;
1126
+ break
1127
+ case 'line-height':
1128
+ validValues = lineHeights;
1129
+ break
1130
+ case 'font-family':
1131
+ validValues = fontStacks;
1132
+ break
1133
+ case 'font':
1134
+ validValues = fontShorthands;
1135
+ break
1136
+ default:
1137
+ validValues = [];
1138
+ }
1139
+
1140
+ if (checkForVariable(validValues, value)) {
1141
+ return
1142
+ }
1143
+
1144
+ const getReplacements = () => {
1145
+ const replacementTokens = validValues.filter(variable => {
1146
+ if (!(variable.values instanceof Array)) {
1147
+ let nodeValue = value;
1148
+
1149
+ if (prop === 'font-weight') {
1150
+ nodeValue = getClosestFontWeight(fontWeightKeywordMap[value] || value, fontWeights);
1151
+ }
1152
+
1153
+ return variable.values.toString() === nodeValue.toString()
1154
+ }
1155
+
1156
+ return variable.values.includes(value.replace('-', ''))
1157
+ });
1158
+
1159
+ if (!replacementTokens.length) {
1160
+ return
1161
+ }
1162
+
1163
+ if (replacementTokens.length > 1) {
1164
+ return replacementTokens
1165
+ }
1166
+
1167
+ return replacementTokens[0]
1168
+ };
1169
+ const replacement = getReplacements();
1170
+ const fixable = replacement && !replacement.length;
1171
+
1172
+ if (fixable && context.fix) {
1173
+ declNode.value = value.replace(value, `var(${replacement['name']})`);
1174
+ } else {
1175
+ problems.push({
1176
+ index: declarationValueIndex(declNode),
1177
+ endIndex: declarationValueIndex(declNode) + value.length,
1178
+ message: messages$1.rejected(value, replacement, prop),
1179
+ });
1180
+ }
1181
+
995
1182
  if (problems.length) {
996
1183
  for (const err of problems) {
997
1184
  report({
@@ -1014,30 +1201,7 @@ ruleFunction.meta = {
1014
1201
  fixable: true,
1015
1202
  };
1016
1203
 
1017
- var spacing = createPlugin(ruleName$1, ruleFunction);
1018
-
1019
- var typography = createVariableRule(
1020
- 'primer/typography',
1021
- {
1022
- 'font-size': {
1023
- expects: 'a font-size variable',
1024
- values: ['$body-font-size', '$h{000,00,0,1,2,3,4,5,6}-size', '$font-size-*', '1', '1em', 'inherit'],
1025
- },
1026
- 'font-weight': {
1027
- props: 'font-weight',
1028
- values: ['$font-weight-*', 'inherit'],
1029
- replacements: {
1030
- bold: '$font-weight-bold',
1031
- normal: '$font-weight-normal',
1032
- },
1033
- },
1034
- 'line-height': {
1035
- props: 'line-height',
1036
- values: ['$body-line-height', '$lh-*', '0', '1', '1em', 'inherit'],
1037
- },
1038
- },
1039
- 'https://primer.style/css/utilities/typography',
1040
- );
1204
+ var typography = createPlugin(ruleName$1, ruleFunction);
1041
1205
 
1042
1206
  const ruleName = 'primer/no-display-colors';
1043
1207
  const messages = stylelint.utils.ruleMessages(ruleName, {
@@ -1222,7 +1386,6 @@ var index = {
1222
1386
  'length-zero-no-unit': null,
1223
1387
  'selector-max-type': null,
1224
1388
  'primer/colors': null,
1225
- 'primer/typography': null,
1226
1389
  'primer/box-shadow': null,
1227
1390
  },
1228
1391
  },
@@ -1273,7 +1436,6 @@ var index = {
1273
1436
  },
1274
1437
  ],
1275
1438
  // temporarily disabiling Primer plugins while we work on upgrades https://github.com/github/primer/issues/3165
1276
- 'primer/typography': null,
1277
1439
  'primer/box-shadow': null,
1278
1440
  },
1279
1441
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primer/stylelint-config",
3
- "version": "13.0.0-rc.ee7c4cb",
3
+ "version": "13.0.0-rc.f49bf6d",
4
4
  "description": "Sharable stylelint config used by GitHub's CSS",
5
5
  "author": "GitHub, Inc.",
6
6
  "license": "MIT",
@@ -13,6 +13,10 @@ export function primitivesVariables(type) {
13
13
  case 'border':
14
14
  files.push('functional/size/border.json')
15
15
  break
16
+ case 'typography':
17
+ files.push('base/typography/typography.json')
18
+ files.push('functional/typography/typography.json')
19
+ break
16
20
  }
17
21
 
18
22
  for (const file of files) {
@@ -1,24 +1,186 @@
1
- import {createVariableRule} from './lib/variable-rules.js'
2
-
3
- export default createVariableRule(
4
- 'primer/typography',
5
- {
6
- 'font-size': {
7
- expects: 'a font-size variable',
8
- values: ['$body-font-size', '$h{000,00,0,1,2,3,4,5,6}-size', '$font-size-*', '1', '1em', 'inherit'],
9
- },
10
- 'font-weight': {
11
- props: 'font-weight',
12
- values: ['$font-weight-*', 'inherit'],
13
- replacements: {
14
- bold: '$font-weight-bold',
15
- normal: '$font-weight-normal',
16
- },
17
- },
18
- 'line-height': {
19
- props: 'line-height',
20
- values: ['$body-line-height', '$lh-*', '0', '1', '1em', 'inherit'],
21
- },
1
+ import stylelint from 'stylelint'
2
+ import declarationValueIndex from 'stylelint/lib/utils/declarationValueIndex.cjs'
3
+ import {primitivesVariables} from './lib/utils.js'
4
+
5
+ const {
6
+ createPlugin,
7
+ utils: {report, ruleMessages, validateOptions},
8
+ } = stylelint
9
+
10
+ export const ruleName = 'primer/typography'
11
+ export const messages = ruleMessages(ruleName, {
12
+ rejected: (value, replacement) => {
13
+ // no possible replacement
14
+ if (!replacement) {
15
+ return `Please use a Primer typography variable instead of '${value}'. Consult the primer docs for a suitable replacement. https://primer.style/foundations/primitives/typography`
16
+ }
17
+
18
+ // multiple possible replacements
19
+ if (replacement.length) {
20
+ 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`
21
+ }
22
+
23
+ // one possible replacement
24
+ return `Please replace '${value}' with Primer typography variable '${replacement['name']}'. https://primer.style/foundations/primitives/typography`
22
25
  },
23
- 'https://primer.style/css/utilities/typography',
24
- )
26
+ })
27
+
28
+ const fontWeightKeywordMap = {
29
+ normal: 400,
30
+ bold: 600,
31
+ bolder: 600,
32
+ lighter: 300,
33
+ }
34
+ const getClosestFontWeight = (goalWeightNumber, fontWeightsTokens) => {
35
+ return fontWeightsTokens.reduce((prev, curr) =>
36
+ Math.abs(curr.values - goalWeightNumber) < Math.abs(prev.values - goalWeightNumber) ? curr : prev,
37
+ ).values
38
+ }
39
+
40
+ const variables = primitivesVariables('typography')
41
+ const fontSizes = []
42
+ const fontWeights = []
43
+ const lineHeights = []
44
+ const fontStacks = []
45
+ const fontShorthands = []
46
+
47
+ // Props that we want to check for typography variables
48
+ const propList = ['font-size', 'font-weight', 'line-height', 'font-family', 'font']
49
+
50
+ for (const variable of variables) {
51
+ const name = variable['name']
52
+
53
+ if (name.includes('size')) {
54
+ fontSizes.push(variable)
55
+ }
56
+
57
+ if (name.includes('weight')) {
58
+ fontWeights.push(variable)
59
+ }
60
+
61
+ if (name.includes('lineHeight')) {
62
+ lineHeights.push(variable)
63
+ }
64
+
65
+ if (name.includes('fontStack')) {
66
+ fontStacks.push(variable)
67
+ }
68
+
69
+ if (name.includes('shorthand')) {
70
+ fontShorthands.push(variable)
71
+ }
72
+ }
73
+
74
+ /** @type {import('stylelint').Rule} */
75
+ const ruleFunction = (primary, secondaryOptions, context) => {
76
+ return (root, result) => {
77
+ const validOptions = validateOptions(result, ruleName, {
78
+ actual: primary,
79
+ possible: [true],
80
+ })
81
+ let validValues = []
82
+
83
+ if (!validOptions) return
84
+
85
+ root.walkDecls(declNode => {
86
+ const {prop, value} = declNode
87
+
88
+ if (!propList.some(typographyProp => prop.startsWith(typographyProp))) return
89
+
90
+ const problems = []
91
+
92
+ const checkForVariable = (vars, nodeValue) =>
93
+ vars.some(variable =>
94
+ new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(nodeValue),
95
+ )
96
+
97
+ // Exact values to ignore.
98
+ if (value === 'inherit') {
99
+ return
100
+ }
101
+
102
+ switch (prop) {
103
+ case 'font-size':
104
+ validValues = fontSizes
105
+ break
106
+ case 'font-weight':
107
+ validValues = fontWeights
108
+ break
109
+ case 'line-height':
110
+ validValues = lineHeights
111
+ break
112
+ case 'font-family':
113
+ validValues = fontStacks
114
+ break
115
+ case 'font':
116
+ validValues = fontShorthands
117
+ break
118
+ default:
119
+ validValues = []
120
+ }
121
+
122
+ if (checkForVariable(validValues, value)) {
123
+ return
124
+ }
125
+
126
+ const getReplacements = () => {
127
+ const replacementTokens = validValues.filter(variable => {
128
+ if (!(variable.values instanceof Array)) {
129
+ let nodeValue = value
130
+
131
+ if (prop === 'font-weight') {
132
+ nodeValue = getClosestFontWeight(fontWeightKeywordMap[value] || value, fontWeights)
133
+ }
134
+
135
+ return variable.values.toString() === nodeValue.toString()
136
+ }
137
+
138
+ return variable.values.includes(value.replace('-', ''))
139
+ })
140
+
141
+ if (!replacementTokens.length) {
142
+ return
143
+ }
144
+
145
+ if (replacementTokens.length > 1) {
146
+ return replacementTokens
147
+ }
148
+
149
+ return replacementTokens[0]
150
+ }
151
+ const replacement = getReplacements()
152
+ const fixable = replacement && !replacement.length
153
+
154
+ if (fixable && context.fix) {
155
+ declNode.value = value.replace(value, `var(${replacement['name']})`)
156
+ } else {
157
+ problems.push({
158
+ index: declarationValueIndex(declNode),
159
+ endIndex: declarationValueIndex(declNode) + value.length,
160
+ message: messages.rejected(value, replacement, prop),
161
+ })
162
+ }
163
+
164
+ if (problems.length) {
165
+ for (const err of problems) {
166
+ report({
167
+ index: err.index,
168
+ endIndex: err.endIndex,
169
+ message: err.message,
170
+ node: declNode,
171
+ result,
172
+ ruleName,
173
+ })
174
+ }
175
+ }
176
+ })
177
+ }
178
+ }
179
+
180
+ ruleFunction.ruleName = ruleName
181
+ ruleFunction.messages = messages
182
+ ruleFunction.meta = {
183
+ fixable: true,
184
+ }
185
+
186
+ export default createPlugin(ruleName, ruleFunction)