@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.
- package/CHANGELOG.md +10 -0
- package/LICENSE +21 -0
- package/README.md +72 -0
- package/cli/Cli.d.ts +10 -0
- package/cli/Cli.d.ts.map +1 -0
- package/cli/Cli.js +32 -0
- package/cli/Cli.js.map +1 -0
- package/cli/CliArgs.d.ts +2 -0
- package/cli/CliArgs.d.ts.map +1 -0
- package/cli/CliArgs.js +88 -0
- package/cli/CliArgs.js.map +1 -0
- package/cli/CliArgsConfig.d.ts +2 -0
- package/cli/CliArgsConfig.d.ts.map +1 -0
- package/cli/CliArgsConfig.js +12 -0
- package/cli/CliArgsConfig.js.map +1 -0
- package/cli/CliRun.d.ts +2 -0
- package/cli/CliRun.d.ts.map +1 -0
- package/cli/CliRun.js +34 -0
- package/cli/CliRun.js.map +1 -0
- package/cli/commands/LintCommand.d.ts +2 -0
- package/cli/commands/LintCommand.d.ts.map +1 -0
- package/cli/commands/LintCommand.js +66 -0
- package/cli/commands/LintCommand.js.map +1 -0
- package/cli/commands/LintCommandArgsConfig.d.ts +2 -0
- package/cli/commands/LintCommandArgsConfig.d.ts.map +1 -0
- package/cli/commands/LintCommandArgsConfig.js +46 -0
- package/cli/commands/LintCommandArgsConfig.js.map +1 -0
- package/cli/lint.d.ts +3 -0
- package/cli/lint.d.ts.map +1 -0
- package/cli/lint.js +55 -0
- package/cli/lint.js.map +1 -0
- package/config/TokenLinterConfigDiscovery.d.ts +16 -0
- package/config/TokenLinterConfigDiscovery.d.ts.map +1 -0
- package/config/TokenLinterConfigDiscovery.js +44 -0
- package/config/TokenLinterConfigDiscovery.js.map +1 -0
- package/config/defineConfig.d.ts +41 -0
- package/config/defineConfig.d.ts.map +1 -0
- package/config/defineConfig.js +37 -0
- package/config/defineConfig.js.map +1 -0
- package/core/interaction.d.ts +25 -0
- package/core/interaction.d.ts.map +1 -0
- package/core/interaction.js +45 -0
- package/core/interaction.js.map +1 -0
- package/core/loader.d.ts +21 -0
- package/core/loader.d.ts.map +1 -0
- package/core/loader.js +84 -0
- package/core/loader.js.map +1 -0
- package/core/rule-registry.d.ts +46 -0
- package/core/rule-registry.d.ts.map +1 -0
- package/core/rule-registry.js +73 -0
- package/core/rule-registry.js.map +1 -0
- package/core/rule.d.ts +46 -0
- package/core/rule.d.ts.map +1 -0
- package/core/rule.js +3 -0
- package/core/rule.js.map +1 -0
- package/core/runner.d.ts +23 -0
- package/core/runner.d.ts.map +1 -0
- package/core/runner.js +103 -0
- package/core/runner.js.map +1 -0
- package/index.d.ts +46 -0
- package/index.d.ts.map +1 -0
- package/index.js +47 -0
- package/index.js.map +1 -0
- package/models/lint-result.d.ts +14 -0
- package/models/lint-result.d.ts.map +1 -0
- package/models/lint-result.js +3 -0
- package/models/lint-result.js.map +1 -0
- package/models/rule-context.d.ts +18 -0
- package/models/rule-context.d.ts.map +1 -0
- package/models/rule-context.js +3 -0
- package/models/rule-context.js.map +1 -0
- package/models/rule-finding.d.ts +15 -0
- package/models/rule-finding.d.ts.map +1 -0
- package/models/rule-finding.js +3 -0
- package/models/rule-finding.js.map +1 -0
- package/models/rule-result.d.ts +11 -0
- package/models/rule-result.d.ts.map +1 -0
- package/models/rule-result.js +3 -0
- package/models/rule-result.js.map +1 -0
- package/models/rule-severity.d.ts +14 -0
- package/models/rule-severity.d.ts.map +1 -0
- package/models/rule-severity.js +10 -0
- package/models/rule-severity.js.map +1 -0
- package/models/token-entry.d.ts +11 -0
- package/models/token-entry.d.ts.map +1 -0
- package/models/token-entry.js +2 -0
- package/models/token-entry.js.map +1 -0
- package/models/token-linter-config.d.ts +30 -0
- package/models/token-linter-config.d.ts.map +1 -0
- package/models/token-linter-config.js +3 -0
- package/models/token-linter-config.js.map +1 -0
- package/orchestration/TokenLinterOrchestrator.d.ts +29 -0
- package/orchestration/TokenLinterOrchestrator.d.ts.map +1 -0
- package/orchestration/TokenLinterOrchestrator.js +98 -0
- package/orchestration/TokenLinterOrchestrator.js.map +1 -0
- package/package.json +20 -0
- package/presets/index.d.ts +2 -0
- package/presets/index.d.ts.map +1 -0
- package/presets/index.js +2 -0
- package/presets/index.js.map +1 -0
- package/presets/recommended.d.ts +23 -0
- package/presets/recommended.d.ts.map +1 -0
- package/presets/recommended.js +41 -0
- package/presets/recommended.js.map +1 -0
- package/reporters/console.reporter.d.ts +15 -0
- package/reporters/console.reporter.d.ts.map +1 -0
- package/reporters/console.reporter.js +75 -0
- package/reporters/console.reporter.js.map +1 -0
- package/reporters/json.reporter.d.ts +11 -0
- package/reporters/json.reporter.d.ts.map +1 -0
- package/reporters/json.reporter.js +37 -0
- package/reporters/json.reporter.js.map +1 -0
- package/reporters/reporter.d.ts +16 -0
- package/reporters/reporter.d.ts.map +1 -0
- package/reporters/reporter.js +3 -0
- package/reporters/reporter.js.map +1 -0
- package/reporters/summary.reporter.d.ts +12 -0
- package/reporters/summary.reporter.d.ts.map +1 -0
- package/reporters/summary.reporter.js +48 -0
- package/reporters/summary.reporter.js.map +1 -0
- package/rules/alphabetical-sort.rule.d.ts +19 -0
- package/rules/alphabetical-sort.rule.d.ts.map +1 -0
- package/rules/alphabetical-sort.rule.js +72 -0
- package/rules/alphabetical-sort.rule.js.map +1 -0
- package/rules/component-presence.rule.d.ts +29 -0
- package/rules/component-presence.rule.d.ts.map +1 -0
- package/rules/component-presence.rule.js +102 -0
- package/rules/component-presence.rule.js.map +1 -0
- package/rules/duplicate-value.rule.d.ts +17 -0
- package/rules/duplicate-value.rule.d.ts.map +1 -0
- package/rules/duplicate-value.rule.js +49 -0
- package/rules/duplicate-value.rule.js.map +1 -0
- package/rules/empty-json.rule.d.ts +52 -0
- package/rules/empty-json.rule.d.ts.map +1 -0
- package/rules/empty-json.rule.js +100 -0
- package/rules/empty-json.rule.js.map +1 -0
- package/rules/forbidden-key.rule.d.ts +57 -0
- package/rules/forbidden-key.rule.d.ts.map +1 -0
- package/rules/forbidden-key.rule.js +118 -0
- package/rules/forbidden-key.rule.js.map +1 -0
- package/rules/forbidden-value.rule.d.ts +53 -0
- package/rules/forbidden-value.rule.d.ts.map +1 -0
- package/rules/forbidden-value.rule.js +130 -0
- package/rules/forbidden-value.rule.js.map +1 -0
- package/rules/includes-consistency.rule.d.ts +30 -0
- package/rules/includes-consistency.rule.d.ts.map +1 -0
- package/rules/includes-consistency.rule.js +153 -0
- package/rules/includes-consistency.rule.js.map +1 -0
- package/rules/index.d.ts +7 -0
- package/rules/index.d.ts.map +1 -0
- package/rules/index.js +47 -0
- package/rules/index.js.map +1 -0
- package/rules/key-consistency.rule.d.ts +38 -0
- package/rules/key-consistency.rule.d.ts.map +1 -0
- package/rules/key-consistency.rule.js +161 -0
- package/rules/key-consistency.rule.js.map +1 -0
- package/rules/missing-includes.rule.d.ts +19 -0
- package/rules/missing-includes.rule.d.ts.map +1 -0
- package/rules/missing-includes.rule.js +51 -0
- package/rules/missing-includes.rule.js.map +1 -0
- package/rules/mixin-reference.rule.d.ts +28 -0
- package/rules/mixin-reference.rule.d.ts.map +1 -0
- package/rules/mixin-reference.rule.js +104 -0
- package/rules/mixin-reference.rule.js.map +1 -0
- package/rules/naming-convention.rule.d.ts +29 -0
- package/rules/naming-convention.rule.d.ts.map +1 -0
- package/rules/naming-convention.rule.js +112 -0
- package/rules/naming-convention.rule.js.map +1 -0
- package/rules/nested-depth.rule.d.ts +42 -0
- package/rules/nested-depth.rule.d.ts.map +1 -0
- package/rules/nested-depth.rule.js +72 -0
- package/rules/nested-depth.rule.js.map +1 -0
- package/rules/required-includes.rule.d.ts +60 -0
- package/rules/required-includes.rule.d.ts.map +1 -0
- package/rules/required-includes.rule.js +118 -0
- package/rules/required-includes.rule.js.map +1 -0
- package/rules/rule-options.d.ts +158 -0
- package/rules/rule-options.d.ts.map +1 -0
- package/rules/rule-options.js +89 -0
- package/rules/rule-options.js.map +1 -0
- package/rules/value-consistency.rule.d.ts +17 -0
- package/rules/value-consistency.rule.d.ts.map +1 -0
- package/rules/value-consistency.rule.js +69 -0
- package/rules/value-consistency.rule.js.map +1 -0
- package/rules/value-schema.rule.d.ts +18 -0
- package/rules/value-schema.rule.d.ts.map +1 -0
- package/rules/value-schema.rule.js +108 -0
- package/rules/value-schema.rule.js.map +1 -0
- package/rules/value-type.rule.d.ts +71 -0
- package/rules/value-type.rule.d.ts.map +1 -0
- package/rules/value-type.rule.js +176 -0
- package/rules/value-type.rule.js.map +1 -0
- package/utils.d.ts +55 -0
- package/utils.d.ts.map +1 -0
- package/utils.js +125 -0
- package/utils.js.map +1 -0
|
@@ -0,0 +1,100 @@
|
|
|
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 Functions
|
|
8
|
+
/**
|
|
9
|
+
* @description Creates typed rule options for the `empty-json` rule.
|
|
10
|
+
* @public
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```js
|
|
14
|
+
* ruleOptions: {
|
|
15
|
+
* ...emptyJsonRuleOptions({ allowIncludes: true }),
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function emptyJsonRuleOptions(options) {
|
|
20
|
+
return { 'empty-json': options };
|
|
21
|
+
}
|
|
22
|
+
// #endregion
|
|
23
|
+
/**
|
|
24
|
+
* @description Detects component token files with no token definitions.
|
|
25
|
+
*
|
|
26
|
+
* @example Interactive prompt
|
|
27
|
+
* ```
|
|
28
|
+
* ? Component 'divider' in theme 'joy' has no token definitions.
|
|
29
|
+
* > Delete file
|
|
30
|
+
* Skip
|
|
31
|
+
* Skip all remaining
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @public
|
|
35
|
+
*/
|
|
36
|
+
export class EmptyJsonRule {
|
|
37
|
+
// #region Fields
|
|
38
|
+
name = 'empty-json';
|
|
39
|
+
description = 'Detects component token files with no token definitions.';
|
|
40
|
+
defaultSeverity = RuleSeverity.Warning;
|
|
41
|
+
fixable = false;
|
|
42
|
+
interactive = true;
|
|
43
|
+
// #endregion
|
|
44
|
+
// #region Methods
|
|
45
|
+
run(context) {
|
|
46
|
+
const findings = [];
|
|
47
|
+
const options = context.config.ruleOptions[this.name];
|
|
48
|
+
const allowIncludes = options?.allowIncludes ?? false;
|
|
49
|
+
for (const entry of context.entries) {
|
|
50
|
+
const tokenKeys = getTokenKeys(entry.content);
|
|
51
|
+
if (tokenKeys.length === 0) {
|
|
52
|
+
if (allowIncludes && Array.isArray(entry.content['$includes']) && entry.content['$includes'].length > 0) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
findings.push({
|
|
56
|
+
component: entry.component,
|
|
57
|
+
theme: entry.theme,
|
|
58
|
+
message: `Component '${entry.component}' in theme '${entry.theme}' has no token definitions.`,
|
|
59
|
+
severity: this.defaultSeverity,
|
|
60
|
+
filePath: entry.filePath,
|
|
61
|
+
fixed: false,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { ruleName: this.name, findings, duration: 0 };
|
|
66
|
+
}
|
|
67
|
+
async interact(_context, findings) {
|
|
68
|
+
const result = [];
|
|
69
|
+
let skipAll = false;
|
|
70
|
+
for (const finding of findings) {
|
|
71
|
+
if (skipAll) {
|
|
72
|
+
result.push(finding);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (!finding.filePath) {
|
|
76
|
+
result.push(finding);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const action = await promptSelect(finding.message, ['Delete file']);
|
|
80
|
+
if (action === SKIP_ALL) {
|
|
81
|
+
skipAll = true;
|
|
82
|
+
result.push(finding);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (action === SKIP) {
|
|
86
|
+
result.push(finding);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (fs.existsSync(finding.filePath)) {
|
|
90
|
+
fs.unlinkSync(finding.filePath);
|
|
91
|
+
result.push({ ...finding, fixed: true });
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
result.push(finding);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=empty-json.rule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"empty-json.rule.js","sourceRoot":"","sources":["../../src/rules/empty-json.rule.ts"],"names":[],"mappings":"AAAA,kBAAkB;AAElB,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAKtE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAmB3C,aAAa;AAEb,oBAAoB;AAEpB;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAA8B;IAC/D,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;AACrC,CAAC;AAED,aAAa;AAEb;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,aAAa;IAEtB,iBAAiB;IAED,IAAI,GAAG,YAAY,CAAC;IACpB,WAAW,GAAG,0DAA0D,CAAC;IACzE,eAAe,GAAiB,YAAY,CAAC,OAAO,CAAC;IACrD,OAAO,GAAG,KAAK,CAAC;IAChB,WAAW,GAAG,IAAI,CAAC;IAEnC,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,CAAsC,CAAC;QAC3F,MAAM,aAAa,GAAG,OAAO,EAAE,aAAa,IAAI,KAAK,CAAC;QAEtD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YAClC,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAE9C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,IAAI,aAAa,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,IAAK,KAAK,CAAC,OAAO,CAAC,WAAW,CAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACrH,SAAS;gBACb,CAAC;gBAED,QAAQ,CAAC,IAAI,CAAC;oBACV,SAAS,EAAE,KAAK,CAAC,SAAS;oBAC1B,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,OAAO,EAAE,cAAc,KAAK,CAAC,SAAS,eAAe,KAAK,CAAC,KAAK,6BAA6B;oBAC7F,QAAQ,EAAE,IAAI,CAAC,eAAe;oBAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;oBACxB,KAAK,EAAE,KAAK;iBACf,CAAC,CAAC;YACP,CAAC;QACL,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IAC1D,CAAC;IAEM,KAAK,CAAC,QAAQ,CAAC,QAAsB,EAAE,QAAqC;QAC/E,MAAM,MAAM,GAAmB,EAAE,CAAC;QAClC,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC7B,IAAI,OAAO,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAErB,SAAS;YACb,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACpB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAErB,SAAS;YACb,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC;YAEpE,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACtB,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAErB,SAAS;YACb,CAAC;YAED,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBAClB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAErB,SAAS;YACb,CAAC;YAED,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAClC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAChC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC7C,CAAC;iBAAM,CAAC;gBACJ,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzB,CAAC;QACL,CAAC;QAED,OAAO,MAAM,CAAC;IAClB,CAAC;CAGJ"}
|
|
@@ -0,0 +1,57 @@
|
|
|
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 { IRuleOptionsEntry, IRuleOptionValues } from '../models/token-linter-config.js';
|
|
7
|
+
/**
|
|
8
|
+
* @description Options for the `forbidden-key` rule.
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export interface IForbiddenKeyRuleOptions extends IRuleOptionValues {
|
|
12
|
+
/**
|
|
13
|
+
* @description Keys that are forbidden in component token files. Overrides the built-in default list.
|
|
14
|
+
* @default ['transition', 'shadow']
|
|
15
|
+
*/
|
|
16
|
+
readonly keys?: ReadonlyArray<string>;
|
|
17
|
+
/**
|
|
18
|
+
* @description If set, only these themes are checked for forbidden keys.
|
|
19
|
+
* @default undefined (all themes)
|
|
20
|
+
*/
|
|
21
|
+
readonly themes?: ReadonlyArray<string>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @description Creates typed rule options for the `forbidden-key` rule.
|
|
25
|
+
* @public
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```js
|
|
29
|
+
* ruleOptions: {
|
|
30
|
+
* ...forbiddenKeyRuleOptions({ keys: ['transition', 'shadow'], themes: ['joy'] }),
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function forbiddenKeyRuleOptions(options: IForbiddenKeyRuleOptions): IRuleOptionsEntry;
|
|
35
|
+
/**
|
|
36
|
+
* @description Detects forbidden keys in component token files.
|
|
37
|
+
*
|
|
38
|
+
* @example Interactive prompt
|
|
39
|
+
* ```
|
|
40
|
+
* ? Forbidden key 'transition' found in 'button' (joy).
|
|
41
|
+
* > Remove key 'transition'
|
|
42
|
+
* Skip
|
|
43
|
+
* Skip all remaining
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
export declare class ForbiddenKeyRule implements IRule {
|
|
49
|
+
readonly name = "forbidden-key";
|
|
50
|
+
readonly description = "Detects forbidden keys in component token files.";
|
|
51
|
+
readonly defaultSeverity: RuleSeverity;
|
|
52
|
+
readonly fixable = false;
|
|
53
|
+
readonly interactive = true;
|
|
54
|
+
run(context: IRuleContext): IRuleResult;
|
|
55
|
+
interact(context: IRuleContext, findings: ReadonlyArray<IRuleFinding>): Promise<IRuleFinding[]>;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=forbidden-key.rule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forbidden-key.rule.d.ts","sourceRoot":"","sources":["../../src/rules/forbidden-key.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;AAE1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAa7F;;;GAGG;AACH,MAAM,WAAW,wBAAyB,SAAQ,iBAAiB;IAE/D;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAEtC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC3C;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAE5F;AAID;;;;;;;;;;;;GAYG;AACH,qBAAa,gBAAiB,YAAW,KAAK;IAI1C,SAAgB,IAAI,mBAAmB;IACvC,SAAgB,WAAW,sDAAsD;IACjF,SAAgB,eAAe,EAAE,YAAY,CAAwB;IACrE,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;CA6D/G"}
|
|
@@ -0,0 +1,118 @@
|
|
|
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 DEFAULT_FORBIDDEN_KEYS = ['transition', 'shadow'];
|
|
9
|
+
// #endregion
|
|
10
|
+
// #region Functions
|
|
11
|
+
/**
|
|
12
|
+
* @description Creates typed rule options for the `forbidden-key` rule.
|
|
13
|
+
* @public
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```js
|
|
17
|
+
* ruleOptions: {
|
|
18
|
+
* ...forbiddenKeyRuleOptions({ keys: ['transition', 'shadow'], themes: ['joy'] }),
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function forbiddenKeyRuleOptions(options) {
|
|
23
|
+
return { 'forbidden-key': options };
|
|
24
|
+
}
|
|
25
|
+
// #endregion
|
|
26
|
+
/**
|
|
27
|
+
* @description Detects forbidden keys in component token files.
|
|
28
|
+
*
|
|
29
|
+
* @example Interactive prompt
|
|
30
|
+
* ```
|
|
31
|
+
* ? Forbidden key 'transition' found in 'button' (joy).
|
|
32
|
+
* > Remove key 'transition'
|
|
33
|
+
* Skip
|
|
34
|
+
* Skip all remaining
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @public
|
|
38
|
+
*/
|
|
39
|
+
export class ForbiddenKeyRule {
|
|
40
|
+
// #region Fields
|
|
41
|
+
name = 'forbidden-key';
|
|
42
|
+
description = 'Detects forbidden keys in component token files.';
|
|
43
|
+
defaultSeverity = RuleSeverity.Warning;
|
|
44
|
+
fixable = false;
|
|
45
|
+
interactive = true;
|
|
46
|
+
// #endregion
|
|
47
|
+
// #region Methods
|
|
48
|
+
run(context) {
|
|
49
|
+
const findings = [];
|
|
50
|
+
const options = context.config.ruleOptions[this.name];
|
|
51
|
+
const forbiddenKeys = options?.keys ?? DEFAULT_FORBIDDEN_KEYS;
|
|
52
|
+
const restrictedThemes = options?.themes;
|
|
53
|
+
for (const entry of context.entries) {
|
|
54
|
+
if (restrictedThemes && !restrictedThemes.includes(entry.theme)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const keys = getTokenKeys(entry.content);
|
|
58
|
+
for (const key of keys) {
|
|
59
|
+
if (forbiddenKeys.includes(key)) {
|
|
60
|
+
findings.push({
|
|
61
|
+
component: entry.component,
|
|
62
|
+
theme: entry.theme,
|
|
63
|
+
key,
|
|
64
|
+
message: `Forbidden key '${key}' found in '${entry.component}' (${entry.theme}).`,
|
|
65
|
+
severity: this.defaultSeverity,
|
|
66
|
+
filePath: entry.filePath,
|
|
67
|
+
fixed: false,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { ruleName: this.name, findings, duration: 0 };
|
|
73
|
+
}
|
|
74
|
+
async interact(context, findings) {
|
|
75
|
+
const entryMap = new Map(context.entries.map((e) => [`${e.theme}:${e.component}`, e]));
|
|
76
|
+
const pendingWrites = new Map();
|
|
77
|
+
const result = [];
|
|
78
|
+
let skipAll = false;
|
|
79
|
+
for (const finding of findings) {
|
|
80
|
+
if (skipAll) {
|
|
81
|
+
result.push(finding);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (!finding.key || !finding.filePath) {
|
|
85
|
+
result.push(finding);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const entry = entryMap.get(`${finding.theme}:${finding.component}`);
|
|
89
|
+
if (!entry) {
|
|
90
|
+
result.push(finding);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const action = await promptSelect(finding.message, [`Remove key '${finding.key}'`]);
|
|
94
|
+
if (action === SKIP_ALL) {
|
|
95
|
+
skipAll = true;
|
|
96
|
+
result.push(finding);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (action === SKIP) {
|
|
100
|
+
result.push(finding);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (Object.prototype.hasOwnProperty.call(entry.content, finding.key)) {
|
|
104
|
+
delete entry.content[finding.key];
|
|
105
|
+
pendingWrites.set(entry.filePath, entry);
|
|
106
|
+
result.push({ ...finding, fixed: true });
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
result.push(finding);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
for (const entry of pendingWrites.values()) {
|
|
113
|
+
fs.writeFileSync(entry.filePath, JSON.stringify(entry.content, null, 2) + '\n', 'utf-8');
|
|
114
|
+
}
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=forbidden-key.rule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forbidden-key.rule.js","sourceRoot":"","sources":["../../src/rules/forbidden-key.rule.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
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 { IRuleOptionsEntry, IRuleOptionValues } from '../models/token-linter-config.js';
|
|
7
|
+
/**
|
|
8
|
+
* @description Options for the `forbidden-value` rule.
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export interface IForbiddenValueRuleOptions extends IRuleOptionValues {
|
|
12
|
+
/**
|
|
13
|
+
* @description Values that are forbidden in token definitions. Overrides the built-in default list.
|
|
14
|
+
* @default ['unset', '0px', 'all']
|
|
15
|
+
*/
|
|
16
|
+
readonly values?: ReadonlyArray<string>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* @description Creates typed rule options for the `forbidden-value` rule.
|
|
20
|
+
* @public
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```js
|
|
24
|
+
* ruleOptions: {
|
|
25
|
+
* ...forbiddenValueRuleOptions({ values: ['unset', '0px', 'none'] }),
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function forbiddenValueRuleOptions(options: IForbiddenValueRuleOptions): IRuleOptionsEntry;
|
|
30
|
+
/**
|
|
31
|
+
* @description Detects forbidden token values such as unset, 0px, all.
|
|
32
|
+
*
|
|
33
|
+
* @example Interactive prompt
|
|
34
|
+
* ```
|
|
35
|
+
* ? Forbidden value 'unset' found for key 'borderColor' in 'button' (joy).
|
|
36
|
+
* > Remove key 'borderColor'
|
|
37
|
+
* Skip
|
|
38
|
+
* Skip all remaining
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
export declare class ForbiddenValueRule implements IRule {
|
|
44
|
+
readonly name = "forbidden-value";
|
|
45
|
+
readonly description = "Detects forbidden token values like unset, 0px, all.";
|
|
46
|
+
readonly defaultSeverity: RuleSeverity;
|
|
47
|
+
readonly fixable = false;
|
|
48
|
+
readonly interactive = true;
|
|
49
|
+
run(context: IRuleContext): IRuleResult;
|
|
50
|
+
interact(context: IRuleContext, findings: ReadonlyArray<IRuleFinding>): Promise<IRuleFinding[]>;
|
|
51
|
+
private _deleteNestedKey;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=forbidden-value.rule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forbidden-value.rule.d.ts","sourceRoot":"","sources":["../../src/rules/forbidden-value.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;AAE1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAa7F;;;GAGG;AACH,MAAM,WAAW,0BAA2B,SAAQ,iBAAiB;IAEjE;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC3C;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,GAAG,iBAAiB,CAEhG;AAID;;;;;;;;;;;;GAYG;AACH,qBAAa,kBAAmB,YAAW,KAAK;IAI5C,SAAgB,IAAI,qBAAqB;IACzC,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;IA0BjC,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IA2D5G,OAAO,CAAC,gBAAgB;CA0B3B"}
|
|
@@ -0,0 +1,130 @@
|
|
|
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 { flattenTokenEntries } from '../utils.js';
|
|
6
|
+
// #endregion
|
|
7
|
+
// #region Constants
|
|
8
|
+
const DEFAULT_FORBIDDEN_VALUES = ['unset', '0px', 'all'];
|
|
9
|
+
// #endregion
|
|
10
|
+
// #region Functions
|
|
11
|
+
/**
|
|
12
|
+
* @description Creates typed rule options for the `forbidden-value` rule.
|
|
13
|
+
* @public
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```js
|
|
17
|
+
* ruleOptions: {
|
|
18
|
+
* ...forbiddenValueRuleOptions({ values: ['unset', '0px', 'none'] }),
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function forbiddenValueRuleOptions(options) {
|
|
23
|
+
return { 'forbidden-value': options };
|
|
24
|
+
}
|
|
25
|
+
// #endregion
|
|
26
|
+
/**
|
|
27
|
+
* @description Detects forbidden token values such as unset, 0px, all.
|
|
28
|
+
*
|
|
29
|
+
* @example Interactive prompt
|
|
30
|
+
* ```
|
|
31
|
+
* ? Forbidden value 'unset' found for key 'borderColor' in 'button' (joy).
|
|
32
|
+
* > Remove key 'borderColor'
|
|
33
|
+
* Skip
|
|
34
|
+
* Skip all remaining
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @public
|
|
38
|
+
*/
|
|
39
|
+
export class ForbiddenValueRule {
|
|
40
|
+
// #region Fields
|
|
41
|
+
name = 'forbidden-value';
|
|
42
|
+
description = 'Detects forbidden token values like unset, 0px, all.';
|
|
43
|
+
defaultSeverity = RuleSeverity.Warning;
|
|
44
|
+
fixable = false;
|
|
45
|
+
interactive = true;
|
|
46
|
+
// #endregion
|
|
47
|
+
// #region Methods
|
|
48
|
+
run(context) {
|
|
49
|
+
const findings = [];
|
|
50
|
+
const options = context.config.ruleOptions[this.name];
|
|
51
|
+
const forbiddenValues = options?.values ?? DEFAULT_FORBIDDEN_VALUES;
|
|
52
|
+
for (const entry of context.entries) {
|
|
53
|
+
const flatEntries = flattenTokenEntries(entry.content);
|
|
54
|
+
for (const { key, value } of flatEntries) {
|
|
55
|
+
if (typeof value === 'string' && forbiddenValues.includes(value.trim())) {
|
|
56
|
+
findings.push({
|
|
57
|
+
component: entry.component,
|
|
58
|
+
theme: entry.theme,
|
|
59
|
+
key,
|
|
60
|
+
message: `Forbidden value '${value}' found for key '${key}' in '${entry.component}' (${entry.theme}).`,
|
|
61
|
+
severity: this.defaultSeverity,
|
|
62
|
+
filePath: entry.filePath,
|
|
63
|
+
fixed: false,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { ruleName: this.name, findings, duration: 0 };
|
|
69
|
+
}
|
|
70
|
+
async interact(context, findings) {
|
|
71
|
+
const entryMap = new Map(context.entries.map((e) => [`${e.theme}:${e.component}`, e]));
|
|
72
|
+
const pendingWrites = new Map();
|
|
73
|
+
const result = [];
|
|
74
|
+
let skipAll = false;
|
|
75
|
+
for (const finding of findings) {
|
|
76
|
+
if (skipAll) {
|
|
77
|
+
result.push(finding);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (!finding.key || !finding.filePath) {
|
|
81
|
+
result.push(finding);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const entry = entryMap.get(`${finding.theme}:${finding.component}`);
|
|
85
|
+
if (!entry) {
|
|
86
|
+
result.push(finding);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const action = await promptSelect(finding.message, [`Remove key '${finding.key}'`]);
|
|
90
|
+
if (action === SKIP_ALL) {
|
|
91
|
+
skipAll = true;
|
|
92
|
+
result.push(finding);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (action === SKIP) {
|
|
96
|
+
result.push(finding);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (this._deleteNestedKey(entry.content, finding.key)) {
|
|
100
|
+
pendingWrites.set(entry.filePath, entry);
|
|
101
|
+
result.push({ ...finding, fixed: true });
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
result.push(finding);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
for (const entry of pendingWrites.values()) {
|
|
108
|
+
fs.writeFileSync(entry.filePath, JSON.stringify(entry.content, null, 2) + '\n', 'utf-8');
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
_deleteNestedKey(obj, dotPath) {
|
|
113
|
+
const parts = dotPath.split('.');
|
|
114
|
+
let current = obj;
|
|
115
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
116
|
+
const next = current[parts[i]];
|
|
117
|
+
if (typeof next !== 'object' || next === null) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
current = next;
|
|
121
|
+
}
|
|
122
|
+
const lastKey = parts[parts.length - 1];
|
|
123
|
+
if (Object.prototype.hasOwnProperty.call(current, lastKey)) {
|
|
124
|
+
delete current[lastKey];
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=forbidden-value.rule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forbidden-value.rule.js","sourceRoot":"","sources":["../../src/rules/forbidden-value.rule.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
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 $includes arrays are consistent across themes for the same component.
|
|
8
|
+
*
|
|
9
|
+
* @example Interactive prompt
|
|
10
|
+
* ```
|
|
11
|
+
* ? $includes differs from 'memphis' for 'button': missing: [ripple]; extra: [shadow].
|
|
12
|
+
* > Use memphis's includes: [focusRing, ripple]
|
|
13
|
+
* Use cosmopolitan's includes: [focusRing, ripple]
|
|
14
|
+
* Union all: [focusRing, ripple, shadow]
|
|
15
|
+
* Skip
|
|
16
|
+
* Skip all remaining
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
export declare class IncludesConsistencyRule implements IRule {
|
|
22
|
+
readonly name = "includes-consistency";
|
|
23
|
+
readonly description = "Ensures $includes arrays are consistent across themes for the same component.";
|
|
24
|
+
readonly defaultSeverity: RuleSeverity;
|
|
25
|
+
readonly fixable = false;
|
|
26
|
+
readonly interactive = true;
|
|
27
|
+
run(context: IRuleContext): IRuleResult;
|
|
28
|
+
interact(context: IRuleContext, findings: ReadonlyArray<IRuleFinding>): Promise<IRuleFinding[]>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=includes-consistency.rule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"includes-consistency.rule.d.ts","sourceRoot":"","sources":["../../src/rules/includes-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;;;;;;;;;;;;;;GAcG;AACH,qBAAa,uBAAwB,YAAW,KAAK;IAIjD,SAAgB,IAAI,0BAA0B;IAC9C,SAAgB,WAAW,mFAAmF;IAC9G,SAAgB,eAAe,EAAE,YAAY,CAAwB;IACrE,SAAgB,OAAO,SAAS;IAChC,SAAgB,WAAW,QAAQ;IAM5B,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW;IA4DjC,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAuG/G"}
|