@markuplint/ml-core 2.0.0-dev.20211213.2 → 2.0.0-dev.20220107.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.
@@ -1,20 +1,51 @@
1
1
  "use strict";
2
2
  var _Selector_ruleset, _Ruleset_selectorGroup, _StructuredSelector_edge, _StructuredSelector_selector, _SelectorTarget_isAdded, _SelectorTarget_combinatedFrom;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.createSelector = void 0;
4
+ exports.compareSpecificity = exports.createSelector = void 0;
5
5
  const tslib_1 = require("tslib");
6
6
  const postcss_selector_parser_1 = (0, tslib_1.__importDefault)(require("postcss-selector-parser"));
7
+ const debug_1 = require("../../debug");
8
+ const log = debug_1.log.extend('selector');
9
+ const resLog = log.extend('result');
7
10
  function createSelector(selector) {
8
11
  return new Selector(selector);
9
12
  }
10
13
  exports.createSelector = createSelector;
14
+ function compareSpecificity(a, b) {
15
+ if (a[0] < b[0]) {
16
+ return -1;
17
+ }
18
+ else if (a[0] > b[0]) {
19
+ return 1;
20
+ }
21
+ else if (a[1] < b[1]) {
22
+ return -1;
23
+ }
24
+ else if (a[1] > b[1]) {
25
+ return 1;
26
+ }
27
+ else if (a[2] < b[2]) {
28
+ return -1;
29
+ }
30
+ else if (a[2] > b[2]) {
31
+ return 1;
32
+ }
33
+ return 0;
34
+ }
35
+ exports.compareSpecificity = compareSpecificity;
11
36
  class Selector {
12
37
  constructor(selector) {
13
38
  _Selector_ruleset.set(this, void 0);
14
39
  (0, tslib_1.__classPrivateFieldSet)(this, _Selector_ruleset, Ruleset.parse(selector), "f");
15
40
  }
16
41
  match(el, caller = el) {
17
- return (0, tslib_1.__classPrivateFieldGet)(this, _Selector_ruleset, "f").match(el, caller);
42
+ const results = (0, tslib_1.__classPrivateFieldGet)(this, _Selector_ruleset, "f").match(el, caller);
43
+ for (const result of results) {
44
+ if (result.matched) {
45
+ return result.specificity;
46
+ }
47
+ }
48
+ return false;
18
49
  }
19
50
  }
20
51
  _Selector_ruleset = new WeakMap();
@@ -31,7 +62,12 @@ class Ruleset {
31
62
  return new Ruleset(selectors);
32
63
  }
33
64
  match(el, caller) {
34
- return (0, tslib_1.__classPrivateFieldGet)(this, _Ruleset_selectorGroup, "f").some(selector => selector.match(el, caller));
65
+ log('%s', el.raw);
66
+ return (0, tslib_1.__classPrivateFieldGet)(this, _Ruleset_selectorGroup, "f").map(selector => {
67
+ const res = selector.match(el, caller);
68
+ resLog('%s => %o', selector.selector, res);
69
+ return res;
70
+ });
35
71
  }
36
72
  }
37
73
  _Ruleset_selectorGroup = new WeakMap();
@@ -65,6 +101,9 @@ class StructuredSelector {
65
101
  }
66
102
  });
67
103
  }
104
+ get selector() {
105
+ return (0, tslib_1.__classPrivateFieldGet)(this, _StructuredSelector_selector, "f").nodes.join('');
106
+ }
68
107
  match(el, caller) {
69
108
  return (0, tslib_1.__classPrivateFieldGet)(this, _StructuredSelector_edge, "f").match(el, caller);
70
109
  }
@@ -81,60 +120,125 @@ class SelectorTarget {
81
120
  _SelectorTarget_combinatedFrom.set(this, null);
82
121
  }
