@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 +35 -0
- package/CLAUDE.md +11 -0
- package/lib/debug.d.ts +6 -0
- package/lib/debug.js +6 -0
- package/lib/ml-core.js +55 -16
- package/lib/ml-dom/helper/accname.js +4 -2
- package/lib/ml-dom/helper/foreign-attr-names.d.ts +13 -0
- package/lib/ml-dom/helper/foreign-attr-names.js +89 -0
- package/lib/ml-dom/node/attr.d.ts +80 -1
- package/lib/ml-dom/node/attr.js +150 -9
- package/lib/ml-dom/node/document.d.ts +53 -1
- package/lib/ml-dom/node/document.js +73 -8
- package/lib/ml-dom/node/element.d.ts +105 -4
- package/lib/ml-dom/node/element.js +327 -58
- package/lib/ml-dom/node/named-node-map.js +1 -1
- package/lib/ml-dom/node/node-store.js +4 -2
- package/lib/ml-dom/node/rule-mapper.js +21 -6
- package/lib/test/index.js +15 -6
- package/package.json +19 -19
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
27
|
-
|
|
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
|
-
*
|
|
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
|