@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.
@@ -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 varString = `var\\(${dashedIdentString}(,\\s*[^\\)]+)?\\)`;
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 = 'url\\([^\\)]+\\)';
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 repeatString = 'repeat\\([^\\)]+\\)';
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 (token.startsWith('var(') && varRegex.test(token))
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 (token.startsWith('var(') && varRegex.test(token))
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*${zeroToOne}\\s*,\\s*(-?\\d+(\\.\\d+)?)\\s*,\\s*${zeroToOne}\\s*,\\s*(-?\\d+(\\.\\d+)?)\\s*\\)`;
1412
- const linearPattern = `linear\\(\\s*(${zeroToOne}(\\s+\\d+(\\.\\d+)?%){0,2}(\\s*,\\s*${zeroToOne}(\\s+\\d+(\\.\\d+)?%){0,2})*)+\\s*\\)`;
1413
- const stepPattern = `steps\\(\\s*(\\d+)\\s*,\\s*(jump-start|jump-end|jump-none|jump-both|start|end)\\s*\\)`;
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
- !varRegex.test(value);
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',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/eslint-plugin",
3
- "version": "18.2.28",
3
+ "version": "18.2.29",
4
4
  "description": "Plumeria ESLint plugin",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",