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