@markuplint/ml-core 5.0.0-rc.5 → 5.0.0-rc.6

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 CHANGED
@@ -3,6 +3,33 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [5.0.0-rc.6](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.5...v5.0.0-rc.6) (2026-08-30)
7
+
8
+ ### Features
9
+
10
+ - split rule-deprecation notices out of config-error ([#4013](https://github.com/markuplint/markuplint/issues/4013)) ([812e6f3](https://github.com/markuplint/markuplint/commit/812e6f356839af8f257cfd91e6b16cfdfdd7cf33))
11
+
12
+ ### BREAKING CHANGES
13
+
14
+ - violations for deprecated rule names now have
15
+ `ruleId: 'rule-deprecation'` instead of `ruleId: 'config-error'`. Any
16
+ consumer filtering `MLCore.verify()` output (or the markuplint CLI/API) by
17
+ `ruleId === 'config-error'` to catch deprecation messages must also check
18
+ for `rule-deprecation`.
19
+
20
+ - feat(markuplint): add --severity-deprecation CLI flag
21
+
22
+ Wires the new severity.deprecation config option (@markuplint/ml-config)
23
+ and the rule-deprecation ruleId (@markuplint/ml-core) through the CLI:
24
+
25
+ - --severity-deprecation flag, mirroring --severity-parse-error
26
+ - --show-config details now also surfaces ruleDeprecations
27
+ - per-run dedupe and failed-file counting generalized to cover both
28
+ config-level ruleIds (config-error and rule-deprecation), not just
29
+ config-error
30
+
31
+ * docs(website): document severity.deprecation (EN + JA)
32
+
6
33
  # [5.0.0-rc.5](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.4...v5.0.0-rc.5) (2026-08-28)
7
34
 
8
35
  ### Bug Fixes
package/lib/ml-core.d.ts CHANGED
@@ -2,6 +2,20 @@ import type { MLFabric } from './types.js';
2
2
  import type { PlainData, RuleConfigValue, TextEdit, Violation } from '@markuplint/ml-config';
3
3
  import { ParserError } from '@markuplint/parser-utils';
4
4
  import { Document } from './ml-dom/index.js';
5
+ /**
6
+ * `ruleId` of a genuinely broken config (unresolved rule reference, plugin
7
+ * resolution failure, ...). Exported so consumers that need to recognize
8
+ * config-level violations (e.g. the CLI's per-run dedupe and failed-file
9
+ * counting in `packages/markuplint/src/cli/command.ts`) reference the same
10
+ * literal `MLCore.verify()` emits, instead of duplicating the string.
11
+ */
12
+ export declare const CONFIG_ERROR_RULE_ID = "config-error";
13
+ /**
14
+ * `ruleId` of a deprecated-but-working rule name notice — see
15
+ * {@link CONFIG_ERROR_RULE_ID} for why this is exported rather than a
16
+ * private literal.
17
+ */
18
+ export declare const RULE_DEPRECATION_RULE_ID = "rule-deprecation";
5
19
  /**
6
20
  * Summary of the multi-pass fix process.
7
21
  */
@@ -74,7 +88,7 @@ export type MLCoreParams = {
74
88
  */
75
89
  export declare class MLCore {
76
90
  #private;
77
- constructor({ parser, sourceCode, ruleset, rules, locale, schemas, ruleCommonSettings, parserOptions, severity, pretenders, filename, debug, configErrors, }: MLCoreParams);
91
+ constructor({ parser, sourceCode, ruleset, rules, locale, schemas, ruleCommonSettings, parserOptions, severity, pretenders, filename, debug, configErrors, ruleDeprecations, }: MLCoreParams);
78
92
  /**
79
93
  * The parsed document, or a {@link ParserError} if parsing failed.
80
94
  */
@@ -91,7 +105,7 @@ export declare class MLCore {
91
105
  *
92
106
  * @param fabric - Partial fabric with the properties to update
93
107
  */
94
- update({ parser, ruleset, rules, locale, schemas, parserOptions, pretenders, configErrors }: Partial<MLFabric>): void;
108
+ update({ parser, ruleset, rules, locale, schemas, parserOptions, pretenders, configErrors, ruleDeprecations, }: Partial<MLFabric>): void;
95
109
  /**
96
110
  * Runs all configured rules against the parsed document and returns violations.
97
111
  *
package/lib/ml-core.js CHANGED
@@ -4,6 +4,20 @@ import { applyFixes } from './fix-applier.js';
4
4
  import { Document } from './ml-dom/index.js';
5
5
  import { expandNamedNodeRules, expandNamedRules } from './virtual-rule.js';
6
6
  const resultLog = log.extend('result');
7
+ /**
8
+ * `ruleId` of a genuinely broken config (unresolved rule reference, plugin
9
+ * resolution failure, ...). Exported so consumers that need to recognize
10
+ * config-level violations (e.g. the CLI's per-run dedupe and failed-file
11
+ * counting in `packages/markuplint/src/cli/command.ts`) reference the same
12
+ * literal `MLCore.verify()` emits, instead of duplicating the string.
13
+ */
14
+ export const CONFIG_ERROR_RULE_ID = 'config-error';
15
+ /**
16
+ * `ruleId` of a deprecated-but-working rule name notice — see
17
+ * {@link CONFIG_ERROR_RULE_ID} for why this is exported rather than a
18
+ * private literal.
19
+ */
20
+ export const RULE_DEPRECATION_RULE_ID = 'rule-deprecation';
7
21
  /**
8
22
  * The core linting engine for markuplint.
9
23
  *
@@ -29,6 +43,13 @@ export class MLCore {
29
43
  * Set once per construction/`update()` and never mutated by re-parsing.
30
44
  */
31
45
  #configErrors;
46
+ /**
47
+ * Deprecated-rule-name notices, kept structured and separate from
48
+ * `#configErrors` so `verify()` can report them under their own
49
+ * `rule-deprecation` ruleId. Set once per construction/`update()`, same
50
+ * lifecycle as `#configErrors`.
51
+ */
52
+ #ruleDeprecations;
32
53
  /**
33
54
  * Rule-mapping errors for the CURRENT document. Reset (not accumulated) on
34
55
  * every `#createDocument()` so repeated `setCode()` calls don't duplicate
@@ -44,7 +65,7 @@ export class MLCore {
44
65
  #originalNodeRules;
45
66
  #originalChildNodeRules;
46
67
  #disabledNamespaces;
47
- constructor({ parser, sourceCode, ruleset, rules, locale, schemas, ruleCommonSettings, parserOptions, severity, pretenders, filename, debug, configErrors, }) {
68
+ constructor({ parser, sourceCode, ruleset, rules, locale, schemas, ruleCommonSettings, parserOptions, severity, pretenders, filename, debug, configErrors, ruleDeprecations, }) {
48
69
  if (debug) {
49
70
  enableDebug();
50
71
  }
@@ -58,6 +79,7 @@ export class MLCore {
58
79
  this.#severity = severity;
59
80
  this.#pretenders = [...pretenders];
60
81
  this.#configErrors = [...(configErrors ?? [])];
82
+ this.#ruleDeprecations = [...(ruleDeprecations ?? [])];
61
83
  // Preserve pre-expansion nodeRules for hot-reload
62
84
  this.#originalNodeRules = ruleset.nodeRules ?? [];
63
85
  this.#originalChildNodeRules = ruleset.childNodeRules ?? [];
@@ -101,12 +123,13 @@ export class MLCore {
101
123
  *
102
124
  * @param fabric - Partial fabric with the properties to update
103
125
  */
104
- update({ parser, ruleset, rules, locale, schemas, parserOptions, pretenders, configErrors }) {
126
+ update({ parser, ruleset, rules, locale, schemas, parserOptions, pretenders, configErrors, ruleDeprecations, }) {
105
127
  this.#parser = parser ?? this.#parser;
106
128
  this.#locale = locale ?? this.#locale;
107
129
  this.#schemas = schemas ?? this.#schemas;
108
130
  this.#pretenders = pretenders ? [...pretenders] : this.#pretenders;
109
131
  this.#configErrors = [...(configErrors ?? [])];
132
+ this.#ruleDeprecations = [...(ruleDeprecations ?? [])];
110
133
  const baseRules = rules ? [...rules] : this.#rules.filter(r => !r.baseRuleId);
111
134
  const incomingNodeRules = ruleset?.nodeRules ?? this.#originalNodeRules;
112
135
  const incomingChildNodeRules = ruleset?.childNodeRules ?? this.#originalChildNodeRules;
@@ -170,7 +193,7 @@ export class MLCore {
170
193
  }
171
194
  if (!definedRuleName.has(setRuleName)) {
172
195
  configViolations.push({
173
- ruleId: 'config-error',
196
+ ruleId: CONFIG_ERROR_RULE_ID,
174
197
  severity: 'warning',
175
198
  message: `Rule not found: ${setRuleName}`,
176
199
  col: 1,
@@ -181,7 +204,7 @@ export class MLCore {
181
204
  }
182
205
  for (const error of [...this.#configErrors, ...this.#mappingErrors]) {
183
206
  configViolations.push({
184
- ruleId: 'config-error',
207
+ ruleId: CONFIG_ERROR_RULE_ID,
185
208
  severity: 'warning',
186
209
  message: error.message,
187
210
  col: 1,
@@ -189,6 +212,19 @@ export class MLCore {
189
212
  raw: '',
190
213
  });
191
214
  }
215
+ const deprecationSeverity = this.#resolveDeprecationSeverity();
216
+ if (deprecationSeverity != null) {
217
+ for (const { deprecatedName, replacedBy } of this.#ruleDeprecations) {
218
+ configViolations.push({
219
+ ruleId: RULE_DEPRECATION_RULE_ID,
220
+ severity: deprecationSeverity,
221
+ message: `Rule "${deprecatedName}" is deprecated and will be removed in v6. Use ${replacedBy.join(', ')} instead.`,
222
+ col: 1,
223
+ line: 1,
224
+ raw: '',
225
+ });
226
+ }
227
+ }
192
228
  violations.push(...configViolations);
193
229
  const ruleViolations = await this.#runAllRules(fix);
194
230
  violations.push(...ruleViolations);
@@ -363,6 +399,22 @@ export class MLCore {
363
399
  }
364
400
  }
365
401
  }
402
+ /**
403
+ * Resolves `severity.deprecation`, honouring its single-value form only
404
+ * (unlike `severity.parseError`, there's no fixed enum of deprecated rule
405
+ * names to key a per-code `Record` on).
406
+ *
407
+ * Unlike `#createParseError`'s parse-error channel, this defaults to
408
+ * `'warning'` (not off/suppressed) when unset — the deprecation channel
409
+ * is being carved out of the always-on `config-error` channel, not
410
+ * introduced cold, so leaving the option unset must not silence a notice
411
+ * users already see today.
412
+ *
413
+ * @returns the resolved severity, or `null` if suppressed.
414
+ */
415
+ #resolveDeprecationSeverity() {
416
+ return resolveUniformSeverity(this.#severity.deprecation, 'warning');
417
+ }
366
418
  /**
367
419
  * Builds a `ruleId: 'parse-error'` violation, honouring
368
420
  * `severity.parseError`.
@@ -377,36 +429,18 @@ export class MLCore {
377
429
  */
378
430
  #createParseError(message, line, col, raw, code) {
379
431
  const cfg = this.#severity.parseError;
380
- if (cfg === false || cfg === 'off') {
432
+ // Fatal ParserErrors (no `code`) can't be targeted by the per-code
433
+ // Record form, so they fall back to `'error'` there; under the
434
+ // uniform form they default to `'error'` too, while non-fatal entries
435
+ // default to suppressed when unset — see `resolveUniformSeverity`.
436
+ const severity = typeof cfg === 'object'
437
+ ? code == null
438
+ ? 'error'
439
+ : resolveUniformSeverity(cfg[code], null)
440
+ : resolveUniformSeverity(cfg, code == null ? 'error' : null);
441
+ if (severity == null) {
381
442
  return null;
382
443
  }
383
- let severity;
384
- if (typeof cfg === 'object') {
385
- if (code == null) {
386
- // Fatal ParserError without a code; the Record form cannot target
387
- // it, so fall back to `'error'` (the channel is otherwise enabled).
388
- severity = 'error';
389
- }
390
- else {
391
- const perCode = cfg[code];
392
- if (perCode == null || perCode === false || perCode === 'off') {
393
- return null;
394
- }
395
- severity = perCode === true ? 'error' : perCode;
396
- }
397
- }
398
- else if (cfg == null) {
399
- // New default: non-fatal `parseErrors` entries are off; fatal
400
- // `ParserError`s (no code) still emit at `'error'`.
401
- if (code != null) {
402
- return null;
403
- }
404
- severity = 'error';
405
- }
406
- else {
407
- // Uniform string/boolean form (legacy).
408
- severity = cfg === true ? 'error' : cfg;
409
- }
410
444
  return {
411
445
  ruleId: 'parse-error',
412
446
  severity,
@@ -597,6 +631,24 @@ export class MLCore {
597
631
  }
598
632
  }
599
633
  }
634
+ /**
635
+ * Normalizes a single-value `Severity | 'off' | boolean | undefined` option
636
+ * (the shape `severity.deprecation` and `severity.parseError`'s uniform/
637
+ * per-code leaf values share) to a resolved severity or `null` (suppressed).
638
+ *
639
+ * Shared so the same three-way rule — `false`/`'off'` → suppressed, `true` →
640
+ * `'error'`, unset → `defaultWhenUnset` — isn't reimplemented at each call
641
+ * site as channels using this shape are added.
642
+ */
643
+ function resolveUniformSeverity(cfg, defaultWhenUnset) {
644
+ if (cfg === false || cfg === 'off') {
645
+ return null;
646
+ }
647
+ if (cfg == null) {
648
+ return defaultWhenUnset;
649
+ }
650
+ return cfg === true ? 'error' : cfg;
651
+ }
600
652
  function extractDisabledNamespaces(rules) {
601
653
  return Object.entries(rules)
602
654
  .filter(([key, value]) => key.endsWith('/*') && value === false)
package/lib/types.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { AnyMLRule } from './ml-rule/index.js';
2
2
  import type { Ruleset } from './ruleset/index.js';
3
3
  import type { LocaleSet } from '@markuplint/i18n';
4
4
  import type { MLParser, ParserOptions } from '@markuplint/ml-ast';
5
- import type { Pretender, RuleCommonSettings, SeverityOptions } from '@markuplint/ml-config';
5
+ import type { Pretender, RuleAliasWarning, RuleCommonSettings, SeverityOptions } from '@markuplint/ml-config';
6
6
  import type { ExtendedSpec, MLMLSpec } from '@markuplint/ml-spec';
7
7
  /**
8
8
  * A tuple of the base HTML/ARIA specification and zero or more
@@ -24,4 +24,11 @@ export type MLFabric = {
24
24
  readonly severity: SeverityOptions;
25
25
  readonly pretenders: readonly Pretender[];
26
26
  readonly configErrors?: readonly Readonly<Error>[];
27
+ /**
28
+ * Deprecated-rule-name notices found while applying the rule-alias table
29
+ * (v5 rule-system redesign, #3989). Kept structured, separate from
30
+ * {@link configErrors}, so `MLCore.verify()` can report them under their
31
+ * own `rule-deprecation` ruleId instead of `config-error`.
32
+ */
33
+ readonly ruleDeprecations?: readonly RuleAliasWarning[];
27
34
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/ml-core",
3
- "version": "5.0.0-rc.5",
3
+ "version": "5.0.0-rc.6",
4
4
  "description": "The core module of markuplint",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,20 +34,20 @@
34
34
  "./lib/configs.js": "./lib/configs.browser.js"
35
35
  },
36
36
  "dependencies": {
37
- "@markuplint/config-presets": "5.0.0-rc.5",
38
- "@markuplint/html-parser": "5.0.0-rc.5",
39
- "@markuplint/html-spec": "5.0.0-rc.5",
40
- "@markuplint/i18n": "5.0.0-rc.5",
41
- "@markuplint/ml-ast": "5.0.0-rc.5",
42
- "@markuplint/ml-config": "5.0.0-rc.5",
43
- "@markuplint/ml-spec": "5.0.0-rc.5",
44
- "@markuplint/parser-utils": "5.0.0-rc.5",
45
- "@markuplint/selector": "5.0.0-rc.5",
46
- "@markuplint/shared": "5.0.0-rc.5",
37
+ "@markuplint/config-presets": "5.0.0-rc.6",
38
+ "@markuplint/html-parser": "5.0.0-rc.6",
39
+ "@markuplint/html-spec": "5.0.0-rc.6",
40
+ "@markuplint/i18n": "5.0.0-rc.6",
41
+ "@markuplint/ml-ast": "5.0.0-rc.6",
42
+ "@markuplint/ml-config": "5.0.0-rc.6",
43
+ "@markuplint/ml-spec": "5.0.0-rc.6",
44
+ "@markuplint/parser-utils": "5.0.0-rc.6",
45
+ "@markuplint/selector": "5.0.0-rc.6",
46
+ "@markuplint/shared": "5.0.0-rc.6",
47
47
  "@types/debug": "4.1.13",
48
48
  "debug": "4.4.3",
49
49
  "is-plain-object": "5.0.0",
50
50
  "type-fest": "5.6.0"
51
51
  },
52
- "gitHead": "8d87463af2ff3f1b83fb28da20f1819362cf3555"
52
+ "gitHead": "c02c3a0783eac6b2fb4707be2dc00b88f6219641"
53
53
  }