@markuplint/ml-core 5.4.0 → 5.6.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/lib/ml-core.js +3 -0
  3. package/lib/ml-dom/helper/accname.d.ts +35 -0
  4. package/lib/ml-dom/helper/accname.js +69 -17
  5. package/lib/ml-dom/helper/debug.js +1 -1
  6. package/lib/ml-dom/helper/literal-values.d.ts +102 -0
  7. package/lib/ml-dom/helper/literal-values.js +193 -0
  8. package/lib/ml-dom/helper/pretended-attrs.d.ts +16 -0
  9. package/lib/ml-dom/helper/pretended-attrs.js +21 -0
  10. package/lib/ml-dom/helper/slot-assignment.d.ts +48 -0
  11. package/lib/ml-dom/helper/slot-assignment.js +292 -0
  12. package/lib/ml-dom/helper/tree-scope.d.ts +130 -0
  13. package/lib/ml-dom/helper/tree-scope.js +341 -0
  14. package/lib/ml-dom/helper/walkers.d.ts +15 -0
  15. package/lib/ml-dom/helper/walkers.js +34 -0
  16. package/lib/ml-dom/index.d.ts +4 -0
  17. package/lib/ml-dom/index.js +2 -0
  18. package/lib/ml-dom/node/attr.d.ts +77 -10
  19. package/lib/ml-dom/node/attr.js +209 -20
  20. package/lib/ml-dom/node/block.d.ts +15 -6
  21. package/lib/ml-dom/node/block.js +17 -8
  22. package/lib/ml-dom/node/document-fragment.d.ts +18 -6
  23. package/lib/ml-dom/node/document-fragment.js +19 -7
  24. package/lib/ml-dom/node/document.d.ts +36 -7
  25. package/lib/ml-dom/node/document.js +60 -17
  26. package/lib/ml-dom/node/dom-token-list.js +28 -3
  27. package/lib/ml-dom/node/element.d.ts +148 -24
  28. package/lib/ml-dom/node/element.js +258 -48
  29. package/lib/ml-dom/node/named-node-map.js +2 -1
  30. package/lib/ml-dom/node/node-list.js +2 -3
  31. package/lib/ml-dom/node/node.d.ts +55 -10
  32. package/lib/ml-dom/node/node.js +98 -13
  33. package/lib/ml-dom/node/parent-node.d.ts +39 -8
  34. package/lib/ml-dom/node/parent-node.js +79 -12
  35. package/lib/ml-dom/node/shadow-root.d.ts +162 -0
  36. package/lib/ml-dom/node/shadow-root.js +219 -0
  37. package/lib/ml-dom/node/text.d.ts +12 -3
  38. package/lib/ml-dom/node/text.js +14 -3
  39. package/lib/ml-rule/ml-rule.js +4 -1
  40. package/lib/test/index.js +3 -0
  41. package/lib/utils/index.d.ts +2 -0
  42. package/lib/utils/index.js +1 -0
  43. package/lib/utils/search-node-at-location.d.ts +39 -0
  44. package/lib/utils/search-node-at-location.js +53 -0
  45. package/package.json +12 -15
