@plumeria/eslint-plugin 18.2.28 → 18.2.29
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/rules/validate-values.js +141 -12
- package/package.json +1 -1
|
@@ -186,7 +186,9 @@ const minString = 'min\\([^()]*\\)';
|
|
|
186
186
|
const maxString = 'max\\([^()]*\\)';
|
|
187
187
|
const minmaxString = 'minmax\\([^()]*\\)';
|
|
188
188
|
const dashedIdentString = '--[a-zA-Z_][a-zA-Z0-9_-]*';
|
|
189
|
-
const
|
|
189
|
+
const cssVariablePlaceholder = '\u0001';
|
|
190
|
+
const canonicalCssVariable = (index) => `var(--x${index})`;
|
|
191
|
+
const varString = `(?:var\\(${dashedIdentString}(,\\s*[^\\)]+)?\\)|${cssVariablePlaceholder})`;
|
|
190
192
|
const varRegex = new RegExp(`^(${varString})$`);
|
|
191
193
|
const pureNumber = `(-?\\d+(\\.\\d+)?)`;
|
|
192
194
|
const numberPattern = `(${pureNumber}|${varString})`;
|
|
@@ -201,7 +203,7 @@ const anchorString = 'anchor\\([^()]*\\)';
|
|
|
201
203
|
const anchorSizeString = 'anchor-size\\([^()]*\\)';
|
|
202
204
|
const clampString = 'clamp\\([^()]*\\)';
|
|
203
205
|
const gradientString = '(?:repeating-)?(?:linear|radial|conic)-gradient\\(.*\\)';
|
|
204
|
-
const urlString =
|
|
206
|
+
const urlString = `url\\([^\\)${cssVariablePlaceholder}]+\\)`;
|
|
205
207
|
const imageSetString = 'image-set\\([^\\)]+\\)';
|
|
206
208
|
const attrString = 'attr\\([^\\)]+\\)';
|
|
207
209
|
const addString = `add\\(${integerPattern}\\)`;
|
|
@@ -210,7 +212,8 @@ const countersString = 'counters\\([^\\)]+\\)';
|
|
|
210
212
|
const doubleQuoteString = '"[^"]*"';
|
|
211
213
|
const singleQuoteString = "'[^']*'";
|
|
212
214
|
const stringString = `(?:${doubleQuoteString}|${singleQuoteString})`;
|
|
213
|
-
const
|
|
215
|
+
const functionContentsString = '(?=[^)]*[^\\s()])(?:[^()]|\\([^()]*\\))*';
|
|
216
|
+
const repeatString = `repeat\\(${functionContentsString}\\)`;
|
|
214
217
|
const colorSpaces = 'srgb|srgb-linear|display-p3|a98-rgb|prophoto-rgb|rec2020|lab|oklab|xyz|xyz-d50|xyz-d65|hsl|hwb|lch|oklch';
|
|
215
218
|
const hueModifiers = '(?:\\s+(?:shorter|longer|increasing|decreasing)\\s+hue)?';
|
|
216
219
|
const colorSpacePattern = `in\\s+(?:${colorSpaces})${hueModifiers}`;
|
|
@@ -282,6 +285,128 @@ const initialLetterProperties = ['initialLetter'];
|
|
|
282
285
|
const hyphenateLimitCharsProperties = ['hyphenateLimitChars'];
|
|
283
286
|
const shapeImageThresholdProperties = ['shapeImageThreshold'];
|
|
284
287
|
const columnsProperties = ['columns'];
|
|
288
|
+
function getParenthesisDepth(value, end) {
|
|
289
|
+
let depth = 0;
|
|
290
|
+
let quote = '';
|
|
291
|
+
let escaped = false;
|
|
292
|
+
for (let index = 0; index < end; index++) {
|
|
293
|
+
const char = value[index];
|
|
294
|
+
if (escaped) {
|
|
295
|
+
escaped = false;
|
|
296
|
+
}
|
|
297
|
+
else if (char === '\\') {
|
|
298
|
+
escaped = true;
|
|
299
|
+
}
|
|
300
|
+
else if (quote) {
|
|
301
|
+
if (char === quote)
|
|
302
|
+
quote = '';
|
|
303
|
+
}
|
|
304
|
+
else if (char === '"' || char === "'") {
|
|
305
|
+
quote = char;
|
|
306
|
+
}
|
|
307
|
+
else if (char === '(') {
|
|
308
|
+
depth++;
|
|
309
|
+
}
|
|
310
|
+
else if (char === ')') {
|
|
311
|
+
depth--;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return depth;
|
|
315
|
+
}
|
|
316
|
+
function findCssVariableStart(value, start) {
|
|
317
|
+
let quote = '';
|
|
318
|
+
let escaped = false;
|
|
319
|
+
for (let index = start; index < value.length; index++) {
|
|
320
|
+
const char = value[index];
|
|
321
|
+
if (escaped) {
|
|
322
|
+
escaped = false;
|
|
323
|
+
}
|
|
324
|
+
else if (char === '\\') {
|
|
325
|
+
escaped = true;
|
|
326
|
+
}
|
|
327
|
+
else if (quote) {
|
|
328
|
+
if (char === quote)
|
|
329
|
+
quote = '';
|
|
330
|
+
}
|
|
331
|
+
else if (char === '"' || char === "'") {
|
|
332
|
+
quote = char;
|
|
333
|
+
}
|
|
334
|
+
else if (value.startsWith('var(', index) &&
|
|
335
|
+
(index === 0 || !/[a-zA-Z0-9_-]/.test(value[index - 1]))) {
|
|
336
|
+
return index;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return -1;
|
|
340
|
+
}
|
|
341
|
+
function normalizeCssVariables(value) {
|
|
342
|
+
if (value.includes(cssVariablePlaceholder))
|
|
343
|
+
return null;
|
|
344
|
+
let normalized = '';
|
|
345
|
+
let cursor = 0;
|
|
346
|
+
let isStandalone = false;
|
|
347
|
+
let canonicalIndex = 0;
|
|
348
|
+
while (cursor < value.length) {
|
|
349
|
+
const start = findCssVariableStart(value, cursor);
|
|
350
|
+
if (start === -1) {
|
|
351
|
+
return { isStandalone, value: normalized + value.slice(cursor) };
|
|
352
|
+
}
|
|
353
|
+
normalized += value.slice(cursor, start);
|
|
354
|
+
let depth = 1;
|
|
355
|
+
let quote = '';
|
|
356
|
+
let escaped = false;
|
|
357
|
+
let firstComma = -1;
|
|
358
|
+
let end = start + 4;
|
|
359
|
+
for (; end < value.length; end++) {
|
|
360
|
+
const char = value[end];
|
|
361
|
+
if (escaped) {
|
|
362
|
+
escaped = false;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (char === '\\') {
|
|
366
|
+
escaped = true;
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (quote) {
|
|
370
|
+
if (char === quote)
|
|
371
|
+
quote = '';
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (char === '"' || char === "'") {
|
|
375
|
+
quote = char;
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
if (char === '(') {
|
|
379
|
+
depth++;
|
|
380
|
+
}
|
|
381
|
+
else if (char === ')') {
|
|
382
|
+
depth--;
|
|
383
|
+
if (depth === 0)
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
else if (char === ',' && depth === 1 && firstComma === -1) {
|
|
387
|
+
firstComma = end;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (depth !== 0 || quote)
|
|
391
|
+
return null;
|
|
392
|
+
const nameEnd = firstComma === -1 ? end : firstComma;
|
|
393
|
+
const name = value.slice(start + 4, nameEnd).trim();
|
|
394
|
+
if (!new RegExp(`^${dashedIdentString}$`).test(name))
|
|
395
|
+
return null;
|
|
396
|
+
if (firstComma !== -1) {
|
|
397
|
+
const fallback = value.slice(firstComma + 1, end).trim();
|
|
398
|
+
if (fallback && normalizeCssVariables(fallback) === null)
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
isStandalone = start === 0 && end === value.length - 1;
|
|
402
|
+
normalized +=
|
|
403
|
+
getParenthesisDepth(value, start) === 0
|
|
404
|
+
? canonicalCssVariable(canonicalIndex++)
|
|
405
|
+
: cssVariablePlaceholder;
|
|
406
|
+
cursor = end + 1;
|
|
407
|
+
}
|
|
408
|
+
return { isStandalone, value: normalized };
|
|
409
|
+
}
|
|
285
410
|
const valueCountMap = {
|
|
286
411
|
inset: 4,
|
|
287
412
|
gap: 2,
|
|
@@ -514,7 +639,7 @@ function isValidTextDecorationLine(value) {
|
|
|
514
639
|
return false;
|
|
515
640
|
const tokens = trimmedValue.split(/\s+/);
|
|
516
641
|
return tokens.every((token) => {
|
|
517
|
-
if (
|
|
642
|
+
if (varRegex.test(token))
|
|
518
643
|
return true;
|
|
519
644
|
if (decorationValues.includes(token) || varString.includes(token))
|
|
520
645
|
return !usedValues.has(token) && usedValues.add(token);
|
|
@@ -531,7 +656,7 @@ function isValidContain(value) {
|
|
|
531
656
|
return false;
|
|
532
657
|
const tokens = trimmedValue.split(/\s+/);
|
|
533
658
|
return tokens.every((token) => {
|
|
534
|
-
if (
|
|
659
|
+
if (varRegex.test(token))
|
|
535
660
|
return true;
|
|
536
661
|
if (singleValues.includes(token))
|
|
537
662
|
return tokens.length === 1;
|
|
@@ -1391,9 +1516,9 @@ function getValidator(key) {
|
|
|
1391
1516
|
}
|
|
1392
1517
|
else if (['backdropFilter', 'filter'].includes(key)) {
|
|
1393
1518
|
const filterNumPattern = `(?:brightness|contrast|grayscale|invert|opacity|sepia|saturate)\\(\\s*${numberPattern}%?\\s*\\)`;
|
|
1394
|
-
const blurPattern = `blur\\(\\s*(${lengthPattern}|${calcString}|${clampString}|${minString}|${maxString})\\s*\\)`;
|
|
1519
|
+
const blurPattern = `blur\\(\\s*(${lengthPattern}|${calcString}|${clampString}|${minString}|${maxString}|${varString})\\s*\\)`;
|
|
1395
1520
|
const anglePatternFunc = `hue-rotate\\(\\s*${anglePattern}\\s*\\)`;
|
|
1396
|
-
const dropShadowPattern = `drop-shadow\\(\\s*(?:${colorSource}|${lvp})(?:\\s+(?:${colorSource}|${lvp})){2,3}\\s*\\)`;
|
|
1521
|
+
const dropShadowPattern = `drop-shadow\\(\\s*(?:${varString}|(?:${colorSource}|${lvp})(?:\\s+(?:${colorSource}|${lvp})){2,3})\\s*\\)`;
|
|
1397
1522
|
const r = new RegExp(`^((?:${filterNumPattern}|${blurPattern}|${anglePatternFunc}|${dropShadowPattern}|(?:${urlString}|${gradientString}|${varString}\\s*)?)\\s*)+$`);
|
|
1398
1523
|
validator = (v) => r.test(v);
|
|
1399
1524
|
}
|
|
@@ -1408,9 +1533,12 @@ function getValidator(key) {
|
|
|
1408
1533
|
'step-end',
|
|
1409
1534
|
].join('|');
|
|
1410
1535
|
const zeroToOne = '(0(\\.\\d+)?|1(\\.0+)?|0?\\.\\d+)';
|
|
1411
|
-
const cubicBezierPattern = `cubic-bezier\\(\\s
|
|
1412
|
-
const linearPattern = `linear\\(\\s*(${zeroToOne}(\\s+\\d+(\\.\\d+)?%){0,2}(\\s*,\\s*${zeroToOne}(\\s+\\d+(\\.\\d+)?%){0,2})*)
|
|
1413
|
-
const
|
|
1536
|
+
const cubicBezierPattern = `cubic-bezier\\(\\s*(?:${varString}|${zeroToOne}\\s*,\\s*(-?\\d+(\\.\\d+)?)\\s*,\\s*${zeroToOne}\\s*,\\s*(-?\\d+(\\.\\d+)?))\\s*\\)`;
|
|
1537
|
+
const linearPattern = `linear\\(\\s*(?:${varString}|(${zeroToOne}(\\s+\\d+(\\.\\d+)?%){0,2}(\\s*,\\s*${zeroToOne}(\\s+\\d+(\\.\\d+)?%){0,2})*)+)\\s*\\)`;
|
|
1538
|
+
const positiveIntegerPattern = '[1-9]\\d*';
|
|
1539
|
+
const atLeastTwoIntegerPattern = '(?:[2-9]|[1-9]\\d+)';
|
|
1540
|
+
const stepPositionPattern = `(?:jump-start|jump-end|jump-both|start|end|${varString})`;
|
|
1541
|
+
const stepPattern = `steps\\(\\s*(?:${varString}|(?:${positiveIntegerPattern}|${varString})(?:\\s*,\\s*${stepPositionPattern})?|(?:${atLeastTwoIntegerPattern}|${varString})\\s*,\\s*jump-none)\\s*\\)`;
|
|
1414
1542
|
const singlePat = `(${easingPattern}|${cubicBezierPattern}|${linearPattern}|${stepPattern}|${varString})`;
|
|
1415
1543
|
const r = new RegExp(`^${singlePat}(\\s*,\\s*${singlePat})*$`);
|
|
1416
1544
|
validator = (v) => r.test(v);
|
|
@@ -1607,13 +1735,14 @@ exports.validateValues = {
|
|
|
1607
1735
|
if (typeof rawValue !== 'string')
|
|
1608
1736
|
return;
|
|
1609
1737
|
const value = rawValue;
|
|
1738
|
+
const normalized = normalizeCssVariables(value);
|
|
1610
1739
|
const globalValue = !validData_1.validData[key].includes(value) &&
|
|
1611
1740
|
!globalValues.includes(value) &&
|
|
1612
|
-
!
|
|
1741
|
+
!normalized?.isStandalone;
|
|
1613
1742
|
if (!globalValue)
|
|
1614
1743
|
return;
|
|
1615
1744
|
const validator = getValidator(key);
|
|
1616
|
-
if (validator && !validator(value)) {
|
|
1745
|
+
if (validator && (!normalized || !validator(normalized.value))) {
|
|
1617
1746
|
context.report({
|
|
1618
1747
|
node: property.value,
|
|
1619
1748
|
messageId: 'validateValue',
|