83
122
  match(el, caller) {
84
- const matched = this._matchWithoutCombinateChecking(el, caller);
85
- if (!matched) {
86
- return false;
123
+ const unitCheck = this._matchWithoutCombinateChecking(el, caller);
124
+ if (!unitCheck.matched) {
125
+ return unitCheck;
87
126
  }
88
127
  if (!(0, tslib_1.__classPrivateFieldGet)(this, _SelectorTarget_combinatedFrom, "f")) {
89
- return true;
128
+ return unitCheck;
90
129
  }
91
130
  const { target, combinator } = (0, tslib_1.__classPrivateFieldGet)(this, _SelectorTarget_combinatedFrom, "f");
92
131
  switch (combinator.value) {
93
132
  // Descendant combinator
94
133
  case ' ': {
95
- let ancestor = el.parentNode;
134
+ let ancestor = el.getParentElement();
135
+ let matched = false;
136
+ let specificity;
96
137
  while (ancestor) {
97
- const ancestorMatched = target._matchWithoutCombinateChecking(ancestor, caller);
98
- if (ancestorMatched) {
99
- return target.match(ancestor, caller);
138
+ const res = target.match(ancestor, caller);
139
+ if (!specificity) {
140
+ specificity = [
141
+ unitCheck.specificity[0] + res.specificity[0],
142
+ unitCheck.specificity[1] + res.specificity[1],
143
+ unitCheck.specificity[2] + res.specificity[2],
144
+ ];
145
+ }
146
+ if (res.matched) {
147
+ matched = true;
100
148
  }
101
- ancestor = ancestor.parentNode;
149
+ ancestor = ancestor.getParentElement();
150
+ }
151
+ if (!specificity) {
152
+ const res = target.match(el, caller);
153
+ specificity = [
154
+ unitCheck.specificity[0] + res.specificity[0],
155
+ unitCheck.specificity[1] + res.specificity[1],
156
+ unitCheck.specificity[2] + res.specificity[2],
157
+ ];
102
158
  }
103
- return false;
159
+ return {
160
+ specificity,
161
+ matched,
162
+ };
104
163
  }
105
164
  // Child combinator
106
165
  case '>': {
107
- if (!el.parentNode) {
108
- return false;
166
+ let matched;
167
+ const specificity = unitCheck.specificity;
168
+ const parentNode = el.getParentElement();
169
+ if (parentNode) {
170
+ const res = target.match(parentNode, caller);
171
+ specificity[0] += res.specificity[0];
172
+ specificity[1] += res.specificity[1];
173
+ specificity[2] += res.specificity[2];
174
+ matched = res.matched;
109
175
  }
110
- const parentMatched = target._matchWithoutCombinateChecking(el.parentNode, caller);
111
- if (parentMatched) {
112
- return target.match(el.parentNode, caller);
176
+ else {
177
+ const res = target.match(el, caller);
178
+ specificity[0] += res.specificity[0];
179
+ specificity[1] += res.specificity[1];
180
+ specificity[2] += res.specificity[2];
181
+ matched = false;
113
182
  }
114
- return false;
183
+ return {
184
+ specificity,
185
+ matched,
186
+ };
115
187
  }
116
188
  // Next-sibling combinator
117
189
  case '+': {
118
- if (!el.previousElementSibling) {
119
- return false;
190
+ let matched;
191
+ const specificity = unitCheck.specificity;
192
+ if (el.previousElementSibling) {
193
+ const res = target.match(el.previousElementSibling, caller);
194
+ specificity[0] += res.specificity[0];
195
+ specificity[1] += res.specificity[1];
196
+ specificity[2] += res.specificity[2];
197
+ matched = res.matched;
120
198
  }
121
- const prevMatched = target._matchWithoutCombinateChecking(el.previousElementSibling, caller);
122
- if (prevMatched) {
123
- return target.match(el.previousElementSibling, caller);
199
+ else {
200
+ const res = target.match(el, caller);
201
+ specificity[0] += res.specificity[0];
202
+ specificity[1] += res.specificity[1];
203
+ specificity[2] += res.specificity[2];
204
+ matched = false;
124
205
  }
125
- return false;
206
+ return {
207
+ specificity,
208
+ matched,
209
+ };
126
210
  }
127
- // Subsequent-sibling combinator
211
+ // // Subsequent-sibling combinator
128
212
  case '~': {
129
213
  let prev = el.previousElementSibling;
214
+ let matched = false;
215
+ let specificity;
130
216
  while (prev) {
131
- const prevMatched = target._matchWithoutCombinateChecking(prev, caller);
132
- if (prevMatched) {
133
- return target.match(prev, caller);
217
+ const res = target.match(prev, caller);
218
+ if (!specificity) {
219
+ specificity = [
220
+ unitCheck.specificity[0] + res.specificity[0],
221
+ unitCheck.specificity[1] + res.specificity[1],
222
+ unitCheck.specificity[2] + res.specificity[2],
223
+ ];
224
+ }
225
+ if (res.matched) {
226
+ matched = true;
134
227
  }
135
228
  prev = prev.previousElementSibling;
136
229
  }
137
- return false;
230
+ if (!specificity) {
231
+ const res = target.match(el, caller);
232
+ specificity = [
233
+ unitCheck.specificity[0] + res.specificity[0],
234
+ unitCheck.specificity[1] + res.specificity[1],
235
+ unitCheck.specificity[2] + res.specificity[2],
236
+ ];
237
+ }
238
+ return {
239
+ specificity,
240
+ matched,
241
+ };
138
242
  }
139
243
  // Column combinator
140
244
  case '||': {
@@ -146,27 +250,42 @@ class SelectorTarget {
146
250
  }
147
251
  }
148
252
  _matchWithoutCombinateChecking(el, caller) {
149
- if (!(0, tslib_1.__classPrivateFieldGet)(this, _SelectorTarget_isAdded, "f")) {
150
- return isScope(el, caller);
151
- }
152
- if (this.tag && this.tag.type === 'tag') {
153
- if (this.tag.value.toLowerCase() !== el.nodeName.toLowerCase()) {
154
- return false;
155
- }
253
+ const specificity = [0, 0, 0];
254
+ let matched = true;
255
+ if (!(0, tslib_1.__classPrivateFieldGet)(this, _SelectorTarget_isAdded, "f") && !isScope(el, caller)) {
256
+ matched = false;
156
257
  }
157
258
  if (!this.id.every(id => id.value === el.id)) {
158
- return false;
259
+ matched = false;
159
260
  }
261
+ specificity[0] += this.id.length;
160
262
  if (!this.class.every(className => el.classList.includes(className.value))) {
161
- return false;
263
+ matched = false;
162
264
  }
265
+ specificity[1] += this.class.length;
163
266
  if (!this.attr.every(attr => attrMatch(attr, el))) {
164
- return false;
267
+ matched = false;
165
268
  }
166
- if (!this.pseudo.every(pseudo => pseudoMatch(pseudo, el, caller))) {
167
- return false;
269
+ specificity[1] += this.attr.length;
270
+ for (const pseudo of this.pseudo) {
271
+ const pseudoRes = pseudoMatch(pseudo, el, caller);
272
+ specificity[0] += pseudoRes.specificity[0];
273
+ specificity[1] += pseudoRes.specificity[1];
274
+ specificity[2] += pseudoRes.specificity[2];
275
+ if (!pseudoRes.matched) {
276
+ matched = false;
277
+ }
168
278
  }
169
- return true;
279
+ if (this.tag && this.tag.type === 'tag') {
280
+ specificity[2] += 1;
281
+ if (this.tag.value.toLowerCase() !== el.nodeName.toLowerCase()) {
282
+ matched = false;
283
+ }
284
+ }
285
+ return {
286
+ specificity,
287
+ matched,
288
+ };
170
289
  }
171
290
  add(selector) {
172
291
  (0, tslib_1.__classPrivateFieldSet)(this, _SelectorTarget_isAdded, true, "f");
@@ -260,51 +379,87 @@ function pseudoMatch(pseudo, el, caller) {
260
379
  */
261
380
  case ':closest': {
262
381
  const ruleset = new Ruleset(pseudo.nodes);
263
- let parent = el.parentNode;
382
+ const specificity = getSpecificity(ruleset.match(el, caller));
383
+ let parent = el.getParentElement();
264
384
  while (parent) {
265
- if (ruleset.match(parent, caller)) {
266
- return true;
385
+ if (ruleset.match(parent, caller).some(r => r.matched)) {
386
+ return {
387
+ specificity,
388
+ matched: true,
389
+ };
267
390
  }
268
- parent = parent.parentNode;
391
+ parent = parent.getParentElement();
269
392
  }
270
- return false;
393
+ return {
394
+ specificity,
395
+ matched: false,
396
+ };
271
397
  }
272
398
  /**
273
399
  * Below, Selector Level 4
274
400
  */
275
401
  case ':not': {
276
402
  const ruleset = new Ruleset(pseudo.nodes);
277
- if (ruleset.match(el, caller)) {
278
- return false;
279
- }
280
- break;
403
+ const resList = ruleset.match(el, caller);
404
+ const specificity = getSpecificity(resList);
405
+ const matched = resList.every(r => !r.matched);
406
+ return {
407
+ specificity,
408
+ matched,
409
+ };
281
410
  }
282
411
  case ':is': {
283
412
  const ruleset = new Ruleset(pseudo.nodes);
284
- if (!ruleset.match(el, caller)) {
285
- return false;
286
- }
287
- break;
413
+ const resList = ruleset.match(el, caller);
414
+ const specificity = getSpecificity(resList);
415
+ const matched = resList.some(r => r.matched);
416
+ return {
417
+ specificity,
418
+ matched,
419
+ };
288
420
  }
289
421
  case ':has': {
290
422
  const ruleset = new Ruleset(pseudo.nodes);
423
+ const specificity = getSpecificity(ruleset.match(el, caller));
291
424
  const descendants = getDescendants(el);
292
- if (!descendants.some(desc => ruleset.match(desc, caller))) {
293
- return false;
294
- }
295
- break;
425
+ const matched = descendants.some(desc => ruleset.match(desc, caller).some(m => m.matched));
426
+ return {
427
+ specificity,
428
+ matched,
429
+ };
430
+ }
431
+ case ':where': {
432
+ const ruleset = new Ruleset(pseudo.nodes);
433
+ const resList = ruleset.match(el, caller);
434
+ const matched = resList.some(r => r.matched);
435
+ return {
436
+ specificity: [0, 0, 0],
437
+ matched,
438
+ };
296
439
  }
297
440
  case ':scope': {
298
441
  if (!isScope(el, caller)) {
299
- return false;
442
+ return {
443
+ specificity: [0, 1, 0],
444
+ matched: false,
445
+ };
300
446
  }
301
- break;
447
+ return {
448
+ specificity: [0, 1, 0],
449
+ matched: true,
450
+ };
302
451
  }
303
452
  case ':root': {
304
453
  if (!(!el.isInFragmentDocument && el.parentNode === null)) {
305
- return false;
454
+ return {
455
+ specificity: [0, 1, 0],
456
+ matched: false,
457
+ };
306
458
  }
307
- break;
459
+ return {
460
+ specificity: [0, 1, 0],
461
+ matched: true,
462
+ };
308
463
  }
309
464
  case ':enable':
310
465
  case ':disable':
@@ -337,7 +492,6 @@ function pseudoMatch(pseudo, el, caller) {
337
492
  case ':nth-col': {
338
493
  throw new Error(`Unsupported pseudo ${pseudo.toString()} selector yet. If you want it, please request it as the issue (https://github.com/markuplint/markuplint/issues/new).`);
339
494
  }
340
- case ':where':
341
495
  case ':dir':
342
496
  case ':lang':
343
497
  case ':any-link':
@@ -360,7 +514,6 @@ function pseudoMatch(pseudo, el, caller) {
360
514
  throw new Error(`Unsupported pseudo ${pseudo.toString()} selector.`);
361
515
  }
362
516
  }
363
- return true;
364
517
  }
365
518
  function isScope(el, caller) {
366
519
  return el.uuid === (caller === null || caller === void 0 ? void 0 : caller.uuid) || (!el.isInFragmentDocument && el.parentNode === null);
@@ -368,3 +521,21 @@ function isScope(el, caller) {
368
521
  function getDescendants(el, includeSelf = false) {
369
522
  return [...el.children.map(child => getDescendants(child, true)).flat(), ...(includeSelf ? [el] : [])];
370
523
  }
524
+ function getSpecificity(result) {
525
+ let specificity;
526
+ for (const res of result) {
527
+ if (specificity) {
528
+ const order = compareSpecificity(specificity, res.specificity);
529
+ if (order === -1) {
530
+ specificity = res.specificity;
531
+ }
532
+ }
533
+ else {
534
+ specificity = res.specificity;
535
+ }
536
+ }
537
+ if (!specificity) {
538
+ throw new Error('Result is empty');
539
+ }
540
+ return specificity;
541
+ }
@@ -1,9 +1,10 @@
1
+ import type { Specificity } from './helper/selector';
1
2
  import type { AnonymousNode } from './types';
2
3
  import type { AnyRule } from '@markuplint/ml-config';
3
4
  declare type RuleType = 'rules' | 'nodeRules' | 'childNodeRules';
4
5
  declare type MappingLayer = {
5
6
  from: RuleType;
6
- index: number;
7
+ specificity: Specificity;
7
8
  rule: AnyRule;
8
9
  };
9
10
  export declare class RuleMapper<N extends AnonymousNode<any, any> = AnonymousNode<any, any>> {
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.RuleMapper = void 0;
5
5
  const tslib_1 = require("tslib");
6
6
  const debug_1 = require("../debug");
7
+ const selector_1 = require("./helper/selector");
7
8
  const ruleMapperLog = debug_1.log.extend('rule-mapper');
8
9
  const ruleMapperNodeLog = ruleMapperLog.extend('node');
9
10
  const ruleMapperNodeRuleLog = ruleMapperNodeLog.extend('rule');
@@ -20,13 +21,12 @@ class RuleMapper {
20
21
  const rules = (0, tslib_1.__classPrivateFieldGet)(this, _RuleMapper_ruleMap, "f").get(node.uuid) || {};
21
22
  const currentRule = rules[ruleName];
22
23
  if (currentRule) {
23
- if (currentRule.index <= rule.index) {
24
- ruleMapperLog('Unset %o from %s', currentRule, node);
25
- }
26
- else {
27
- ruleMapperLog("Don't set %o (%d vs %d)", rule, currentRule.index, rule.index);
24
+ const order = (0, selector_1.compareSpecificity)(currentRule.specificity, rule.specificity);
25
+ if (order === 1) {
26
+ ruleMapperLog("Don't set %o ([%s] vs [%s])", rule, currentRule.specificity, rule.specificity);
28
27
  return;
29
28
  }
29
+ ruleMapperLog('Unset %o from %s', currentRule, node);
30
30
  }
31
31
  rules[ruleName] = rule;
32
32
  (0, tslib_1.__classPrivateFieldGet)(this, _RuleMapper_ruleMap, "f").set(node.uuid, rules);
@@ -42,13 +42,13 @@ class RuleMapper {
42
42
  if (!rules) {
43
43
  return;
44
44
  }
45
- const shash = node.type === 'ElementCloseTag' ? '/' : '';
45
+ const slash = node.type === 'ElementCloseTag' ? '/' : '';
46
46
  const nodeName = 'nodeName' in node ? node.nodeName : `#${node.type}`;
47
- ruleMapperNodeLog('<%s%s>', shash, nodeName);
47
+ ruleMapperNodeLog('<%s%s>', slash, nodeName);
48
48
  Object.keys(rules).forEach(ruleName => {
49
49
  const rule = rules[ruleName];
50
50
  node.rules[ruleName] = rule.rule;
51
- ruleMapperNodeRuleLog('[from: %s(%d)] %s: %o', rule.from, rule.index, ruleName, rule.rule);
51
+ ruleMapperNodeRuleLog('[from: %s(%s)] %s: %o', rule.from, rule.specificity, ruleName, rule.rule);
52
52
  });
53
53
  });
54
54
  }
@@ -40,4 +40,5 @@ export default abstract class MLDOMAbstractElement<T extends RuleConfigValue, O
40
40
  */
41
41
  hasMutableChildren(): boolean;
42
42
  isDescendantByUUIDList(uuidList: string[]): boolean;
43
+ toNormalizeString(): string;
43
44
  }
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- var _MLDOMAbstractElement_fixedNodeName;
2
+ var _MLDOMAbstractElement_fixedNodeName, _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache, _MLDOMAbstractElement_normalizedString;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const tslib_1 = require("tslib");
5
5
  const ml_spec_1 = require("@markuplint/ml-spec");
@@ -15,6 +15,8 @@ class MLDOMAbstractElement extends node_1.default {
15
15
  this.childModels = new Set();
16
16
  this.descendantModels = new Set();
17
17
  _MLDOMAbstractElement_fixedNodeName.set(this, void 0);
18
+ _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache.set(this, null);
19
+ _MLDOMAbstractElement_normalizedString.set(this, null);
18
20
  this.nodeName = astNode.nodeName;
19
21
  (0, tslib_1.__classPrivateFieldSet)(this, _MLDOMAbstractElement_fixedNodeName, astNode.nodeName, "f");
20
22
  this.namespaceURI = astNode.namespace;
@@ -113,7 +115,7 @@ class MLDOMAbstractElement extends node_1.default {
113
115
  if (selecor.match(el, this)) {
114
116
  return el;
115
117
  }
116
- el = el.parentNode;
118
+ el = el.getParentElement();
117
119
  } while (el !== null && el.type === 'Element');
118
120
  return null;
119
121
  }
@@ -145,12 +147,15 @@ class MLDOMAbstractElement extends node_1.default {
145
147
  return !!this.getAttributeToken(attrName).length;
146
148
  }
147
149
  matches(selector) {
148
- return (0, helper_1.createSelector)(selector).match(this);
150
+ return !!(0, helper_1.createSelector)(selector).match(this);
149
151
  }
150
152
  fixNodeName(name) {
151
153
  (0, tslib_1.__classPrivateFieldSet)(this, _MLDOMAbstractElement_fixedNodeName, name, "f");
152
154
  }
153
155
  getChildElementsAndTextNodeWithoutWhitespaces() {
156
+ if ((0, tslib_1.__classPrivateFieldGet)(this, _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache, "f")) {
157
+ return (0, tslib_1.__classPrivateFieldGet)(this, _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache, "f");
158
+ }
154
159
  const filteredNodes = [];
155
160
  this.childNodes.forEach(node => {
156
161
  if (node.type === 'Element') {
@@ -164,6 +169,7 @@ class MLDOMAbstractElement extends node_1.default {
164
169
  filteredNodes.push(...children);
165
170
  }
166
171
  });
172
+ (0, tslib_1.__classPrivateFieldSet)(this, _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache, filteredNodes, "f");
167
173
  return filteredNodes;
168
174
  }
169
175
  /**
@@ -173,7 +179,7 @@ class MLDOMAbstractElement extends node_1.default {
173
179
  return this.childNodes.some(node => node.type === 'PSBlock');
174
180
  }
175
181
  isDescendantByUUIDList(uuidList) {
176
- let el = this.parentNode;
182
+ let el = this.getParentElement();
177
183
  if (el === null) {
178
184
  return false;
179
185
  }
@@ -181,10 +187,29 @@ class MLDOMAbstractElement extends node_1.default {
181
187
  if (uuidList.includes(el.uuid)) {
182
188
  return true;
183
189
  }
184
- el = el.parentNode;
190
+ el = el.getParentElement();
185
191
  } while (el !== null && el.type === 'Element');
186
192
  return false;
187
193
  }
194
+ toNormalizeString() {
195
+ if ((0, tslib_1.__classPrivateFieldGet)(this, _MLDOMAbstractElement_normalizedString, "f")) {
196
+ return (0, tslib_1.__classPrivateFieldGet)(this, _MLDOMAbstractElement_normalizedString, "f");
197
+ }
198
+ const children = this.getChildElementsAndTextNodeWithoutWhitespaces();
199
+ const attrs = this.attributes.map(attr => attr.toNormalizeString());
200
+ const attrString = attrs.length ? ' ' + attrs.join('') : '';
201
+ const startTag = `<${this.nodeName}${attrString}>`;
202
+ const childNodes = children.map(node => {
203
+ if (node.type === 'Element') {
204
+ return node.toNormalizeString();
205
+ }
206
+ return node.originRaw;
207
+ });
208
+ const endTag = `</${this.nodeName}>`;
209
+ const normalizedString = `${startTag}${childNodes.join('')}${endTag}`;
210
+ (0, tslib_1.__classPrivateFieldSet)(this, _MLDOMAbstractElement_normalizedString, normalizedString, "f");
211
+ return normalizedString;
212
+ }
188
213
  }
189
214
  exports.default = MLDOMAbstractElement;
190
- _MLDOMAbstractElement_fixedNodeName = new WeakMap();
215
+ _MLDOMAbstractElement_fixedNodeName = new WeakMap(), _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache = new WeakMap(), _MLDOMAbstractElement_normalizedString = new WeakMap();
@@ -36,4 +36,5 @@ export default class MLDOMAttribute extends MLDOMToken<MLASTHTMLAttr> {
36
36
  raw: string;
37
37
  };
38
38
  toString(withSpace?: boolean): string;
39
+ toNormalizeString(): string;
39
40
  }
@@ -69,5 +69,12 @@ class MLDOMAttribute extends token_1.default {
69
69
  toString(withSpace = true) {
70
70
  return (withSpace ? this.spacesBeforeName.raw : '') + this.raw;
71
71
  }
72
+ toNormalizeString() {
73
+ return (this.name.originRaw +
74
+ this.equal.originRaw +
75
+ this.startQuote.originRaw +
76
+ this.value.originRaw +
77
+ this.endQuote.originRaw);
78
+ }
72
79
  }
73
80
  exports.default = MLDOMAttribute;
@@ -1,7 +1,7 @@
1
1
  import type { Document } from '../';
2
2
  import type { RuleInfo } from '../../';
3
3
  import type { AnonymousNode, IMLDOMNode, NodeType } from '../types';
4
- import type { MLDOMElement, MLDOMOmittedElement } from './';
4
+ import type { MLDOMElement, MLDOMOmittedElement, MLDOMPreprocessorSpecificBlock } from './';
5
5
  import type { MLASTAbstructNode } from '@markuplint/ml-ast';
6
6
  import type { AnyRule, RuleConfigValue } from '@markuplint/ml-config';
7
7
  import MLDOMToken from './token';
@@ -11,13 +11,14 @@ export default abstract class MLDOMNode<T extends RuleConfigValue, O = null, A e
11
11
  readonly rules: Record<string, AnyRule>;
12
12
  protected _astToken: A;
13
13
  constructor(astNode: A, document: Document<T, O>);
14
- get parentNode(): MLDOMElement<T, O> | MLDOMOmittedElement<T, O> | null;
14
+ get parentNode(): MLDOMElement<T, O> | MLDOMOmittedElement<T, O> | MLDOMPreprocessorSpecificBlock<T, O> | null;
15
15
  get prevNode(): AnonymousNode<T, O> | null;
16
16
  get nextNode(): AnonymousNode<T, O> | null;
17
- get syntaxicalParentNode(): MLDOMElement<T, O> | null;
17
+ get syntaxicalParentNode(): MLDOMElement<T, O> | MLDOMPreprocessorSpecificBlock<T, O> | null;
18
18
  get prevToken(): AnonymousNode<T, O> | null;
19
19
  get nodeStore(): import("../helper").NodeStore;
20
- toString(): string;
21
- is(type: NodeType): boolean;
22
20
  get rule(): RuleInfo<T, O>;
21
+ getParentElement(): MLDOMElement<T, O> | MLDOMOmittedElement<T, O> | null;
22
+ is(type: NodeType): boolean;
23
+ toString(): string;
23
24
  }
@@ -70,12 +70,6 @@ class MLDOMNode extends token_1.default {
70
70
  get nodeStore() {
71
71
  return (0, tslib_1.__classPrivateFieldGet)(this, _MLDOMNode_doc, "f").nodeStore;
72
72
  }
73
- toString() {
74
- return this.raw;
75
- }
76
- is(type) {
77
- return this.type === type;
78
- }
79
73
  get rule() {
80
74
  if (!(0, tslib_1.__classPrivateFieldGet)(this, _MLDOMNode_doc, "f").currentRule) {
81
75
  throw new Error('Invalid call.');
@@ -87,6 +81,26 @@ class MLDOMNode extends token_1.default {
87
81
  }
88
82
  return (0, tslib_1.__classPrivateFieldGet)(this, _MLDOMNode_doc, "f").currentRule.optimizeOption(rule);
89
83
  }
84
+ getParentElement() {
85
+ let parent = this.parentNode;
86
+ if (!parent) {
87
+ return null;
88
+ }
89
+ while (parent) {
90
+ if (parent.type === 'PSBlock') {
91
+ parent = parent.parentNode;
92
+ continue;
93
+ }
94
+ return parent;
95
+ }
96
+ return null;
97
+ }
98
+ is(type) {
99
+ return this.type === type;
100
+ }
101
+ toString() {
102
+ return this.raw;
103
+ }
90
104
  }
91
105
  exports.default = MLDOMNode;
92
106
  _MLDOMNode_doc = new WeakMap(), _MLDOMNode_prevToken = new WeakMap();