stylelint-plugin-rhythmguard 2.0.0 → 2.0.1

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/CHANGELOG.md CHANGED
@@ -6,6 +6,18 @@ The format follows Keep a Changelog principles and semantic versioning.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [2.0.1] - 2026-06-17
10
+
11
+ ### Added
12
+
13
+ - Added TypeScript declaration files for the public plugin, config, rule, preset, ESLint companion, and Audit 2.0 API exports.
14
+ - Added dependency-free Audit 2.0 dashboard and Figma-friendly export examples under `examples/`.
15
+ - Added CI adoption and motion-default evidence docs for safer baseline-based rollout.
16
+
17
+ ### Fixed
18
+
19
+ - Fixed `rhythmguard init` scripted input handling so multiple prompts work reliably in piped/CI contexts.
20
+
9
21
  ## [2.0.0] - 2026-05-23
10
22
 
11
23
  ### Changed
package/README.md CHANGED
@@ -291,6 +291,8 @@ Framework-specific setup for Vue, Lit, Astro, and SvelteKit: [`docs/FRAMEWORKS.m
291
291
 
292
292
  - Side-by-side tool fit guide with migration snippets: [`docs/COMPARISON.md`](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/blob/main/docs/COMPARISON.md)
293
293
  - Audit 2.0 validation and roadmap: [`docs/AUDIT_2_VALIDATION.md`](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/blob/main/docs/AUDIT_2_VALIDATION.md)
294
+ - CI baseline rollout recipe: [`docs/CI_ADOPTION.md`](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/blob/main/docs/CI_ADOPTION.md)
295
+ - Programmatic dashboard and Figma-friendly export examples: [`docs/AUDIT_API_EXAMPLES.md`](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/blob/main/docs/AUDIT_API_EXAMPLES.md)
294
296
  - Real-world before/after excerpts from public repos: [`docs/ADOPTION_DIFFS.md`](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/blob/main/docs/ADOPTION_DIFFS.md)
295
297
  - Distribution submissions to Stylelint discovery surfaces: [`docs/DISTRIBUTION.md`](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/blob/main/docs/DISTRIBUTION.md)
296
298
 
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { createAuditReport, toAuditContractReport } from 'stylelint-plugin-rhythmguard/audit';
6
+
7
+ function readOption(argv, name, fallback) {
8
+ const index = argv.indexOf(name);
9
+ if (index === -1 || !argv[index + 1] || argv[index + 1].startsWith('--')) {
10
+ return fallback;
11
+ }
12
+
13
+ return argv[index + 1];
14
+ }
15
+
16
+ function escapeHtml(value) {
17
+ return String(value)
18
+ .replace(/&/g, '&')
19
+ .replace(/</g, '&lt;')
20
+ .replace(/>/g, '&gt;')
21
+ .replace(/"/g, '&quot;');
22
+ }
23
+
24
+ function collectFindings(findings) {
25
+ return [
26
+ ...(findings.css || []),
27
+ ...(findings.tailwind || []),
28
+ ...(findings.motion || []),
29
+ ];
30
+ }
31
+
32
+ function renderMetric(label, value) {
33
+ return `<article class="metric"><span>${escapeHtml(label)}</span><strong>${escapeHtml(value)}</strong></article>`;
34
+ }
35
+
36
+ function renderDashboard(contract) {
37
+ const findings = collectFindings(contract.findings);
38
+ const topFindings = findings.slice(0, 12);
39
+ const cleanliness = contract.contracts.scale.cleanliness;
40
+ const score = typeof cleanliness === 'number' ? cleanliness : contract.summary.cleanliness;
41
+
42
+ return `<!doctype html>
43
+ <html lang="en">
44
+ <head>
45
+ <meta charset="utf-8">
46
+ <meta name="viewport" content="width=device-width, initial-scale=1">
47
+ <title>Rhythmguard Audit Dashboard</title>
48
+ <style>
49
+ body{font-family:Inter,ui-sans-serif,system-ui,sans-serif;margin:0;background:#f7f8fa;color:#111827;}
50
+ main{max-width:1120px;margin:0 auto;padding:32px 20px;}
51
+ h1{font-size:28px;margin:0 0 8px;}
52
+ p{color:#4b5563;margin:0 0 24px;}
53
+ .metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin:24px 0;}
54
+ .metric{border:1px solid #d8dde5;background:#fff;border-radius:8px;padding:16px;}
55
+ .metric span{display:block;color:#6b7280;font-size:12px;text-transform:uppercase;}
56
+ .metric strong{display:block;font-size:28px;margin-top:8px;}
57
+ table{width:100%;border-collapse:collapse;background:#fff;border:1px solid #d8dde5;border-radius:8px;overflow:hidden;}
58
+ th,td{text-align:left;border-bottom:1px solid #e5e7eb;padding:10px 12px;font-size:14px;}
59
+ th{background:#f1f5f9;color:#374151;font-size:12px;text-transform:uppercase;}
60
+ tr:last-child td{border-bottom:0;}
61
+ code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;}
62
+ </style>
63
+ </head>
64
+ <body>
65
+ <main>
66
+ <h1>Rhythmguard Audit Dashboard</h1>
67
+ <p>${escapeHtml(contract.command.directory)} · ${escapeHtml(contract.command.scanScope)} · schema ${escapeHtml(contract.schemaVersion)}</p>
68
+ <section class="metrics">
69
+ ${renderMetric('Scale cleanliness', `${score ?? 100}%`)}
70
+ ${renderMetric('CSS files', contract.scanned.cssFiles)}
71
+ ${renderMetric('Template files', contract.scanned.templateFiles)}
72
+ ${renderMetric('Findings', findings.length)}
73
+ </section>
74
+ <table>
75
+ <thead><tr><th>Type</th><th>File</th><th>Value</th><th>Message</th></tr></thead>
76
+ <tbody>
77
+ ${topFindings.map((finding) => `<tr><td>${escapeHtml(finding.type)}</td><td><code>${escapeHtml(finding.file)}</code></td><td><code>${escapeHtml(finding.value || finding.rawValue || '')}</code></td><td>${escapeHtml(finding.message || '')}</td></tr>`).join('\n')}
78
+ </tbody>
79
+ </table>
80
+ </main>
81
+ </body>
82
+ </html>`;
83
+ }
84
+
85
+ const argv = process.argv.slice(2);
86
+ const dir = readOption(argv, '--dir', argv.find((arg) => !arg.startsWith('--')) || 'src');
87
+ const output = readOption(argv, '--output', 'rhythmguard-dashboard.html');
88
+ const includeMotion = argv.includes('--include-motion');
89
+
90
+ const report = await createAuditReport({ dir, includeMotion });
91
+ const contract = toAuditContractReport(report);
92
+ const outputPath = path.resolve(process.cwd(), output);
93
+
94
+ fs.writeFileSync(outputPath, renderDashboard(contract));
95
+ process.stdout.write(`Wrote ${outputPath}\n`);
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { createAuditReport, toAuditContractReport } from 'stylelint-plugin-rhythmguard/audit';
6
+
7
+ function readOption(argv, name, fallback) {
8
+ const index = argv.indexOf(name);
9
+ if (index === -1 || !argv[index + 1] || argv[index + 1].startsWith('--')) {
10
+ return fallback;
11
+ }
12
+
13
+ return argv[index + 1];
14
+ }
15
+
16
+ function collectFindings(findings) {
17
+ return [
18
+ ...(findings.css || []),
19
+ ...(findings.tailwind || []),
20
+ ...(findings.motion || []),
21
+ ];
22
+ }
23
+
24
+ function countBy(items, getKey) {
25
+ const counts = new Map();
26
+ for (const item of items) {
27
+ const key = getKey(item) || 'unknown';
28
+ counts.set(key, (counts.get(key) || 0) + 1);
29
+ }
30
+
31
+ return [...counts.entries()]
32
+ .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
33
+ .map(([label, value]) => ({ label, value }));
34
+ }
35
+
36
+ function buildFigmaPayload(contract) {
37
+ const findings = collectFindings(contract.findings);
38
+ const cleanliness = contract.contracts.scale.cleanliness;
39
+
40
+ return {
41
+ schema: 'rhythmguard.figma-export.v1',
42
+ generatedAt: new Date().toISOString(),
43
+ source: {
44
+ directory: contract.command.directory,
45
+ scanScope: contract.command.scanScope,
46
+ schemaVersion: contract.schemaVersion,
47
+ },
48
+ summaryCards: [
49
+ {
50
+ label: 'Scale cleanliness',
51
+ value: `${typeof cleanliness === 'number' ? cleanliness : contract.summary.cleanliness ?? 100}%`,
52
+ },
53
+ { label: 'CSS files', value: contract.scanned.cssFiles },
54
+ { label: 'Template files', value: contract.scanned.templateFiles },
55
+ { label: 'Findings', value: findings.length },
56
+ ],
57
+ charts: {
58
+ findingsByType: countBy(findings, (finding) => finding.type),
59
+ findingsByFile: countBy(findings, (finding) => finding.file).slice(0, 10),
60
+ },
61
+ findings: findings.slice(0, 25).map((finding) => ({
62
+ file: finding.file,
63
+ line: finding.line || null,
64
+ message: finding.message || '',
65
+ type: finding.type,
66
+ value: finding.value || finding.rawValue || null,
67
+ })),
68
+ };
69
+ }
70
+
71
+ const argv = process.argv.slice(2);
72
+ const dir = readOption(argv, '--dir', argv.find((arg) => !arg.startsWith('--')) || 'src');
73
+ const output = readOption(argv, '--output', 'rhythmguard-figma-export.json');
74
+ const includeMotion = argv.includes('--include-motion');
75
+
76
+ const report = await createAuditReport({ dir, includeMotion });
77
+ const contract = toAuditContractReport(report);
78
+ const outputPath = path.resolve(process.cwd(), output);
79
+
80
+ fs.writeFileSync(outputPath, `${JSON.stringify(buildFigmaPayload(contract), null, 2)}\n`);
81
+ process.stdout.write(`Wrote ${outputPath}\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stylelint-plugin-rhythmguard",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Token governance for CSS and Tailwind — enforce spacing scales, require design tokens, catch arbitrary values",
5
5
  "bin": {
6
6
  "rhythmguard": "src/cli/index.js"
@@ -20,76 +20,95 @@
20
20
  ],
21
21
  "type": "commonjs",
22
22
  "main": "src/index.js",
23
+ "types": "./types/index.d.ts",
23
24
  "exports": {
24
25
  ".": {
26
+ "types": "./types/index.d.ts",
25
27
  "require": "./src/index.js",
26
28
  "import": "./src/index.mjs"
27
29
  },
28
30
  "./configs/recommended": {
31
+ "types": "./types/config.d.ts",
29
32
  "require": "./src/configs/recommended.js",
30
33
  "import": "./src/configs/recommended.mjs"
31
34
  },
32
35
  "./configs/strict": {
36
+ "types": "./types/config.d.ts",
33
37
  "require": "./src/configs/strict.js",
34
38
  "import": "./src/configs/strict.mjs"
35
39
  },
36
40
  "./configs/tailwind": {
41
+ "types": "./types/config.d.ts",
37
42
  "require": "./src/configs/tailwind.js",
38
43
  "import": "./src/configs/tailwind.mjs"
39
44
  },
40
45
  "./configs/expanded": {
46
+ "types": "./types/config.d.ts",
41
47
  "require": "./src/configs/expanded.js",
42
48
  "import": "./src/configs/expanded.mjs"
43
49
  },
44
50
  "./configs/logical": {
51
+ "types": "./types/config.d.ts",
45
52
  "require": "./src/configs/logical.js",
46
53
  "import": "./src/configs/logical.mjs"
47
54
  },
48
55
  "./configs/migration": {
56
+ "types": "./types/config.d.ts",
49
57
  "require": "./src/configs/migration.js",
50
58
  "import": "./src/configs/migration.mjs"
51
59
  },
52
60
  "./configs/motion": {
61
+ "types": "./types/config.d.ts",
53
62
  "require": "./src/configs/motion.js",
54
63
  "import": "./src/configs/motion.mjs"
55
64
  },
56
65
  "./configs/react-tailwind": {
66
+ "types": "./types/config.d.ts",
57
67
  "require": "./src/configs/react-tailwind.js",
58
68
  "import": "./src/configs/react-tailwind.mjs"
59
69
  },
60
70
  "./presets": {
71
+ "types": "./types/presets.d.ts",
61
72
  "require": "./src/presets/index.js",
62
73
  "import": "./src/presets/index.mjs"
63
74
  },
64
75
  "./audit": {
76
+ "types": "./types/audit.d.ts",
65
77
  "require": "./src/audit/index.js",
66
78
  "import": "./src/audit/index.mjs"
67
79
  },
68
80
  "./rules/use-scale": {
81
+ "types": "./types/rule.d.ts",
69
82
  "require": "./src/rules/use-scale/index.js",
70
83
  "import": "./src/rules/use-scale/index.mjs"
71
84
  },
72
85
  "./rules/prefer-token": {
86
+ "types": "./types/rule.d.ts",
73
87
  "require": "./src/rules/prefer-token/index.js",
74
88
  "import": "./src/rules/prefer-token/index.mjs"
75
89
  },
76
90
  "./rules/no-offscale-transform": {
91
+ "types": "./types/rule.d.ts",
77
92
  "require": "./src/rules/no-offscale-transform/index.js",
78
93
  "import": "./src/rules/no-offscale-transform/index.mjs"
79
94
  },
80
95
  "./rules/use-motion-scale": {
96
+ "types": "./types/rule.d.ts",
81
97
  "require": "./src/rules/use-motion-scale/index.js",
82
98
  "import": "./src/rules/use-motion-scale/index.mjs"
83
99
  },
84
100
  "./eslint": {
101
+ "types": "./types/eslint.d.ts",
85
102
  "require": "./src/eslint/index.js",
86
103
  "import": "./src/eslint/index.mjs"
87
104
  }
88
105
  },
89
106
  "files": [
107
+ "examples",
90
108
  "scales",
91
109
  "schemas",
92
110
  "src",
111
+ "types",
93
112
  "README.md",
94
113
  "CHANGELOG.md",
95
114
  "CONTRIBUTING.md",
package/src/cli/init.js CHANGED
@@ -4,17 +4,39 @@ const fs = require('node:fs');
4
4
  const path = require('node:path');
5
5
  const readline = require('node:readline');
6
6
 
7
- function ask(question) {
7
+ function createPrompter() {
8
+ if (!process.stdin.isTTY) {
9
+ const answers = fs.readFileSync(0, 'utf8').split(/\r?\n/);
10
+ let answerIndex = 0;
11
+
12
+ return {
13
+ ask(question) {
14
+ process.stdout.write(question);
15
+ const answer = answers[answerIndex] || '';
16
+ answerIndex += 1;
17
+ return Promise.resolve(answer.trim().toLowerCase());
18
+ },
19
+ close() {},
20
+ };
21
+ }
22
+
8
23
  const rl = readline.createInterface({
9
24
  input: process.stdin,
10
25
  output: process.stdout,
11
26
  });
12
- return new Promise((resolve) => {
13
- rl.question(question, (answer) => {
27
+
28
+ return {
29
+ ask(question) {
30
+ return new Promise((resolve) => {
31
+ rl.question(question, (answer) => {
32
+ resolve(answer.trim().toLowerCase());
33
+ });
34
+ });
35
+ },
36
+ close() {
14
37
  rl.close();
15
- resolve(answer.trim().toLowerCase());
16
- });
17
- });
38
+ },
39
+ };
18
40
  }
19
41
 
20
42
  function detect() {
@@ -80,49 +102,55 @@ function selectProfile(stack) {
80
102
  }
81
103
 
82
104
  async function run() {
83
- process.stdout.write('\nRhythmguard Init\n\n');
84
-
85
- const stack = detect();
86
-
87
- // Report detection
88
- const detected = [];
89
- if (stack.tailwind) detected.push('Tailwind CSS');
90
- if (stack.nextjs) detected.push('Next.js');
91
- if (detected.length > 0) {
92
- process.stdout.write(`Detected: ${detected.join(', ')}\n`);
93
- } else {
94
- process.stdout.write('Detected: plain CSS project\n');
95
- }
105
+ const prompter = createPrompter();
106
+
107
+ try {
108
+ process.stdout.write('\nRhythmguard Init\n\n');
109
+
110
+ const stack = detect();
111
+
112
+ // Report detection
113
+ const detected = [];
114
+ if (stack.tailwind) detected.push('Tailwind CSS');
115
+ if (stack.nextjs) detected.push('Next.js');
116
+ if (detected.length > 0) {
117
+ process.stdout.write(`Detected: ${detected.join(', ')}\n`);
118
+ } else {
119
+ process.stdout.write('Detected: plain CSS project\n');
120
+ }
96
121
 
97
- // Warn about existing config
98
- if (stack.hasExistingConfig) {
99
- process.stdout.write('\n⚠ Existing Stylelint config found.\n');
100
- const answer = await ask('Overwrite? (y/n) ');
122
+ // Warn about existing config
123
+ if (stack.hasExistingConfig) {
124
+ process.stdout.write('\n⚠ Existing Stylelint config found.\n');
125
+ const answer = await prompter.ask('Overwrite? (y/n) ');
126
+ if (answer !== 'y' && answer !== 'yes') {
127
+ process.stdout.write('Aborted.\n');
128
+ process.exit(0);
129
+ }
130
+ }
131
+
132
+ const profile = selectProfile(stack);
133
+ process.stdout.write(`\nProfile: ${profile}\n`);
134
+
135
+ const answer = await prompter.ask('Write .stylelintrc.json? (y/n) ');
101
136
  if (answer !== 'y' && answer !== 'yes') {
102
137
  process.stdout.write('Aborted.\n');
103
138
  process.exit(0);
104
139
  }
105
- }
106
140
 
107
- const profile = selectProfile(stack);
108
- process.stdout.write(`\nProfile: ${profile}\n`);
141
+ const config = {
142
+ extends: [`stylelint-plugin-rhythmguard/configs/${profile}`],
143
+ };
109
144
 
110
- const answer = await ask('Write .stylelintrc.json? (y/n) ');
111
- if (answer !== 'y' && answer !== 'yes') {
112
- process.stdout.write('Aborted.\n');
113
- process.exit(0);
114
- }
145
+ const configPath = path.join(process.cwd(), '.stylelintrc.json');
146
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
115
147
 
116
- const config = {
117
- extends: [`stylelint-plugin-rhythmguard/configs/${profile}`],
118
- };
119
-
120
- const configPath = path.join(process.cwd(), '.stylelintrc.json');
121
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
122
-
123
- process.stdout.write(`\n✓ Wrote ${configPath}\n`);
124
- process.stdout.write(`\nNext steps:\n`);
125
- process.stdout.write(` npx stylelint "src/**/*.css"\n\n`);
148
+ process.stdout.write(`\n✓ Wrote ${configPath}\n`);
149
+ process.stdout.write(`\nNext steps:\n`);
150
+ process.stdout.write(` npx stylelint "src/**/*.css"\n\n`);
151
+ } finally {
152
+ prompter.close();
153
+ }
126
154
  }
127
155
 
128
156
  run();
@@ -0,0 +1,156 @@
1
+ export type AuditTokenKind =
2
+ | "all"
3
+ | "motion"
4
+ | "radius"
5
+ | "size"
6
+ | "spacing"
7
+ | "typography";
8
+
9
+ export type AuditTokenSourceFormat =
10
+ | "auto"
11
+ | "css"
12
+ | "dtcg"
13
+ | "flat-json"
14
+ | "style-dictionary";
15
+
16
+ export interface AuditTokenSource {
17
+ baseDir?: string;
18
+ file?: string;
19
+ format?: AuditTokenSourceFormat;
20
+ path?: string;
21
+ }
22
+
23
+ export interface AuditOptions {
24
+ baseFontSize?: number;
25
+ baselinePath?: string;
26
+ config?: string;
27
+ dir?: string;
28
+ failOnNewDrift?: boolean;
29
+ format?: "json" | "json-v1" | "markdown" | "text" | "html";
30
+ ignorePath?: string;
31
+ ignorePatterns?: string[];
32
+ includeMotion?: boolean;
33
+ maxFindings?: number;
34
+ minCleanliness?: number;
35
+ output?: string;
36
+ scale?: Array<number | string>;
37
+ since?: string;
38
+ sinceBaseline?: boolean;
39
+ staged?: boolean;
40
+ tokenCandidateMinCount?: number;
41
+ tokenKind?: AuditTokenKind;
42
+ tokenSources?: AuditTokenSource[];
43
+ writeBaseline?: boolean;
44
+ }
45
+
46
+ export interface AuditSummary {
47
+ cleanliness: number;
48
+ filesWithFindings: number;
49
+ findingCount: number;
50
+ motionFindingCount?: number;
51
+ tokenOpportunityCount?: number;
52
+ [key: string]: unknown;
53
+ }
54
+
55
+ export interface AuditScanned {
56
+ cssFiles: number;
57
+ templateFiles: number;
58
+ totalFiles?: number;
59
+ [key: string]: unknown;
60
+ }
61
+
62
+ export interface AuditFinding {
63
+ column?: number;
64
+ file: string;
65
+ key?: string;
66
+ line?: number;
67
+ message: string;
68
+ property?: string;
69
+ rule?: string;
70
+ type: string;
71
+ value?: string;
72
+ [key: string]: unknown;
73
+ }
74
+
75
+ export interface AuditBaselineComparison {
76
+ baselineFindings: number;
77
+ newFindings: AuditFinding[];
78
+ newFindingsCount: number;
79
+ resolvedFindings: AuditFinding[];
80
+ resolvedFindingsCount: number;
81
+ [key: string]: unknown;
82
+ }
83
+
84
+ export interface AuditReport {
85
+ baseline?: AuditBaselineComparison | null;
86
+ config?: string | null;
87
+ directory: string;
88
+ findings: {
89
+ css: AuditFinding[];
90
+ motion: AuditFinding[];
91
+ tailwind: AuditFinding[];
92
+ };
93
+ scanned: AuditScanned;
94
+ summary: AuditSummary;
95
+ [key: string]: unknown;
96
+ }
97
+
98
+ export interface AuditContractReport {
99
+ baseline: AuditBaselineComparison | null;
100
+ command: {
101
+ config?: string | null;
102
+ directory: string;
103
+ scanScope: string;
104
+ };
105
+ contracts: {
106
+ motion?: unknown;
107
+ scale: {
108
+ cleanliness?: unknown;
109
+ offScaleValues?: unknown;
110
+ tokenOpportunities?: unknown;
111
+ };
112
+ tokens?: unknown;
113
+ };
114
+ findings: unknown;
115
+ scanned: AuditScanned;
116
+ schemaVersion: "2.0";
117
+ summary: AuditSummary;
118
+ }
119
+
120
+ export interface TokenSourceReport {
121
+ file: string;
122
+ format: AuditTokenSourceFormat;
123
+ requestedFormat: AuditTokenSourceFormat;
124
+ tokenCount: number;
125
+ warnings: string[];
126
+ }
127
+
128
+ export interface ParsedTokenSources {
129
+ definitions: Map<string, unknown>;
130
+ sources: TokenSourceReport[];
131
+ warnings: string[];
132
+ }
133
+
134
+ export const AUDIT_JSON_SCHEMA: Readonly<Record<string, unknown>>;
135
+
136
+ export function createAuditReport(options?: AuditOptions): Promise<AuditReport>;
137
+
138
+ export function loadAuditConfig(options?: AuditOptions): Record<string, unknown>;
139
+
140
+ export function parseTokenSources(options?: {
141
+ baseFontSize?: number;
142
+ sources?: AuditTokenSource[];
143
+ tokenKind?: AuditTokenKind;
144
+ }): ParsedTokenSources;
145
+
146
+ export function toAuditContractReport(report: AuditReport): AuditContractReport;
147
+
148
+ declare const audit: {
149
+ AUDIT_JSON_SCHEMA: typeof AUDIT_JSON_SCHEMA;
150
+ createAuditReport: typeof createAuditReport;
151
+ loadAuditConfig: typeof loadAuditConfig;
152
+ parseTokenSources: typeof parseTokenSources;
153
+ toAuditContractReport: typeof toAuditContractReport;
154
+ };
155
+
156
+ export default audit;
@@ -0,0 +1,5 @@
1
+ import type { RhythmguardStylelintConfig } from "./shared";
2
+
3
+ declare const config: RhythmguardStylelintConfig;
4
+
5
+ export default config;
@@ -0,0 +1,10 @@
1
+ import type { RhythmguardEslintPlugin } from "./shared";
2
+
3
+ export type { EslintRuleModule, RhythmguardEslintPlugin } from "./shared";
4
+
5
+ export const configs: RhythmguardEslintPlugin["configs"];
6
+ export const rules: RhythmguardEslintPlugin["rules"];
7
+
8
+ declare const plugin: RhythmguardEslintPlugin;
9
+
10
+ export default plugin;
@@ -0,0 +1,65 @@
1
+ import type {
2
+ RhythmguardEslintPlugin,
3
+ RhythmguardPresets,
4
+ RhythmguardStylelintConfig,
5
+ } from "./shared";
6
+ import type * as auditApi from "./audit";
7
+ import type { StylelintRuleModule } from "./rule";
8
+
9
+ export type {
10
+ EslintRuleModule,
11
+ RhythmguardEslintPlugin,
12
+ RhythmguardPresets,
13
+ RhythmguardRuleConfig,
14
+ RhythmguardRuleOptions,
15
+ RhythmguardStylelintConfig,
16
+ ScalePresetMetadata,
17
+ ScaleValue,
18
+ } from "./shared";
19
+ export type {
20
+ AuditBaselineComparison,
21
+ AuditContractReport,
22
+ AuditFinding,
23
+ AuditOptions,
24
+ AuditReport,
25
+ AuditScanned,
26
+ AuditSummary,
27
+ AuditTokenKind,
28
+ AuditTokenSource,
29
+ AuditTokenSourceFormat,
30
+ ParsedTokenSources,
31
+ TokenSourceReport,
32
+ } from "./audit";
33
+ export type { StylelintRuleModule } from "./rule";
34
+
35
+ export interface RhythmguardPlugin extends Array<StylelintRuleModule> {
36
+ audit: typeof auditApi;
37
+ configs: {
38
+ expanded: RhythmguardStylelintConfig;
39
+ logical: RhythmguardStylelintConfig;
40
+ migration: RhythmguardStylelintConfig;
41
+ motion: RhythmguardStylelintConfig;
42
+ recommended: RhythmguardStylelintConfig;
43
+ strict: RhythmguardStylelintConfig;
44
+ tailwind: RhythmguardStylelintConfig;
45
+ [configName: string]: RhythmguardStylelintConfig;
46
+ };
47
+ eslint: RhythmguardEslintPlugin;
48
+ presets: RhythmguardPresets;
49
+ rules: {
50
+ "rhythmguard/no-offscale-transform": StylelintRuleModule;
51
+ "rhythmguard/prefer-token": StylelintRuleModule;
52
+ "rhythmguard/use-motion-scale": StylelintRuleModule;
53
+ "rhythmguard/use-scale": StylelintRuleModule;
54
+ [ruleName: string]: StylelintRuleModule;
55
+ };
56
+ }
57
+
58
+ declare const plugin: RhythmguardPlugin;
59
+
60
+ export const audit: typeof auditApi;
61
+ export const configs: RhythmguardPlugin["configs"];
62
+ export const eslint: RhythmguardEslintPlugin;
63
+ export const presets: RhythmguardPresets;
64
+ export const rules: RhythmguardPlugin["rules"];
65
+ export default plugin;
@@ -0,0 +1,14 @@
1
+ import type { RhythmguardPresets } from "./shared";
2
+
3
+ export type { RhythmguardPresets, ScalePresetMetadata } from "./shared";
4
+
5
+ export const communityScaleMetadata: RhythmguardPresets["communityScaleMetadata"];
6
+ export const getCommunityScaleMetadata: RhythmguardPresets["getCommunityScaleMetadata"];
7
+ export const getScalePreset: RhythmguardPresets["getScalePreset"];
8
+ export const listCommunityScalePresetNames: RhythmguardPresets["listCommunityScalePresetNames"];
9
+ export const listScalePresetNames: RhythmguardPresets["listScalePresetNames"];
10
+ export const scales: RhythmguardPresets["scales"];
11
+
12
+ declare const presets: RhythmguardPresets;
13
+
14
+ export default presets;
@@ -0,0 +1,20 @@
1
+ import type { RhythmguardRuleOptions } from "./shared";
2
+
3
+ export interface StylelintRuleModule {
4
+ messages: Record<string, unknown>;
5
+ meta: {
6
+ fixable?: boolean;
7
+ url?: string;
8
+ [key: string]: unknown;
9
+ };
10
+ ruleName: string;
11
+ [key: string]: unknown;
12
+ }
13
+
14
+ declare const rule: StylelintRuleModule;
15
+
16
+ export const messages: StylelintRuleModule["messages"];
17
+ export const meta: StylelintRuleModule["meta"];
18
+ export const ruleName: string;
19
+ export type { RhythmguardRuleOptions };
20
+ export default rule;
@@ -0,0 +1,66 @@
1
+ export type ScaleValue = number | string;
2
+
3
+ export type RuleSeverity = boolean | "always" | "never";
4
+
5
+ export interface RhythmguardRuleOptions {
6
+ baseFontSize?: number;
7
+ customScale?: ScaleValue[];
8
+ includeMathFunctions?: boolean;
9
+ preset?: string;
10
+ properties?: Array<string | RegExp>;
11
+ scale?: ScaleValue[];
12
+ tokenMap?: Record<string, string>;
13
+ tokenMapFile?: string;
14
+ tokenMapFromCssCustomProperties?: boolean;
15
+ tokenMapFromTailwindSpacing?: boolean;
16
+ tokenPattern?: string;
17
+ }
18
+
19
+ export type RhythmguardRuleConfig =
20
+ | null
21
+ | RuleSeverity
22
+ | [RuleSeverity, RhythmguardRuleOptions];
23
+
24
+ export interface RhythmguardStylelintConfig {
25
+ customSyntax?: string;
26
+ extends?: string | string[];
27
+ ignoreFiles?: string | string[];
28
+ plugins?: string[];
29
+ rules?: Record<string, RhythmguardRuleConfig>;
30
+ }
31
+
32
+ export interface ScalePresetMetadata {
33
+ aliases?: string[];
34
+ base?: number;
35
+ description?: string;
36
+ name?: string;
37
+ values?: number[];
38
+ [key: string]: unknown;
39
+ }
40
+
41
+ export interface RhythmguardPresets {
42
+ communityScaleMetadata: Record<string, ScalePresetMetadata>;
43
+ getCommunityScaleMetadata(name: string): ScalePresetMetadata | undefined;
44
+ getScalePreset(name: string): readonly number[] | undefined;
45
+ listCommunityScalePresetNames(): string[];
46
+ listScalePresetNames(): string[];
47
+ scales: Record<string, readonly number[]>;
48
+ }
49
+
50
+ export interface EslintRuleModule {
51
+ meta: Record<string, unknown>;
52
+ create(context: unknown): Record<string, unknown>;
53
+ }
54
+
55
+ export interface RhythmguardEslintPlugin {
56
+ configs: {
57
+ recommended: {
58
+ rules: Record<string, "off" | "warn" | "error" | number>;
59
+ };
60
+ };
61
+ rules: {
62
+ "tailwind-class-use-motion-scale": EslintRuleModule;
63
+ "tailwind-class-use-scale": EslintRuleModule;
64
+ [ruleName: string]: EslintRuleModule;
65
+ };
66
+ }