@markuplint/ml-core 4.13.2 → 5.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.ja.md +524 -0
- package/ARCHITECTURE.md +524 -0
- package/CHANGELOG.md +52 -2
- package/README.md +5 -0
- package/SKILL.md +61 -0
- package/docs/linting-pipeline.ja.md +307 -0
- package/docs/linting-pipeline.md +307 -0
- package/docs/maintenance.ja.md +210 -0
- package/docs/maintenance.md +210 -0
- package/docs/ml-dom/attr.ja.md +103 -0
- package/docs/ml-dom/attr.md +103 -0
- package/docs/ml-dom/block.ja.md +272 -0
- package/docs/ml-dom/block.md +272 -0
- package/docs/ml-dom/document.ja.md +141 -0
- package/docs/ml-dom/document.md +141 -0
- package/docs/ml-dom/element.ja.md +176 -0
- package/docs/ml-dom/element.md +176 -0
- package/docs/ml-dom/helpers.ja.md +203 -0
- package/docs/ml-dom/helpers.md +203 -0
- package/docs/ml-dom/node.ja.md +199 -0
- package/docs/ml-dom/node.md +199 -0
- package/docs/ml-dom/others.ja.md +120 -0
- package/docs/ml-dom/others.md +120 -0
- package/docs/ml-dom/overview.ja.md +102 -0
- package/docs/ml-dom/overview.md +102 -0
- package/docs/ml-dom/pretender.ja.md +269 -0
- package/docs/ml-dom/pretender.md +269 -0
- package/docs/ml-dom/rule-mapping.ja.md +371 -0
- package/docs/ml-dom/rule-mapping.md +371 -0
- package/docs/ml-dom.ja.md +18 -0
- package/docs/ml-dom.md +18 -0
- package/docs/rule-system.ja.md +287 -0
- package/docs/rule-system.md +287 -0
- package/lib/convert-ruleset.d.ts +7 -0
- package/lib/convert-ruleset.js +7 -0
- package/lib/debug.d.ts +4 -0
- package/lib/debug.js +4 -0
- package/lib/index.d.ts +4 -3
- package/lib/index.js +1 -1
- package/lib/ml-core.d.ts +37 -1
- package/lib/ml-core.js +171 -82
- package/lib/ml-dom/helper/accname.d.ts +8 -0
- package/lib/ml-dom/helper/accname.js +71 -55
- package/lib/ml-dom/helper/create-node.js +1 -0
- package/lib/ml-dom/helper/get-indent.d.ts +4 -1
- package/lib/ml-dom/helper/get-indent.js +21 -30
- package/lib/ml-dom/node/attr.d.ts +65 -4
- package/lib/ml-dom/node/attr.js +151 -53
- package/lib/ml-dom/node/block.d.ts +23 -2
- package/lib/ml-dom/node/block.js +24 -1
- package/lib/ml-dom/node/child-node.d.ts +9 -0
- package/lib/ml-dom/node/child-node.js +9 -0
- package/lib/ml-dom/node/comment.d.ts +7 -0
- package/lib/ml-dom/node/comment.js +7 -0
- package/lib/ml-dom/node/document-fragment.d.ts +8 -0
- package/lib/ml-dom/node/document-fragment.js +8 -0
- package/lib/ml-dom/node/document-type.d.ts +22 -0
- package/lib/ml-dom/node/document-type.js +25 -0
- package/lib/ml-dom/node/document.d.ts +88 -7
- package/lib/ml-dom/node/document.js +128 -32
- package/lib/ml-dom/node/dom-token-list.js +17 -30
- package/lib/ml-dom/node/element-close-tag.js +1 -0
- package/lib/ml-dom/node/element.d.ts +151 -5
- package/lib/ml-dom/node/element.js +242 -50
- package/lib/ml-dom/node/node-store.js +6 -15
- package/lib/ml-dom/node/node.d.ts +19 -1
- package/lib/ml-dom/node/node.js +175 -166
- package/lib/ml-dom/node/parent-node.js +14 -30
- package/lib/ml-dom/node/rule-mapper.js +7 -20
- package/lib/ml-dom/node/text.d.ts +19 -0
- package/lib/ml-dom/node/text.js +21 -0
- package/lib/ml-dom/node/types.d.ts +68 -0
- package/lib/ml-dom/token/token.d.ts +42 -0
- package/lib/ml-dom/token/token.js +59 -39
- package/lib/ml-rule/create-rule.d.ts +17 -1
- package/lib/ml-rule/ml-rule-context.js +7 -11
- package/lib/ml-rule/ml-rule.d.ts +66 -1
- package/lib/ml-rule/ml-rule.js +95 -25
- package/lib/ml-rule/types.d.ts +41 -0
- package/lib/plugin/plugin.d.ts +8 -0
- package/lib/plugin/plugin.js +8 -0
- package/lib/plugin/types.d.ts +21 -0
- package/lib/ruleset/index.d.ts +10 -0
- package/lib/ruleset/index.js +13 -0
- package/lib/test/index.d.ts +42 -1
- package/lib/test/index.js +39 -2
- package/lib/types.d.ts +10 -1
- package/lib/violation-collector.d.ts +33 -0
- package/lib/violation-collector.js +48 -28
- package/lib/virtual-rule.d.ts +72 -0
- package/lib/virtual-rule.js +233 -0
- package/package.json +16 -13
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
# Rule Mapping — How Rules Are Applied to Nodes
|
|
2
|
+
|
|
3
|
+
**Source:** `src/ml-dom/node/document.ts` (`_ruleMapping()`), `src/ml-dom/node/rule-mapper.ts` (`RuleMapper`)
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Rule mapping is the process of distributing rule configurations from the user's config to individual MLDOM nodes. Each node has a `rules` property (`Record<string, AnyRule>`) that stores the resolved rule configuration for every rule that applies to it.
|
|
8
|
+
|
|
9
|
+
The mapping uses three configuration layers — `rules`, `nodeRules`, and `childNodeRules` — processed in a defined order, with CSS selector specificity used to resolve conflicts.
|
|
10
|
+
|
|
11
|
+
For configuration syntax, see the [markuplint configuration documentation](https://markuplint.dev/docs/configuration).
|
|
12
|
+
|
|
13
|
+
## Architecture
|
|
14
|
+
|
|
15
|
+
### When Rule Mapping Happens
|
|
16
|
+
|
|
17
|
+
Rule mapping happens **once** during `MLDocument` construction, after pretender initialization and before any rule execution:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
MLDocument constructor
|
|
21
|
+
├── 1. Parse AST → create MLDOM nodes (nodeList)
|
|
22
|
+
├── 2. _pretending(pretenders)
|
|
23
|
+
├── 3. _ruleMapping(ruleset) ← Rule mapping
|
|
24
|
+
└── 4. (ready for rule verification)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This ordering matters:
|
|
28
|
+
|
|
29
|
+
- Pretenders must be established first, so that selector matching in rule mapping can match against pretender identities (e.g., a `nodeRules` entry targeting `button` should match `<MyButton>` pretending to be `<button>`)
|
|
30
|
+
- Rule mapping must complete before verification, so that rules can read `node.rules` during `walkOn()`
|
|
31
|
+
|
|
32
|
+
### Components
|
|
33
|
+
|
|
34
|
+
| Component | Source | Role |
|
|
35
|
+
| ---------------- | -------------------------------- | ------------------------------------------------------------------------- |
|
|
36
|
+
| `Ruleset` | `src/ruleset/index.ts` | Extracts `rules`, `nodeRules`, `childNodeRules` from user config |
|
|
37
|
+
| `RuleMapper` | `src/ml-dom/node/rule-mapper.ts` | Accumulates rule-to-node mappings with specificity, applies them to nodes |
|
|
38
|
+
| `_ruleMapping()` | `src/ml-dom/node/document.ts` | Orchestrates the three-layer processing |
|
|
39
|
+
|
|
40
|
+
## The Three Layers
|
|
41
|
+
|
|
42
|
+
### Layer 1: Global Rules (`rules`)
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"rules": {
|
|
47
|
+
"attr-duplication": true,
|
|
48
|
+
"case-sensitive-tag-name": "warning"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Global rules are applied to **every node** in the document, including the `#document` node itself. They have a fixed specificity of `[0, 0, 0]`.
|
|
54
|
+
|
|
55
|
+
**Processing:**
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// Apply to #document
|
|
59
|
+
for (const ruleName of Object.keys(ruleset.rules)) {
|
|
60
|
+
ruleMapper.set(document, ruleName, {
|
|
61
|
+
from: 'rules',
|
|
62
|
+
specificity: [0, 0, 0],
|
|
63
|
+
rule,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Apply to every node in nodeList
|
|
68
|
+
for (const node of document.nodeList) {
|
|
69
|
+
for (const ruleName of Object.keys(ruleset.rules)) {
|
|
70
|
+
ruleMapper.set(node, ruleName, {
|
|
71
|
+
from: 'rules',
|
|
72
|
+
specificity: [0, 0, 0],
|
|
73
|
+
rule,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Layer 2: Node Rules (`nodeRules`)
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"nodeRules": [
|
|
84
|
+
{
|
|
85
|
+
"selector": "img",
|
|
86
|
+
"rules": {
|
|
87
|
+
"required-attr": { "value": "alt" }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Node rules override global rules for **elements that match the selector**. Only `ELEMENT_NODE` and `TEXT_NODE` are considered; text nodes cannot be selector targets, so only elements are actually matched.
|
|
95
|
+
|
|
96
|
+
**Processing:**
|
|
97
|
+
|
|
98
|
+
1. For each `nodeRule` entry, check if the current element matches via `matchMLSelector(selector)`
|
|
99
|
+
2. If matched, for each rule in the entry:
|
|
100
|
+
- `exchangeValueOnRule(rule, matches.data)` — render Mustache template variables from regex selector captures
|
|
101
|
+
- `mergeRule(globalRule, convertedRule)` — merge with the global rule config (see [Merging](#merging-with-global-rules))
|
|
102
|
+
- `ruleMapper.set(node, ruleName, { from: 'nodeRules', specificity: matches.specificity, rule: mergedRule })`
|
|
103
|
+
|
|
104
|
+
The specificity comes from the CSS selector used to match the element.
|
|
105
|
+
|
|
106
|
+
### Layer 3: Child Node Rules (`childNodeRules`)
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"childNodeRules": [
|
|
111
|
+
{
|
|
112
|
+
"selector": "table",
|
|
113
|
+
"inheritance": true,
|
|
114
|
+
"rules": {
|
|
115
|
+
"class-naming": "/^table-/"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Child node rules apply rules to **children (or descendants)** of matched elements. The selector matches the **parent**, and the rules are distributed to its children.
|
|
123
|
+
|
|
124
|
+
**Processing:**
|
|
125
|
+
|
|
126
|
+
1. For each `childNodeRule` entry, check if the current element matches via `matchMLSelector(selector)`
|
|
127
|
+
2. If matched, determine the target nodes:
|
|
128
|
+
- `inheritance: true` → all **descendants** (collected via `syncWalk`)
|
|
129
|
+
- `inheritance: false` (default) → only **direct children** (`childNodes`)
|
|
130
|
+
3. For each rule in the entry, apply the merged rule to every target node
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
const targetDescendants = nodeRule.inheritance ? descendants : children;
|
|
134
|
+
|
|
135
|
+
for (const descendant of targetDescendants) {
|
|
136
|
+
ruleMapper.set(descendant, ruleName, {
|
|
137
|
+
from: 'childNodeRules',
|
|
138
|
+
specificity: matches.specificity,
|
|
139
|
+
rule: mergedRule,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Note: the specificity is that of the **parent's** selector match, not the child's.
|
|
145
|
+
|
|
146
|
+
## Processing Order
|
|
147
|
+
|
|
148
|
+
The three layers are processed in a specific order within `_ruleMapping()`:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
For the #document node:
|
|
152
|
+
1. Apply all global rules (specificity [0,0,0])
|
|
153
|
+
|
|
154
|
+
For each node in nodeList:
|
|
155
|
+
2. Apply all global rules (specificity [0,0,0])
|
|
156
|
+
3. Apply matching nodeRules (selector specificity)
|
|
157
|
+
4. Apply matching childNodeRules (selector specificity of parent)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Because `RuleMapper.set()` resolves conflicts by specificity, the processing order within the same specificity level matters:
|
|
161
|
+
|
|
162
|
+
- **Same specificity**: later `set()` calls overwrite earlier ones (last-write-wins)
|
|
163
|
+
- **Higher specificity**: always wins regardless of order
|
|
164
|
+
- **Lower specificity**: silently skipped
|
|
165
|
+
|
|
166
|
+
This means:
|
|
167
|
+
|
|
168
|
+
| Scenario | Winner |
|
|
169
|
+
| ------------------------------------------------------------------------ | ---------------------------------------------------------- |
|
|
170
|
+
| `rules` vs `nodeRules` (any selector) | `nodeRules` (selector specificity `≥ [0,0,1]` > `[0,0,0]`) |
|
|
171
|
+
| `rules` vs `childNodeRules` (any selector) | `childNodeRules` (same reasoning) |
|
|
172
|
+
| `nodeRules[0]` vs `nodeRules[1]` (same specificity) | `nodeRules[1]` (later in array) |
|
|
173
|
+
| `nodeRules` (lower specificity) vs `childNodeRules` (higher specificity) | `childNodeRules` (higher specificity wins) |
|
|
174
|
+
| `nodeRules` (higher specificity) vs `childNodeRules` (lower specificity) | `nodeRules` (higher specificity wins) |
|
|
175
|
+
|
|
176
|
+
## Specificity
|
|
177
|
+
|
|
178
|
+
### What is Specificity?
|
|
179
|
+
|
|
180
|
+
Specificity is a three-element tuple `[a, b, c]` based on the CSS Selectors specification:
|
|
181
|
+
|
|
182
|
+
| Component | Counts | Examples |
|
|
183
|
+
| --------- | ---------------------------------------------------- | --------------------------------------------------- |
|
|
184
|
+
| `a` | ID selectors | `#main` → `[1, 0, 0]` |
|
|
185
|
+
| `b` | Class selectors, attribute selectors, pseudo-classes | `.foo` → `[0, 1, 0]`, `[type="text"]` → `[0, 1, 0]` |
|
|
186
|
+
| `c` | Type selectors, pseudo-elements | `div` → `[0, 0, 1]`, `img` → `[0, 0, 1]` |
|
|
187
|
+
|
|
188
|
+
### How Specificity is Compared
|
|
189
|
+
|
|
190
|
+
`compareSpecificity(a, b)` from `@markuplint/selector` performs a lexicographic comparison:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
function compareSpecificity(a: Specificity, b: Specificity): -1 | 0 | 1 {
|
|
194
|
+
// Compare a[0] vs b[0], then a[1] vs b[1], then a[2] vs b[2]
|
|
195
|
+
// Returns: -1 (a < b), 0 (equal), 1 (a > b)
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### How `RuleMapper.set()` Uses Specificity
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
set(node, ruleName, rule: MappingLayer) {
|
|
203
|
+
const currentRule = rules[ruleName];
|
|
204
|
+
if (currentRule) {
|
|
205
|
+
const order = compareSpecificity(currentRule.specificity, rule.specificity);
|
|
206
|
+
if (order === 1) {
|
|
207
|
+
return; // Current has higher specificity → skip new rule
|
|
208
|
+
}
|
|
209
|
+
// order === 0 or -1 → overwrite with new rule
|
|
210
|
+
}
|
|
211
|
+
rules[ruleName] = rule;
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
- `order === 1` (current > new): **skip** — existing higher-specificity rule is preserved
|
|
216
|
+
- `order === 0` (equal): **overwrite** — later mapping wins
|
|
217
|
+
- `order === -1` (current < new): **overwrite** — higher-specificity rule wins
|
|
218
|
+
|
|
219
|
+
### Specificity Examples
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"rules": {
|
|
224
|
+
"class-naming": "/^prefix-/"
|
|
225
|
+
},
|
|
226
|
+
"nodeRules": [
|
|
227
|
+
{
|
|
228
|
+
"selector": "div",
|
|
229
|
+
"rules": { "class-naming": "/^div-/" }
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"selector": "div.special",
|
|
233
|
+
"rules": { "class-naming": "/^special-/" }
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"selector": "#main",
|
|
237
|
+
"rules": { "class-naming": "/^main-/" }
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
For `<div id="main" class="special">`:
|
|
244
|
+
|
|
245
|
+
| Source | Selector | Specificity | `class-naming` value |
|
|
246
|
+
| -------------- | ------------- | ----------- | -------------------- |
|
|
247
|
+
| `rules` | (global) | `[0, 0, 0]` | `/^prefix-/` |
|
|
248
|
+
| `nodeRules[0]` | `div` | `[0, 0, 1]` | `/^div-/` |
|
|
249
|
+
| `nodeRules[1]` | `div.special` | `[0, 1, 1]` | `/^special-/` |
|
|
250
|
+
| `nodeRules[2]` | `#main` | `[1, 0, 0]` | `/^main-/` |
|
|
251
|
+
|
|
252
|
+
Processing order: `rules` → `nodeRules[0]` → `nodeRules[1]` → `nodeRules[2]`
|
|
253
|
+
|
|
254
|
+
Result: `class-naming` = `/^main-/` (specificity `[1, 0, 0]` is highest)
|
|
255
|
+
|
|
256
|
+
## Merging with Global Rules
|
|
257
|
+
|
|
258
|
+
When a `nodeRules` or `childNodeRules` entry specifies a rule, the value is **merged** with the global rule configuration rather than simply replacing it:
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
const globalRule = ruleset.rules[ruleName];
|
|
262
|
+
const mergedRule = globalRule == null ? convertedRule : mergeRule(globalRule, convertedRule);
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
`mergeRule(a, b)` (from `@markuplint/ml-config`) applies right-side precedence:
|
|
266
|
+
|
|
267
|
+
| Scenario | Result |
|
|
268
|
+
| ------------------------ | -------------------------------------------------------------------------------------------------- |
|
|
269
|
+
| `b` is `false` | Rule is disabled (returns `false`) |
|
|
270
|
+
| `b` is a primitive value | Replaces `a`'s value |
|
|
271
|
+
| Both are objects | Properties from `b` override `a`; `severity`, `value`, `options`, `reason` are individually merged |
|
|
272
|
+
| `b.options` exists | Merged with `a.options` (objects are spread, arrays are concatenated) |
|
|
273
|
+
| `b.value` is not set | Inherits from `a.value` |
|
|
274
|
+
|
|
275
|
+
### Merging Example
|
|
276
|
+
|
|
277
|
+
```json
|
|
278
|
+
{
|
|
279
|
+
"rules": {
|
|
280
|
+
"my-rule": {
|
|
281
|
+
"severity": "error",
|
|
282
|
+
"value": "strict",
|
|
283
|
+
"options": { "allow": ["a", "b"] }
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
"nodeRules": [
|
|
287
|
+
{
|
|
288
|
+
"selector": "nav",
|
|
289
|
+
"rules": {
|
|
290
|
+
"my-rule": {
|
|
291
|
+
"options": { "allow": ["c"] }
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
For `<nav>`, `my-rule` is resolved as:
|
|
300
|
+
|
|
301
|
+
```json
|
|
302
|
+
{
|
|
303
|
+
"severity": "error",
|
|
304
|
+
"value": "strict",
|
|
305
|
+
"options": { "allow": ["c"] }
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
`severity` and `value` are inherited from the global config. `options.allow` is overwritten by the `nodeRules` entry (object spread, not array concatenation at the `options` level — `options` itself is spread, not individual sub-properties).
|
|
310
|
+
|
|
311
|
+
## Regex Selector and Template Variables
|
|
312
|
+
|
|
313
|
+
`nodeRules` and `childNodeRules` support regex selectors, which can capture data from element attributes. Captured values are available as Mustache template variables in the rule configuration:
|
|
314
|
+
|
|
315
|
+
```json
|
|
316
|
+
{
|
|
317
|
+
"nodeRules": [
|
|
318
|
+
{
|
|
319
|
+
"regexSelector": {
|
|
320
|
+
"attrName": "data-prefix",
|
|
321
|
+
"attrValue": "/^(?<prefix>.+)$/"
|
|
322
|
+
},
|
|
323
|
+
"rules": {
|
|
324
|
+
"class-naming": "/^{{ prefix }}-/"
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
]
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
`exchangeValueOnRule(rule, matches.data)` renders these templates before merging.
|
|
332
|
+
|
|
333
|
+
## Data Flow Diagram
|
|
334
|
+
|
|
335
|
+
```
|
|
336
|
+
Config
|
|
337
|
+
│
|
|
338
|
+
▼
|
|
339
|
+
Ruleset (rules, nodeRules, childNodeRules)
|
|
340
|
+
│
|
|
341
|
+
▼
|
|
342
|
+
_ruleMapping(ruleset)
|
|
343
|
+
│
|
|
344
|
+
├─── Layer 1: Global rules
|
|
345
|
+
│ └─ For every node (including #document):
|
|
346
|
+
│ ruleMapper.set(node, name, { from: 'rules', specificity: [0,0,0], rule })
|
|
347
|
+
│
|
|
348
|
+
├─── Layer 2: Node rules
|
|
349
|
+
│ └─ For each nodeRule entry:
|
|
350
|
+
│ └─ For each ELEMENT_NODE matching the selector:
|
|
351
|
+
│ ├─ exchangeValueOnRule (template rendering)
|
|
352
|
+
│ ├─ mergeRule (merge with global)
|
|
353
|
+
│ └─ ruleMapper.set(node, name, { from: 'nodeRules', specificity, rule })
|
|
354
|
+
│
|
|
355
|
+
└─── Layer 3: Child node rules
|
|
356
|
+
└─ For each childNodeRule entry:
|
|
357
|
+
└─ For each ELEMENT_NODE matching the selector:
|
|
358
|
+
└─ For each child (or descendant if inheritance: true):
|
|
359
|
+
├─ exchangeValueOnRule (template rendering)
|
|
360
|
+
├─ mergeRule (merge with global)
|
|
361
|
+
└─ ruleMapper.set(child, name, { from: 'childNodeRules', specificity, rule })
|
|
362
|
+
│
|
|
363
|
+
▼
|
|
364
|
+
ruleMapper.apply()
|
|
365
|
+
│
|
|
366
|
+
└─ For each node in the map:
|
|
367
|
+
node.rules[ruleName] = rule
|
|
368
|
+
│
|
|
369
|
+
▼
|
|
370
|
+
node.rules populated → ready for walkOn() verification
|
|
371
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# MLDOM リファレンス
|
|
2
|
+
|
|
3
|
+
`@markuplint/ml-core` の MLDOM サブシステムの詳細リファレンスです。
|
|
4
|
+
|
|
5
|
+
## ドキュメント
|
|
6
|
+
|
|
7
|
+
| ドキュメント | 説明 |
|
|
8
|
+
| -------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
9
|
+
| [概要](./ml-dom/overview.ja.md) | 概要、クラス階層、MLToken |
|
|
10
|
+
| [MLNode](./ml-dom/node.ja.md) | MLNode 抽象基底、MLParentNode |
|
|
11
|
+
| [MLDocument](./ml-dom/document.ja.md) | ドキュメントルートノード |
|
|
12
|
+
| [MLElement](./ml-dom/element.ja.md) | 要素ノード(セレクタマッチング、属性、省略要素) |
|
|
13
|
+
| [Pretender システム](./ml-dom/pretender.ja.md) | コンポーネントリンティング用の仮想要素マッピング(初期化、プロパティ委譲、アクセシブル名) |
|
|
14
|
+
| [MLAttr](./ml-dom/attr.ja.md) | 属性ノード(トークン分解、スプレッド属性) |
|
|
15
|
+
| [MLBlock](./ml-dom/block.ja.md) | プリプロセッサブロックノード(透過性、条件分岐子ノード、コンテンツモデル検証) |
|
|
16
|
+
| [ルールマッピング](./ml-dom/rule-mapping.ja.md) | ルールがノードに適用される仕組み(3層処理、詳細度解決) |
|
|
17
|
+
| [その他のノード型](./ml-dom/others.ja.md) | MLCharacterData, MLText, MLComment, MLDocumentType, MLElementCloseTag, MLDocumentFragment |
|
|
18
|
+
| [ヘルパーとユーティリティ](./ml-dom/helpers.ja.md) | ヘルパー関数、補助クラス、型ユーティリティ |
|
package/docs/ml-dom.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# MLDOM Reference
|
|
2
|
+
|
|
3
|
+
Detailed reference for the MLDOM subsystem in `@markuplint/ml-core`.
|
|
4
|
+
|
|
5
|
+
## Documents
|
|
6
|
+
|
|
7
|
+
| Document | Description |
|
|
8
|
+
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------- |
|
|
9
|
+
| [Overview](./ml-dom/overview.md) | Overview, class hierarchy, MLToken |
|
|
10
|
+
| [MLNode](./ml-dom/node.md) | MLNode abstract base, MLParentNode |
|
|
11
|
+
| [MLDocument](./ml-dom/document.md) | Document root node |
|
|
12
|
+
| [MLElement](./ml-dom/element.md) | Element node (selector matching, attributes, omitted elements) |
|
|
13
|
+
| [Pretender System](./ml-dom/pretender.md) | Virtual element mapping for component linting (initialization, property delegation, accessible name) |
|
|
14
|
+
| [MLAttr](./ml-dom/attr.md) | Attribute node (token decomposition, spread attributes) |
|
|
15
|
+
| [MLBlock](./ml-dom/block.md) | Preprocessor block node (transparency, conditional child nodes, content model validation) |
|
|
16
|
+
| [Rule Mapping](./ml-dom/rule-mapping.md) | How rules are applied to nodes (three-layer processing, specificity resolution) |
|
|
17
|
+
| [Other Node Types](./ml-dom/others.md) | MLCharacterData, MLText, MLComment, MLDocumentType, MLElementCloseTag, MLDocumentFragment |
|
|
18
|
+
| [Helpers & Utilities](./ml-dom/helpers.md) | Helper functions, supplementary classes, type utilities |
|