@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.
@@ -1,11 +1,25 @@
1
- import deepmerge from 'deepmerge';
2
- import { deleteUndefProp, cleanOptions, isRuleConfigValue } from './utils.js';
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 value = Array.isArray(oA.value) && Array.isArray(oB) ? [...oA.value, ...oB] : oB;
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 ? convertPretenersToDetails(a) : undefined;
84
- const bDetails = b ? convertPretenersToDetails(b) : undefined;
85
- const details = mergeObject(aDetails, bDetails) ?? {};
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 convertPretenersToDetails(pretenders) {
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 = deepmerge(a, b);
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
- const merged = mergeObject(existed, item);
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
- const merged = mergeRule(res[key], rule);
194
- if (merged != null) {
195
- res[key] = merged;
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
- * @deprecated
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
- * @deprecated
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 AnyRuleV2 = RuleV2<RuleConfigValue, PlainData>;
231
- export type Rules = {
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
- * @deprecated
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
- readonly rules?: Rules;
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
- readonly rules?: Rules;
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, AnyRuleV2, PlainData, RuleConfig, RuleConfigV2, RuleConfigValue } from './types.js';
1
+ import type { AnyRule, NamedRuleGroup, PlainData, RuleConfig, RuleConfigValue, Severity } from './types.js';
2
2
  /**
3
- * Return undefined if the template doesn't include the variable that is set as a property in data.
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
- * @param template Mustache template string
7
- * @param data Captured string for replacement
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
- export declare function exchangeValueOnRule(rule: AnyRule | AnyRuleV2, data: Readonly<Record<string, string>>): AnyRule | undefined;
11
- export declare function cleanOptions(rule: RuleConfig<RuleConfigValue, PlainData> | RuleConfigV2<RuleConfigValue, PlainData>): RuleConfig<RuleConfigValue, PlainData>;
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;