@markuplint/ml-core 5.1.1 → 5.3.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/CHANGELOG.md CHANGED
@@ -3,6 +3,41 @@
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.3.0](https://github.com/markuplint/markuplint/compare/v5.2.0...v5.3.0) (2026-10-21)
7
+
8
+ ### Bug Fixes
9
+
10
+ * make published packages loadable from require() and put types first ([723106f](https://github.com/markuplint/markuplint/commit/723106f51aac4e60dc02db2c1a894617368b0253)), closes [#4198](https://github.com/markuplint/markuplint/issues/4198)
11
+ * **ml-core:** add MLElement.isCustomElement so :model() matches #custom ([befdf3b](https://github.com/markuplint/markuplint/commit/befdf3be6623331b4fc5dca773df6ba745ea10ee)), closes [#custom](https://github.com/markuplint/markuplint/issues/custom)
12
+ * **ml-core:** decide by the name that the DOM gives an attribute ([68f732c](https://github.com/markuplint/markuplint/commit/68f732ce3eec1203fbed479af1f1488e5f272a47))
13
+ * **ml-core:** make the type selector and the pretender guard read `isNativeTag` of the parser ([9f7877f](https://github.com/markuplint/markuplint/commit/9f7877f10a02d86e5efe5254b414ef232f08c2a6))
14
+ * **ml-core:** match attribute names case-insensitively in getAttributeToken ([418e627](https://github.com/markuplint/markuplint/commit/418e627d6f7402136654ed6f1007b06ad6da3d86)), closes [#4180](https://github.com/markuplint/markuplint/issues/4180)
15
+ * **ml-core:** read a prefixed name as the HTML parser does and match Vue props by either spelling ([fe6cd8f](https://github.com/markuplint/markuplint/commit/fe6cd8f949fd28212e2123e4129c8dc3d7b0404f))
16
+ * **ml-core:** tell the selector whether the document tells the case of a tag name apart ([f0a9c17](https://github.com/markuplint/markuplint/commit/f0a9c1783bd237f307647f0b3cb978edeb814037))
17
+ * **ml-core:** treat a pretender target that is a component name as a component ([f20dc1b](https://github.com/markuplint/markuplint/commit/f20dc1be225e2f2e90f2c721bc2c10e8847b9809))
18
+ * **rules:** compare attribute names as the DOM does in the rules that list attributes ([51faee6](https://github.com/markuplint/markuplint/commit/51faee6f13a8f09f136438873991f0581071012a))
19
+ * **selector:** match a prefixed attribute by its local name as well ([a9beb11](https://github.com/markuplint/markuplint/commit/a9beb112b87bf9ade372ede0fe0d7958d05027fc))
20
+
21
+ ### Features
22
+
23
+ * **ml-core:** ask whether a name is a prefix of an attribute, and keep suggesting the property ([bd195ef](https://github.com/markuplint/markuplint/commit/bd195ef6ededb80482c642a86360c5f932f8b91b))
24
+ * **ml-core:** expose attrNameCaseSensitive on MLDocument ([d73a2fd](https://github.com/markuplint/markuplint/commit/d73a2fdc337425e6203da696abe785172f7b277d)), closes [#4229](https://github.com/markuplint/markuplint/issues/4229)
25
+ * **ml-core:** read the parser's name rules through the document ([b4f03ea](https://github.com/markuplint/markuplint/commit/b4f03eaff13f52421993bbec490330a4039ee850)), closes [MLElement#tagNameKind](https://github.com/MLElement/issues/tagNameKind)
26
+ * **parser-utils:** let a parser say how an attribute name gets into the DOM ([abd81e5](https://github.com/markuplint/markuplint/commit/abd81e5d20f4ffeb0ba878e77fd4122c1a803a9f))
27
+
28
+ ### Performance Improvements
29
+
30
+ * **ml-core:** guard debug logging on the rule mapper and verify paths ([1a6249f](https://github.com/markuplint/markuplint/commit/1a6249f333e2dc75d65607b5059f9d8d647e7b3b))
31
+
32
+
33
+ # [5.2.0](https://github.com/markuplint/markuplint/compare/v5.1.1...v5.2.0) (2026-10-13)
34
+
35
+ ### Bug Fixes
36
+
37
+ - **ml-core:** resolve the namespace of a component and the children given to it ([00ecbba](https://github.com/markuplint/markuplint/commit/00ecbbae6095dd8eb7d10148633d6610bed7f1e2))
38
+ - **ml-core:** return the DOM values from MLAttr.namespaceURI ([05467ad](https://github.com/markuplint/markuplint/commit/05467ad28d61ae0a272c8335db9a0b2f614e5592))
39
+ - use caret ranges for third-party dependencies of published packages ([5fb5d2e](https://github.com/markuplint/markuplint/commit/5fb5d2ee81eaeea45fe7256a40dd41c95ff0d672))
40
+
6
41
  ## [5.1.1](https://github.com/markuplint/markuplint/compare/v5.1.0...v5.1.1) (2026-10-09)
7
42
 
8
43
  **Note:** Version bump only for package @markuplint/ml-core
package/CLAUDE.md ADDED
@@ -0,0 +1,11 @@
1
+ # @markuplint/ml-core
2
+
3
+ ## Keeping the MLDOM classes in step with `lib.dom`
4
+
5
+ `MLElement`, `MLNode`, `MLAttr` and the other MLDOM classes `implements` the DOM interfaces, so a TypeScript release that adds a property or makes one nullable breaks the build with a long list of "is missing the following properties" errors. Fix that by adding the missing stubs and widening types to `| null` where `lib.dom` did, one stub at a time, rebuilding between rounds.
6
+
7
+ Do not change a parameter or return type from an MLDOM class to the plain DOM type (`MLElement` → `Element`, `MLNode` → `Node`) to make the errors go away. That drops the markuplint-specific type information for every caller, and the error cascade it seems to cure is only the missing stubs. Adding a method overload (for example on `matches()`) counts as a stub and is fine.
8
+
9
+ ## Consumer reports of a type error after a TypeScript release
10
+
11
+ The `implements` clauses land in the published `.d.ts`, so a consumer whose `lib.dom` is newer than ours (a newer TypeScript, or `libReplacement` with third-party DOM types) gets the same "is missing the following properties" errors from `node_modules/@markuplint/ml-core` without changing anything on their side; `skipLibCheck: false` is enough. Adding the stubs here fixes our build, and the consumer only once they upgrade markuplint. The structural fix — drop `implements` from the public classes and move DOM conformance to a typecheck spec that is not published (`yarn test:types` now type-checks the specs) — is tracked in #4076; check its state before closing another such report with a stub-only release.
package/lib/debug.d.ts CHANGED
@@ -1,4 +1,10 @@
1
1
  import debug from 'debug';
2
+ /**
3
+ * Read `.enabled` at call time; never cache it at module load. `debug.enable()` (including via `verbosely()` /
4
+ * `enableDebug()`) can switch a namespace on after import. A verify or fix run reads it once on entry to decide
5
+ * whether to build log arguments. Enabling debug mid-run is not seen until the next run; disabling takes effect at
6
+ * once because the logger itself re-checks.
7
+ */
2
8
  export declare const log: debug.Debugger;
3
9
  /**
4
10
  * Enables debug logging for the `ml-core` namespace and the CLI namespace.
package/lib/debug.js CHANGED
@@ -1,5 +1,11 @@
1
1
  import debug from 'debug';
2
2
  const CLI_NS = 'markuplint-cli';
3
+ /**
4
+ * Read `.enabled` at call time; never cache it at module load. `debug.enable()` (including via `verbosely()` /
5
+ * `enableDebug()`) can switch a namespace on after import. A verify or fix run reads it once on entry to decide
6
+ * whether to build log arguments. Enabling debug mid-run is not seen until the next run; disabling takes effect at
7
+ * once because the logger itself re-checks.
8
+ */
3
9
  export const log = debug('ml-core');
4
10
  /**
5
11
  * Enables debug logging for the `ml-core` namespace and the CLI namespace.
package/lib/ml-core.js CHANGED
@@ -191,7 +191,10 @@ export class MLCore {
191
191
  async verify(fixOrOptions) {
192
192
  const options = typeof fixOrOptions === 'boolean' ? { fix: fixOrOptions } : (fixOrOptions ?? {});
193
193
  const fix = options.fix ?? false;
194
- log('verify: start');
194
+ const logEnabled = log.enabled;
195
+ if (logEnabled) {
196
+ log('verify: start');
197
+ }
195
198
  const violations = [];
196
199
  if (this.#document instanceof ParserError) {
197
200
  const parseError = this.#createParseError(this.#document.message, this.#document.line, this.#document.col, this.#document.raw);
@@ -199,7 +202,9 @@ export class MLCore {
199
202
  return { violations: [], fixedCode: fix ? this.#sourceCode : undefined };
200
203
  }
201
204
  violations.push(parseError);
202
- log('verify: error %o', this.#document.message);
205
+ if (logEnabled) {
206
+ log('verify: error %o', this.#document.message);
207
+ }
203
208
  return { violations, fixedCode: fix ? this.#sourceCode : undefined };
204
209
  }
205
210
  this.#pushNonFatalParseErrors(violations);
@@ -321,7 +326,9 @@ export class MLCore {
321
326
  };
322
327
  }
323
328
  }
324
- log('verify: end');
329
+ if (logEnabled) {
330
+ log('verify: end');
331
+ }
325
332
  return { violations, fixedCode, fixSummary };
326
333
  }
327
334
  #createDocument() {
@@ -338,6 +345,12 @@ export class MLCore {
338
345
  endTag: this.#parser.endTag,
339
346
  booleanish: this.#parser.booleanish,
340
347
  tagNameCaseSensitive: this.#parser.tagNameCaseSensitive,
348
+ componentNameVariants: this.#parser.componentNameVariants,
349
+ // The parser's own rule, called on the parser: a method of a class that reads its own state
350
+ isComponentName: this.#parser.isComponentName?.bind(this.#parser),
351
+ isNativeTag: this.#parser.isNativeTag?.bind(this.#parser),
352
+ attrNameCaseSensitive: this.#parser.attrNameCaseSensitive,
353
+ attrNameRoute: this.#parser.attrNameRoute,
341
354
  pretenders: this.#pretenders,
342
355
  });
343
356
  // Collect errors from rule mapping (e.g., invalid wildcard usage).
@@ -494,6 +507,7 @@ export class MLCore {
494
507
  async #multiPassFix(initialViolations) {
495
508
  // Same safety cap as ESLint's SourceCodeFixer (10 passes).
496
509
  const MAX_FIX_PASSES = 10;
510
+ const logEnabled = log.enabled;
497
511
  let currentCode = this.#sourceCode;
498
512
  let fixes = extractFixes(initialViolations);
499
513
  let totalApplied = 0;
@@ -511,7 +525,9 @@ export class MLCore {
511
525
  let lastParsableCode = currentCode;
512
526
  let pass = 0;
513
527
  for (; pass < MAX_FIX_PASSES; pass++) {
514
- log('fix pass %d: %d fixes', pass, fixes.length);
528
+ if (logEnabled) {
529
+ log('fix pass %d: %d fixes', pass, fixes.length);
530
+ }
515
531
  const result = applyFixes(currentCode, fixes);
516
532
  totalApplied += result.applied.length;
517
533
  totalSkipped += result.skipped.length;
@@ -519,18 +535,24 @@ export class MLCore {
519
535
  firstPassEdits = result.appliedEdits;
520
536
  }
521
537
  if (result.applied.length === 0) {
522
- log('fix pass %d: no fixes applied, stopping', pass);
538
+ if (logEnabled) {
539
+ log('fix pass %d: no fixes applied, stopping', pass);
540
+ }
523
541
  break;
524
542
  }
525
543
  if (result.output === currentCode) {
526
- log('fix pass %d: output unchanged, stopping', pass);
544
+ if (logEnabled) {
545
+ log('fix pass %d: output unchanged, stopping', pass);
546
+ }
527
547
  break;
528
548
  }
529
549
  // Cycle detection: if the output matches the input of any earlier pass,
530
550
  // fixes are oscillating (A → B → A, A → B → C → A, ...) and will never converge.
531
551
  const cycleStart = codeHistory.get(result.output);
532
552
  if (cycleStart !== undefined) {
533
- log('fix pass %d: cycle detected (output matches the input of pass %d, cycle length %d), stopping', pass, cycleStart, pass + 1 - cycleStart);
553
+ if (logEnabled) {
554
+ log('fix pass %d: cycle detected (output matches the input of pass %d, cycle length %d), stopping', pass, cycleStart, pass + 1 - cycleStart);
555
+ }
534
556
  currentCode = result.output;
535
557
  latestViolations = undefined;
536
558
  break;
@@ -545,7 +567,9 @@ export class MLCore {
545
567
  this.#parse();
546
568
  this.#createDocument();
547
569
  if (this.#document instanceof ParserError) {
548
- log('fix pass %d: produced unparsable code, reverting to the previous parsable code', pass);
570
+ if (logEnabled) {
571
+ log('fix pass %d: produced unparsable code, reverting to the previous parsable code', pass);
572
+ }
549
573
  currentCode = lastParsableCode;
550
574
  totalApplied -= result.applied.length;
551
575
  if (pass === 0) {
@@ -555,21 +579,27 @@ export class MLCore {
555
579
  }
556
580
  lastParsableCode = currentCode;
557
581
  if (result.skipped.length === 0) {
558
- log('fix pass %d: all fixes applied, stopping', pass);
582
+ if (logEnabled) {
583
+ log('fix pass %d: all fixes applied, stopping', pass);
584
+ }
559
585
  break;
560
586
  }
561
587
  // --- Multi-pass path (only when overlapping fixes exist) ---
562
- log('fix pass %d: %d skipped, re-running rules for next pass', pass, result.skipped.length);
588
+ if (logEnabled) {
589
+ log('fix pass %d: %d skipped, re-running rules for next pass', pass, result.skipped.length);
590
+ }
563
591
  const newViolations = await this.#runAllRules(true);
564
592
  latestViolations = newViolations;
565
593
  fixes = extractFixes(newViolations);
566
594
  if (fixes.length === 0) {
567
- log('fix pass %d: no more fixable violations, stopping', pass);
595
+ if (logEnabled) {
596
+ log('fix pass %d: no more fixable violations, stopping', pass);
597
+ }
568
598
  break;
569
599
  }
570
600
  }
571
601
  const reachedMaxPasses = pass === MAX_FIX_PASSES;
572
- if (reachedMaxPasses) {
602
+ if (reachedMaxPasses && logEnabled) {
573
603
  log('fix: reached maximum number of passes (%d), some fixes may not have been applied', MAX_FIX_PASSES);
574
604
  }
575
605
  // Final verification (#3890): compute the violations that remain in the
@@ -608,6 +638,7 @@ export class MLCore {
608
638
  };
609
639
  }
610
640
  async #runAllRules(fix) {
641
+ const logEnabled = log.enabled;
611
642
  const violations = [];
612
643
  if (this.#document instanceof ParserError) {
613
644
  return violations;
@@ -629,7 +660,9 @@ export class MLCore {
629
660
  if (ruleInfo.disabled && ruleInfo.nodeRules.length === 0 && ruleInfo.childNodeRules.length === 0) {
630
661
  continue;
631
662
  }
632
- log('%s Rule: verify', rule.name);
663
+ if (logEnabled) {
664
+ log('%s Rule: verify', rule.name);
665
+ }
633
666
  const results = await rule.verify(this.#document, this.#locale, fix).catch(error => {
634
667
  if (error instanceof ParserError) {
635
668
  return error;
@@ -639,14 +672,18 @@ export class MLCore {
639
672
  if (results instanceof ParserError) {
640
673
  const parseError = this.#createParseError(results.message, results.line, results.col, results.raw);
641
674
  if (parseError) {
642
- log('%s Rule: verify error %o', rule.name, results.message);
675
+ if (logEnabled) {
676
+ log('%s Rule: verify error %o', rule.name, results.message);
677
+ }
643
678
  violations.push(parseError);
644
679
  }
645
680
  }
646
681
  else {
647
682
  violations.push(...results);
648
683
  }
649
- log('%s Rule: verify end', rule.name);
684
+ if (logEnabled) {
685
+ log('%s Rule: verify end', rule.name);
686
+ }
650
687
  }
651
688
  return violations;
652
689
  }
@@ -655,7 +692,9 @@ export class MLCore {
655
692
  this.#ast = this.#parser.parse(this.#sourceCode, this.#parserOptions);
656
693
  }
657
694
  catch (error) {
658
- log('Caught the parse error: %O', error);
695
+ if (log.enabled) {
696
+ log('Caught the parse error: %O', error);
697
+ }
659
698
  this.#ast = null;
660
699
  if (error instanceof ParserError) {
661
700
  this.#document = error;
@@ -23,8 +23,10 @@ el, version) {
23
23
  return result.name;
24
24
  }
25
25
  catch (error) {
26
- accnameLog('Raw: %s', el.raw);
27
- accnameLog('Error: %O', error);
26
+ if (accnameLog.enabled) {
27
+ accnameLog('Raw: %s', el.raw);
28
+ accnameLog('Error: %O', error);
29
+ }
28
30
  return '';
29
31
  }
30
32
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The name that the HTML parser gives an attribute of an element in a foreign namespace: the name
3
+ * lower-cased, with the case restored for the names of SVG (`viewbox` is `viewBox`) and MathML
4
+ * (`definitionurl` is `definitionURL`).
5
+ *
6
+ * @see https://html.spec.whatwg.org/multipage/parsing.html#adjust-svg-attributes
7
+ * @see https://html.spec.whatwg.org/multipage/parsing.html#adjust-mathml-attributes
8
+ *
9
+ * @param lowerCaseName - The name with its ASCII letters lower-cased
10
+ * @param namespaceURI - The namespace of the element that the attribute is on
11
+ * @returns The name in the DOM
12
+ */
13
+ export declare function adjustForeignAttrName(lowerCaseName: string, namespaceURI: string | null): string;
@@ -0,0 +1,89 @@
1
+ const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
2
+ const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
3
+ /**
4
+ * The names of SVG attributes that are not all lower case, the second column of the table of
5
+ * "adjust SVG attributes". The HTML parser lower-cases a name it reads and then restores these.
6
+ *
7
+ * @see https://html.spec.whatwg.org/multipage/parsing.html#adjust-svg-attributes
8
+ */
9
+ const SVG_ATTR_NAMES = new Map([
10
+ 'attributeName',
11
+ 'attributeType',
12
+ 'baseFrequency',
13
+ 'baseProfile',
14
+ 'calcMode',
15
+ 'clipPathUnits',
16
+ 'diffuseConstant',
17
+ 'edgeMode',
18
+ 'filterUnits',
19
+ 'glyphRef',
20
+ 'gradientTransform',
21
+ 'gradientUnits',
22
+ 'kernelMatrix',
23
+ 'kernelUnitLength',
24
+ 'keyPoints',
25
+ 'keySplines',
26
+ 'keyTimes',
27
+ 'lengthAdjust',
28
+ 'limitingConeAngle',
29
+ 'markerHeight',
30
+ 'markerUnits',
31
+ 'markerWidth',
32
+ 'maskContentUnits',
33
+ 'maskUnits',
34
+ 'numOctaves',
35
+ 'pathLength',
36
+ 'patternContentUnits',
37
+ 'patternTransform',
38
+ 'patternUnits',
39
+ 'pointsAtX',
40
+ 'pointsAtY',
41
+ 'pointsAtZ',
42
+ 'preserveAlpha',
43
+ 'preserveAspectRatio',
44
+ 'primitiveUnits',
45
+ 'refX',
46
+ 'refY',
47
+ 'repeatCount',
48
+ 'repeatDur',
49
+ 'requiredExtensions',
50
+ 'requiredFeatures',
51
+ 'specularConstant',
52
+ 'specularExponent',
53
+ 'spreadMethod',
54
+ 'startOffset',
55
+ 'stdDeviation',
56
+ 'stitchTiles',
57
+ 'surfaceScale',
58
+ 'systemLanguage',
59
+ 'tableValues',
60
+ 'targetX',
61
+ 'targetY',
62
+ 'textLength',
63
+ 'viewBox',
64
+ 'viewTarget',
65
+ 'xChannelSelector',
66
+ 'yChannelSelector',
67
+ 'zoomAndPan',
68
+ ].map(name => [name.toLowerCase(), name]));
69
+ /**
70
+ * The name that the HTML parser gives an attribute of an element in a foreign namespace: the name
71
+ * lower-cased, with the case restored for the names of SVG (`viewbox` is `viewBox`) and MathML
72
+ * (`definitionurl` is `definitionURL`).
73
+ *
74
+ * @see https://html.spec.whatwg.org/multipage/parsing.html#adjust-svg-attributes
75
+ * @see https://html.spec.whatwg.org/multipage/parsing.html#adjust-mathml-attributes
76
+ *
77
+ * @param lowerCaseName - The name with its ASCII letters lower-cased
78
+ * @param namespaceURI - The namespace of the element that the attribute is on
79
+ * @returns The name in the DOM
80
+ */
81
+ export function adjustForeignAttrName(lowerCaseName, namespaceURI) {
82
+ if (namespaceURI === SVG_NAMESPACE) {
83
+ return SVG_ATTR_NAMES.get(lowerCaseName) ?? lowerCaseName;
84
+ }
85
+ if (namespaceURI === MATHML_NAMESPACE && lowerCaseName === 'definitionurl') {
86
+ return 'definitionURL';
87
+ }
88
+ return lowerCaseName;
89
+ }
@@ -98,7 +98,86 @@ export declare class MLAttr<T extends RuleConfigValue, O extends PlainData = und
98
98
  */
99
99
  get name(): string;
100
100
  /**
101
- * Returns the namespace URI of this attribute, resolved from the attribute name.
101
+ * The name that the DOM gives this attribute, which two spellings of a name share when they are
102
+ * one attribute (`ID` and `id` on a `<div>`, `VIEWBOX` and `viewbox` on an `<svg>` that the
103
+ * HTML parser reads). `name` keeps the spelling of the source for a message, and for
104
+ * `case-sensitive-attr-name`.
105
+ *
106
+ * It depends on how the name gets into the DOM (`MLDocument#attrNameRoute`) and on the element:
107
+ * the HTML parser lower-cases a name, and restores the case of the names of SVG and MathML;
108
+ * `setAttribute` lower-cases a name only on an element of the HTML namespace, so `ROLE` on an
109
+ * `<svg>` that code sets is `ROLE`, which is not `role`. The attribute of a component is a prop,
110
+ * which keeps its spelling, except in Vue where `my-label` and `myLabel` are one prop.
111
+ *
112
+ * @see https://dom.spec.whatwg.org/#dom-element-setattribute
113
+ * @see https://html.spec.whatwg.org/multipage/parsing.html#attribute-name-state
114
+ */
115
+ get nameKey(): string;
116
+ /**
117
+ * The key of `name`, a name of a spec, of an option or of a lookup, as this attribute is keyed:
118
+ * `name` is this attribute when its key is {@link MLAttr.nameKey}. Only the ASCII letters fold,
119
+ * as the HTML syntax says (`toLowerCase` also turns U+212A KELVIN SIGN into `k`).
120
+ *
121
+ * It is computed when asked, not stored by the constructor: it reads the owner element (its
122
+ * type and namespace) and the document, which are complete only after the attribute is built.
123
+ *
124
+ * @param name - A name as written
125
+ * @returns The key that two spellings of one attribute share
126
+ */
127
+ keyOf(name: string): string;
128
+ /**
129
+ * Whether `name` is the name of this attribute: the spelling it is written in, or another that
130
+ * the DOM makes the same attribute ({@link MLAttr.keyOf}). A spec, an option and the lookups of
131
+ * the element ask this, so the case of a name is decided here and not in each of them.
132
+ *
133
+ * A name with a capital letter that code sets on an element of the HTML namespace is a prop of
134
+ * the framework (`defaultValue` of React), not an attribute of the DOM: only its exact spelling
135
+ * is it, and `defaultvalue` is not (see {@link MLAttr.isPrefixedBy} for a prefix).
136
+ *
137
+ * @param name - A name of a spec, an option or a lookup
138
+ * @returns `true` if `name` is this attribute
139
+ */
140
+ isNamedBy(name: string): boolean;
141
+ /**
142
+ * Whether the name of this attribute starts with `prefix`, a prefix of an option
143
+ * (`ignoreAttrNamePrefix`): as it is written, or as the DOM gives the name ({@link MLAttr.keyOf}).
144
+ * A prefix with a capital letter that code sets on an element of the HTML namespace is the prefix
145
+ * of a name of the framework, and only its exact spelling is it, as for {@link MLAttr.isNamedBy}.
146
+ *
147
+ * @param prefix - A prefix of an option
148
+ * @returns `true` if the name of this attribute starts with `prefix`
149
+ */
150
+ isPrefixedBy(prefix: string): boolean;
151
+ /**
152
+ * Whether the name of this attribute reaches the DOM through the DOM API (`setAttribute`) and
153
+ * not through the HTML parser of the browser: what the parser says of its language
154
+ * (`MLDocument#attrNameRoute`), and for a parser that says it of a dynamic attribute only
155
+ * (Svelte), whether this one is dynamic.
156
+ */
157
+ get isSetThroughDOMAPI(): boolean;
158
+ /**
159
+ * Whether a selector matches the name of this attribute without regard to ASCII case: the DOM
160
+ * does so for an element of the HTML namespace and not for one of SVG or MathML, nor for a prop.
161
+ *
162
+ * @implements `SelectorAttr#nameCaseInsensitive` of `@markuplint/selector`
163
+ */
164
+ get nameCaseInsensitive(): boolean;
165
+ /**
166
+ * The name that a selector is matched with.
167
+ *
168
+ * @implements `SelectorAttr#domName` of `@markuplint/selector`
169
+ */
170
+ get domName(): string;
171
+ /**
172
+ * Whether a name of a selector is this prop once it is camelized: the props of a Vue component.
173
+ *
174
+ * @implements `SelectorAttr#nameIsCamelized` of `@markuplint/selector`
175
+ */
176
+ get nameIsCamelized(): boolean;
177
+ /**
178
+ * Returns the namespace URI of this attribute: that of its prefix when the name has a known
179
+ * one (e.g. `xlink:href`), otherwise `null`. An attribute without a prefix has no namespace
180
+ * whichever element it is on, and so does a spread attribute.
102
181
  *
103
182
  * @implements DOM API: `Attr`
104
183
  * @see https://dom.spec.whatwg.org/#ref-for-dom-attr-namespaceuri