Design Token Lint

Type to search...

to open search from anywhere

Programmatic API

CreatedApr 10, 2026UpdatedApr 13, 2026Takeshi Takatsudo

Use design-token-lint as a library from Node.js — lint strings, files, or individual class names.

@takazudo/zudo-design-token-lint exports a small API for integration with build tools, editors, or custom tooling.

Installation

pnpm add @takazudo/zudo-design-token-lint

Exports

import {
  // File/content linting
  lintFile,
  lintContent,
  type LintResult,

  // Single-class checking
  checkClass,
  checkClassWithConfig,
  type Violation,

  // Config loading and compilation
  loadConfig,
  compileConfig,
  compilePattern,
  setConfig,
  getConfig,
  DEFAULT_CONFIG,
  type LintConfig,
  type CompiledConfig,
  type CompiledRule,

  // Class extraction
  extractClasses,
  DEFAULT_CLASS_ATTRIBUTES,
  DEFAULT_CLASS_FUNCTIONS,
  type ExtractedClass,
  type ExtractorOptions,
} from '@takazudo/zudo-design-token-lint';

Linting Files and Content

lintFile(filePath)

Read a file from disk and return an array of lint results — one entry per violation.

const results = await lintFile('src/App.tsx');
for (const r of results) {
  console.log(`${r.filePath}:${r.line}  ${r.className}  ${r.reason}`);
}

Returns Promise<LintResult[]>:

interface LintResult {
  filePath: string;
  line: number;
  className: string;
  reason: string;
}

Each entry is a flat record for one violation. If the file has no violations, the returned array is empty.

lintContent(filePath, content)

Lint a string directly — useful for editor plugins or in-memory content. Returns LintResult[] (same shape as above).

const results = lintContent('file.tsx', '<div className="p-4 bg-gray-500">');
// [
//   { filePath: 'file.tsx', line: 1, className: 'p-4', reason: '...' },
//   { filePath: 'file.tsx', line: 1, className: 'bg-gray-500', reason: '...' }
// ]

Checking a Single Class

checkClass(className)

Check one class name against the active config. Returns a Violation if the class is prohibited, or null if it passes.

const violation = checkClass('p-4');
if (violation) {
  console.error(violation.reason);
  // "Numeric spacing \"p-4\" — use semantic token (hgap-*/vgap-*) or arbitrary value"
}

Returns Violation | null:

interface Violation {
  className: string;
  reason: string;
}

checkClassWithConfig(className, compiledConfig)

Same as above, but with an explicit compiled config instead of the global one.

import { loadConfig, compileConfig, checkClassWithConfig } from '@takazudo/zudo-design-token-lint';

const config = await loadConfig(process.cwd());
const compiled = compileConfig(config);

const violation = checkClassWithConfig('bg-blue-500', compiled);

Working with Config

loadConfig(cwd)

Load .design-token-lint.json or design-token-lint.config.json from a directory. Falls back to DEFAULT_CONFIG if neither exists.

const config = await loadConfig(process.cwd());

compileConfig(config)

Compile a plain config object into an efficient rule set ready for matching.

const compiled = compileConfig({
  prohibited: ['p-{n}', 'bg-{color}-{shade}'],
  allowed: ['p-0'],
  ignore: [],
});

setConfig(compiled) / getConfig()

Set or get the global compiled config used by checkClass() and lintFile().

setConfig(compiled);
const active = getConfig();

compilePattern(pattern)

Compile a single pattern string (like p-{n}) into a CompiledRule.

const rule = compilePattern('bg-{color}-{shade}');
// { prefix: 'bg', valuePattern: /^(slate|gray|...)-(\d{2,3})$/, reasonTemplate: '...', isSpacingRule: false }

Extracting Classes

extractClasses(content, options?)

Extract all class name tokens from a source file string, with their line numbers.

const extracted = extractClasses('<div className="p-4 bg-red-500">');
// [
//   { className: 'p-4', line: 1 },
//   { className: 'bg-red-500', line: 1 }
// ]

Accepts an optional options parameter to customize which attributes and functions are scanned:

const extracted = extractClasses(content, {
  classAttributes: ['className', 'class', 'inputClassName'],
  classFunctions: ['cn', 'clsx', 'cva', 'tv'],
});

Returns ExtractedClass[]:

interface ExtractedClass {
  className: string;
  line: number;
}

Supported syntaxes by default:

  • className="..." and class="..." (JSX/Astro)
  • className={'...'} single-quote brace
  • className={`...`} template literals (simple cases)
  • class:list={["...", '...']} Astro class
    arrays (always scanned)
  • cn(...), clsx(...), classNames(...), twMerge(...) utility calls

DEFAULT_CLASS_ATTRIBUTES

The default list of attribute names scanned by extractClasses:

const DEFAULT_CLASS_ATTRIBUTES: string[];
// ["className", "class"]

DEFAULT_CLASS_FUNCTIONS

The default list of utility function names scanned by extractClasses:

const DEFAULT_CLASS_FUNCTIONS: string[];
// ["cn", "clsx", "classNames", "twMerge"]

Types

LintConfig

interface LintConfig {
  prohibited: string[];
  allowed: string[];
  ignore: string[];
  patterns?: string[];
  suggestionSuffix?: string;
  classAttributes?: string[];
  classFunctions?: string[];
}

ExtractorOptions

Options passed to extractClasses() to customize which attributes and functions are scanned.

interface ExtractorOptions {
  classAttributes?: string[];
  classFunctions?: string[];
}

Both fields are optional. When omitted, DEFAULT_CLASS_ATTRIBUTES and DEFAULT_CLASS_FUNCTIONS are used respectively.

LintResult

interface LintResult {
  filePath: string;
  line: number;
  className: string;
  reason: string;
}

Violation

interface Violation {
  className: string;
  reason: string;
}

Example: Custom Linter Script

import { glob } from 'glob';
import {
  loadConfig,
  compileConfig,
  setConfig,
  lintFile,
} from '@takazudo/zudo-design-token-lint';

async function main() {
  const config = await loadConfig(process.cwd());
  setConfig(compileConfig(config));

  const files = await glob('src/**/*.{tsx,jsx}');
  let totalViolations = 0;

  for (const file of files) {
    const results = await lintFile(file);
    for (const r of results) {
      console.log(`${r.filePath}:${r.line}  ${r.className}  ${r.reason}`);
      totalViolations++;
    }
  }

  process.exit(totalViolations > 0 ? 1 : 0);
}

main();

Revision History