@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,104 @@
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
+ // #endregion
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 class MixinReferenceRule {
20
+ // #region Fields
21
+ name = 'mixin-reference';
22
+ description = 'Validates that $includes references point to existing mixin files.';
23
+ defaultSeverity = RuleSeverity.Error;
24
+ fixable = false;
25
+ interactive = true;
26
+ // #endregion
27
+ // #region Methods
28
+ run(context) {
29
+ const findings = [];
30
+ for (const entry of context.entries) {
31
+ const includes = entry.content['$includes'];
32
+ if (!Array.isArray(includes)) {
33
+ continue;
34
+ }
35
+ for (const mixin of includes) {
36
+ // Mixins can have modifiers like "color:background" — extract the base mixin name
37
+ const baseMixinName = mixin.split(':')[0];
38
+ if (!context.mixinNames.includes(baseMixinName)) {
39
+ findings.push({
40
+ component: entry.component,
41
+ theme: entry.theme,
42
+ key: '$includes',
43
+ message: `Mixin '${mixin}' (base: '${baseMixinName}') referenced in '$includes' does not exist as a mixin file.`,
44
+ severity: this.defaultSeverity,
45
+ filePath: entry.filePath,
46
+ fixed: false,
47
+ });
48
+ }
49
+ }
50
+ }
51
+ return { ruleName: this.name, findings, duration: 0 };
52
+ }
53
+ async interact(context, findings) {
54
+ const entryMap = new Map(context.entries.map((e) => [`${e.theme}:${e.component}`, e]));
55
+ const pendingWrites = new Map();
56
+ const result = [];
57
+ let skipAll = false;
58
+ for (const finding of findings) {
59
+ if (skipAll) {
60
+ result.push(finding);
61
+ continue;
62
+ }
63
+ if (!finding.filePath) {
64
+ result.push(finding);
65
+ continue;
66
+ }
67
+ const entry = entryMap.get(`${finding.theme}:${finding.component}`);
68
+ if (!entry) {
69
+ result.push(finding);
70
+ continue;
71
+ }
72
+ const mixinMatch = finding.message.match(/^Mixin '(.+?)'/);
73
+ const mixinToRemove = mixinMatch?.[1];
74
+ if (!mixinToRemove) {
75
+ result.push(finding);
76
+ continue;
77
+ }
78
+ const action = await promptSelect(finding.message, [`Remove '${mixinToRemove}' from $includes`]);
79
+ if (action === SKIP_ALL) {
80
+ skipAll = true;
81
+ result.push(finding);
82
+ continue;
83
+ }
84
+ if (action === SKIP) {
85
+ result.push(finding);
86
+ continue;
87
+ }
88
+ const includes = entry.content['$includes'];
89
+ if (Array.isArray(includes)) {
90
+ entry.content['$includes'] = includes.filter(m => m !== mixinToRemove);
91
+ pendingWrites.set(entry.filePath, entry);
92
+ result.push({ ...finding, fixed: true });
93
+ }
94
+ else {
95
+ result.push(finding);
96
+ }
97
+ }
98
+ for (const entry of pendingWrites.values()) {
99
+ fs.writeFileSync(entry.filePath, JSON.stringify(entry.content, null, 2) + '\n', 'utf-8');
100
+ }
101
+ return result;
102
+ }
103
+ }
104
+ //# sourceMappingURL=mixin-reference.rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mixin-reference.rule.js","sourceRoot":"","sources":["../../src/rules/mixin-reference.rule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
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 Enforces camelCase naming convention for token keys.
8
+ *
9
+ * @example Interactive prompt
10
+ * ```
11
+ * ? Key 'border_radius' does not follow camelCase naming convention.
12
+ * > Rename to 'borderRadius'
13
+ * Skip
14
+ * Skip all remaining
15
+ * ```
16
+ *
17
+ * @public
18
+ */
19
+ export declare class NamingConventionRule implements IRule {
20
+ readonly name = "naming-convention";
21
+ readonly description = "Enforces camelCase naming convention for token keys.";
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
+ private _toCamelCase;
28
+ }
29
+ //# sourceMappingURL=naming-convention.rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naming-convention.rule.d.ts","sourceRoot":"","sources":["../../src/rules/naming-convention.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;AAY1D;;;;;;;;;;;;GAYG;AACH,qBAAa,oBAAqB,YAAW,KAAK;IAI9C,SAAgB,IAAI,uBAAuB;IAC3C,SAAgB,WAAW,0DAA0D;IACrF,SAAgB,eAAe,EAAE,YAAY,CAAwB;IACrE,SAAgB,OAAO,SAAS;IAChC,SAAgB,WAAW,QAAQ;IAM5B,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW;IAwBjC,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IA4E5G,OAAO,CAAC,YAAY;CAOvB"}
@@ -0,0 +1,112 @@
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 } from '../utils.js';
6
+ // #endregion
7
+ // #region Constants
8
+ const CAMEL_CASE_PATTERN = /^[a-z][a-zA-Z0-9]*$/;
9
+ // #endregion
10
+ /**
11
+ * @description Enforces camelCase naming convention for token keys.
12
+ *
13
+ * @example Interactive prompt
14
+ * ```
15
+ * ? Key 'border_radius' does not follow camelCase naming convention.
16
+ * > Rename to 'borderRadius'
17
+ * Skip
18
+ * Skip all remaining
19
+ * ```
20
+ *
21
+ * @public
22
+ */
23
+ export class NamingConventionRule {
24
+ // #region Fields
25
+ name = 'naming-convention';
26
+ description = 'Enforces camelCase naming convention for token keys.';
27
+ defaultSeverity = RuleSeverity.Warning;
28
+ fixable = false;
29
+ interactive = true;
30
+ // #endregion
31
+ // #region Methods
32
+ run(context) {
33
+ const findings = [];
34
+ for (const entry of context.entries) {
35
+ const keys = getTokenKeys(entry.content);
36
+ for (const key of keys) {
37
+ if (!CAMEL_CASE_PATTERN.test(key)) {
38
+ findings.push({
39
+ component: entry.component,
40
+ theme: entry.theme,
41
+ key,
42
+ message: `Key '${key}' does not follow camelCase naming convention.`,
43
+ severity: this.defaultSeverity,
44
+ filePath: entry.filePath,
45
+ fixed: false,
46
+ });
47
+ }
48
+ }
49
+ }
50
+ return { ruleName: this.name, findings, duration: 0 };
51
+ }
52
+ async interact(context, findings) {
53
+ const entryMap = new Map(context.entries.map((e) => [`${e.theme}:${e.component}`, e]));
54
+ const pendingWrites = new Map();
55
+ const result = [];
56
+ let skipAll = false;
57
+ for (const finding of findings) {
58
+ if (skipAll) {
59
+ result.push(finding);
60
+ continue;
61
+ }
62
+ if (!finding.key || !finding.filePath) {
63
+ result.push(finding);
64
+ continue;
65
+ }
66
+ const entry = entryMap.get(`${finding.theme}:${finding.component}`);
67
+ if (!entry) {
68
+ result.push(finding);
69
+ continue;
70
+ }
71
+ const suggested = this._toCamelCase(finding.key);
72
+ if (suggested === finding.key) {
73
+ result.push(finding);
74
+ continue;
75
+ }
76
+ const choices = [{
77
+ name: 'rename',
78
+ message: `Rename to '${suggested}'`,
79
+ }];
80
+ const action = await promptSelect(finding.message, choices);
81
+ if (action === SKIP_ALL) {
82
+ skipAll = true;
83
+ result.push(finding);
84
+ continue;
85
+ }
86
+ if (action === SKIP) {
87
+ result.push(finding);
88
+ continue;
89
+ }
90
+ if (action === 'rename' && Object.prototype.hasOwnProperty.call(entry.content, finding.key)) {
91
+ const value = entry.content[finding.key];
92
+ delete entry.content[finding.key];
93
+ entry.content[suggested] = value;
94
+ pendingWrites.set(entry.filePath, entry);
95
+ result.push({ ...finding, fixed: true });
96
+ }
97
+ else {
98
+ result.push(finding);
99
+ }
100
+ }
101
+ for (const entry of pendingWrites.values()) {
102
+ fs.writeFileSync(entry.filePath, JSON.stringify(entry.content, null, 2) + '\n', 'utf-8');
103
+ }
104
+ return result;
105
+ }
106
+ _toCamelCase(str) {
107
+ return str
108
+ .replace(/[-_]+(.)/g, (_, c) => c.toUpperCase())
109
+ .replace(/^[A-Z]/, c => c.toLowerCase());
110
+ }
111
+ }
112
+ //# sourceMappingURL=naming-convention.rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naming-convention.rule.js","sourceRoot":"","sources":["../../src/rules/naming-convention.rule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { IRule } from '../core/rule.js';
2
+ import type { IRuleContext } from '../models/rule-context.js';
3
+ import type { IRuleResult } from '../models/rule-result.js';
4
+ import { RuleSeverity } from '../models/rule-severity.js';
5
+ import type { IRuleOptionValues, IRuleOptionsEntry } from '../models/token-linter-config.js';
6
+ /**
7
+ * @description Options for the `nested-depth` rule.
8
+ * @public
9
+ */
10
+ export interface INestedDepthRuleOptions extends IRuleOptionValues {
11
+ /**
12
+ * @description Maximum allowed nesting depth for token objects.
13
+ * @default 2
14
+ */
15
+ readonly maxDepth?: number;
16
+ }
17
+ /**
18
+ * @description Creates typed rule options for the `nested-depth` rule.
19
+ * @public
20
+ *
21
+ * @example
22
+ * ```js
23
+ * ruleOptions: {
24
+ * ...nestedDepthRuleOptions({ maxDepth: 3 }),
25
+ * }
26
+ * ```
27
+ */
28
+ export declare function nestedDepthRuleOptions(options: INestedDepthRuleOptions): IRuleOptionsEntry;
29
+ /**
30
+ * @description Limits the nesting depth of token objects to prevent overly complex structures.
31
+ * @public
32
+ */
33
+ export declare class NestedDepthRule implements IRule {
34
+ readonly name = "nested-depth";
35
+ readonly description = "Limits the nesting depth of token objects.";
36
+ readonly defaultSeverity: RuleSeverity;
37
+ readonly fixable = false;
38
+ readonly interactive = false;
39
+ run(context: IRuleContext): IRuleResult;
40
+ private checkDepth;
41
+ }
42
+ //# sourceMappingURL=nested-depth.rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested-depth.rule.d.ts","sourceRoot":"","sources":["../../src/rules/nested-depth.rule.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAa7F;;;GAGG;AACH,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAE9D;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC9B;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,iBAAiB,CAE1F;AAID;;;GAGG;AACH,qBAAa,eAAgB,YAAW,KAAK;IAIzC,SAAgB,IAAI,kBAAkB;IACtC,SAAgB,WAAW,gDAAgD;IAC3E,SAAgB,eAAe,EAAE,YAAY,CAAwB;IACrE,SAAgB,OAAO,SAAS;IAChC,SAAgB,WAAW,SAAS;IAM7B,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW;IAY9C,OAAO,CAAC,UAAU;CAoCrB"}
@@ -0,0 +1,72 @@
1
+ // #region Imports
2
+ import { RuleSeverity } from '../models/rule-severity.js';
3
+ import { METADATA_KEYS } from '../utils.js';
4
+ // #endregion
5
+ // #region Constants
6
+ const DEFAULT_MAX_DEPTH = 2;
7
+ // #endregion
8
+ // #region Functions
9
+ /**
10
+ * @description Creates typed rule options for the `nested-depth` rule.
11
+ * @public
12
+ *
13
+ * @example
14
+ * ```js
15
+ * ruleOptions: {
16
+ * ...nestedDepthRuleOptions({ maxDepth: 3 }),
17
+ * }
18
+ * ```
19
+ */
20
+ export function nestedDepthRuleOptions(options) {
21
+ return { 'nested-depth': options };
22
+ }
23
+ // #endregion
24
+ /**
25
+ * @description Limits the nesting depth of token objects to prevent overly complex structures.
26
+ * @public
27
+ */
28
+ export class NestedDepthRule {
29
+ // #region Fields
30
+ name = 'nested-depth';
31
+ description = 'Limits the nesting depth of token objects.';
32
+ defaultSeverity = RuleSeverity.Warning;
33
+ fixable = false;
34
+ interactive = false;
35
+ // #endregion
36
+ // #region Methods
37
+ run(context) {
38
+ const findings = [];
39
+ const options = context.config.ruleOptions[this.name];
40
+ const maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
41
+ for (const entry of context.entries) {
42
+ this.checkDepth(entry.content, 0, maxDepth, '', entry, findings);
43
+ }
44
+ return { ruleName: this.name, findings, duration: 0 };
45
+ }
46
+ checkDepth(obj, currentDepth, maxDepth, path, entry, findings) {
47
+ for (const [key, val] of Object.entries(obj)) {
48
+ if (METADATA_KEYS.includes(key)) {
49
+ continue;
50
+ }
51
+ const fullPath = path ? `${path}.${key}` : key;
52
+ if (typeof val === 'object' && val !== null && !('value' in val)) {
53
+ const nextDepth = currentDepth + 1;
54
+ if (nextDepth > maxDepth) {
55
+ findings.push({
56
+ component: entry.component,
57
+ theme: entry.theme,
58
+ key: fullPath,
59
+ message: `Nesting depth ${nextDepth} exceeds maximum of ${maxDepth} at '${fullPath}'.`,
60
+ severity: this.defaultSeverity,
61
+ filePath: entry.filePath,
62
+ fixed: false,
63
+ });
64
+ }
65
+ else {
66
+ this.checkDepth(val, nextDepth, maxDepth, fullPath, entry, findings);
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+ //# sourceMappingURL=nested-depth.rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested-depth.rule.js","sourceRoot":"","sources":["../../src/rules/nested-depth.rule.ts"],"names":[],"mappings":"AAAA,kBAAkB;AAMlB,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,aAAa;AAEb,oBAAoB;AAEpB,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAmB5B,aAAa;AAEb,oBAAoB;AAEpB;;;;;;;;;;GAUG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAgC;IACnE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC;AACvC,CAAC;AAED,aAAa;AAEb;;;GAGG;AACH,MAAM,OAAO,eAAe;IAExB,iBAAiB;IAED,IAAI,GAAG,cAAc,CAAC;IACtB,WAAW,GAAG,4CAA4C,CAAC;IAC3D,eAAe,GAAiB,YAAY,CAAC,OAAO,CAAC;IACrD,OAAO,GAAG,KAAK,CAAC;IAChB,WAAW,GAAG,KAAK,CAAC;IAEpC,aAAa;IAEb,kBAAkB;IAEX,GAAG,CAAC,OAAqB;QAC5B,MAAM,QAAQ,GAAmB,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAwC,CAAC;QAC7F,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,iBAAiB,CAAC;QAExD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;QACrE,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IAC1D,CAAC;IAEO,UAAU,CACd,GAA4B,EAC5B,YAAoB,EACpB,QAAgB,EAChB,IAAY,EACZ,KAA6D,EAC7D,QAAwB;QAExB,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3C,IAAI,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC9B,SAAS;YACb,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;YAE/C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,CAAC,OAAO,IAAI,GAAG,CAAC,EAAE,CAAC;gBAC/D,MAAM,SAAS,GAAG,YAAY,GAAG,CAAC,CAAC;gBAEnC,IAAI,SAAS,GAAG,QAAQ,EAAE,CAAC;oBACvB,QAAQ,CAAC,IAAI,CAAC;wBACV,SAAS,EAAE,KAAK,CAAC,SAAS;wBAC1B,KAAK,EAAE,KAAK,CAAC,KAAK;wBAClB,GAAG,EAAE,QAAQ;wBACb,OAAO,EAAE,iBAAiB,SAAS,uBAAuB,QAAQ,QAAQ,QAAQ,IAAI;wBACtF,QAAQ,EAAE,IAAI,CAAC,eAAe;wBAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;wBACxB,KAAK,EAAE,KAAK;qBACf,CAAC,CAAC;gBACP,CAAC;qBAAM,CAAC;oBACJ,IAAI,CAAC,UAAU,CAAC,GAA8B,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;gBACpG,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;CAGJ"}
@@ -0,0 +1,60 @@
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
+ import type { IRuleOptionValues, IRuleOptionsEntry } from '../models/token-linter-config.js';
7
+ /**
8
+ * @description Options for the `required-includes` rule.
9
+ * @public
10
+ */
11
+ export interface IRequiredIncludesRuleOptions extends IRuleOptionValues {
12
+ /**
13
+ * @description List of mixin names that every component must include. Overrides the built-in default list.
14
+ * @default ['font', 'padding', 'gap', 'transition', 'translate', 'shadow']
15
+ */
16
+ readonly includes?: ReadonlyArray<string>;
17
+ }
18
+ /**
19
+ * @description Creates typed rule options for the `required-includes` rule.
20
+ * @public
21
+ *
22
+ * @example
23
+ * ```js
24
+ * ruleOptions: {
25
+ * ...requiredIncludesRuleOptions({ includes: ['font', 'padding', 'gap'] }),
26
+ * }
27
+ * ```
28
+ */
29
+ export declare function requiredIncludesRuleOptions(options: IRequiredIncludesRuleOptions): IRuleOptionsEntry;
30
+ /**
31
+ * @description Ensures that every component token file contains a predefined set of required `$includes` entries.
32
+ * Missing entries are reported as findings. When auto-fix is enabled, missing entries are appended
33
+ * to the `$includes` array and the file is rewritten.
34
+ *
35
+ * The required list defaults to `['focusRing']` and can be overridden via `ruleOptions`:
36
+ *
37
+ * @example Configuration
38
+ * ```js
39
+ * export default defineConfig({
40
+ * rules: { 'required-includes': 'error' },
41
+ * ruleOptions: {
42
+ * 'required-includes': {
43
+ * includes: ['focusRing', 'ripple', 'transition'],
44
+ * },
45
+ * },
46
+ * });
47
+ * ```
48
+ *
49
+ * @public
50
+ */
51
+ export declare class RequiredIncludesRule implements IRule {
52
+ readonly name = "required-includes";
53
+ readonly description = "Ensures every component includes a predefined set of required $includes entries.";
54
+ readonly defaultSeverity: RuleSeverity;
55
+ readonly fixable = true;
56
+ readonly interactive = false;
57
+ run(context: IRuleContext): IRuleResult;
58
+ fix(context: IRuleContext, findings: ReadonlyArray<IRuleFinding>): IRuleFinding[];
59
+ }
60
+ //# sourceMappingURL=required-includes.rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"required-includes.rule.d.ts","sourceRoot":"","sources":["../../src/rules/required-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;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAmB7F;;;GAGG;AACH,MAAM,WAAW,4BAA6B,SAAQ,iBAAiB;IAEnE;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC7C;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,4BAA4B,GAAG,iBAAiB,CAEpG;AAID;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,oBAAqB,YAAW,KAAK;IAI9C,SAAgB,IAAI,uBAAuB;IAC3C,SAAgB,WAAW,sFAAsF;IACjH,SAAgB,eAAe,EAAE,YAAY,CAAsB;IACnE,SAAgB,OAAO,QAAQ;IAC/B,SAAgB,WAAW,SAAS;IAM7B,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW;IA4BvC,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,YAAY,EAAE;CA6C3F"}
@@ -0,0 +1,118 @@
1
+ // #region Imports
2
+ import * as fs from 'fs';
3
+ import { RuleSeverity } from '../models/rule-severity.js';
4
+ // #endregion
5
+ // #region Constants
6
+ const DEFAULT_REQUIRED_INCLUDES = [
7
+ 'font',
8
+ 'padding',
9
+ 'gap',
10
+ 'transition',
11
+ 'translate',
12
+ 'shadow'
13
+ ];
14
+ // #endregion
15
+ // #region Functions
16
+ /**
17
+ * @description Creates typed rule options for the `required-includes` rule.
18
+ * @public
19
+ *
20
+ * @example
21
+ * ```js
22
+ * ruleOptions: {
23
+ * ...requiredIncludesRuleOptions({ includes: ['font', 'padding', 'gap'] }),
24
+ * }
25
+ * ```
26
+ */
27
+ export function requiredIncludesRuleOptions(options) {
28
+ return { 'required-includes': options };
29
+ }
30
+ // #endregion
31
+ /**
32
+ * @description Ensures that every component token file contains a predefined set of required `$includes` entries.
33
+ * Missing entries are reported as findings. When auto-fix is enabled, missing entries are appended
34
+ * to the `$includes` array and the file is rewritten.
35
+ *
36
+ * The required list defaults to `['focusRing']` and can be overridden via `ruleOptions`:
37
+ *
38
+ * @example Configuration
39
+ * ```js
40
+ * export default defineConfig({
41
+ * rules: { 'required-includes': 'error' },
42
+ * ruleOptions: {
43
+ * 'required-includes': {
44
+ * includes: ['focusRing', 'ripple', 'transition'],
45
+ * },
46
+ * },
47
+ * });
48
+ * ```
49
+ *
50
+ * @public
51
+ */
52
+ export class RequiredIncludesRule {
53
+ // #region Fields
54
+ name = 'required-includes';
55
+ description = 'Ensures every component includes a predefined set of required $includes entries.';
56
+ defaultSeverity = RuleSeverity.Error;
57
+ fixable = true;
58
+ interactive = false;
59
+ // #endregion
60
+ // #region Methods
61
+ run(context) {
62
+ const findings = [];
63
+ const options = context.config.ruleOptions[this.name];
64
+ const requiredIncludes = options?.includes ?? DEFAULT_REQUIRED_INCLUDES;
65
+ for (const entry of context.entries) {
66
+ const includes = entry.content['$includes'];
67
+ const currentIncludes = Array.isArray(includes) ? includes : [];
68
+ const currentBases = new Set(currentIncludes.map(i => i.split(':')[0]));
69
+ for (const required of requiredIncludes) {
70
+ if (!currentBases.has(required)) {
71
+ findings.push({
72
+ component: entry.component,
73
+ theme: entry.theme,
74
+ key: '$includes',
75
+ message: `Required include '${required}' is missing in component '${entry.component}' (${entry.theme}).`,
76
+ severity: this.defaultSeverity,
77
+ filePath: entry.filePath,
78
+ fixed: false,
79
+ });
80
+ }
81
+ }
82
+ }
83
+ return { ruleName: this.name, findings, duration: 0 };
84
+ }
85
+ fix(context, findings) {
86
+ const entryMap = new Map(context.entries.map(e => [e.filePath, e]));
87
+ const pendingWrites = new Map();
88
+ const result = [];
89
+ for (const finding of findings) {
90
+ const entry = finding.filePath ? entryMap.get(finding.filePath) : undefined;
91
+ if (!entry) {
92
+ result.push(finding);
93
+ continue;
94
+ }
95
+ const missingInclude = finding.message.match(/Required include '(.+?)'/)?.[1];
96
+ if (!missingInclude) {
97
+ result.push(finding);
98
+ continue;
99
+ }
100
+ const content = pendingWrites.get(entry.filePath) ?? entry.content;
101
+ const currentIncludes = Array.isArray(content['$includes'])
102
+ ? [...content['$includes']]
103
+ : [];
104
+ if (!currentIncludes.some(i => i.split(':')[0] === missingInclude)) {
105
+ currentIncludes.push(missingInclude);
106
+ currentIncludes.sort((a, b) => a.localeCompare(b));
107
+ }
108
+ content['$includes'] = currentIncludes;
109
+ pendingWrites.set(entry.filePath, content);
110
+ result.push({ ...finding, fixed: true });
111
+ }
112
+ for (const [filePath, content] of pendingWrites.entries()) {
113
+ fs.writeFileSync(filePath, JSON.stringify(content, null, 2) + '\n', 'utf-8');
114
+ }
115
+ return result;
116
+ }
117
+ }
118
+ //# sourceMappingURL=required-includes.rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"required-includes.rule.js","sourceRoot":"","sources":["../../src/rules/required-includes.rule.ts"],"names":[],"mappings":"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"}