@@ -0,0 +1,341 @@
1
+ import { isPotentialCustomElementName } from '@markuplint/parser-utils';
2
+ import { decodeEntities } from '@markuplint/shared';
3
+ /**
4
+ * @module
5
+ *
6
+ * The tree of a node is what its `getRootNode()` returns: the document, the content of an
7
+ * HTML `template` element, or a declarative shadow root. An id is unique in a tree, and an id
8
+ * reference is resolved in a tree, as the specification says of "the element's tree".
9
+ *
10
+ * markuplint keeps the content of a `template` element as the children of the element (the
11
+ * syntax tree has no other place for it), so the boundary is made here and not by the shape of
12
+ * the tree: the queries of `MLParentNode`, `getRootNode()`, `treeParentElement`, the selector
13
+ * engine and `contains()` stop at it. `parentNode` / `parentElement` do not: the content
14
+ * model of a `template` element is validated against the elements that the syntax tree puts
15
+ * in it, so they go on to the `template` element. The algorithms of `@markuplint/ml-spec` walk up
16
+ * with `getTreeParentElement` / `getComposedParentElement`, which read `getRootNode()`, so the
17
+ * boundary made here is the boundary they stop at.
18
+ *
19
+ * @see https://dom.spec.whatwg.org/#concept-tree
20
+ * @see https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute
21
+ * @see https://html.spec.whatwg.org/multipage/scripting.html#the-template-element
22
+ */
23
+ export const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
24
+ /**
25
+ * The names that the DOM accepts as a shadow host, besides a valid custom element name.
26
+ *
27
+ * @see https://dom.spec.whatwg.org/#valid-shadow-host-name
28
+ */
29
+ const SHADOW_HOST_NAMES = new Set([
30
+ 'article',
31
+ 'aside',
32
+ 'blockquote',
33
+ 'body',
34
+ 'div',
35
+ 'footer',
36
+ 'h1',
37
+ 'h2',
38
+ 'h3',
39
+ 'h4',
40
+ 'h5',
41
+ 'h6',
42
+ 'header',
43
+ 'main',
44
+ 'nav',
45
+ 'p',
46
+ 'section',
47
+ 'span',
48
+ ]);
49
+ /**
50
+ * Whether `node` is skipped when the parent of a `template` element is looked for: what the
51
+ * template engine or the framework renders away (a block of a template language, a fragment of
52
+ * JSX, a `template` with a directive such as `v-if`) leaves its children to the next element
53
+ * above.
54
+ *
55
+ * `isRenderedAway` of `@markuplint/rules` (`src/helpers.ts`) decides the same of an element for the
56
+ * rules that walk up the ancestors: a kind added here belongs to it too.
57
+ *
58
+ * @param node - A node of the syntax tree
59
+ * @returns `true` if the node is not an element of the output
60
+ */
61
+ export function isTransparent(
62
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
63
+ node) {
64
+ if (node.is(node.MARKUPLINT_PREPROCESSOR_BLOCK)) {
65
+ return true;
66
+ }
67
+ if (node.isFragment) {
68
+ return true;
69
+ }
70
+ return node.is(node.ELEMENT_NODE) && hasDirectiveTemplate(node);
71
+ }
72
+ /**
73
+ * Whether the element is a `template` that has a directive of a template engine (Vue's `v-if`,
74
+ * `v-for`, `v-slot`). The engine renders its children in place of it and drops it, so it is not
75
+ * an HTML `template` element.
76
+ *
77
+ * @param el - An element
78
+ * @returns `true` if it is such a `template`
79
+ */
80
+ function hasDirectiveTemplate(
81
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
82
+ el) {
83
+ if (el.localName !== 'template') {
84
+ return false;
85
+ }
86
+ for (const attr of el.attributes) {
87
+ // A spread attribute (`{...props}`) sets attributes of the element, which stays
88
+ if (attr.isDirective && attr.localName !== '#spread') {
89
+ return true;
90
+ }
91
+ }
92
+ return false;
93
+ }
94
+ /**
95
+ * Whether the element is an HTML `template` element, which holds its children in `content`, a tree
96
+ * of its own.
97
+ *
98
+ * It is not when the element is a fragment, has a directive of a template engine, or the
99
+ * framework puts the children in the element itself (`templateContent: 'in-place'` of the spec).
100
+ *
101
+ * @template T - The rule configuration value type
102
+ * @template O - The rule options type
103
+ * @param el - An element
104
+ * @returns `true` if the children of the element belong to a tree of their own
105
+ */
106
+ export function isHTMLTemplateElement(
107
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
108
+ el) {
109
+ if (el.isFragment || el.localName !== 'template' || el.namespaceURI !== HTML_NAMESPACE) {
110
+ return false;
111
+ }
112
+ if (el.ownerMLDocument.specs.templateContent === 'in-place') {
113
+ return false;
114
+ }
115
+ return !hasDirectiveTemplate(el);
116
+ }
117
+ /**
118
+ * The state of the `shadowrootmode` attribute of an HTML `template` element.
119
+ *
120
+ * @param template - A `template` element
121
+ * @returns `open` or `closed`, `none` for a missing or invalid value (the missing value default and
122
+ * the invalid value default are both the None state), and `dynamic` for a value that is not known
123
+ * until the template is rendered
124
+ * @see https://html.spec.whatwg.org/multipage/scripting.html#attr-template-shadowrootmode
125
+ */
126
+ function getShadowRootModeState(
127
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
128
+ template) {
129
+ const attr = template.getAttributeNode('shadowrootmode');
130
+ if (!attr || attr.isAbsent) {
131
+ return 'none';
132
+ }
133
+ if (attr.isDynamicValue) {
134
+ return 'dynamic';
135
+ }
136
+ const value = attr.value.toLowerCase();
137
+ return value === 'open' || value === 'closed' ? value : 'none';
138
+ }
139
+ /**
140
+ * The mode of the declarative shadow root that a `template` element asks for.
141
+ *
142
+ * @template T - The rule configuration value type
143
+ * @template O - The rule options type
144
+ * @param template - An element
145
+ * @returns `open` or `closed`, or `null` when it is not an HTML `template` element, the
146
+ * attribute is missing or invalid, or its value is dynamic
147
+ */
148
+ export function getDeclarativeShadowRootMode(
149
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
150
+ template) {
151
+ if (!isHTMLTemplateElement(template)) {
152
+ return null;
153
+ }
154
+ const state = getShadowRootModeState(template);
155
+ return state === 'open' || state === 'closed' ? state : null;
156
+ }
157
+ /**
158
+ * The element that the HTML parser makes the shadow host of a `template` element: its parent in
159
+ * the output, where what the engine renders away (see {@link isTransparent}) is skipped. It is the
160
+ * parent in the output of any node, so it is the host a light child is a child of.
161
+ *
162
+ * @template T - The rule configuration value type
163
+ * @template O - The rule options type
164
+ * @param template - A `template` element, or any node whose parent in the output is wanted
165
+ * @returns The element, or `null` when the file has none above the node
166
+ */
167
+ export function getShadowHost(
168
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
169
+ template) {
170
+ let node = template.syntacticalParentNode;
171
+ while (node) {
172
+ if (node.is(node.DOCUMENT_NODE) || node.is(node.DOCUMENT_FRAGMENT_NODE)) {
173
+ return null;
174
+ }
175
+ if (!isTransparent(node) && node.is(node.ELEMENT_NODE)) {
176
+ return node;
177
+ }
178
+ node = node.syntacticalParentNode;
179
+ }
180
+ return null;
181
+ }
182
+ /**
183
+ * Whether the host can have a shadow root, judged by what the markup says.
184
+ *
185
+ * @param host - The parent element of a `template` element
186
+ * @returns `true` or `false`, and `null` for an element that is not what it is written as (a
187
+ * component whose output is not known)
188
+ * @see https://dom.spec.whatwg.org/#concept-attach-a-shadow-root
189
+ */
190
+ function canBeShadowHost(
191
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
192
+ host) {
193
+ if (host.elementType === 'authored' && host.pretenderContext?.type !== 'pretender') {
194
+ return null;
195
+ }
196
+ if (host.namespaceURI !== HTML_NAMESPACE) {
197
+ return false;
198
+ }
199
+ const name = host.localName;
200
+ return SHADOW_HOST_NAMES.has(name) || isPotentialCustomElementName(name);
201
+ }
202
+ /**
203
+ * Whether the children written in `host` are its children in the output, so that a `template`
204
+ * among them is parsed in it. A pretended component renders them in the element it pretends to
205
+ * be only when it neither drops them (`slots: null`) nor puts them in a slot wrapper; a custom
206
+ * element keeps them in its light DOM. This is the same placement as the rendered parent
207
+ * `MLElement` gives a child for its namespace.
208
+ *
209
+ * @param host - The parent element of a `template` element
210
+ * @returns `false` when the component places the children elsewhere or not at all
211
+ */
212
+ function rendersChildrenInItself(
213
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
214
+ host) {
215
+ const context = host.pretenderContext;
216
+ if (context?.type !== 'pretender' || host.elementType === 'web-component') {
217
+ return true;
218
+ }
219
+ if (context.as.childNodes.length === 0) {
220
+ return false;
221
+ }
222
+ return !context.slotContent || context.slotContent.wrapper === context.as;
223
+ }
224
+ /**
225
+ * The `template` elements that sit in the output under the host, in the order of the markup,
226
+ * seen through what the engine renders away.
227
+ *
228
+ * @param host - A shadow host candidate
229
+ * @returns The `template` elements, which are HTML ones
230
+ */
231
+ function getTemplatesOfHost(
232
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
233
+ host) {
234
+ const templates = [];
235
+ for (const child of host.getPureChildNodes()) {
236
+ if (isTransparent(child)) {
237
+ templates.push(...getTemplatesOfHost(child));
238
+ continue;
239
+ }
240
+ if (child.is(child.ELEMENT_NODE) && isHTMLTemplateElement(child)) {
241
+ templates.push(child);
242
+ }
243
+ }
244
+ return templates;
245
+ }
246
+ /**
247
+ * Whether a declarative shadow root is attached to the parent of a `template` element, by what
248
+ * the HTML parser does with the start tag of `template` and what the DOM does to attach a shadow
249
+ * root: the mode is `open` or `closed` (ASCII case-insensitive), the parent is an HTML element with
250
+ * a valid shadow host name (a valid custom element name, or one of the names the DOM lists), and no
251
+ * earlier `template` of the same parent has attached one.
252
+ *
253
+ * The file is taken as parsed as a document that was served, so declarative shadow roots are
254
+ * allowed. The parser would not attach one to the top level of a fragment parsed through `innerHTML`,
255
+ * but whether a file is that is not something its markup says.
256
+ *
257
+ * Known limitation: a custom element definition may disable the shadow root (`disable shadow`),
258
+ * which only the script that defines the element tells. It is taken as not disabled.
259
+ *
260
+ * @template T - The rule configuration value type
261
+ * @template O - The rule options type
262
+ * @param template - An element
263
+ * @returns The state. An element that is not an HTML `template` element is `not-attached`
264
+ * @see https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inhead
265
+ * @see https://dom.spec.whatwg.org/#concept-attach-a-shadow-root
266
+ */
267
+ export function getDeclarativeShadowRootAttachment(
268
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
269
+ template) {
270
+ if (!isHTMLTemplateElement(template)) {
271
+ return 'not-attached';
272
+ }
273
+ const mode = getShadowRootModeState(template);
274
+ if (mode === 'none') {
275
+ return 'not-attached';
276
+ }
277
+ const host = getShadowHost(template);
278
+ if (!host || !rendersChildrenInItself(host)) {
279
+ return 'unknown';
280
+ }
281
+ const hostCheck = canBeShadowHost(host);
282
+ if (hostCheck === false) {
283
+ return 'not-attached';
284
+ }
285
+ if (hostCheck === null || mode === 'dynamic') {
286
+ return 'unknown';
287
+ }
288
+ // The first `template` that asks for a shadow root and can have one takes the host.
289
+ let undecided = false;
290
+ for (const earlier of getTemplatesOfHost(host)) {
291
+ if (earlier === template) {
292
+ break;
293
+ }
294
+ const earlierMode = getShadowRootModeState(earlier);
295
+ if (earlierMode === 'none') {
296
+ continue;
297
+ }
298
+ if (earlierMode === 'dynamic') {
299
+ undecided = true;
300
+ continue;
301
+ }
302
+ return 'not-attached';
303
+ }
304
+ return undecided ? 'unknown' : 'attached';
305
+ }
306
+ /**
307
+ * The `template` element of a host whose declarative shadow root is attached, if there is one.
308
+ *
309
+ * @template T - The rule configuration value type
310
+ * @template O - The rule options type
311
+ * @param host - An element
312
+ * @returns The `template` element whose content is the shadow root of the host, or `null`
313
+ */
314
+ export function getShadowRootTemplate(
315
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
316
+ host) {
317
+ return (getTemplatesOfHost(host).find(template => getDeclarativeShadowRootAttachment(template) === 'attached') ?? null);
318
+ }
319
+ /**
320
+ * The string that the DOM has for an attribute: the value with the character references in the markup
321
+ * resolved, so that `ab` is `ab`. A value that is written as code is a string already, with no
322
+ * character reference in it.
323
+ *
324
+ * @template T - The rule configuration value type
325
+ * @template O - The rule options type
326
+ * @param attr - An attribute, or `null` for one that is missing
327
+ * @param value - A part of the value of the attribute as it is written (one token of a list), which the
328
+ * attribute decides on whether it has a character reference; the whole value by default
329
+ * @returns The string, or `null` when the attribute is missing, a directive, or its value is not known
330
+ * until it is rendered (a dynamic value)
331
+ * @see https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute
332
+ */
333
+ export function getDOMValue(
334
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
335
+ attr, value) {
336
+ if (!attr || attr.isDynamicValue || attr.isDirective) {
337
+ return null;
338
+ }
339
+ const written = value ?? attr.value;
340
+ return attr.isExpressionValue ? written : decodeEntities(written);
341
+ }
@@ -3,4 +3,19 @@ import type { PlainData, RuleConfigValue } from '@markuplint/ml-config';
3
3
  export type Walker<T extends RuleConfigValue, O extends PlainData = undefined, N = MLNode<T, O>> = (node: N) => void | Promise<void> | Promise<void>[];
