@markuplint/ml-config 4.8.14 → 5.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.ja.md +314 -0
- package/ARCHITECTURE.md +314 -0
- package/CHANGELOG.md +41 -2
- package/SKILL.md +114 -0
- package/docs/maintenance.ja.md +178 -0
- package/docs/maintenance.md +178 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +0 -1
- package/lib/merge-config.d.ts +28 -2
- package/lib/merge-config.js +106 -23
- package/lib/types.d.ts +211 -20
- package/lib/utils.d.ts +50 -9
- package/lib/utils.js +59 -22
- package/package.json +10 -8
package/lib/merge-config.js
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { deleteUndefProp, cleanOptions, isRuleConfigValue, isNamedRuleGroup } from './utils.js';
|
|
2
|
+
/**
|
|
3
|
+
* Merges two markuplint configurations into an optimized result.
|
|
4
|
+
*
|
|
5
|
+
* Plugins, arrays, and rules are merged with specific strategies:
|
|
6
|
+
* - Plugins are concatenated and deduplicated by name (settings shallow-merged)
|
|
7
|
+
* - Arrays (excludeFiles, nodeRules, childNodeRules) are concatenated
|
|
8
|
+
* - Rules are merged per-key with right-side precedence
|
|
9
|
+
* - Objects (parser, specs, etc.) are shallow-merged
|
|
10
|
+
* - The `extends` property is removed from the result when `b` is provided
|
|
11
|
+
*
|
|
12
|
+
* @param a - The base configuration
|
|
13
|
+
* @param b - The configuration to merge on top of `a`
|
|
14
|
+
* @returns The merged and optimized configuration
|
|
15
|
+
*/
|
|
3
16
|
export function mergeConfig(a, b) {
|
|
4
17
|
const deleteExtendsProp = !!b;
|
|
5
18
|
b = b ?? {};
|
|
6
19
|
const config = {
|
|
7
20
|
...a,
|
|
8
21
|
...b,
|
|
22
|
+
ruleCommonSettings: mergeObject(a.ruleCommonSettings, b.ruleCommonSettings),
|
|
9
23
|
plugins: concatArray(a.plugins, b.plugins, true, 'name')?.map(plugin => {
|
|
10
24
|
if (typeof plugin === 'string') {
|
|
11
25
|
return {
|
|
@@ -23,8 +37,8 @@ export function mergeConfig(a, b) {
|
|
|
23
37
|
rules: mergeRules(
|
|
24
38
|
// TODO: Deep merge
|
|
25
39
|
a.rules, b.rules),
|
|
26
|
-
nodeRules: concatArray(a.nodeRules, b.nodeRules),
|
|
27
|
-
childNodeRules: concatArray(a.childNodeRules, b.childNodeRules),
|
|
40
|
+
nodeRules: concatArray(a.nodeRules, b.nodeRules, true, 'name'),
|
|
41
|
+
childNodeRules: concatArray(a.childNodeRules, b.childNodeRules, true, 'name'),
|
|
28
42
|
overrideMode: b.overrideMode ?? a.overrideMode,
|
|
29
43
|
overrides: mergeOverrides(a.overrides, b.overrides),
|
|
30
44
|
extends: concatArray(toReadonlyArray(a.extends), toReadonlyArray(b.extends)),
|
|
@@ -36,6 +50,18 @@ export function mergeConfig(a, b) {
|
|
|
36
50
|
deleteUndefProp(config);
|
|
37
51
|
return config;
|
|
38
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Merges two rule configurations with right-side precedence.
|
|
55
|
+
*
|
|
56
|
+
* If `b` is `false`, the rule is unconditionally disabled.
|
|
57
|
+
* If `b` is a direct value (including arrays), it overrides `a`.
|
|
58
|
+
* If both are full config objects, their properties are merged
|
|
59
|
+
* (severity/value/reason: right-side wins, options: shallow-merged).
|
|
60
|
+
*
|
|
61
|
+
* @param a - The base rule configuration (may be `null` or `undefined`)
|
|
62
|
+
* @param b - The rule configuration to merge on top
|
|
63
|
+
* @returns The merged rule configuration
|
|
64
|
+
*/
|
|
39
65
|
export function mergeRule(a, b) {
|
|
40
66
|
const oA = optimizeRule(a);
|
|
41
67
|
const oB = optimizeRule(b);
|
|
@@ -53,13 +79,9 @@ export function mergeRule(a, b) {
|
|
|
53
79
|
}
|
|
54
80
|
if (isRuleConfigValue(oB)) {
|
|
55
81
|
if (isRuleConfigValue(oA)) {
|
|
56
|
-
if (Array.isArray(oA) && Array.isArray(oB)) {
|
|
57
|
-
return [...oA, ...oB];
|
|
58
|
-
}
|
|
59
82
|
return oB;
|
|
60
83
|
}
|
|
61
|
-
const
|
|
62
|
-
const res = cleanOptions({ ...oA, value });
|
|
84
|
+
const res = cleanOptions({ ...oA, value: oB });
|
|
63
85
|
deleteUndefProp(res);
|
|
64
86
|
return res;
|
|
65
87
|
}
|
|
@@ -80,13 +102,25 @@ function mergePretenders(a, b) {
|
|
|
80
102
|
if (!a && !b) {
|
|
81
103
|
return;
|
|
82
104
|
}
|
|
83
|
-
const aDetails = a ?
|
|
84
|
-
const bDetails = b ?
|
|
85
|
-
|
|
105
|
+
const aDetails = a ? toPretenderDetails(a) : undefined;
|
|
106
|
+
const bDetails = b ? toPretenderDetails(b) : undefined;
|
|
107
|
+
if (!aDetails) {
|
|
108
|
+
return bDetails;
|
|
109
|
+
}
|
|
110
|
+
if (!bDetails) {
|
|
111
|
+
return aDetails;
|
|
112
|
+
}
|
|
113
|
+
// files/imports: override (right-side wins)
|
|
114
|
+
// data: append (concatenate)
|
|
115
|
+
const details = {
|
|
116
|
+
files: bDetails.files ?? aDetails.files,
|
|
117
|
+
imports: bDetails.imports ?? aDetails.imports,
|
|
118
|
+
data: concatArray(aDetails.data, bDetails.data),
|
|
119
|
+
};
|
|
86
120
|
deleteUndefProp(details);
|
|
87
121
|
return details;
|
|
88
122
|
}
|
|
89
|
-
function
|
|
123
|
+
function toPretenderDetails(pretenders) {
|
|
90
124
|
if (isReadonlyArray(pretenders)) {
|
|
91
125
|
return {
|
|
92
126
|
data: pretenders,
|
|
@@ -124,7 +158,7 @@ function mergeObject(a, b) {
|
|
|
124
158
|
if (b == null) {
|
|
125
159
|
return a ?? undefined;
|
|
126
160
|
}
|
|
127
|
-
const res =
|
|
161
|
+
const res = { ...a, ...b };
|
|
128
162
|
deleteUndefProp(res);
|
|
129
163
|
return res;
|
|
130
164
|
}
|
|
@@ -156,12 +190,7 @@ function concatArray(a, b, uniquely = false, comparePropName) {
|
|
|
156
190
|
return;
|
|
157
191
|
}
|
|
158
192
|
const existed = newArray[existedIndex];
|
|
159
|
-
|
|
160
|
-
if (!merged) {
|
|
161
|
-
newArray.push(item);
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
newArray.splice(existedIndex, 1, merged);
|
|
193
|
+
newArray.splice(existedIndex, 1, { ...existed, ...item });
|
|
165
194
|
}
|
|
166
195
|
// eslint-disable-next-line unicorn/no-array-for-each
|
|
167
196
|
a?.forEach(concat);
|
|
@@ -181,6 +210,15 @@ function getName(item, comparePropName) {
|
|
|
181
210
|
}
|
|
182
211
|
return null;
|
|
183
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Merges two Rules dictionaries. Keys containing `/` use named rule group
|
|
215
|
+
* merge semantics ({@link mergeNamedRuleGroupEntry}); other keys use standard
|
|
216
|
+
* rule merge semantics ({@link mergeRule}).
|
|
217
|
+
*
|
|
218
|
+
* @param a - The base rules (lower priority)
|
|
219
|
+
* @param b - The override rules (higher priority)
|
|
220
|
+
* @returns The merged rules, or `undefined` if both inputs are nullish
|
|
221
|
+
*/
|
|
184
222
|
function mergeRules(a, b) {
|
|
185
223
|
if (a == null) {
|
|
186
224
|
return b && optimizeRules(b);
|
|
@@ -190,17 +228,62 @@ function mergeRules(a, b) {
|
|
|
190
228
|
}
|
|
191
229
|
const res = optimizeRules(a);
|
|
192
230
|
for (const [key, rule] of Object.entries(b)) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
res[key] =
|
|
231
|
+
if (key.includes('/')) {
|
|
232
|
+
// Named rule group key: special merge semantics
|
|
233
|
+
res[key] = mergeNamedRuleGroupEntry(res[key], rule);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
const merged = mergeRule(res[key], rule);
|
|
237
|
+
if (merged != null) {
|
|
238
|
+
res[key] = merged;
|
|
239
|
+
}
|
|
196
240
|
}
|
|
197
241
|
}
|
|
198
242
|
deleteUndefProp(res);
|
|
199
243
|
return Object.freeze(res);
|
|
200
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* Merges a named rule group entry (key containing `/`).
|
|
247
|
+
*
|
|
248
|
+
* - `false` disables the group entirely
|
|
249
|
+
* - A partial override object (e.g., `{ severity: "warning" }`) is merged into the existing NamedRuleGroup
|
|
250
|
+
* - Otherwise, right side wins
|
|
251
|
+
*
|
|
252
|
+
* @param a - The existing entry from a lower-priority config (may be a NamedRuleGroup)
|
|
253
|
+
* @param b - The overriding entry from a higher-priority config
|
|
254
|
+
* @returns The merged entry
|
|
255
|
+
*/
|
|
256
|
+
function mergeNamedRuleGroupEntry(a, b) {
|
|
257
|
+
// false disables the group
|
|
258
|
+
if (b === false) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
// Partial override: object without `rules` merging into an existing NamedRuleGroup
|
|
262
|
+
// Only merge valid NamedRuleGroup keys to avoid contamination from RuleConfig keys
|
|
263
|
+
if (typeof b === 'object' && b !== null && !isNamedRuleGroup(b) && a !== undefined && isNamedRuleGroup(a)) {
|
|
264
|
+
const bObj = b;
|
|
265
|
+
const override = {};
|
|
266
|
+
if ('severity' in bObj) {
|
|
267
|
+
override.severity = bObj.severity;
|
|
268
|
+
}
|
|
269
|
+
if ('specConformance' in bObj) {
|
|
270
|
+
override.specConformance = bObj.specConformance;
|
|
271
|
+
}
|
|
272
|
+
const merged = { ...a, ...override };
|
|
273
|
+
deleteUndefProp(merged);
|
|
274
|
+
return merged;
|
|
275
|
+
}
|
|
276
|
+
// Right side wins for everything else
|
|
277
|
+
return b;
|
|
278
|
+
}
|
|
201
279
|
function optimizeRules(rules) {
|
|
202
280
|
const res = {};
|
|
203
281
|
for (const [key, rule] of Object.entries(rules)) {
|
|
282
|
+
// Pass through NamedRuleGroup entries without optimization
|
|
283
|
+
if (key.includes('/') && isNamedRuleGroup(rule)) {
|
|
284
|
+
res[key] = rule;
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
204
287
|
const _rule = optimizeRule(rule);
|
|
205
288
|
if (_rule != null) {
|
|
206
289
|
res[key] = _rule;
|
package/lib/types.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import type { ParserOptions } from '@markuplint/ml-ast';
|
|
2
|
+
import type { ARIAVersion } from '@markuplint/ml-spec';
|
|
2
3
|
import type { RegexSelector } from '@markuplint/selector';
|
|
3
4
|
import type { Nullable } from '@markuplint/shared';
|
|
4
5
|
export type { RegexSelector } from '@markuplint/selector';
|
|
6
|
+
/**
|
|
7
|
+
* The root configuration object for markuplint.
|
|
8
|
+
* Defines rules, parsers, specs, plugins, and overrides for linting markup.
|
|
9
|
+
*/
|
|
5
10
|
export type Config = {
|
|
6
11
|
readonly $schema?: string;
|
|
12
|
+
readonly ruleCommonSettings?: RuleCommonSettings;
|
|
7
13
|
readonly extends?: string | readonly string[];
|
|
8
14
|
readonly plugins?: readonly (PluginConfig | string)[];
|
|
9
15
|
readonly parser?: ParserConfig;
|
|
@@ -18,35 +24,79 @@ export type Config = {
|
|
|
18
24
|
readonly overrideMode?: 'merge' | 'reset';
|
|
19
25
|
readonly overrides?: Readonly<Record<string, OverrideConfig>>;
|
|
20
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Common settings applied globally to all rules.
|
|
29
|
+
* These values serve as fallbacks when individual rules do not specify their own.
|
|
30
|
+
*/
|
|
31
|
+
export type RuleCommonSettings = {
|
|
32
|
+
readonly ariaVersion?: ARIAVersion;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* A primitive scalar value that can appear in rule configuration.
|
|
36
|
+
*/
|
|
21
37
|
export type PrimitiveScalar = string | number | boolean;
|
|
38
|
+
/**
|
|
39
|
+
* A JSON-compatible data structure used for rule options and plugin settings.
|
|
40
|
+
* Can be a primitive, null/undefined, an array of `PlainData`, or a plain object.
|
|
41
|
+
*/
|
|
22
42
|
export type PlainData = Nullable<PrimitiveScalar> | readonly PlainData[] | {
|
|
23
43
|
readonly [key: string]: PlainData | any;
|
|
24
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* A non-nullable variant of {@link PlainData}, excluding `null` and `undefined`.
|
|
47
|
+
*/
|
|
25
48
|
export type NonNullablePlainData = PrimitiveScalar | readonly NonNullablePlainData[] | {
|
|
26
49
|
readonly [key: string]: NonNullablePlainData;
|
|
27
50
|
};
|
|
28
51
|
type NoInherit = '$schema' | 'extends' | 'overrideMode' | 'overrides';
|
|
52
|
+
/**
|
|
53
|
+
* Configuration applied to specific file patterns via the `overrides` field.
|
|
54
|
+
* Excludes top-level-only properties like `$schema`, `extends`, `overrideMode`, and `overrides`.
|
|
55
|
+
*/
|
|
29
56
|
export type OverrideConfig = Omit<Config, NoInherit>;
|
|
57
|
+
/**
|
|
58
|
+
* A fully resolved and optimized configuration after merging.
|
|
59
|
+
* Plugins are normalized to objects, and pretenders are converted to {@link PretenderDetails}.
|
|
60
|
+
*/
|
|
30
61
|
export type OptimizedConfig = Omit<Config, '$schema' | 'extends' | 'plugins' | 'pretenders' | 'overrides'> & {
|
|
31
62
|
readonly extends?: readonly string[];
|
|
32
63
|
readonly plugins?: readonly PluginConfig[];
|
|
33
64
|
readonly pretenders?: PretenderDetails;
|
|
34
65
|
readonly overrides?: Readonly<Record<string, OptimizedOverrideConfig>>;
|
|
35
66
|
};
|
|
67
|
+
/**
|
|
68
|
+
* An optimized override configuration, excluding top-level-only properties.
|
|
69
|
+
*/
|
|
36
70
|
export type OptimizedOverrideConfig = Omit<OptimizedConfig, NoInherit>;
|
|
71
|
+
/**
|
|
72
|
+
* Configuration for a single markuplint plugin.
|
|
73
|
+
*/
|
|
37
74
|
export type PluginConfig = {
|
|
38
75
|
readonly name: string;
|
|
39
76
|
readonly settings?: Readonly<Record<string, NonNullablePlainData>>;
|
|
40
77
|
};
|
|
78
|
+
/**
|
|
79
|
+
* Maps file extension patterns to parser module names or paths.
|
|
80
|
+
*/
|
|
41
81
|
export type ParserConfig = {
|
|
42
82
|
readonly [extensionPattern: string]: string;
|
|
43
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* Maps file extension patterns to spec module names or paths.
|
|
86
|
+
*/
|
|
44
87
|
export type SpecConfig = {
|
|
45
88
|
readonly [extensionPattern: string]: string;
|
|
46
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* Options for controlling the severity of specific diagnostic categories.
|
|
92
|
+
*/
|
|
47
93
|
export type SeverityOptions = {
|
|
48
94
|
readonly parseError?: Severity | 'off' | boolean;
|
|
49
95
|
};
|
|
96
|
+
/**
|
|
97
|
+
* Normalized form of pretender configuration used after merging.
|
|
98
|
+
* Contains optional file references, import paths, and inline pretender data.
|
|
99
|
+
*/
|
|
50
100
|
export type PretenderDetails = {
|
|
51
101
|
/**
|
|
52
102
|
* @experimental
|
|
@@ -58,10 +108,18 @@ export type PretenderDetails = {
|
|
|
58
108
|
readonly imports?: readonly string[];
|
|
59
109
|
readonly data?: readonly Pretender[];
|
|
60
110
|
};
|
|
111
|
+
/**
|
|
112
|
+
* Data structure for a pretender definition file.
|
|
113
|
+
*/
|
|
61
114
|
export type PretenderFileData = {
|
|
62
115
|
readonly version: string;
|
|
63
116
|
readonly data: readonly Pretender[];
|
|
64
117
|
};
|
|
118
|
+
/**
|
|
119
|
+
* Defines a mapping from a custom element (matched by CSS selector) to a standard
|
|
120
|
+
* HTML element for linting purposes, allowing rules to treat custom components
|
|
121
|
+
* as if they were native elements.
|
|
122
|
+
*/
|
|
65
123
|
export type Pretender = {
|
|
66
124
|
/**
|
|
67
125
|
* Target node selectors
|
|
@@ -218,82 +276,209 @@ export interface PretenderScanOptions {
|
|
|
218
276
|
readonly cwd?: string;
|
|
219
277
|
readonly ignoreComponentNames?: readonly string[];
|
|
220
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* A rule setting: either a full {@link RuleConfig} object, a direct value, or a boolean to enable/disable.
|
|
281
|
+
*
|
|
282
|
+
* @template T - The type of the rule's value
|
|
283
|
+
* @template O - The type of the rule's options
|
|
284
|
+
*/
|
|
221
285
|
export type Rule<T extends RuleConfigValue, O extends PlainData = undefined> = RuleConfig<T, O> | Readonly<T> | boolean;
|
|
222
286
|
/**
|
|
223
|
-
*
|
|
287
|
+
* A rule setting with any value and option types.
|
|
224
288
|
*/
|
|
225
|
-
export type RuleV2<T extends RuleConfigValue, O extends PlainData = undefined> = RuleConfigV2<T, O> | Readonly<T> | boolean;
|
|
226
289
|
export type AnyRule = Rule<RuleConfigValue, PlainData>;
|
|
227
290
|
/**
|
|
228
|
-
*
|
|
291
|
+
* A named rule group in the `rules` section.
|
|
292
|
+
* Keys containing `/` in `rules` are treated as named rule groups.
|
|
293
|
+
* Each group wraps one or more base rules under a namespace,
|
|
294
|
+
* enabling per-check control and spec conformance metadata.
|
|
295
|
+
*
|
|
296
|
+
* @example
|
|
297
|
+
* ```jsonc
|
|
298
|
+
* {
|
|
299
|
+
* "rules": {
|
|
300
|
+
* "a11y/id-duplication": {
|
|
301
|
+
* "specConformance": "normative",
|
|
302
|
+
* "rules": { "id-duplication": true }
|
|
303
|
+
* }
|
|
304
|
+
* }
|
|
305
|
+
* }
|
|
306
|
+
* ```
|
|
229
307
|
*/
|
|
230
|
-
export type
|
|
231
|
-
|
|
308
|
+
export type NamedRuleGroup = {
|
|
309
|
+
readonly specConformance?: SpecConformance;
|
|
310
|
+
/** User-applied severity override for all rules in the group */
|
|
311
|
+
readonly severity?: Severity;
|
|
312
|
+
readonly rules: BaseRules;
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* A dictionary mapping base rule names to their configurations.
|
|
316
|
+
* Does not accept {@link NamedRuleGroup} entries.
|
|
317
|
+
* Used inside {@link NamedRuleGroup}, {@link NodeRule}, and {@link ChildNodeRule}.
|
|
318
|
+
*/
|
|
319
|
+
export type BaseRules = {
|
|
232
320
|
readonly [ruleName: string]: AnyRule;
|
|
233
321
|
};
|
|
322
|
+
/**
|
|
323
|
+
* A dictionary mapping rule names to their configurations.
|
|
324
|
+
* Keys containing `/` may be {@link NamedRuleGroup} entries.
|
|
325
|
+
*/
|
|
326
|
+
export type Rules = {
|
|
327
|
+
readonly [ruleName: string]: AnyRule | NamedRuleGroup;
|
|
328
|
+
};
|
|
329
|
+
/**
|
|
330
|
+
* Full configuration for a single rule, specifying severity, value, options, and reason.
|
|
331
|
+
*
|
|
332
|
+
* @template T - The type of the rule's value
|
|
333
|
+
* @template O - The type of the rule's options
|
|
334
|
+
*/
|
|
234
335
|
export type RuleConfig<T extends RuleConfigValue, O extends PlainData = undefined> = {
|
|
336
|
+
/** The severity level for violations of this rule */
|
|
235
337
|
readonly severity?: Severity;
|
|
338
|
+
/** The rule's primary configuration value */
|
|
236
339
|
readonly value?: Readonly<T>;
|
|
340
|
+
/** Additional options for the rule */
|
|
237
341
|
readonly options?: Readonly<O>;
|
|
342
|
+
/** A human-readable reason for this rule configuration, included in violation messages */
|
|
238
343
|
readonly reason?: string;
|
|
239
344
|
};
|
|
240
345
|
/**
|
|
241
|
-
*
|
|
346
|
+
* The severity level of a lint violation.
|
|
242
347
|
*/
|
|
243
|
-
export type RuleConfigV2<T extends RuleConfigValue, O extends PlainData = undefined> = {
|
|
244
|
-
readonly severity?: Severity;
|
|
245
|
-
readonly value?: Readonly<T>;
|
|
246
|
-
readonly reason?: string;
|
|
247
|
-
/**
|
|
248
|
-
* Old property
|
|
249
|
-
*
|
|
250
|
-
* @deprecated
|
|
251
|
-
* @see {this.options}
|
|
252
|
-
*/
|
|
253
|
-
readonly option?: Readonly<O>;
|
|
254
|
-
};
|
|
255
348
|
export type Severity = 'error' | 'warning' | 'info';
|
|
349
|
+
/**
|
|
350
|
+
* The spec conformance classification of a rule, based on RFC 2119 keyword strength.
|
|
351
|
+
*
|
|
352
|
+
* - `'normative'` — derived from MUST/REQUIRED requirements in the HTML spec
|
|
353
|
+
* - `'non-normative'` — derived from SHOULD/RECOMMENDED requirements in the HTML spec
|
|
354
|
+
* - `undefined` — plugin/preset recommendation or user-defined (no spec backing)
|
|
355
|
+
*/
|
|
356
|
+
export type SpecConformance = 'normative' | 'non-normative';
|
|
357
|
+
/**
|
|
358
|
+
* The value portion of a rule configuration. Can be a primitive scalar,
|
|
359
|
+
* an array of scalars or objects, or `null` to represent no value.
|
|
360
|
+
*/
|
|
256
361
|
export type RuleConfigValue = PrimitiveScalar | readonly (PrimitiveScalar | Readonly<Record<string, any>>)[] | null;
|
|
362
|
+
/**
|
|
363
|
+
* A rule override that targets specific nodes by CSS selector, regex selector, ARIA roles, or categories.
|
|
364
|
+
*
|
|
365
|
+
* When a `name` is provided (must contain `/`), this becomes a **named nodeRule**
|
|
366
|
+
* that creates a virtual rule instance running independently from the base rule.
|
|
367
|
+
* Named nodeRules can be individually enabled/disabled via `rules["name/here"]: false`.
|
|
368
|
+
*/
|
|
257
369
|
export type NodeRule = {
|
|
370
|
+
/**
|
|
371
|
+
* Alias name for this nodeRule, creating a virtual rule.
|
|
372
|
+
* Must contain `/` (e.g., `"a11y/img-has-alt"`).
|
|
373
|
+
* With a single non-false entry, this name is used directly.
|
|
374
|
+
* With multiple non-false entries, derived names (`name/baseRuleName`)
|
|
375
|
+
* are generated automatically, and this name becomes the group name.
|
|
376
|
+
*/
|
|
377
|
+
readonly name?: string;
|
|
378
|
+
/**
|
|
379
|
+
* The spec conformance classification of this rule.
|
|
380
|
+
* Included in violations as metadata for downstream tools and reporting.
|
|
381
|
+
*/
|
|
382
|
+
readonly specConformance?: SpecConformance;
|
|
258
383
|
readonly selector?: string;
|
|
259
384
|
readonly regexSelector?: RegexSelector;
|
|
260
385
|
readonly categories?: readonly string[];
|
|
261
386
|
readonly roles?: readonly string[];
|
|
262
387
|
readonly obsolete?: boolean;
|
|
263
|
-
|
|
388
|
+
/** Base rule settings. Does not accept {@link NamedRuleGroup} entries. */
|
|
389
|
+
readonly rules?: BaseRules;
|
|
264
390
|
};
|
|
391
|
+
/**
|
|
392
|
+
* A rule override that targets child nodes of elements matching the selector.
|
|
393
|
+
*
|
|
394
|
+
* When a `name` is provided (must contain `/`), this becomes a **named childNodeRule**
|
|
395
|
+
* that creates a virtual rule instance, just like named nodeRules.
|
|
396
|
+
*/
|
|
265
397
|
export type ChildNodeRule = {
|
|
398
|
+
/**
|
|
399
|
+
* Alias name for this childNodeRule, creating a virtual rule.
|
|
400
|
+
* Must contain `/` (e.g., `"a11y/heading-in-section"`).
|
|
401
|
+
* With a single non-false entry, this name is used directly.
|
|
402
|
+
* With multiple non-false entries, derived names (`name/baseRuleName`)
|
|
403
|
+
* are generated automatically, and this name becomes the group name.
|
|
404
|
+
*/
|
|
405
|
+
readonly name?: string;
|
|
406
|
+
/**
|
|
407
|
+
* The spec conformance classification of this rule.
|
|
408
|
+
* Included in violations as metadata for downstream tools and reporting.
|
|
409
|
+
*/
|
|
410
|
+
readonly specConformance?: SpecConformance;
|
|
266
411
|
readonly selector?: string;
|
|
267
412
|
readonly regexSelector?: RegexSelector;
|
|
268
413
|
readonly inheritance?: boolean;
|
|
269
|
-
|
|
414
|
+
/** Base rule settings. Does not accept {@link NamedRuleGroup} entries. */
|
|
415
|
+
readonly rules?: BaseRules;
|
|
270
416
|
};
|
|
417
|
+
/**
|
|
418
|
+
* A violation report from a rule. Can report against a scope (node-based)
|
|
419
|
+
* or against explicit line/column coordinates, or both.
|
|
420
|
+
*
|
|
421
|
+
* @template T - The type of the rule's value
|
|
422
|
+
* @template O - The type of the rule's options
|
|
423
|
+
*/
|
|
271
424
|
export type Report<T extends RuleConfigValue, O extends PlainData = undefined> = Report1<T, O> | Report2 | (Report1<T, O> & Report2);
|
|
425
|
+
/**
|
|
426
|
+
* A scope-based violation report, referencing the rule info and position via a {@link Scope}.
|
|
427
|
+
*
|
|
428
|
+
* @template T - The type of the rule's value
|
|
429
|
+
* @template O - The type of the rule's options
|
|
430
|
+
*/
|
|
272
431
|
export type Report1<T extends RuleConfigValue, O extends PlainData = undefined> = {
|
|
273
432
|
readonly message: string;
|
|
274
433
|
readonly scope: Scope<T, O>;
|
|
275
434
|
};
|
|
435
|
+
/**
|
|
436
|
+
* A coordinate-based violation report with explicit line, column, and raw text.
|
|
437
|
+
*/
|
|
276
438
|
export type Report2 = {
|
|
277
439
|
readonly message: string;
|
|
278
440
|
readonly line: number;
|
|
279
441
|
readonly col: number;
|
|
280
442
|
readonly raw: string;
|
|
281
443
|
};
|
|
444
|
+
/**
|
|
445
|
+
* Identifies the location and rule context of a reported violation.
|
|
446
|
+
*
|
|
447
|
+
* @template T - The type of the rule's value
|
|
448
|
+
* @template O - The type of the rule's options
|
|
449
|
+
*/
|
|
282
450
|
export type Scope<T extends RuleConfigValue, O extends PlainData = undefined> = {
|
|
283
451
|
readonly rule: RuleInfo<T, O>;
|
|
284
452
|
readonly startLine: number;
|
|
285
453
|
readonly startCol: number;
|
|
286
454
|
readonly raw: string;
|
|
287
455
|
};
|
|
456
|
+
/**
|
|
457
|
+
* A fully resolved lint violation with all information needed for reporting.
|
|
458
|
+
*/
|
|
288
459
|
export type Violation = {
|
|
460
|
+
/** The base rule ID (always the underlying rule name, for backwards compatibility) */
|
|
289
461
|
readonly ruleId: string;
|
|
462
|
+
/**
|
|
463
|
+
* The display name of the rule. Present only on virtual rules (named nodeRules).
|
|
464
|
+
* For regular rules, use `ruleId` as the display name.
|
|
465
|
+
*/
|
|
466
|
+
readonly name?: string;
|
|
290
467
|
readonly severity: Severity;
|
|
291
468
|
readonly message: string;
|
|
292
469
|
readonly reason?: string;
|
|
470
|
+
/** The normative level of the rule that produced this violation */
|
|
471
|
+
readonly specConformance?: SpecConformance;
|
|
293
472
|
readonly line: number;
|
|
294
473
|
readonly col: number;
|
|
295
474
|
readonly raw: string;
|
|
296
475
|
};
|
|
476
|
+
/**
|
|
477
|
+
* Resolved rule information after configuration merging, used at runtime by rules.
|
|
478
|
+
*
|
|
479
|
+
* @template T - The type of the rule's value
|
|
480
|
+
* @template O - The type of the rule's options
|
|
481
|
+
*/
|
|
297
482
|
export type RuleInfo<T extends RuleConfigValue, O extends PlainData = undefined> = {
|
|
298
483
|
readonly disabled: boolean;
|
|
299
484
|
readonly severity: Severity;
|
|
@@ -301,6 +486,12 @@ export type RuleInfo<T extends RuleConfigValue, O extends PlainData = undefined>
|
|
|
301
486
|
readonly options: Readonly<O>;
|
|
302
487
|
readonly reason?: string;
|
|
303
488
|
};
|
|
489
|
+
/**
|
|
490
|
+
* Extended rule information that includes node-level and child-node-level overrides.
|
|
491
|
+
*
|
|
492
|
+
* @template T - The type of the rule's value
|
|
493
|
+
* @template O - The type of the rule's options
|
|
494
|
+
*/
|
|
304
495
|
export type GlobalRuleInfo<T extends RuleConfigValue, O extends PlainData = undefined> = RuleInfo<T, O> & {
|
|
305
496
|
nodeRules: RuleInfo<T, O>[];
|
|
306
497
|
childNodeRules: RuleInfo<T, O>[];
|
package/lib/utils.d.ts
CHANGED
|
@@ -1,18 +1,59 @@
|
|
|
1
|
-
import type { AnyRule,
|
|
1
|
+
import type { AnyRule, NamedRuleGroup, PlainData, RuleConfig, RuleConfigValue, Severity } from './types.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* But return template string without changes if it doesn't have a variable.
|
|
3
|
+
* Renders a Mustache template with the provided data.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Returns `undefined` if the template contains variables but none of them
|
|
6
|
+
* are present in `data`. Returns the template unchanged if it has no variables.
|
|
7
|
+
*
|
|
8
|
+
* @param template - A Mustache template string with `{{variable}}` placeholders
|
|
9
|
+
* @param data - Key-value pairs for template variable replacement
|
|
10
|
+
* @returns The rendered string, or `undefined` if no matching variables were found
|
|
8
11
|
*/
|
|
9
12
|
export declare function provideValue(template: string, data: Readonly<Record<string, string>>): string | undefined;
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Applies Mustache template rendering to all string values within a rule configuration,
|
|
15
|
+
* including the rule's value, options, and reason fields.
|
|
16
|
+
*
|
|
17
|
+
* @param rule - The rule configuration containing potential template strings
|
|
18
|
+
* @param data - Key-value pairs for template variable replacement
|
|
19
|
+
* @returns The rule with all template strings rendered, or `undefined` if rendering fails
|
|
20
|
+
*/
|
|
21
|
+
export declare function exchangeValueOnRule(rule: AnyRule, data: Readonly<Record<string, string>>): AnyRule | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Normalizes a rule configuration by extracting the standard fields
|
|
24
|
+
* (`severity`, `value`, `options`, `reason`) and removing `undefined` properties.
|
|
25
|
+
*
|
|
26
|
+
* @param rule - The rule configuration to normalize
|
|
27
|
+
* @returns A clean rule configuration with only defined properties
|
|
28
|
+
*/
|
|
29
|
+
export declare function cleanOptions(rule: RuleConfig<RuleConfigValue, PlainData>): RuleConfig<RuleConfigValue, PlainData>;
|
|
30
|
+
/**
|
|
31
|
+
* Type guard that checks whether a value is a {@link NamedRuleGroup}.
|
|
32
|
+
* A NamedRuleGroup is an object with a `rules` property (and optionally `specConformance` and `severity`).
|
|
33
|
+
*
|
|
34
|
+
* @param v - The value to check
|
|
35
|
+
* @returns `true` if `v` is a named rule group
|
|
36
|
+
*/
|
|
37
|
+
export declare function isNamedRuleGroup(v: unknown): v is NamedRuleGroup;
|
|
38
|
+
/**
|
|
39
|
+
* Type guard that checks whether a string is a valid {@link Severity} value.
|
|
40
|
+
*
|
|
41
|
+
* @param v - The string to check
|
|
42
|
+
* @returns `true` if `v` is "error", "warning", or "info"
|
|
43
|
+
*/
|
|
44
|
+
export declare function isSeverity(v: string): v is Severity;
|
|
45
|
+
/**
|
|
46
|
+
* Type guard that checks whether a value is a {@link RuleConfigValue}
|
|
47
|
+
* (i.e. a primitive, `null`, or an array) rather than a full {@link RuleConfig} object.
|
|
48
|
+
*
|
|
49
|
+
* @param v - The value to check
|
|
50
|
+
* @returns `true` if `v` is a rule config value (string, number, boolean, null, or array)
|
|
51
|
+
*/
|
|
12
52
|
export declare function isRuleConfigValue(v: any): v is RuleConfigValue;
|
|
13
53
|
/**
|
|
54
|
+
* Removes all properties with `undefined` values from a plain object in-place.
|
|
55
|
+
* Has no effect on non-plain-object values.
|
|
14
56
|
*
|
|
15
|
-
* @param obj
|
|
16
|
-
* @returns
|
|
57
|
+
* @param obj - The object to clean up
|
|
17
58
|
*/
|
|
18
59
|
export declare function deleteUndefProp(obj: any): void;
|