All files / packages/sds-stylelint-config/src/plugins design-token-global-pattern.ts

94.73% Statements 36/38
84.61% Branches 22/26
100% Functions 6/6
100% Lines 32/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93                            1x 1x                 50x 25x   24x 24x   24x 24x   22x 1x 1x     21x 21x 1x               1x     20x 50x   50x 3x     20x 50x 1x                       1x   1x 24x 24x 24x   24x 24x   24x 25x 50x                
import stylelint from 'stylelint';
import valueParser from 'postcss-value-parser';
import type { Declaration } from 'postcss';
import metadata, { getMergedMetadata } from '../metadata/metadata.js';
import {
  validateNamespace,
  validateGlobalTokens,
  tokenize,
  shouldValidate,
  formatErrors,
  formatLegacyWarning,
} from '../utils/index.js';
import type { RuleOptions } from '../types.js';
 
const { report, validateOptions } = stylelint.utils;
const ruleName = 'sds-stylelint-plugin/design-token-global-pattern';
 
function processNode(
  node: valueParser.WordNode,
  decl: Declaration,
  result: stylelint.PostcssResult,
  meta: ReturnType<typeof getMergedMetadata>,
  privatePrefix: string,
): void {
  if (node.type !== 'word') return;
  if (!shouldValidate(node.value, privatePrefix)) return;
 
  const tokens = tokenize(node.value);
  Iif (!tokens[1]) return;
 
  const nonGlobalScopes = new Set(['c', 's', 'r']);
  if (nonGlobalScopes.has(tokens[1])) return;
 
  if (tokens[1] !== 'g') {
    report({ ruleName, result, message: `"${node.value}" requires a scope of ["g"]`, node: decl });
    return;
  }
 
  const nsResult = validateNamespace(tokens[0], privatePrefix);
  if (!nsResult.valid) {
    report({
      ruleName,
      result,
      message: formatErrors(node.value, [
        { segment: 'namespace', expected: nsResult.names, received: tokens[0] },
      ]),
      node: decl,
    });
    return;
  }
 
  const aliases = meta.legacyPropertyAliases || {};
  const { errors, warnings } = validateGlobalTokens(tokens, meta, aliases);
 
  if (errors.length > 0) {
    report({ ruleName, result, message: formatErrors(node.value, errors), node: decl });
  }
 
  const legacyWarnings = warnings?.filter((w) => w.type === 'legacy-syntax') ?? [];
  if (legacyWarnings.length > 0) {
    report({
      ruleName,
      result,
      message: formatLegacyWarning(node.value, legacyWarnings),
      node: decl,
      severity: 'warning',
    });
  }
}
 
type Rule = Parameters<typeof stylelint.createPlugin>[1];
 
const messages = stylelint.utils.ruleMessages(ruleName, {});
 
const globalTokens = Object.assign(
  (primary: boolean, options?: RuleOptions) => (root: any, result: stylelint.PostcssResult) => {
    const valid = validateOptions(result, ruleName, { actual: primary, possible: [true] });
    Iif (!valid) return;
 
    const meta = getMergedMetadata(metadata, options ?? {});
    const privatePrefix = options?.privateSyntax || metadata.privateSyntax[0];
 
    root.walkDecls((decl: Declaration) => {
      valueParser(decl.value).walk((node: any) => {
        processNode(node, decl, result, meta, privatePrefix);
      });
    });
  },
  { ruleName, messages },
) satisfies Rule;
 
export default stylelint.createPlugin(ruleName, globalTokens);