@breadstone-infrastructure/token-linter 0.0.231

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.
Files changed (196) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/LICENSE +21 -0
  3. package/README.md +72 -0
  4. package/cli/Cli.d.ts +10 -0
  5. package/cli/Cli.d.ts.map +1 -0
  6. package/cli/Cli.js +32 -0
  7. package/cli/Cli.js.map +1 -0
  8. package/cli/CliArgs.d.ts +2 -0
  9. package/cli/CliArgs.d.ts.map +1 -0
  10. package/cli/CliArgs.js +88 -0
  11. package/cli/CliArgs.js.map +1 -0
  12. package/cli/CliArgsConfig.d.ts +2 -0
  13. package/cli/CliArgsConfig.d.ts.map +1 -0
  14. package/cli/CliArgsConfig.js +12 -0
  15. package/cli/CliArgsConfig.js.map +1 -0
  16. package/cli/CliRun.d.ts +2 -0
  17. package/cli/CliRun.d.ts.map +1 -0
  18. package/cli/CliRun.js +34 -0
  19. package/cli/CliRun.js.map +1 -0
  20. package/cli/commands/LintCommand.d.ts +2 -0
  21. package/cli/commands/LintCommand.d.ts.map +1 -0
  22. package/cli/commands/LintCommand.js +66 -0
  23. package/cli/commands/LintCommand.js.map +1 -0
  24. package/cli/commands/LintCommandArgsConfig.d.ts +2 -0
  25. package/cli/commands/LintCommandArgsConfig.d.ts.map +1 -0
  26. package/cli/commands/LintCommandArgsConfig.js +46 -0
  27. package/cli/commands/LintCommandArgsConfig.js.map +1 -0
  28. package/cli/lint.d.ts +3 -0
  29. package/cli/lint.d.ts.map +1 -0
  30. package/cli/lint.js +55 -0
  31. package/cli/lint.js.map +1 -0
  32. package/config/TokenLinterConfigDiscovery.d.ts +16 -0
  33. package/config/TokenLinterConfigDiscovery.d.ts.map +1 -0
  34. package/config/TokenLinterConfigDiscovery.js +44 -0
  35. package/config/TokenLinterConfigDiscovery.js.map +1 -0
  36. package/config/defineConfig.d.ts +41 -0
  37. package/config/defineConfig.d.ts.map +1 -0
  38. package/config/defineConfig.js +37 -0
  39. package/config/defineConfig.js.map +1 -0
  40. package/core/interaction.d.ts +25 -0
  41. package/core/interaction.d.ts.map +1 -0
  42. package/core/interaction.js +45 -0
  43. package/core/interaction.js.map +1 -0
  44. package/core/loader.d.ts +21 -0
  45. package/core/loader.d.ts.map +1 -0
  46. package/core/loader.js +84 -0
  47. package/core/loader.js.map +1 -0
  48. package/core/rule-registry.d.ts +46 -0
  49. package/core/rule-registry.d.ts.map +1 -0
  50. package/core/rule-registry.js +73 -0
  51. package/core/rule-registry.js.map +1 -0
  52. package/core/rule.d.ts +46 -0
  53. package/core/rule.d.ts.map +1 -0
  54. package/core/rule.js +3 -0
  55. package/core/rule.js.map +1 -0
  56. package/core/runner.d.ts +23 -0
  57. package/core/runner.d.ts.map +1 -0
  58. package/core/runner.js +103 -0
  59. package/core/runner.js.map +1 -0
  60. package/index.d.ts +46 -0
  61. package/index.d.ts.map +1 -0
  62. package/index.js +47 -0
  63. package/index.js.map +1 -0
  64. package/models/lint-result.d.ts +14 -0
  65. package/models/lint-result.d.ts.map +1 -0
  66. package/models/lint-result.js +3 -0
  67. package/models/lint-result.js.map +1 -0
  68. package/models/rule-context.d.ts +18 -0
  69. package/models/rule-context.d.ts.map +1 -0
  70. package/models/rule-context.js +3 -0
  71. package/models/rule-context.js.map +1 -0
  72. package/models/rule-finding.d.ts +15 -0
  73. package/models/rule-finding.d.ts.map +1 -0
  74. package/models/rule-finding.js +3 -0
  75. package/models/rule-finding.js.map +1 -0
  76. package/models/rule-result.d.ts +11 -0
  77. package/models/rule-result.d.ts.map +1 -0
  78. package/models/rule-result.js +3 -0
  79. package/models/rule-result.js.map +1 -0
  80. package/models/rule-severity.d.ts +14 -0
  81. package/models/rule-severity.d.ts.map +1 -0
  82. package/models/rule-severity.js +10 -0
  83. package/models/rule-severity.js.map +1 -0
  84. package/models/token-entry.d.ts +11 -0
  85. package/models/token-entry.d.ts.map +1 -0
  86. package/models/token-entry.js +2 -0
  87. package/models/token-entry.js.map +1 -0
  88. package/models/token-linter-config.d.ts +30 -0
  89. package/models/token-linter-config.d.ts.map +1 -0
  90. package/models/token-linter-config.js +3 -0
  91. package/models/token-linter-config.js.map +1 -0
  92. package/orchestration/TokenLinterOrchestrator.d.ts +29 -0
  93. package/orchestration/TokenLinterOrchestrator.d.ts.map +1 -0
  94. package/orchestration/TokenLinterOrchestrator.js +98 -0
  95. package/orchestration/TokenLinterOrchestrator.js.map +1 -0
  96. package/package.json +20 -0
  97. package/presets/index.d.ts +2 -0
  98. package/presets/index.d.ts.map +1 -0
  99. package/presets/index.js +2 -0
  100. package/presets/index.js.map +1 -0
  101. package/presets/recommended.d.ts +23 -0
  102. package/presets/recommended.d.ts.map +1 -0
  103. package/presets/recommended.js +41 -0
  104. package/presets/recommended.js.map +1 -0
  105. package/reporters/console.reporter.d.ts +15 -0
  106. package/reporters/console.reporter.d.ts.map +1 -0
  107. package/reporters/console.reporter.js +75 -0
  108. package/reporters/console.reporter.js.map +1 -0
  109. package/reporters/json.reporter.d.ts +11 -0
  110. package/reporters/json.reporter.d.ts.map +1 -0
  111. package/reporters/json.reporter.js +37 -0
  112. package/reporters/json.reporter.js.map +1 -0
  113. package/reporters/reporter.d.ts +16 -0
  114. package/reporters/reporter.d.ts.map +1 -0
  115. package/reporters/reporter.js +3 -0
  116. package/reporters/reporter.js.map +1 -0
  117. package/reporters/summary.reporter.d.ts +12 -0
  118. package/reporters/summary.reporter.d.ts.map +1 -0
  119. package/reporters/summary.reporter.js +48 -0
  120. package/reporters/summary.reporter.js.map +1 -0
  121. package/rules/alphabetical-sort.rule.d.ts +19 -0
  122. package/rules/alphabetical-sort.rule.d.ts.map +1 -0
  123. package/rules/alphabetical-sort.rule.js +72 -0
  124. package/rules/alphabetical-sort.rule.js.map +1 -0
  125. package/rules/component-presence.rule.d.ts +29 -0
  126. package/rules/component-presence.rule.d.ts.map +1 -0
  127. package/rules/component-presence.rule.js +102 -0
  128. package/rules/component-presence.rule.js.map +1 -0
  129. package/rules/duplicate-value.rule.d.ts +17 -0
  130. package/rules/duplicate-value.rule.d.ts.map +1 -0
  131. package/rules/duplicate-value.rule.js +49 -0
  132. package/rules/duplicate-value.rule.js.map +1 -0
  133. package/rules/empty-json.rule.d.ts +52 -0
  134. package/rules/empty-json.rule.d.ts.map +1 -0
  135. package/rules/empty-json.rule.js +100 -0
  136. package/rules/empty-json.rule.js.map +1 -0
  137. package/rules/forbidden-key.rule.d.ts +57 -0
  138. package/rules/forbidden-key.rule.d.ts.map +1 -0
  139. package/rules/forbidden-key.rule.js +118 -0
  140. package/rules/forbidden-key.rule.js.map +1 -0
  141. package/rules/forbidden-value.rule.d.ts +53 -0
  142. package/rules/forbidden-value.rule.d.ts.map +1 -0
  143. package/rules/forbidden-value.rule.js +130 -0
  144. package/rules/forbidden-value.rule.js.map +1 -0
  145. package/rules/includes-consistency.rule.d.ts +30 -0
  146. package/rules/includes-consistency.rule.d.ts.map +1 -0
  147. package/rules/includes-consistency.rule.js +153 -0
  148. package/rules/includes-consistency.rule.js.map +1 -0
  149. package/rules/index.d.ts +7 -0
  150. package/rules/index.d.ts.map +1 -0
  151. package/rules/index.js +47 -0
  152. package/rules/index.js.map +1 -0
  153. package/rules/key-consistency.rule.d.ts +38 -0
  154. package/rules/key-consistency.rule.d.ts.map +1 -0
  155. package/rules/key-consistency.rule.js +161 -0
  156. package/rules/key-consistency.rule.js.map +1 -0
  157. package/rules/missing-includes.rule.d.ts +19 -0
  158. package/rules/missing-includes.rule.d.ts.map +1 -0
  159. package/rules/missing-includes.rule.js +51 -0
  160. package/rules/missing-includes.rule.js.map +1 -0
  161. package/rules/mixin-reference.rule.d.ts +28 -0
  162. package/rules/mixin-reference.rule.d.ts.map +1 -0
  163. package/rules/mixin-reference.rule.js +104 -0
  164. package/rules/mixin-reference.rule.js.map +1 -0
  165. package/rules/naming-convention.rule.d.ts +29 -0
  166. package/rules/naming-convention.rule.d.ts.map +1 -0
  167. package/rules/naming-convention.rule.js +112 -0
  168. package/rules/naming-convention.rule.js.map +1 -0
  169. package/rules/nested-depth.rule.d.ts +42 -0
  170. package/rules/nested-depth.rule.d.ts.map +1 -0
  171. package/rules/nested-depth.rule.js +72 -0
  172. package/rules/nested-depth.rule.js.map +1 -0
  173. package/rules/required-includes.rule.d.ts +60 -0
  174. package/rules/required-includes.rule.d.ts.map +1 -0
  175. package/rules/required-includes.rule.js +118 -0
  176. package/rules/required-includes.rule.js.map +1 -0
  177. package/rules/rule-options.d.ts +158 -0
  178. package/rules/rule-options.d.ts.map +1 -0
  179. package/rules/rule-options.js +89 -0
  180. package/rules/rule-options.js.map +1 -0
  181. package/rules/value-consistency.rule.d.ts +17 -0
  182. package/rules/value-consistency.rule.d.ts.map +1 -0
  183. package/rules/value-consistency.rule.js +69 -0
  184. package/rules/value-consistency.rule.js.map +1 -0
  185. package/rules/value-schema.rule.d.ts +18 -0
  186. package/rules/value-schema.rule.d.ts.map +1 -0
  187. package/rules/value-schema.rule.js +108 -0
  188. package/rules/value-schema.rule.js.map +1 -0
  189. package/rules/value-type.rule.d.ts +71 -0
  190. package/rules/value-type.rule.d.ts.map +1 -0
  191. package/rules/value-type.rule.js +176 -0
  192. package/rules/value-type.rule.js.map +1 -0
  193. package/utils.d.ts +55 -0
  194. package/utils.d.ts.map +1 -0
  195. package/utils.js +125 -0
  196. package/utils.js.map +1 -0