4
4
  export type SyncWalker<T extends RuleConfigValue, O extends PlainData = undefined, N = MLNode<T, O>> = (node: N) => void;
5
5
  export declare function syncWalk<T extends RuleConfigValue, O extends PlainData = undefined>(nodeList: ReadonlyArray<MLNode<T, O>>, walker: SyncWalker<T, O>): void;
6
+ /**
7
+ * Walks the nodes in tree order (a node before its descendants), within one tree.
8
+ *
9
+ * Within one tree means it does not go into the children of an HTML `template` element, which
10
+ * are a tree of their own (see `tree-scope.ts`), and it does not report a `template` element that is
11
+ * in no tree (a declarative shadow root is attached to its parent, or whether it is cannot be told).
12
+ * With `across`, it goes into every tree and reports every node, in the order of the markup.
13
+ *
14
+ * @template T - The rule configuration value type
15
+ * @template O - The rule options type
16
+ * @param nodeList - The nodes to start with: the children of the root of the tree
17
+ * @param walker - Called with each node
18
+ * @param across - Whether to go into the trees that other trees hold
19
+ */
20
+ export declare function walkTree<T extends RuleConfigValue, O extends PlainData = undefined>(nodeList: ReadonlyArray<MLNode<T, O>>, walker: SyncWalker<T, O>, across?: boolean): void;
6
21
  export declare function sequentialWalker<T extends RuleConfigValue, O extends PlainData = undefined, N = MLNode<T, O>>(list: ReadonlyArray<N>, walker: Walker<T, O, N>): Promise<void>;
