stylelint-plugin-rhythmguard 2.0.0 → 2.1.0

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/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();
@@ -76,6 +76,7 @@ function buildMessage(analysis, segment, lower, upper) {
76
76
  module.exports = {
77
77
  meta: {
78
78
  docs: {
79
+ url: 'https://github.com/PetriLahdelma/stylelint-plugin-rhythmguard/blob/main/docs/rules/tailwind-class-use-motion-scale.md',
79
80
  description: 'Enforce duration scale for Tailwind arbitrary motion utilities in class strings',
80
81
  },
81
82
  fixable: 'code',
@@ -67,6 +67,7 @@ function maybeCheckNodeText(node, sourceCode, context, analyzer, allowFix) {
67
67
  module.exports = {
68
68
  meta: {
69
69
  docs: {
70
+ url: 'https://github.com/PetriLahdelma/stylelint-plugin-rhythmguard/blob/main/docs/rules/tailwind-class-use-scale.md',
70
71
  description: 'Enforce spacing scale for Tailwind arbitrary spacing utilities in class strings',
71
72
  },
72
73
  fixable: 'code',
package/src/index.js CHANGED
@@ -22,6 +22,7 @@ module.exports.configs = {
22
22
  logical: require('./configs/logical'),
23
23
  migration: require('./configs/migration'),
24
24
  motion: require('./configs/motion'),
25
+ 'react-tailwind': require('./configs/react-tailwind'),
25
26
  };
26
27
  module.exports.eslint = require('./eslint');
27
28
  module.exports.presets = require('./presets');
@@ -269,7 +269,7 @@ ruleFunction.ruleName = ruleName;
269
269
  ruleFunction.messages = messages;
270
270
  ruleFunction.meta = {
271
271
  fixable: true,
272
- url: 'https://github.com/petrilahdelma/stylelint-plugin-rhythmguard#rhythmguardno-offscale-transform',
272
+ url: 'https://github.com/PetriLahdelma/stylelint-plugin-rhythmguard/blob/main/docs/rules/no-offscale-transform.md',
273
273
  };
274
274
 
275
275
  module.exports = stylelint.createPlugin(ruleName, ruleFunction);
@@ -288,7 +288,7 @@ ruleFunction.ruleName = ruleName;
288
288
  ruleFunction.messages = messages;
289
289
  ruleFunction.meta = {
290
290
  fixable: true,
291
- url: 'https://github.com/petrilahdelma/stylelint-plugin-rhythmguard#rhythmguardprefer-token',
291
+ url: 'https://github.com/PetriLahdelma/stylelint-plugin-rhythmguard/blob/main/docs/rules/prefer-token.md',
292
292
  };
293
293
 
294
294
  module.exports = stylelint.createPlugin(ruleName, ruleFunction);
@@ -236,7 +236,7 @@ ruleFunction.ruleName = ruleName;
236
236
  ruleFunction.messages = messages;
237
237
  ruleFunction.meta = {
238
238
  fixable: true,
239
- url: 'https://github.com/petrilahdelma/stylelint-plugin-rhythmguard#rhythmguarduse-motion-scale',
239
+ url: 'https://github.com/PetriLahdelma/stylelint-plugin-rhythmguard/blob/main/docs/rules/use-motion-scale.md',
240
240
  };
241
241
 
242
242
  module.exports = stylelint.createPlugin(ruleName, ruleFunction);
@@ -332,7 +332,7 @@ ruleFunction.ruleName = ruleName;
332
332
  ruleFunction.messages = messages;
333
333
  ruleFunction.meta = {
334
334
  fixable: true,
335
- url: 'https://github.com/petrilahdelma/stylelint-plugin-rhythmguard#rhythmguarduse-scale',
335
+ url: 'https://github.com/PetriLahdelma/stylelint-plugin-rhythmguard/blob/main/docs/rules/use-scale.md',
336
336
  };
337
337
 
338
338
  module.exports = stylelint.createPlugin(ruleName, ruleFunction);
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Compile-time consumer of the public declarations.
3
+ * `npm run typecheck` fails if any published export or type drifts.
4
+ * Nothing here runs; it only has to type-check.
5
+ */
6
+ import plugin, { audit, configs, eslint, presets, rules } from 'stylelint-plugin-rhythmguard';
7
+ import type {
8
+ AuditContractReport,
9
+ AuditReport,
10
+ RhythmguardRuleOptions,
11
+ RhythmguardStylelintConfig,
12
+ } from 'stylelint-plugin-rhythmguard';
13
+ import { createAuditReport, toAuditContractReport } from 'stylelint-plugin-rhythmguard/audit';
14
+ import eslintPlugin from 'stylelint-plugin-rhythmguard/eslint';
15
+ import { getScalePreset, listScalePresetNames } from 'stylelint-plugin-rhythmguard/presets';
16
+ import recommended from 'stylelint-plugin-rhythmguard/configs/recommended';
17
+ import reactTailwind from 'stylelint-plugin-rhythmguard/configs/react-tailwind';
18
+ import useScale, { ruleName as useScaleName } from 'stylelint-plugin-rhythmguard/rules/use-scale';
19
+
20
+ const pluginConfigs: readonly RhythmguardStylelintConfig[] = [
21
+ plugin.configs.recommended,
22
+ plugin.configs.strict,
23
+ plugin.configs.tailwind,
24
+ plugin.configs.expanded,
25
+ plugin.configs.logical,
26
+ plugin.configs.migration,
27
+ plugin.configs.motion,
28
+ plugin.configs['react-tailwind'],
29
+ configs.recommended,
30
+ recommended,
31
+ reactTailwind,
32
+ ];
33
+
34
+ const ruleOptions: RhythmguardRuleOptions = {
35
+ preset: 'rhythmic-4',
36
+ scale: [0, 4, 8, 12, 16],
37
+ tokenMap: { '4px': 'var(--space-1)' },
38
+ tokenMapFromCssCustomProperties: true,
39
+ };
40
+
41
+ const useScaleRule = rules['rhythmguard/use-scale'];
42
+ const fixable: boolean | undefined = useScaleRule.meta.fixable;
43
+ const docUrl: string | undefined = useScale.meta.url;
44
+ const name: string = useScaleName;
45
+
46
+ const presetNames: string[] = listScalePresetNames();
47
+ const preset = getScalePreset('rhythmic-4');
48
+ const presetsAgain: string[] = presets.listScalePresetNames();
49
+
50
+ const eslintRule = eslint.rules['tailwind-class-use-scale'];
51
+ const eslintRuleAgain = eslintPlugin.rules['tailwind-class-use-motion-scale'];
52
+
53
+ async function runAudit(): Promise<AuditContractReport> {
54
+ const report: AuditReport = await createAuditReport({ dir: './src', noConfig: true });
55
+ const viaPlugin: AuditReport = await audit.createAuditReport({ dir: './src' });
56
+ void viaPlugin;
57
+ return toAuditContractReport(report);
58
+ }
59
+
60
+ void pluginConfigs;
61
+ void ruleOptions;
62
+ void fixable;
63
+ void docUrl;
64
+ void name;
65
+ void presetNames;
66
+ void preset;
67
+ void presetsAgain;
68
+ void eslintRule;
69
+ void eslintRuleAgain;
70
+ void runAudit;
@@ -0,0 +1,162 @@
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
+ /** Path to a `.rhythmguardrc.json`-style config. Defaults to `.rhythmguardrc.json` in `cwd`. */
27
+ configPath?: string;
28
+ dir?: string;
29
+ failOnNewDrift?: boolean;
30
+ format?: "json" | "json-v1" | "markdown" | "text" | "html" | "github";
31
+ ignorePath?: string;
32
+ ignorePatterns?: string[];
33
+ includeMotion?: boolean;
34
+ maxFindings?: number;
35
+ minCleanliness?: number;
36
+ /** Skip config discovery entirely (equivalent to `--no-config`). */
37
+ noConfig?: boolean;
38
+ /** Write rendered output to this file instead of stdout (equivalent to `--output`). */
39
+ outputPath?: string;
40
+ scale?: Array<number | string>;
41
+ since?: string;
42
+ sinceBaseline?: boolean;
43
+ staged?: boolean;
44
+ tokenCandidateMinCount?: number;
45
+ tokenKind?: AuditTokenKind;
46
+ /** Format applied to `tokenSources` entries that do not declare their own. */
47
+ tokenSourceFormat?: AuditTokenSourceFormat | "auto";
48
+ tokenSources?: AuditTokenSource[];
49
+ writeBaseline?: boolean;
50
+ }
51
+
52
+ export interface AuditSummary {
53
+ cleanliness: number;
54
+ filesWithFindings: number;
55
+ findingCount: number;
56
+ motionFindingCount?: number;
57
+ tokenOpportunityCount?: number;
58
+ [key: string]: unknown;
59
+ }
60
+
61
+ export interface AuditScanned {
62
+ cssFiles: number;
63
+ templateFiles: number;
64
+ totalFiles?: number;
65
+ [key: string]: unknown;
66
+ }
67
+
68
+ export interface AuditFinding {
69
+ column?: number;
70
+ file: string;
71
+ key?: string;
72
+ line?: number;
73
+ message: string;
74
+ property?: string;
75
+ rule?: string;
76
+ type: string;
77
+ value?: string;
78
+ [key: string]: unknown;
79
+ }
80
+
81
+ export interface AuditBaselineComparison {
82
+ baselineFindings: number;
83
+ newFindings: AuditFinding[];
84
+ newFindingsCount: number;
85
+ resolvedFindings: AuditFinding[];
86
+ resolvedFindingsCount: number;
87
+ [key: string]: unknown;
88
+ }
89
+
90
+ export interface AuditReport {
91
+ baseline?: AuditBaselineComparison | null;
92
+ config?: string | null;
93
+ directory: string;
94
+ findings: {
95
+ css: AuditFinding[];
96
+ motion: AuditFinding[];
97
+ tailwind: AuditFinding[];
98
+ };
99
+ scanned: AuditScanned;
100
+ summary: AuditSummary;
101
+ [key: string]: unknown;
102
+ }
103
+
104
+ export interface AuditContractReport {
105
+ baseline: AuditBaselineComparison | null;
106
+ command: {
107
+ config?: string | null;
108
+ directory: string;
109
+ scanScope: string;
110
+ };
111
+ contracts: {
112
+ motion?: unknown;
113
+ scale: {
114
+ cleanliness?: unknown;
115
+ offScaleValues?: unknown;
116
+ tokenOpportunities?: unknown;
117
+ };
118
+ tokens?: unknown;
119
+ };
120
+ findings: unknown;
121
+ scanned: AuditScanned;
122
+ schemaVersion: "2.0";
123
+ summary: AuditSummary;
124
+ }
125
+
126
+ export interface TokenSourceReport {
127
+ file: string;
128
+ format: AuditTokenSourceFormat;
129
+ requestedFormat: AuditTokenSourceFormat;
130
+ tokenCount: number;
131
+ warnings: string[];
132
+ }
133
+
134
+ export interface ParsedTokenSources {
135
+ definitions: Map<string, unknown>;
136
+ sources: TokenSourceReport[];
137
+ warnings: string[];
138
+ }
139
+
140
+ export const AUDIT_JSON_SCHEMA: Readonly<Record<string, unknown>>;
141
+
142
+ export function createAuditReport(options?: AuditOptions): Promise<AuditReport>;
143
+
144
+ export function loadAuditConfig(options?: AuditOptions): Record<string, unknown>;
145
+
146
+ export function parseTokenSources(options?: {
147
+ baseFontSize?: number;
148
+ sources?: AuditTokenSource[];
149
+ tokenKind?: AuditTokenKind;
150
+ }): ParsedTokenSources;
151
+
152
+ export function toAuditContractReport(report: AuditReport): AuditContractReport;
153
+
154
+ declare const audit: {
155
+ AUDIT_JSON_SCHEMA: typeof AUDIT_JSON_SCHEMA;
156
+ createAuditReport: typeof createAuditReport;
157
+ loadAuditConfig: typeof loadAuditConfig;
158
+ parseTokenSources: typeof parseTokenSources;
159
+ toAuditContractReport: typeof toAuditContractReport;
160
+ };
161
+
162
+ 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,66 @@
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
+ "react-tailwind": RhythmguardStylelintConfig;
43
+ recommended: RhythmguardStylelintConfig;
44
+ strict: RhythmguardStylelintConfig;
45
+ tailwind: RhythmguardStylelintConfig;
46
+ [configName: string]: RhythmguardStylelintConfig;
47
+ };
48
+ eslint: RhythmguardEslintPlugin;
49
+ presets: RhythmguardPresets;
50
+ rules: {
51
+ "rhythmguard/no-offscale-transform": StylelintRuleModule;
52
+ "rhythmguard/prefer-token": StylelintRuleModule;
53
+ "rhythmguard/use-motion-scale": StylelintRuleModule;
54
+ "rhythmguard/use-scale": StylelintRuleModule;
55
+ [ruleName: string]: StylelintRuleModule;
56
+ };
57
+ }
58
+
59
+ declare const plugin: RhythmguardPlugin;
60
+
61
+ export const audit: typeof auditApi;
62
+ export const configs: RhythmguardPlugin["configs"];
63
+ export const eslint: RhythmguardEslintPlugin;
64
+ export const presets: RhythmguardPresets;
65
+ export const rules: RhythmguardPlugin["rules"];
66
+ 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
+ }