@@ -0,0 +1,153 @@
1
+ // #region Imports
2
+ import * as fs from 'fs';
3
+ import { promptSelect, SKIP, SKIP_ALL } from '../core/interaction.js';
4
+ import { RuleSeverity } from '../models/rule-severity.js';
5
+ import { groupByComponent } from '../utils.js';
6
+ // #endregion
7
+ /**
8
+ * @description Ensures $includes arrays are consistent across themes for the same component.
9
+ *
10
+ * @example Interactive prompt
11
+ * ```
12
+ * ? $includes differs from 'memphis' for 'button': missing: [ripple]; extra: [shadow].
13
+ * > Use memphis's includes: [focusRing, ripple]
14
+ * Use cosmopolitan's includes: [focusRing, ripple]
15
+ * Union all: [focusRing, ripple, shadow]
16
+ * Skip
17
+ * Skip all remaining
18
+ * ```
19
+ *
20
+ * @public
21
+ */
22
+ export class IncludesConsistencyRule {
23
+ // #region Fields
24
+ name = 'includes-consistency';
25
+ description = 'Ensures $includes arrays are consistent across themes for the same component.';
26
+ defaultSeverity = RuleSeverity.Warning;
27
+ fixable = false;
28
+ interactive = true;
29
+ // #endregion
30
+ // #region Methods
31
+ run(context) {
32
+ const findings = [];
33
+ const grouped = groupByComponent(context.entries);
34
+ for (const [component, entries] of grouped.entries()) {
35
+ const includesMap = new Map();
36
+ for (const entry of entries) {
37
+ const includes = entry.content['$includes'];
38
+ const normalized = Array.isArray(includes) ? includes : [];
39
+ includesMap.set(entry.theme, normalized);
40
+ }
41
+ const signatures = new Set(Array.from(includesMap.values()).map(arr => [...arr].sort().join(',')));
42
+ if (signatures.size <= 1) {
43
+ continue;
44
+ }
45
+ const referenceEntry = entries[0];
46
+ const referenceIncludes = includesMap.get(referenceEntry.theme) ?? [];
47
+ const referenceSet = new Set(referenceIncludes);
48
+ for (const entry of entries.slice(1)) {
49
+ const entryIncludes = includesMap.get(entry.theme) ?? [];
50
+ const entrySet = new Set(entryIncludes);
51
+ const missingInEntry = referenceIncludes.filter(i => !entrySet.has(i));
52
+ const extraInEntry = entryIncludes.filter(i => !referenceSet.has(i));
53
+ if (missingInEntry.length > 0 || extraInEntry.length > 0) {
54
+ const parts = [];
55
+ if (missingInEntry.length > 0) {
56
+ parts.push(`missing: [${missingInEntry.join(', ')}]`);
57
+ }
58
+ if (extraInEntry.length > 0) {
59
+ parts.push(`extra: [${extraInEntry.join(', ')}]`);
60
+ }
61
+ findings.push({
62
+ component,
63
+ theme: entry.theme,
64
+ key: '$includes',
65
+ message: `$includes differs from '${referenceEntry.theme}' for '${component}': ${parts.join('; ')}.`,
66
+ severity: this.defaultSeverity,
67
+ filePath: entry.filePath,
68
+ fixed: false,
69
+ });
70
+ }
71
+ }
72
+ }
73
+ return { ruleName: this.name, findings, duration: 0 };
74
+ }
75
+ async interact(context, findings) {
76
+ const grouped = groupByComponent(context.entries);
77
+ const pendingWrites = new Map();
78
+ const result = [];
79
+ let skipAll = false;
80
+ for (const finding of findings) {
81
+ if (skipAll) {
82
+ result.push(finding);
83
+ continue;
84
+ }
85
+ const entries = grouped.get(finding.component);
86
+ if (!entries || !finding.filePath) {
87
+ result.push(finding);
88
+ continue;
89
+ }
90
+ const targetEntry = entries.find(e => e.theme === finding.theme);
91
+ if (!targetEntry) {
92
+ result.push(finding);
93
+ continue;
94
+ }
95
+ const otherEntries = entries.filter(e => e.theme !== finding.theme);
96
+ const choices = [];
97
+ for (const other of otherEntries) {
98
+ const includes = Array.isArray(other.content['$includes']) ? other.content['$includes'] : [];
99
+ choices.push({
100
+ name: `use-${other.theme}`,
101
+ message: `Use ${other.theme}'s includes: [${includes.join(', ')}]`,
102
+ });
103
+ }
104
+ const allIncludes = new Set();
105
+ for (const entry of entries) {
106
+ const includes = Array.isArray(entry.content['$includes']) ? entry.content['$includes'] : [];
107
+ for (const inc of includes) {
108
+ allIncludes.add(inc);
109
+ }
110
+ }
111
+ choices.push({
112
+ name: 'union',
113
+ message: `Union all: [${[...allIncludes].sort().join(', ')}]`,
114
+ });
115
+ const action = await promptSelect(finding.message, choices);
116
+ if (action === SKIP_ALL) {
117
+ skipAll = true;
118
+ result.push(finding);
119
+ continue;
120
+ }
121
+ if (action === SKIP) {
122
+ result.push(finding);
123
+ continue;
124
+ }
125
+ let newIncludes;
126
+ if (action === 'union') {
127
+ newIncludes = [...allIncludes].sort();
128
+ }
129
+ else if (action.startsWith('use-')) {
130
+ const sourceTheme = action.substring(4);
131
+ const sourceEntry = entries.find(e => e.theme === sourceTheme);
132
+ if (sourceEntry) {
133
+ newIncludes = Array.isArray(sourceEntry.content['$includes'])
134
+ ? [...sourceEntry.content['$includes']]
135
+ : [];
136
+ }
137
+ }
138
+ if (newIncludes !== undefined) {
139
+ targetEntry.content['$includes'] = newIncludes;
140
+ pendingWrites.set(targetEntry.filePath, targetEntry);
141
+ result.push({ ...finding, fixed: true });
142
+ }
143
+ else {
144
+ result.push(finding);
145
+ }
146
+ }
147
+ for (const entry of pendingWrites.values()) {
148
+ fs.writeFileSync(entry.filePath, JSON.stringify(entry.content, null, 2) + '\n', 'utf-8');
149
+ }
150
+ return result;
151
+ }
152
+ }
153
+ //# sourceMappingURL=includes-consistency.rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"includes-consistency.rule.js","sourceRoot":"","sources":["../../src/rules/includes-consistency.rule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { IRule } from '../core/rule.js';
2
+ /**
3
+ * @description Creates and returns all built-in token linter rules.
4
+ * @public
5
+ */
6
+ export declare function builtInRules(): IRule[];
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAoB7C;;;GAGG;AACH,wBAAgB,YAAY,IAAI,KAAK,EAAE,CA0BtC"}
package/rules/index.js ADDED
@@ -0,0 +1,47 @@
1
+ // #region Imports
2
+ import { AlphabeticalSortRule } from './alphabetical-sort.rule.js';
3
+ import { ComponentPresenceRule } from './component-presence.rule.js';
4
+ import { DuplicateValueRule } from './duplicate-value.rule.js';
5
+ import { EmptyJsonRule } from './empty-json.rule.js';
6
+ import { ForbiddenKeyRule } from './forbidden-key.rule.js';
7
+ import { ForbiddenValueRule } from './forbidden-value.rule.js';
8
+ import { IncludesConsistencyRule } from './includes-consistency.rule.js';
9
+ import { KeyConsistencyRule } from './key-consistency.rule.js';
10
+ import { MissingIncludesRule } from './missing-includes.rule.js';
11
+ import { MixinReferenceRule } from './mixin-reference.rule.js';
12
+ import { NamingConventionRule } from './naming-convention.rule.js';
13
+ import { NestedDepthRule } from './nested-depth.rule.js';
14
+ import { RequiredIncludesRule } from './required-includes.rule.js';
15
+ import { ValueConsistencyRule } from './value-consistency.rule.js';
16
+ import { ValueSchemaRule } from './value-schema.rule.js';
17
+ import { ValueTypeRule } from './value-type.rule.js';
18
+ // #endregion
19
+ /**
20
+ * @description Creates and returns all built-in token linter rules.
21
+ * @public
22
+ */
23
+ export function builtInRules() {
24
+ return [
25
+ // Core structural rules
26
+ new ComponentPresenceRule(),
27
+ new KeyConsistencyRule(),
28
+ new IncludesConsistencyRule(),
29
+ new MissingIncludesRule(),
30
+ new RequiredIncludesRule(),
31
+ new EmptyJsonRule(),
32
+ // Quality rules
33
+ new ForbiddenValueRule(),
34
+ new ForbiddenKeyRule(),
35
+ new NamingConventionRule(),
36
+ new ValueSchemaRule(),
37
+ new ValueTypeRule(),
38
+ new ValueConsistencyRule(),
39
+ new DuplicateValueRule(),
40
+ // Structural depth
41
+ new NestedDepthRule(),
42
+ new MixinReferenceRule(),
43
+ // Formatting
44
+ new AlphabeticalSortRule(),
45
+ ];
46
+ }
47
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA,kBAAkB;AAGlB,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,aAAa;AAEb;;;GAGG;AACH,MAAM,UAAU,YAAY;IACxB,OAAO;QACH,wBAAwB;QACxB,IAAI,qBAAqB,EAAE;QAC3B,IAAI,kBAAkB,EAAE;QACxB,IAAI,uBAAuB,EAAE;QAC7B,IAAI,mBAAmB,EAAE;QACzB,IAAI,oBAAoB,EAAE;QAC1B,IAAI,aAAa,EAAE;QAEnB,gBAAgB;QAChB,IAAI,kBAAkB,EAAE;QACxB,IAAI,gBAAgB,EAAE;QACtB,IAAI,oBAAoB,EAAE;QAC1B,IAAI,eAAe,EAAE;QACrB,IAAI,aAAa,EAAE;QACnB,IAAI,oBAAoB,EAAE;QAC1B,IAAI,kBAAkB,EAAE;QAExB,mBAAmB;QACnB,IAAI,eAAe,EAAE;QACrB,IAAI,kBAAkB,EAAE;QAExB,aAAa;QACb,IAAI,oBAAoB,EAAE;KAC7B,CAAC;AACN,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type { IRule } from '../core/rule.js';
2
+ import type { IRuleContext } from '../models/rule-context.js';
3
+ import type { IRuleFinding } from '../models/rule-finding.js';
4
+ import type { IRuleResult } from '../models/rule-result.js';
5
+ import { RuleSeverity } from '../models/rule-severity.js';
6
+ /**
7
+ * @description Ensures all themes use the same set of keys for each component.
8
+ * Keys that are provided by `$includes` mixins are excluded from the consistency check.
9
+ * When auto-fix is enabled, missing keys are copied from the first theme that defines them,
10
+ * with theme-specific CSS variable prefixes automatically replaced.
11
+ *
12
+ * @example Interactive prompt
13
+ * ```
14
+ * ? Key 'borderRadius' is missing in component 'button' for theme 'joy'.
15
+ * > Copy from 'memphis'
16
+ * Copy from 'cosmopolitan'
17
+ * Skip
18
+ * Skip all remaining
19
+ * ```
20
+ *
21
+ * @public
22
+ */
23
+ export declare class KeyConsistencyRule implements IRule {
24
+ readonly name = "key-consistency";
25
+ readonly description = "Ensures all themes use the same keys for each component.";
26
+ readonly defaultSeverity: RuleSeverity;
27
+ readonly fixable = true;
28
+ readonly interactive = true;
29
+ run(context: IRuleContext): IRuleResult;
30
+ fix(context: IRuleContext, findings: ReadonlyArray<IRuleFinding>): IRuleFinding[];
31
+ interact(context: IRuleContext, findings: ReadonlyArray<IRuleFinding>): Promise<IRuleFinding[]>;
32
+ /**
33
+ * @description Deep-clones a value and replaces occurrences of the source theme prefix with the target theme prefix
34
+ * in all CSS variable references (e.g. `--memphis-` → `--joy-`).
35
+ */
36
+ private _replaceThemePrefix;
37
+ }
38
+ //# sourceMappingURL=key-consistency.rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-consistency.rule.d.ts","sourceRoot":"","sources":["../../src/rules/key-consistency.rule.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAM1D;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,kBAAmB,YAAW,KAAK;IAI5C,SAAgB,IAAI,qBAAqB;IACzC,SAAgB,WAAW,8DAA8D;IACzF,SAAgB,eAAe,EAAE,YAAY,CAAsB;IACnE,SAAgB,OAAO,QAAQ;IAC/B,SAAgB,WAAW,QAAQ;IAM5B,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW;IAiDvC,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,YAAY,EAAE;IAqC3E,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAyE5G;;;OAGG;IACH,OAAO,CAAC,mBAAmB;CAK9B"}
@@ -0,0 +1,161 @@
1
+ // #region Imports
2
+ import * as fs from 'fs';
3
+ import { promptSelect, SKIP, SKIP_ALL } from '../core/interaction.js';
4
+ import { RuleSeverity } from '../models/rule-severity.js';
5
+ import { getTokenKeys, groupByComponent, replaceThemePrefix, resolveMixinKeys } from '../utils.js';
6
+ // #endregion
7
+ /**
8
+ * @description Ensures all themes use the same set of keys for each component.
9
+ * Keys that are provided by `$includes` mixins are excluded from the consistency check.
10
+ * When auto-fix is enabled, missing keys are copied from the first theme that defines them,
11
+ * with theme-specific CSS variable prefixes automatically replaced.
12
+ *
13
+ * @example Interactive prompt
14
+ * ```
15
+ * ? Key 'borderRadius' is missing in component 'button' for theme 'joy'.
16
+ * > Copy from 'memphis'
17
+ * Copy from 'cosmopolitan'
18
+ * Skip
19
+ * Skip all remaining
20
+ * ```
21
+ *
22
+ * @public
23
+ */
24
+ export class KeyConsistencyRule {
25
+ // #region Fields
26
+ name = 'key-consistency';
27
+ description = 'Ensures all themes use the same keys for each component.';
28
+ defaultSeverity = RuleSeverity.Error;
29
+ fixable = true;
30
+ interactive = true;
31
+ // #endregion
32
+ // #region Methods
33
+ run(context) {
34
+ const findings = [];
35
+ const grouped = groupByComponent(context.entries);
36
+ for (const [component, entries] of grouped.entries()) {
37
+ const themeEntryMap = new Map(entries.map(e => [e.theme, e]));
38
+ const allKeys = new Set();
39
+ for (const entry of entries) {
40
+ for (const key of getTokenKeys(entry.content)) {
41
+ allKeys.add(key);
42
+ }
43
+ }
44
+ for (const key of allKeys) {
45
+ for (const theme of context.themes) {
46
+ const entry = themeEntryMap.get(theme);
47
+ if (!entry || Object.prototype.hasOwnProperty.call(entry.content, key)) {
48
+ continue;
49
+ }
50
+ // Check if the key is provided by one of the entry's $includes mixins
51
+ const includes = entry.content['$includes'];
52
+ if (Array.isArray(includes)) {
53
+ const mixinProvidedKeys = resolveMixinKeys(includes, context.mixinKeys);
54
+ if (mixinProvidedKeys.has(key)) {
55
+ continue;
56
+ }
57
+ }
58
+ findings.push({
59
+ component,
60
+ theme,
61
+ key,
62
+ message: `Key '${key}' is missing in component '${component}' for theme '${theme}'.`,
63
+ severity: this.defaultSeverity,
64
+ filePath: entry.filePath,
65
+ fixed: false,
66
+ });
67
+ }
68
+ }
69
+ }
70
+ return { ruleName: this.name, findings, duration: 0 };
71
+ }
72
+ fix(context, findings) {
73
+ const grouped = groupByComponent(context.entries);
74
+ const pendingWrites = new Map();
75
+ const result = [];
76
+ for (const finding of findings) {
77
+ const entries = grouped.get(finding.component);
78
+ if (!entries || !finding.key || !finding.filePath) {
79
+ result.push(finding);
80
+ continue;
81
+ }
82
+ const targetEntry = entries.find(e => e.theme === finding.theme);
83
+ const sourceEntry = entries.find(e => e.theme !== finding.theme && Object.prototype.hasOwnProperty.call(e.content, finding.key));
84
+ if (targetEntry && sourceEntry) {
85
+ const sourceValue = sourceEntry.content[finding.key];
86
+ targetEntry.content[finding.key] = replaceThemePrefix(sourceValue, sourceEntry.theme, finding.theme);
87
+ pendingWrites.set(targetEntry.filePath, targetEntry);
88
+ result.push({ ...finding, fixed: true });
89
+ }
90
+ else {
91
+ result.push(finding);
92
+ }
93
+ }
94
+ for (const entry of pendingWrites.values()) {
95
+ fs.writeFileSync(entry.filePath, JSON.stringify(entry.content, null, 2) + '\n', 'utf-8');
96
+ }
97
+ return result;
98
+ }
99
+ async interact(context, findings) {
100
+ const grouped = groupByComponent(context.entries);
101
+ const pendingWrites = new Map();
102
+ const result = [];
103
+ let skipAll = false;
104
+ for (const finding of findings) {
105
+ if (skipAll) {
106
+ result.push(finding);
107
+ continue;
108
+ }
109
+ const entries = grouped.get(finding.component);
110
+ if (!entries || !finding.key || !finding.filePath) {
111
+ result.push(finding);
112
+ continue;
113
+ }
114
+ const targetEntry = entries.find(e => e.theme === finding.theme);
115
+ const sourceThemes = entries
116
+ .filter(e => e.theme !== finding.theme && Object.prototype.hasOwnProperty.call(e.content, finding.key))
117
+ .map(e => e.theme);
118
+ if (!targetEntry || sourceThemes.length === 0) {
119
+ result.push(finding);
120
+ continue;
121
+ }
122
+ const choices = sourceThemes.map(theme => ({
123
+ name: `copy-${theme}`,
124
+ message: `Copy from '${theme}'`,
125
+ }));
126
+ const action = await promptSelect(finding.message, choices);
127
+ if (action === SKIP_ALL) {
128
+ skipAll = true;
129
+ result.push(finding);
130
+ continue;
131
+ }
132
+ if (action === SKIP) {
133
+ result.push(finding);
134
+ continue;
135
+ }
136
+ const sourceTheme = action.startsWith('copy-') ? action.substring(5) : undefined;
137
+ const sourceEntry = sourceTheme ? entries.find(e => e.theme === sourceTheme) : undefined;
138
+ if (sourceEntry && Object.prototype.hasOwnProperty.call(sourceEntry.content, finding.key)) {
139
+ const sourceValue = sourceEntry.content[finding.key];
140
+ targetEntry.content[finding.key] = replaceThemePrefix(sourceValue, sourceEntry.theme, finding.theme);
141
+ pendingWrites.set(targetEntry.filePath, targetEntry);
142
+ result.push({ ...finding, fixed: true });
143
+ }
144
+ else {
145
+ result.push(finding);
146
+ }
147
+ }
148
+ for (const entry of pendingWrites.values()) {
149
+ fs.writeFileSync(entry.filePath, JSON.stringify(entry.content, null, 2) + '\n', 'utf-8');
150
+ }
151
+ return result;
152
+ }
153
+ /**
154
+ * @description Deep-clones a value and replaces occurrences of the source theme prefix with the target theme prefix
155
+ * in all CSS variable references (e.g. `--memphis-` → `--joy-`).
156
+ */
157
+ _replaceThemePrefix(value, sourceTheme, targetTheme) {
158
+ return replaceThemePrefix(value, sourceTheme, targetTheme);
159
+ }
160
+ }
161
+ //# sourceMappingURL=key-consistency.rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-consistency.rule.js","sourceRoot":"","sources":["../../src/rules/key-consistency.rule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { IRule } from '../core/rule.js';
2
+ import type { IRuleContext } from '../models/rule-context.js';
3
+ import type { IRuleFinding } from '../models/rule-finding.js';
4
+ import type { IRuleResult } from '../models/rule-result.js';
5
+ import { RuleSeverity } from '../models/rule-severity.js';
6
+ /**
7
+ * @description Ensures all component token files have a `$includes` field. Auto-fixable.
8
+ * @public
9
+ */
10
+ export declare class MissingIncludesRule implements IRule {
11
+ readonly name = "missing-includes";
12
+ readonly description = "Ensures all component token files have a $includes field. Auto-fixable.";
13
+ readonly defaultSeverity: RuleSeverity;
14
+ readonly fixable = true;
15
+ readonly interactive = false;
16
+ run(context: IRuleContext): IRuleResult;
17
+ fix(context: IRuleContext, findings: ReadonlyArray<IRuleFinding>): IRuleFinding[];
18
+ }
19
+ //# sourceMappingURL=missing-includes.rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-includes.rule.d.ts","sourceRoot":"","sources":["../../src/rules/missing-includes.rule.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAI1D;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,KAAK;IAI7C,SAAgB,IAAI,sBAAsB;IAC1C,SAAgB,WAAW,6EAA6E;IACxG,SAAgB,eAAe,EAAE,YAAY,CAAwB;IACrE,SAAgB,OAAO,QAAQ;IAC/B,SAAgB,WAAW,SAAS;IAM7B,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW;IAqBvC,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,YAAY,EAAE;CAuB3F"}
@@ -0,0 +1,51 @@
1
+ // #region Imports
2
+ import * as fs from 'fs';
3
+ import { RuleSeverity } from '../models/rule-severity.js';
4
+ // #endregion
5
+ /**
6
+ * @description Ensures all component token files have a `$includes` field. Auto-fixable.
7
+ * @public
8
+ */
9
+ export class MissingIncludesRule {
10
+ // #region Fields
11
+ name = 'missing-includes';
12
+ description = 'Ensures all component token files have a $includes field. Auto-fixable.';
13
+ defaultSeverity = RuleSeverity.Warning;
14
+ fixable = true;
15
+ interactive = false;
16
+ // #endregion
17
+ // #region Methods
18
+ run(context) {
19
+ const findings = [];
20
+ for (const entry of context.entries) {
21
+ if (Array.isArray(entry.content['$includes'])) {
22
+ continue;
23
+ }
24
+ findings.push({
25
+ component: entry.component,
26
+ theme: entry.theme,
27
+ message: `Component '${entry.component}' in theme '${entry.theme}' is missing '$includes'.`,
28
+ severity: this.defaultSeverity,
29
+ filePath: entry.filePath,
30
+ fixed: false,
31
+ });
32
+ }
33
+ return { ruleName: this.name, findings, duration: 0 };
34
+ }
35
+ fix(context, findings) {
36
+ const entryMap = new Map(context.entries.map(e => [e.filePath, e]));
37
+ const result = [];
38
+ for (const finding of findings) {
39
+ const entry = finding.filePath ? entryMap.get(finding.filePath) : undefined;
40
+ if (!entry) {
41
+ result.push(finding);
42
+ continue;
43
+ }
44
+ const updated = { '$includes': [], ...entry.content };
45
+ fs.writeFileSync(entry.filePath, JSON.stringify(updated, null, 2) + '\n', 'utf-8');
46
+ result.push({ ...finding, fixed: true });
47
+ }
48
+ return result;
49
+ }
50
+ }
51
+ //# sourceMappingURL=missing-includes.rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-includes.rule.js","sourceRoot":"","sources":["../../src/rules/missing-includes.rule.ts"],"names":[],"mappings":"AAAA,kBAAkB;AAElB,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAKzB,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,aAAa;AAEb;;;GAGG;AACH,MAAM,OAAO,mBAAmB;IAE5B,iBAAiB;IAED,IAAI,GAAG,kBAAkB,CAAC;IAC1B,WAAW,GAAG,yEAAyE,CAAC;IACxF,eAAe,GAAiB,YAAY,CAAC,OAAO,CAAC;IACrD,OAAO,GAAG,IAAI,CAAC;IACf,WAAW,GAAG,KAAK,CAAC;IAEpC,aAAa;IAEb,kBAAkB;IAEX,GAAG,CAAC,OAAqB;QAC5B,MAAM,QAAQ,GAAmB,EAAE,CAAC;QAEpC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;gBAC5C,SAAS;YACb,CAAC;YAED,QAAQ,CAAC,IAAI,CAAC;gBACV,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,OAAO,EAAE,cAAc,KAAK,CAAC,SAAS,eAAe,KAAK,CAAC,KAAK,2BAA2B;gBAC3F,QAAQ,EAAE,IAAI,CAAC,eAAe;gBAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,KAAK,EAAE,KAAK;aACf,CAAC,CAAC;QACP,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IAC1D,CAAC;IAEM,GAAG,CAAC,OAAqB,EAAE,QAAqC;QACnE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACpE,MAAM,MAAM,GAAmB,EAAE,CAAC;QAElC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAE5E,IAAI,CAAC,KAAK,EAAE,CAAC;gBACT,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAErB,SAAS;YACb,CAAC;YAED,MAAM,OAAO,GAA4B,EAAE,WAAW,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;YAE/E,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;YACnF,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,CAAC;QAED,OAAO,MAAM,CAAC;IAClB,CAAC;CAGJ"}
@@ -0,0 +1,28 @@
1
+ import type { IRule } from '../core/rule.js';
2
+ import type { IRuleContext } from '../models/rule-context.js';
3
+ import type { IRuleFinding } from '../models/rule-finding.js';
4
+ import type { IRuleResult } from '../models/rule-result.js';
5
+ import { RuleSeverity } from '../models/rule-severity.js';
6
+ /**
7
+ * @description Validates that $includes references point to existing mixin files.
8
+ *
9
+ * @example Interactive prompt
10
+ * ```
11
+ * ? Mixin 'glow' (base: 'glow') referenced in '$includes' does not exist as a mixin file.
12
+ * > Remove 'glow' from $includes
13
+ * Skip
14
+ * Skip all remaining
15
+ * ```
16
+ *
17
+ * @public
18
+ */
19
+ export declare class MixinReferenceRule implements IRule {
20
+ readonly name = "mixin-reference";
21
+ readonly description = "Validates that $includes references point to existing mixin files.";
22
+ readonly defaultSeverity: RuleSeverity;
23
+ readonly fixable = false;
24
+ readonly interactive = true;
25
+ run(context: IRuleContext): IRuleResult;
26
+ interact(context: IRuleContext, findings: ReadonlyArray<IRuleFinding>): Promise<IRuleFinding[]>;
27
+ }
28
+ //# sourceMappingURL=mixin-reference.rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mixin-reference.rule.d.ts","sourceRoot":"","sources":["../../src/rules/mixin-reference.rule.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAK1D;;;;;;;;;;;;GAYG;AACH,qBAAa,kBAAmB,YAAW,KAAK;IAI5C,SAAgB,IAAI,qBAAqB;IACzC,SAAgB,WAAW,wEAAwE;IACnG,SAAgB,eAAe,EAAE,YAAY,CAAsB;IACnE,SAAgB,OAAO,SAAS;IAChC,SAAgB,WAAW,QAAQ;IAM5B,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW;IA+BjC,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAwE/G"}