@@ -8,6 +8,40 @@ nodeList, walker) {
8
8
  walker(node);
9
9
  }
10
10
  }
11
+ /**
12
+ * Walks the nodes in tree order (a node before its descendants), within one tree.
13
+ *
14
+ * Within one tree means it does not go into the children of an HTML `template` element, which
15
+ * are a tree of their own (see `tree-scope.ts`), and it does not report a `template` element that is
16
+ * in no tree (a declarative shadow root is attached to its parent, or whether it is cannot be told).
17
+ * With `across`, it goes into every tree and reports every node, in the order of the markup.
18
+ *
19
+ * @template T - The rule configuration value type
20
+ * @template O - The rule options type
21
+ * @param nodeList - The nodes to start with: the children of the root of the tree
22
+ * @param walker - Called with each node
23
+ * @param across - Whether to go into the trees that other trees hold
24
+ */
25
+ export function walkTree(
26
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
27
+ nodeList, walker, across = false) {
28
+ for (const node of nodeList) {
29
+ if (node.is(node.ELEMENT_NODE)) {
30
+ const holdsOtherTree = node.isHTMLTemplateElement;
31
+ if (across || !node.isInNoTree) {
32
+ walker(node);
33
+ }
34
+ if (across || !holdsOtherTree) {
35
+ walkTree([...node.childNodes], walker, across);
36
+ }
37
+ continue;
38
+ }
39
+ walker(node);
40
+ if (node.is(node.MARKUPLINT_PREPROCESSOR_BLOCK)) {
41
+ walkTree([...node.childNodes], walker, across);
42
+ }
43
+ }
44
+ }
11
45
  export function sequentialWalker(list, walker) {
12
46
  if (list.length === 0) {
13
47
  return Promise.resolve();
@@ -6,4 +6,8 @@ export type { MLDocumentType as DocumentType } from './node/document-type.js';
6
6
  export type { MLElement as Element } from './node/element.js';
7
7
  export type { MLText as Text } from './node/text.js';
8
8
  export type { MLChildNode as ChildNode } from './node/child-node.js';
9
+ export type { MLShadowRoot as ShadowRoot } from './node/shadow-root.js';
10
+ export type { DeclarativeShadowRootAttachment } from './helper/tree-scope.js';
11
+ export { getDOMValue } from './helper/tree-scope.js';
12
+ export { getRenderedChildNodes } from './helper/accname.js';
9
13
  export { MLDocument as Document } from './node/document.js';
@@ -1 +1,3 @@
1
+ export { getDOMValue } from './helper/tree-scope.js';
2
+ export { getRenderedChildNodes } from './helper/accname.js';
1
3
  export { MLDocument as Document } from './node/document.js';
@@ -2,6 +2,7 @@ import type { MLElement } from './element.js';
2
2
  import type { AttributeNodeType } from './types.js';
3
3
  import type { MLASTAttr } from '@markuplint/ml-ast';
4
4
  import type { PlainData, RuleConfigValue } from '@markuplint/ml-config';
5
+ import type { LiteralExpression } from '@markuplint/parser-utils';
5
6
  import { MLToken } from '../token/token.js';
6
7
  import { MLDomTokenList } from './dom-token-list.js';
7
8
  import { MLNode } from './node.js';
@@ -37,9 +38,13 @@ export declare class MLAttr<T extends RuleConfigValue, O extends PlainData = und
37
38
  */
38
39
  readonly isDuplicatable: boolean;
39
40
  /**
40
- * Whether this attribute has a dynamic value (e.g., a template expression rather than a static string).
41
+ * Whether the value of this attribute is written as code (`{x}` of JSX, `:x="…"` of Vue, a value of
42
+ * `=` in Pug, a masked block of a template language), whatever the code is: a literal (`{true}`) or not
43
+ * (`{cond}`). It is a matter of syntax, which a rule that checks how the value is written reads
44
+ * (`attr-value-quotes`, `no-unescaped-char`), while {@link MLAttr.isDynamicValue} is a matter of the
45
+ * value that results.
41
46
  */
42
- readonly isDynamicValue?: true;
47
+ readonly isExpressionValue?: true;
43
48
  /**
44
49
  * The token representing the attribute name, or null for spread attributes.
45
50
  */
@@ -69,13 +74,6 @@ export declare class MLAttr<T extends RuleConfigValue, O extends PlainData = und
69
74
  * The token representing the attribute value, or null if the attribute has no value.
70
75
  */
71
76
  readonly valueNode: MLToken | null;
72
- /**
73
- * Returns the "string" if HTML syntax. Otherwise, returns a type in its syntax.
74
- *
75
- * @default "string"
76
- * @implements `@markuplint/ml-core` API: `MLAttr`
77
- */
78
- readonly valueType: 'string' | 'number' | 'boolean' | 'code';
79
77
  /**
80
78
  * Creates a new MLAttr instance from an AST attribute token.
81
79
  *
@@ -83,6 +81,66 @@ export declare class MLAttr<T extends RuleConfigValue, O extends PlainData = und
83
81
  * @param ownElement - The element that owns this attribute
84
82
  */
85
83
  constructor(astToken: MLASTAttr, ownElement: MLElement<T, O>);
84
+ /**
85
+ * Whether the framework does not output this attribute, which is known from its value being a literal
86
+ * that the framework drops (`disabled={false}`, `title={null}`). It is still in the list of the
87
+ * attributes of the element (`MLElement#attributes`), as a name that is misspelled is so whatever its
88
+ * value is, and a lookup by name (`getAttribute`, `hasAttribute`) does not find it, as the DOM does not.
89
+ * An absent attribute is {@link MLAttr.isDynamicValue} as well, so that a rule that reads the value skips it.
90
+ *
91
+ * Which rules skip it: a rule that judges the value or the presence of an attribute (the value of
92
+ * `aria-*`, a required attribute, a conflict between two attributes) reads what the framework outputs,
93
+ * and an attribute that is not output is not there to judge. A rule that judges the name (a misspelling,
94
+ * a deprecated or obsolete attribute, the order) judges what the author wrote, so it still reports an
95
+ * absent attribute.
96
+ */
97
+ get isAbsent(): true | undefined;
98
+ /**
99
+ * Whether the value of this attribute is not known: the code is not a literal (`{cond}`), the
100
+ * framework renders a literal in a way that is not known (`disabled={""}`), or the attribute is
101
+ * not output ({@link MLAttr.isAbsent}), where there is no value to read.
102
+ *
103
+ * It is not whether the value is written as code (that is {@link MLAttr.isExpressionValue}): a
104
+ * literal that is written as code (`aria-busy={true}`) is rendered by the framework to a value
105
+ * (`"true"`), which is known.
106
+ *
107
+ * A rule that reads it to skip a value that it cannot know reads this; a rule that asks how the
108
+ * value is written (its quotes, its escapes) reads {@link MLAttr.isExpressionValue} instead.
109
+ *
110
+ * On a component (`elementType: "authored"`) an attribute is a prop, which is rendered where it is
111
+ * forwarded to, not here: a literal in it is not dynamic, and its `value` is the string form of the
112
+ * literal ({@link MLAttr.literal}). What the component renders is not known, and that is told by
113
+ * `MLElement#hasMutableChildren`, not by the attributes of the component.
114
+ */
115
+ get isDynamicValue(): true | undefined;
116
+ /**
117
+ * Whether this attribute is made by a pretender (`pretenders` of a configuration): it is not in the
118
+ * source, so the position of its tokens is that of the element, and a fix of a violation on it
119
+ * would rewrite the tag.
120
+ *
121
+ * An attribute that a pretender takes over from the source (`inheritAttrs`) is not.
122
+ *
123
+ * A fix of a violation that is reported on a pretended attribute is dropped (`MLRule`), and a rule
124
+ * that fixes by the range of the attributes of an element (`attr-order`) leaves them out.
125
+ */
126
+ get isPretended(): boolean;
127
+ /**
128
+ * The literal that the value of this attribute is written as, when it is nothing but a literal in a
129
+ * language that can be read as one: `{true}`, `{"x"}`, `:x="0"`, or no value at all in the
130
+ * shorthand of JSX (`<div aria-hidden>` is `true`). `null` otherwise.
131
+ *
132
+ * What a literal is rendered as depends on the framework, the element and the attribute name
133
+ * ({@link MLAttr.value}, {@link MLAttr.isAbsent}); this is what is written.
134
+ */
135
+ get literal(): LiteralExpression | null;
136
+ /**
137
+ * Returns the "string" if HTML syntax. Otherwise, returns a type in its syntax. An attribute whose value
138
+ * is a literal that is resolved is "string", whichever language it is written in.
139
+ *
140
+ * @default "string"
141
+ * @implements `@markuplint/ml-core` API: `MLAttr`
142
+ */
143
+ get valueType(): 'string' | 'number' | 'boolean' | 'code';
86
144
  /**
87
145
  * Returns the local name portion of the attribute (without namespace prefix).
88
146
  *
@@ -152,7 +210,8 @@ export declare class MLAttr<T extends RuleConfigValue, O extends PlainData = und
152
210
  * Whether the name of this attribute reaches the DOM through the DOM API (`setAttribute`) and
153
211
  * not through the HTML parser of the browser: what the parser says of its language
154
212
  * (`MLDocument#attrNameRoute`), and for a parser that says it of a dynamic attribute only
155
- * (Svelte), whether this one is dynamic.
213
+ * (Svelte), whether this one is written as code: how its name is set is a matter of how it is
214
+ * written, not of the value that results.
156
215
  */
157
216
  get isSetThroughDOMAPI(): boolean;
158
217
  /**
@@ -162,6 +221,14 @@ export declare class MLAttr<T extends RuleConfigValue, O extends PlainData = und
162
221
  * @implements `SelectorAttr#nameCaseInsensitive` of `@markuplint/selector`
163
222
  */
164
223
  get nameCaseInsensitive(): boolean;
224
+ /**
225
+ * Whether the framework does not output this attribute, so that no selector matches it
226
+ * ({@link MLAttr.isAbsent}). It is a boolean because `SelectorAttr#absent` of `@markuplint/selector`
227
+ * is the one that a selector reads.
228
+ *
229
+ * @implements `SelectorAttr#absent` of `@markuplint/selector`
230
+ */
231
+ get absent(): boolean;
165
232
  /**
166
233
  * The name that a selector is matched with.
167
234
  *