@markuplint/ml-core 5.0.0-rc.0 → 5.0.0-rc.2

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.
@@ -31,8 +31,7 @@ src/
31
31
  │ │ ├── element-close-tag.ts — MLElementCloseTag
32
32
  │ │ ├── rule-mapper.ts — RuleMapper(ルールセット → ノードマッピング)
33
33
  │ │ ├── types.ts — ノード型定数、AccessibilityProperties
34
- │ │ ├── node-list.ts — NodeList/HTMLCollection ユーティリティ
35
- │ │ └── unexpected-call-error.ts — 未サポート DOM メソッドのエラー
34
+ │ │ └── node-list.ts — NodeList/HTMLCollection ユーティリティ
36
35
  │ ├── token/
37
36
  │ │ └── token.ts — MLToken(位置情報付き基底トークン)
38
37
  │ ├── helper/
package/ARCHITECTURE.md CHANGED
@@ -31,8 +31,7 @@ src/
31
31
  │ │ ├── element-close-tag.ts — MLElementCloseTag
32
32
  │ │ ├── rule-mapper.ts — RuleMapper (ruleset → node mapping)
33
33
  │ │ ├── types.ts — Node type constants, AccessibilityProperties
34
- │ │ ├── node-list.ts — NodeList/HTMLCollection utilities
35
- │ │ └── unexpected-call-error.ts — Error for unsupported DOM methods
34
+ │ │ └── node-list.ts — NodeList/HTMLCollection utilities
36
35
  │ ├── token/
37
36
  │ │ └── token.ts — MLToken (base positional token)
38
37
  │ ├── helper/
package/CHANGELOG.md CHANGED
@@ -3,6 +3,40 @@
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.0.0-rc.2](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.1...v5.0.0-rc.2) (2026-04-15)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **ml-core:** propagate base rule disable to virtual rules in nodeRules/childNodeRules ([06fa269](https://github.com/markuplint/markuplint/commit/06fa2695d43671adc1ebcadf1304aca44fd10919)), closes [#3578](https://github.com/markuplint/markuplint/issues/3578)
11
+
12
+ - build!: remove ESLint and replace with oxlint ([1e0a337](https://github.com/markuplint/markuplint/commit/1e0a337707f76b903b16beeeb8c4d4fc0d8fc9e4))
13
+ - feat(ml-core)!: remove deprecated getIndent function ([7ab1a2d](https://github.com/markuplint/markuplint/commit/7ab1a2dae8f36002f4e5f3108a400bb9095db319))
14
+
15
+ ### Features
16
+
17
+ - **ml-core:** propagate option overrides to virtual rules in nodeRules/childNodeRules ([9cb9521](https://github.com/markuplint/markuplint/commit/9cb9521fc1303c132f547ffd1e8c82b3b9099bef))
18
+
19
+ ### BREAKING CHANGES
20
+
21
+ - ESLint is no longer used. Use oxlint instead.
22
+ - getIndent() has been removed from the public API.
23
+
24
+ # [5.0.0-rc.1](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.0...v5.0.0-rc.1) (2026-03-27)
25
+
26
+ ### Bug Fixes
27
+
28
+ - add isFatalError guard to MLEngine.exec and fix accname Deno crash ([c4b20de](https://github.com/markuplint/markuplint/commit/c4b20de128b2cfee582b3588e5004cb90065825b))
29
+ - **ml-core:** add DOM stubs for TS 6 lib.dom.d.ts updates ([837a2ba](https://github.com/markuplint/markuplint/commit/837a2bad5afe967d4d309b91d056b46fc91c7512))
30
+
31
+ - feat(ml-core)!: resolve node references via UUID instead of object refs ([e9b6f4e](https://github.com/markuplint/markuplint/commit/e9b6f4e68bda2f60c9fa69699bf251b51874a4ac))
32
+
33
+ ### BREAKING CHANGES
34
+
35
+ - MLDOM now resolves parent and pair nodes via UUID
36
+ strings from the AST instead of direct object references.
37
+
38
+ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
39
+
6
40
  # [5.0.0-rc.0](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.3...v5.0.0-rc.0) (2026-03-12)
7
41
 
8
42
  ### Bug Fixes
@@ -18,7 +18,7 @@ AST ノード型を対応する MLDOM コンストラクタにディスパッチ
18
18
  | `'invalid'`(`kind: 'starttag'`) | `MLElement`(`x-invalid`、`elementType: 'web-component'` として) | `1` |
19
19
  | `'invalid'`(その他の kind) | `MLText` | `3` |
20
20
 
21
- **注:** `'endtag'` は `createNode()` を通過しません -- ドキュメントのコンストラクション中にスキップされます。`MLElement` は `pairNode` 参照から内部的に `MLElementCloseTag` を作成します。`MLASTAttr` は `MLElement` によって処理され、要素の `attributes` 配列から `MLAttr` インスタンスを作成します。
21
+ **注:** `'endtag'` は `createNode()` を通過しません -- ドキュメントのコンストラクション中にスキップされます。`MLElement` は `pairNodeUuid` を解決して内部的に `MLElementCloseTag` を作成します。`MLASTAttr` は `MLElement` によって処理され、要素の `attributes` 配列から `MLAttr` インスタンスを作成します。
22
22
 
23
23
  ### ウォーカー
24
24
 
@@ -18,7 +18,7 @@ Factory function that dispatches AST node types to their corresponding MLDOM con
18
18
  | `'invalid'` (`kind: 'starttag'`) | `MLElement` (as `x-invalid`, `elementType: 'web-component'`) | `1` |
19
19
  | `'invalid'` (other kind) | `MLText` | `3` |
20
20
 
21
- **Note:** `'endtag'` is not passed through `createNode()` -- it is skipped during document construction. `MLElement` creates `MLElementCloseTag` internally from its `pairNode` reference. `MLASTAttr` is handled by `MLElement` which creates `MLAttr` instances from the element's `attributes` array.
21
+ **Note:** `'endtag'` is not passed through `createNode()` -- it is skipped during document construction. `MLElement` creates `MLElementCloseTag` internally by resolving its `pairNodeUuid`. `MLASTAttr` is handled by `MLElement` which creates `MLAttr` instances from the element's `attributes` array.
22
22
 
23
23
  ### Walkers
24
24
 
package/lib/index.d.ts CHANGED
@@ -5,7 +5,6 @@ export { enableDebug } from './debug.js';
5
5
  export { computeCursorOffset } from './cursor-offset.js';
6
6
  export { applyFixes } from './fix-applier.js';
7
7
  export type { FixResult } from './fix-applier.js';
8
- export { getIndent } from './ml-dom/helper/get-indent.js';
9
8
  export * from './convert-ruleset.js';
10
9
  export * from './ml-core.js';
11
10
  export * from './ml-dom/index.js';
package/lib/index.js CHANGED
@@ -3,7 +3,6 @@ export { Ruleset } from './ruleset/index.js';
3
3
  export { enableDebug } from './debug.js';
4
4
  export { computeCursorOffset } from './cursor-offset.js';
5
5
  export { applyFixes } from './fix-applier.js';
6
- export { getIndent } from './ml-dom/helper/get-indent.js';
7
6
  export * from './convert-ruleset.js';
8
7
  export * from './ml-core.js';
9
8
  export * from './ml-dom/index.js';
package/lib/ml-core.js CHANGED
@@ -67,6 +67,8 @@ export class MLCore {
67
67
  rules: resolvedRules,
68
68
  nodeRules: nodeRuleResult.transformedNodeRules,
69
69
  childNodeRules: childNodeRuleResult.transformedNodeRules,
70
+ baseRuleToVirtualNames: buildBaseRuleToVirtualNames(namedRulesResult.virtualRules),
71
+ mappingErrors: [],
70
72
  };
71
73
  this.#disabledNamespaces = extractDisabledNamespaces(resolvedRules);
72
74
  this.#configErrors.push(...namedRulesResult.errors, ...nodeRuleResult.errors, ...childNodeRuleResult.errors);
@@ -100,7 +102,7 @@ export class MLCore {
100
102
  this.#locale = locale ?? this.#locale;
101
103
  this.#schemas = schemas ?? this.#schemas;
102
104
  this.#configErrors = [...(configErrors ?? [])];
103
- const baseRules = rules?.slice() ?? this.#rules.filter(r => !r.baseRuleId);
105
+ const baseRules = rules ? [...rules] : this.#rules.filter(r => !r.baseRuleId);
104
106
  // Use pre-expansion originals as fallback when ruleset is not provided
105
107
  const incomingNodeRules = ruleset?.nodeRules ?? this.#originalNodeRules;
106
108
  const incomingChildNodeRules = ruleset?.childNodeRules ?? this.#originalChildNodeRules;
@@ -123,6 +125,8 @@ export class MLCore {
123
125
  rules: resolvedRules,
124
126
  nodeRules: nodeRuleResult.transformedNodeRules,
125
127
  childNodeRules: childNodeRuleResult.transformedNodeRules,
128
+ baseRuleToVirtualNames: buildBaseRuleToVirtualNames(namedRulesResult.virtualRules),
129
+ mappingErrors: [],
126
130
  };
127
131
  this.#disabledNamespaces = extractDisabledNamespaces(resolvedRules);
128
132
  this.#configErrors.push(...namedRulesResult.errors, ...nodeRuleResult.errors, ...childNodeRuleResult.errors);
@@ -243,6 +247,9 @@ export class MLCore {
243
247
  tagNameCaseSensitive: this.#parser.tagNameCaseSensitive,
244
248
  pretenders: this.#pretenders,
245
249
  });
250
+ // Collect errors from rule mapping (e.g., invalid wildcard usage)
251
+ this.#configErrors.push(...this.#ruleset.mappingErrors);
252
+ this.#ruleset.mappingErrors.length = 0;
246
253
  }
247
254
  catch (error) {
248
255
  if (error instanceof ParserError) {
@@ -426,6 +433,29 @@ function extractDisabledNamespaces(rules) {
426
433
  .filter(([key, value]) => key.endsWith('/*') && value === false)
427
434
  .map(([key]) => key.slice(0, -1)); // "a11y/*" → "a11y/"
428
435
  }
436
+ /**
437
+ * Builds a mapping from base rule names to virtual rule names.
438
+ * Used by nodeRules/childNodeRules to propagate settings (especially `false`)
439
+ * to virtual rules created by NamedRuleGroups.
440
+ */
441
+ function buildBaseRuleToVirtualNames(
442
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
443
+ virtualRules) {
444
+ const map = new Map();
445
+ for (const vRule of virtualRules) {
446
+ if (!vRule.baseRuleId) {
447
+ continue;
448
+ }
449
+ const list = map.get(vRule.baseRuleId);
450
+ if (list) {
451
+ list.push(vRule.name);
452
+ }
453
+ else {
454
+ map.set(vRule.baseRuleId, [vRule.name]);
455
+ }
456
+ }
457
+ return map;
458
+ }
429
459
  /**
430
460
  * Collects all `FixData` from violations that have a fix callback result.
431
461
  *
@@ -14,20 +14,18 @@ export function getAccname(
14
14
  el, version) {
15
15
  // AccName computation involves DOM traversal, role resolution, and label
16
16
  // lookup via querySelector. These may fail on unexpected DOM states (e.g.,
17
- // detached nodes, malformed attributes) or incomplete spec data.
17
+ // detached nodes, malformed attributes), incomplete spec data, or runtime
18
+ // environment differences (e.g., Deno lacking certain DOM APIs).
18
19
  // A single element's failure must not abort the entire linting process,
19
- // so we catch operational errors and return an empty name (= "unnamed").
20
- // Programmer errors (TypeError, ReferenceError) are rethrown to avoid
21
- // masking implementation bugs.
20
+ // so we catch all errors and return an empty name (= "unnamed").
21
+ // This is an intentional exception to the Tier 1 re-throw policy — see
22
+ // docs/architectures/ERROR-HANDLING.md § "accname computation errors".
22
23
  try {
23
24
  const resolver = createMLCoreResolver(el, version);
24
25
  const result = computeAccessibleName(el, resolver);
25
26
  return result.name;
26
27
  }
27
28
  catch (error) {
28
- if (error instanceof TypeError || error instanceof ReferenceError) {
29
- throw error;
30
- }
31
29
  accnameLog('Raw: %s', el.raw);
32
30
  accnameLog('Error: %O', error);
33
31
  return '';
@@ -34,7 +34,7 @@ document) {
34
34
  attributes: [],
35
35
  childNodes: [],
36
36
  blockBehavior: null,
37
- pairNode: null,
37
+ pairNodeUuid: null,
38
38
  tagOpenChar: '',
39
39
  tagCloseChar: '',
40
40
  isGhost: false,
@@ -1,4 +1,4 @@
1
- import { UnexpectedCallError } from '../node/unexpected-call-error.js';
1
+ import { UnexpectedCallError } from '@markuplint/shared';
2
2
  /**
3
3
  *
4
4
  * @see https://dom.spec.whatwg.org/#ref-for-dom-childnode-before%E2%91%A0
@@ -3,7 +3,7 @@ import { searchIDLAttribute } from '@markuplint/parser-utils';
3
3
  import { MLToken } from '../token/token.js';
4
4
  import { MLDomTokenList } from './dom-token-list.js';
5
5
  import { MLNode } from './node.js';
6
- import { UnexpectedCallError } from './unexpected-call-error.js';
6
+ import { UnexpectedCallError } from '@markuplint/shared';
7
7
  /**
8
8
  * Represents a DOM Attr (attribute) node wrapper in the markuplint DOM tree.
9
9
  * Wraps an AST attribute token and provides access to the attribute's name, value,
@@ -1,6 +1,6 @@
1
1
  import { after, before, nextElementSibling, previousElementSibling, remove, replaceWith, } from '../manipulations/child-node-methods.js';
2
2
  import { MLNode } from './node.js';
3
- import { UnexpectedCallError } from './unexpected-call-error.js';
3
+ import { UnexpectedCallError } from '@markuplint/shared';
4
4
  export class MLCharacterData extends MLNode {
5
5
  /**
6
6
  * @implements DOM API: `CharacterData`
@@ -1,5 +1,5 @@
1
1
  import { MLParentNode } from './parent-node.js';
2
- import { UnexpectedCallError } from './unexpected-call-error.js';
2
+ import { UnexpectedCallError } from '@markuplint/shared';
3
3
  /**
4
4
  * Represents a DOM DocumentFragment node wrapper in the markuplint DOM tree.
5
5
  * Used for JSX fragments and similar constructs where a set of nodes
@@ -10,7 +10,7 @@ import type { Ruleset } from '../../ruleset/index.js';
10
10
  import type { MLSchema } from '../../types.js';
11
11
  import type { Walker } from '../helper/walkers.js';
12
12
  import type { MLToken } from '../token/token.js';
13
- import type { EndTagType, MLASTDocument } from '@markuplint/ml-ast';
13
+ import type { EndTagType, MLASTDocument, MLASTNodeTreeItem } from '@markuplint/ml-ast';
14
14
  import type { PlainData, Pretender, RuleCommonSettings, RuleConfigValue } from '@markuplint/ml-config';
15
15
  import type { ARIAVersion, MLMLSpec } from '@markuplint/ml-spec';
16
16
  import { MLParentNode } from './parent-node.js';
@@ -93,6 +93,13 @@ export declare class MLDocument<T extends RuleConfigValue, O extends PlainData =
93
93
  readonly tagNameCaseSensitive?: boolean;
94
94
  readonly pretenders?: readonly Pretender[];
95
95
  });
96
+ /**
97
+ * Looks up an AST node by its UUID from the parsed document's node list.
98
+ *
99
+ * @param uuid - The UUID of the AST node to find
100
+ * @returns The AST node, or `undefined` if not found
101
+ */
102
+ getAstNodeByUuid(uuid: string): MLASTNodeTreeItem | undefined;
96
103
  /**
97
104
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
98
105
  *
@@ -107,6 +114,14 @@ export declare class MLDocument<T extends RuleConfigValue, O extends PlainData =
107
114
  * @implements DOM API: `Document`
108
115
  */
109
116
  get activeElement(): Element | null;
117
+ /**
118
+ * **IT THROWS AN ERROR WHEN CALLING THIS.**
119
+ *
120
+ * @deprecated
121
+ * @unsupported
122
+ * @implements DOM API: `Document`
123
+ */
124
+ get activeViewTransition(): ViewTransition | null;
110
125
  /**
111
126
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
112
127
  *
@@ -204,6 +219,14 @@ export declare class MLDocument<T extends RuleConfigValue, O extends PlainData =
204
219
  * @implements DOM API: `Document`
205
220
  */
206
221
  get currentScript(): HTMLOrSVGScriptElement | null;
222
+ /**
223
+ * **IT THROWS AN ERROR WHEN CALLING THIS.**
224
+ *
225
+ * @deprecated
226
+ * @unsupported
227
+ * @implements DOM API: `Document`
228
+ */
229
+ get customElementRegistry(): CustomElementRegistry | null;
207
230
  /**
208
231
  * Window object for calling the `getComputedStyle` and the `getPropertyValue` that
209
232
  * are needed by **Accessible Name and Description Computation**.
@@ -519,6 +542,14 @@ export declare class MLDocument<T extends RuleConfigValue, O extends PlainData =
519
542
  * @implements DOM API: `Document`
520
543
  */
521
544
  get onclose(): ((this: GlobalEventHandlers, ev: Event) => any) | null;
545
+ /**
546
+ * **IT THROWS AN ERROR WHEN CALLING THIS.**
547
+ *
548
+ * @deprecated
549
+ * @unsupported
550
+ * @implements DOM API: `Document`
551
+ */
552
+ get oncommand(): ((this: GlobalEventHandlers, ev: Event) => any) | null;
522
553
  /**
523
554
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
524
555
  *
@@ -9,7 +9,7 @@ import { sequentialWalker, syncWalk } from '../helper/walkers.js';
9
9
  import { nodeListToHTMLCollection } from './node-list.js';
10
10
  import { MLParentNode } from './parent-node.js';
11
11
  import { RuleMapper } from './rule-mapper.js';
12
- import { UnexpectedCallError } from './unexpected-call-error.js';
12
+ import { UnexpectedCallError } from '@markuplint/shared';
13
13
  const log = coreLog.extend('ml-dom');
14
14
  const docLog = log.extend('document');
15
15
  const ruleLog = docLog.extend('rule');
@@ -51,6 +51,7 @@ export class MLDocument extends MLParentNode {
51
51
  /**
52
52
  * The file path of the source document, if available.
53
53
  */
54
+ #astNodeMap;
54
55
  #filename;
55
56
  /**
56
57
  * Whether this document represents a fragment rather than a complete document.
@@ -98,6 +99,8 @@ export class MLDocument extends MLParentNode {
98
99
  this.#filename = options?.filename;
99
100
  this.tagNameCaseSensitive = options?.tagNameCaseSensitive ?? false;
100
101
  this.ruleCommonSettings = ruleCommonSettings;
102
+ // Build UUID → AST node map for cross-reference lookups (e.g., pairNodeUuid)
103
+ this.#astNodeMap = new Map(ast.nodeList.map(n => [n.uuid, n]));
101
104
  // console.log(ast.nodeList.map((n, i) => `${i}: ${n.uuid} "${n.raw.trim()}"(${n.type})`));
102
105
  this.nodeList = Object.freeze(ast.nodeList
103
106
  .map(astNode => {
@@ -120,6 +123,15 @@ export class MLDocument extends MLParentNode {
120
123
  throw error;
121
124
  }
122
125
  }
126
+ /**
127
+ * Looks up an AST node by its UUID from the parsed document's node list.
128
+ *
129
+ * @param uuid - The UUID of the AST node to find
130
+ * @returns The AST node, or `undefined` if not found
131
+ */
132
+ getAstNodeByUuid(uuid) {
133
+ return this.#astNodeMap.get(uuid);
134
+ }
123
135
  /**
124
136
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
125
137
  *
@@ -138,6 +150,16 @@ export class MLDocument extends MLParentNode {
138
150
  get activeElement() {
139
151
  throw new UnexpectedCallError('Not supported "activeElement" property');
140
152
  }
153
+ /**
154
+ * **IT THROWS AN ERROR WHEN CALLING THIS.**
155
+ *
156
+ * @deprecated
157
+ * @unsupported
158
+ * @implements DOM API: `Document`
159
+ */
160
+ get activeViewTransition() {
161
+ throw new UnexpectedCallError('Not supported "activeViewTransition" property');
162
+ }
141
163
  /**
142
164
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
143
165
  *
@@ -261,6 +283,16 @@ export class MLDocument extends MLParentNode {
261
283
  get currentScript() {
262
284
  throw new UnexpectedCallError('Not supported "currentScript" property');
263
285
  }
286
+ /**
287
+ * **IT THROWS AN ERROR WHEN CALLING THIS.**
288
+ *
289
+ * @deprecated
290
+ * @unsupported
291
+ * @implements DOM API: `Document`
292
+ */
293
+ get customElementRegistry() {
294
+ throw new UnexpectedCallError('Not supported "customElementRegistry" property');
295
+ }
264
296
  /**
265
297
  * Window object for calling the `getComputedStyle` and the `getPropertyValue` that
266
298
  * are needed by **Accessible Name and Description Computation**.
@@ -677,6 +709,16 @@ export class MLDocument extends MLParentNode {
677
709
  get onclose() {
678
710
  throw new UnexpectedCallError('Not supported "onclose" property');
679
711
  }
712
+ /**
713
+ * **IT THROWS AN ERROR WHEN CALLING THIS.**
714
+ *
715
+ * @deprecated
716
+ * @unsupported
717
+ * @implements DOM API: `Document`
718
+ */
719
+ get oncommand() {
720
+ throw new UnexpectedCallError('Not supported "oncommand" property');
721
+ }
680
722
  /**
681
723
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
682
724
  *
@@ -2357,6 +2399,24 @@ export class MLDocument extends MLParentNode {
2357
2399
  if (rule == null) {
2358
2400
  continue;
2359
2401
  }
2402
+ // Namespace wildcard (e.g., "a11y/*": false)
2403
+ if (ruleName.endsWith('/*')) {
2404
+ if (rule !== false) {
2405
+ ruleset.mappingErrors.push(new Error(`Namespace wildcard "${ruleName}" only accepts false; use a specific rule name for options`));
2406
+ continue;
2407
+ }
2408
+ const nsPrefix = ruleName.slice(0, -1); // "a11y/*" → "a11y/"
2409
+ for (const key of Object.keys(ruleset.rules)) {
2410
+ if (key.startsWith(nsPrefix)) {
2411
+ ruleMapper.set(node, key, {
2412
+ from: 'nodeRules',
2413
+ specificity: matches.specificity,
2414
+ rule: false,
2415
+ });
2416
+ }
2417
+ }
2418
+ continue;
2419
+ }
2360
2420
  const convertedRule = exchangeValueOnRule(rule, matches.data ?? {});
2361
2421
  if (convertedRule === undefined) {
2362
2422
  continue;
@@ -2369,6 +2429,19 @@ export class MLDocument extends MLParentNode {
2369
2429
  specificity: matches.specificity,
2370
2430
  rule: mergedRule,
2371
2431
  });
2432
+ // Propagate to virtual rules that wrap this base rule
2433
+ const virtualNames = ruleset.baseRuleToVirtualNames.get(ruleName);
2434
+ if (virtualNames) {
2435
+ for (const vName of virtualNames) {
2436
+ const vGlobalRule = ruleset.rules[vName];
2437
+ const vMergedRule = vGlobalRule == null ? convertedRule : mergeRule(vGlobalRule, convertedRule);
2438
+ ruleMapper.set(node, vName, {
2439
+ from: 'nodeRules',
2440
+ specificity: matches.specificity,
2441
+ rule: vMergedRule,
2442
+ });
2443
+ }
2444
+ }
2372
2445
  }
2373
2446
  }
2374
2447
  // overwrite rule to child node
@@ -2400,6 +2473,26 @@ export class MLDocument extends MLParentNode {
2400
2473
  if (rule == null) {
2401
2474
  continue;
2402
2475
  }
2476
+ // Namespace wildcard (e.g., "a11y/*": false)
2477
+ if (ruleName.endsWith('/*')) {
2478
+ if (rule !== false) {
2479
+ ruleset.mappingErrors.push(new Error(`Namespace wildcard "${ruleName}" only accepts false; use a specific rule name for options`));
2480
+ continue;
2481
+ }
2482
+ const nsPrefix = ruleName.slice(0, -1); // "a11y/*" → "a11y/"
2483
+ for (const key of Object.keys(ruleset.rules)) {
2484
+ if (key.startsWith(nsPrefix)) {
2485
+ for (const descendant of targetDescendants) {
2486
+ ruleMapper.set(descendant, key, {
2487
+ from: 'childNodeRules',
2488
+ specificity: matches.specificity,
2489
+ rule: false,
2490
+ });
2491
+ }
2492
+ }
2493
+ }
2494
+ continue;
2495
+ }
2403
2496
  const convertedRule = exchangeValueOnRule(rule, matches.data ?? {});
2404
2497
  if (convertedRule === undefined) {
2405
2498
  continue;
@@ -2414,6 +2507,21 @@ export class MLDocument extends MLParentNode {
2414
2507
  rule: mergedRule,
2415
2508
  });
2416
2509
  }
2510
+ // Propagate to virtual rules that wrap this base rule
2511
+ const virtualNames = ruleset.baseRuleToVirtualNames.get(ruleName);
2512
+ if (virtualNames) {
2513
+ for (const vName of virtualNames) {
2514
+ const vGlobalRule = ruleset.rules[vName];
2515
+ const vMergedRule = vGlobalRule == null ? convertedRule : mergeRule(vGlobalRule, convertedRule);
2516
+ for (const descendant of targetDescendants) {
2517
+ ruleMapper.set(descendant, vName, {
2518
+ from: 'childNodeRules',
2519
+ specificity: matches.specificity,
2520
+ rule: vMergedRule,
2521
+ });
2522
+ }
2523
+ }
2524
+ }
2417
2525
  }
2418
2526
  }
2419
2527
  }
@@ -1,5 +1,5 @@
1
1
  import { getPosition } from '@markuplint/parser-utils/location';
2
- import { UnexpectedCallError } from './unexpected-call-error.js';
2
+ import { UnexpectedCallError } from '@markuplint/shared';
3
3
  export class MLDomTokenList extends Array {
4
4
  #origin;
5
5
  /**
@@ -185,7 +185,7 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
185
185
  * @unsupported
186
186
  * @implements DOM API: `Element`
187
187
  */
188
- get ariaControlsElements(): readonly MLElement<T, O>[];
188
+ get ariaControlsElements(): readonly MLElement<T, O>[] | null;
189
189
  /**
190
190
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
191
191
  *
@@ -201,7 +201,7 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
201
201
  * @unsupported
202
202
  * @implements DOM API: `Element`
203
203
  */
204
- get ariaDescribedByElements(): readonly MLElement<T, O>[];
204
+ get ariaDescribedByElements(): readonly MLElement<T, O>[] | null;
205
205
  /**
206
206
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
207
207
  *
@@ -217,7 +217,7 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
217
217
  * @unsupported
218
218
  * @implements DOM API: `Element`
219
219
  */
220
- get ariaDetailsElements(): readonly MLElement<T, O>[];
220
+ get ariaDetailsElements(): readonly MLElement<T, O>[] | null;
221
221
  /**
222
222
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
223
223
  *
@@ -233,7 +233,7 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
233
233
  * @unsupported
234
234
  * @implements DOM API: `Element`
235
235
  */
236
- get ariaErrorMessageElements(): readonly MLElement<T, O>[];
236
+ get ariaErrorMessageElements(): readonly MLElement<T, O>[] | null;
237
237
  /**
238
238
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
239
239
  *
@@ -249,7 +249,7 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
249
249
  * @unsupported
250
250
  * @implements DOM API: `Element`
251
251
  */
252
- get ariaFlowToElements(): readonly MLElement<T, O>[];
252
+ get ariaFlowToElements(): readonly MLElement<T, O>[] | null;
253
253
  /**
254
254
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
255
255
  *
@@ -297,7 +297,7 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
297
297
  * @unsupported
298
298
  * @implements DOM API: `Element`
299
299
  */
300
- get ariaLabelledByElements(): readonly MLElement<T, O>[];
300
+ get ariaLabelledByElements(): readonly MLElement<T, O>[] | null;
301
301
  /**
302
302
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
303
303
  *
@@ -353,7 +353,7 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
353
353
  * @unsupported
354
354
  * @implements DOM API: `Element`
355
355
  */
356
- get ariaOwnsElements(): readonly MLElement<T, O>[];
356
+ get ariaOwnsElements(): readonly MLElement<T, O>[] | null;
357
357
  /**
358
358
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
359
359
  *
@@ -600,6 +600,14 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
600
600
  * @implements DOM API: `Element`
601
601
  */
602
602
  get currentCSSZoom(): number;
603
+ /**
604
+ * **IT THROWS AN ERROR WHEN CALLING THIS.**
605
+ *
606
+ * @deprecated
607
+ * @unsupported
608
+ * @implements DOM API: `Element`
609
+ */
610
+ get customElementRegistry(): CustomElementRegistry | null;
603
611
  /**
604
612
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
605
613
  *
@@ -739,7 +747,7 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
739
747
  * @unsupported
740
748
  * @implements DOM API: `Element`
741
749
  */
742
- get nonce(): string | undefined;
750
+ get nonce(): string;
743
751
  /**
744
752
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
745
753
  *
@@ -908,6 +916,14 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
908
916
  * @implements DOM API: `Element`
909
917
  */
910
918
  get onclose(): ((this: GlobalEventHandlers, ev: Event) => any) | null;
919
+ /**
920
+ * **IT THROWS AN ERROR WHEN CALLING THIS.**
921
+ *
922
+ * @deprecated
923
+ * @unsupported
924
+ * @implements DOM API: `Element`
925
+ */
926
+ get oncommand(): ((this: GlobalEventHandlers, ev: Event) => any) | null;
911
927
  /**
912
928
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
913
929
  *
@@ -2065,7 +2081,10 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
2065
2081
  * @implements DOM API: `Element`
2066
2082
  * @see https://dom.spec.whatwg.org/#ref-for-dom-element-matches%E2%91%A0
2067
2083
  */
2068
- matches(selector: string, scope?: MLParentNode<T, O>): boolean;
2084
+ matches<K extends keyof HTMLElementTagNameMap>(selectors: K): this is HTMLElementTagNameMap[K];
2085
+ matches<K extends keyof SVGElementTagNameMap>(selectors: K): this is SVGElementTagNameMap[K];
2086
+ matches<K extends keyof MathMLElementTagNameMap>(selectors: K): this is MathMLElementTagNameMap[K];
2087
+ matches(selectors: string, scope?: MLParentNode<T, O>): boolean;
2069
2088
  /**
2070
2089
  * Matches this element against a CSS selector or regex selector pattern,
2071
2090
  * returning detailed match results. When the element is a pretender,
@@ -9,7 +9,7 @@ import { MLElementCloseTag } from './element-close-tag.js';
9
9
  import { toNamedNodeMap } from './named-node-map.js';
10
10
  import { toHTMLCollection, toNodeList } from './node-list.js';
11
11
  import { MLParentNode } from './parent-node.js';
12
- import { UnexpectedCallError } from './unexpected-call-error.js';
12
+ import { UnexpectedCallError } from '@markuplint/shared';
13
13
  const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
14
14
  /**
15
15
  * Represents a DOM Element node wrapper in the markuplint DOM tree.
@@ -116,7 +116,10 @@ export class MLElement extends MLParentNode {
116
116
  document) {
117
117
  super(astNode, document, astNode.isFragment);
118
118
  this.#attributes = astNode.attributes.map(attr => new MLAttr(attr, this));
119
- this.closeTag = astNode.pairNode ? new MLElementCloseTag(astNode.pairNode, document, this) : null;
119
+ const pairAstNode = astNode.pairNodeUuid
120
+ ? document.getAstNodeByUuid(astNode.pairNodeUuid)
121
+ : null;
122
+ this.closeTag = pairAstNode ? new MLElementCloseTag(pairAstNode, document, this) : null;
120
123
  const ns = resolveNamespace(astNode.nodeName, astNode.namespace);
121
124
  this.namespaceURI = astNode.namespace;
122
125
  this.elementType = astNode.elementType;
@@ -808,6 +811,16 @@ export class MLElement extends MLParentNode {
808
811
  get currentCSSZoom() {
809
812
  throw new UnexpectedCallError('Not supported "currentCSSZoom" property');
810
813
  }
814
+ /**
815
+ * **IT THROWS AN ERROR WHEN CALLING THIS.**
816
+ *
817
+ * @deprecated
818
+ * @unsupported
819
+ * @implements DOM API: `Element`
820
+ */
821
+ get customElementRegistry() {
822
+ throw new UnexpectedCallError('Not supported "customElementRegistry" property');
823
+ }
811
824
  /**
812
825
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
813
826
  *
@@ -1209,6 +1222,16 @@ export class MLElement extends MLParentNode {
1209
1222
  get onclose() {
1210
1223
  throw new UnexpectedCallError('Not supported "onclose" property');
1211
1224
  }
1225
+ /**
1226
+ * **IT THROWS AN ERROR WHEN CALLING THIS.**
1227
+ *
1228
+ * @deprecated
1229
+ * @unsupported
1230
+ * @implements DOM API: `Element`
1231
+ */
1232
+ get oncommand() {
1233
+ throw new UnexpectedCallError('Not supported "oncommand" property');
1234
+ }
1212
1235
  /**
1213
1236
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
1214
1237
  *
@@ -2783,10 +2806,6 @@ export class MLElement extends MLParentNode {
2783
2806
  }
2784
2807
  return true;
2785
2808
  }
2786
- /**
2787
- * @implements DOM API: `Element`
2788
- * @see https://dom.spec.whatwg.org/#ref-for-dom-element-matches%E2%91%A0
2789
- */
2790
2809
  matches(selector,
2791
2810
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
2792
2811
  scope) {
@@ -1,4 +1,4 @@
1
- import { UnexpectedCallError } from './unexpected-call-error.js';
1
+ import { UnexpectedCallError } from '@markuplint/shared';
2
2
  export class MLNamedNodeMap extends Array {
3
3
  getNamedItem(qualifiedName) {
4
4
  return this.find(attr => attr.name === qualifiedName) ?? null;
@@ -5,6 +5,7 @@ import type { PlainData, RuleConfigValue } from '@markuplint/ml-config';
5
5
  declare class NodeStore {
6
6
  #private;
7
7
  getNode<N extends MLASTNode, T extends RuleConfigValue, O extends PlainData = undefined>(astNode: N): MappedNode<N, T, O>;
8
+ getNodeByUuid<T extends RuleConfigValue, O extends PlainData = undefined>(uuid: string): MLNode<T, O, any>;
8
9
  setNode<A extends MLASTNode, T extends RuleConfigValue, O extends PlainData = undefined>(astNode: A, node: MLNode<T, O, A>): void;
9
10
  }
10
11
  /**
@@ -22,6 +22,16 @@ class NodeStore {
22
22
  }
23
23
  return node;
24
24
  }
25
+ getNodeByUuid(uuid) {
26
+ const node = this.#store.get(uuid);
27
+ if (!node) {
28
+ nodeStoreError('Ref UUID: %s', uuid);
29
+ throw new TargetParserError('Broke mapping nodes.', {
30
+ raw: `uuid:${uuid}`,
31
+ });
32
+ }
33
+ return node;
34
+ }
25
35
  setNode(astNode,
26
36
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
27
37
  node) {
@@ -441,6 +441,13 @@ export declare abstract class MLNode<T extends RuleConfigValue, O extends PlainD
441
441
  * @see https://dom.spec.whatwg.org/#dom-node-insertbefore
442
442
  */
443
443
  insertBefore<T extends Node>(node: T, child: Node | null): T;
444
+ /**
445
+ * **IT THROWS AN ERROR WHEN CALLING THIS.**
446
+ *
447
+ * @unsupported
448
+ * @implements DOM API: `Node`
449
+ */
450
+ moveBefore(node: Node, child: Node | null): Node;
444
451
  /**
445
452
  * @implements `@markuplint/ml-core` API: `MLNode`
446
453
  */
@@ -1,9 +1,8 @@
1
- import { branchesToPatterns } from '@markuplint/shared';
1
+ import { branchesToPatterns, UnexpectedCallError } from '@markuplint/shared';
2
2
  import { MLToken } from '../token/token.js';
3
3
  import { isChildNode } from './child-node.js';
4
4
  import { toNodeList } from './node-list.js';
5
5
  import { nodeStore } from './node-store.js';
6
- import { UnexpectedCallError } from './unexpected-call-error.js';
7
6
  /**
8
7
  * Abstract base class for all markuplint DOM node wrappers.
9
8
  * Extends `MLToken` with DOM `Node` interface compliance, tree traversal,
@@ -481,10 +480,10 @@ export class MLNode extends MLToken {
481
480
  if (this._astToken.type === 'attr' || this._astToken.type === 'spread') {
482
481
  return null;
483
482
  }
484
- if (!this._astToken.parentNode) {
483
+ if (!this._astToken.parentNodeUuid) {
485
484
  return this.ownerMLDocument;
486
485
  }
487
- return nodeStore.getNode(this._astToken.parentNode);
486
+ return nodeStore.getNodeByUuid(this._astToken.parentNodeUuid);
488
487
  }
489
488
  /**
490
489
  * Return the text content.
@@ -753,6 +752,19 @@ export class MLNode extends MLToken {
753
752
  child) {
754
753
  throw new UnexpectedCallError('Not supported "insertBefore" method');
755
754
  }
755
+ /**
756
+ * **IT THROWS AN ERROR WHEN CALLING THIS.**
757
+ *
758
+ * @unsupported
759
+ * @implements DOM API: `Node`
760
+ */
761
+ moveBefore(
762
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
763
+ node,
764
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
765
+ child) {
766
+ throw new UnexpectedCallError('Not supported "moveBefore" method');
767
+ }
756
768
  /**
757
769
  * @implements `@markuplint/ml-core` API: `MLNode`
758
770
  */
@@ -2,7 +2,7 @@ import { syncWalk } from '../helper/walkers.js';
2
2
  import { getChildren } from '../manipulations/get-children.js';
3
3
  import { toNodeList } from './node-list.js';
4
4
  import { MLNode } from './node.js';
5
- import { UnexpectedCallError } from './unexpected-call-error.js';
5
+ import { UnexpectedCallError } from '@markuplint/shared';
6
6
  /**
7
7
  *
8
8
  * @see https://dom.spec.whatwg.org/#interface-parentnode
@@ -1,5 +1,5 @@
1
1
  import { MLCharacterData } from './character-data.js';
2
- import { UnexpectedCallError } from './unexpected-call-error.js';
2
+ import { UnexpectedCallError } from '@markuplint/shared';
3
3
  /**
4
4
  * Raw text elements
5
5
  *
@@ -4,14 +4,26 @@ import type { ChildNodeRule, Config, NodeRule, Rules } from '@markuplint/ml-conf
4
4
  * rule overrides extracted from a markuplint {@link Config}.
5
5
  */
6
6
  export declare class Ruleset {
7
+ /**
8
+ * Maps base rule names to their virtual rule names created by NamedRuleGroups.
9
+ * For example, if `a11y/wai-aria` wraps `wai-aria`, this maps `"wai-aria"` → `["a11y/wai-aria"]`.
10
+ * Used by nodeRules/childNodeRules to propagate settings to virtual rules.
11
+ */
12
+ readonly baseRuleToVirtualNames: ReadonlyMap<string, readonly string[]>;
7
13
  /** Rule overrides that apply to child nodes matching specific selectors */
8
14
  readonly childNodeRules: readonly ChildNodeRule[];
15
+ /**
16
+ * Errors collected during rule mapping (e.g., invalid wildcard usage).
17
+ * Consumed by MLCore and reported as config-error violations.
18
+ */
19
+ readonly mappingErrors: Error[];
9
20
  /** Rule overrides that apply to nodes matching specific selectors */
10
21
  readonly nodeRules: readonly NodeRule[];
11
22
  /** The global rule definitions */
12
23
  readonly rules: Rules;
13
24
  /**
14
25
  * @param config - The markuplint configuration to extract rules from
26
+ * @param baseRuleToVirtualNames - Mapping from base rule names to virtual rule names
15
27
  */
16
- constructor(config: Config);
28
+ constructor(config: Config, baseRuleToVirtualNames?: ReadonlyMap<string, readonly string[]>);
17
29
  }
@@ -3,18 +3,31 @@
3
3
  * rule overrides extracted from a markuplint {@link Config}.
4
4
  */
5
5
  export class Ruleset {
6
+ /**
7
+ * Maps base rule names to their virtual rule names created by NamedRuleGroups.
8
+ * For example, if `a11y/wai-aria` wraps `wai-aria`, this maps `"wai-aria"` → `["a11y/wai-aria"]`.
9
+ * Used by nodeRules/childNodeRules to propagate settings to virtual rules.
10
+ */
11
+ baseRuleToVirtualNames;
6
12
  /** Rule overrides that apply to child nodes matching specific selectors */
7
13
  childNodeRules;
14
+ /**
15
+ * Errors collected during rule mapping (e.g., invalid wildcard usage).
16
+ * Consumed by MLCore and reported as config-error violations.
17
+ */
18
+ mappingErrors = [];
8
19
  /** Rule overrides that apply to nodes matching specific selectors */
9
20
  nodeRules;
10
21
  /** The global rule definitions */
11
22
  rules;
12
23
  /**
13
24
  * @param config - The markuplint configuration to extract rules from
25
+ * @param baseRuleToVirtualNames - Mapping from base rule names to virtual rule names
14
26
  */
15
- constructor(config) {
27
+ constructor(config, baseRuleToVirtualNames) {
16
28
  this.rules = config.rules ?? {};
17
29
  this.nodeRules = config.nodeRules ?? [];
18
30
  this.childNodeRules = config.childNodeRules ?? [];
31
+ this.baseRuleToVirtualNames = baseRuleToVirtualNames ?? new Map();
19
32
  }
20
33
  }
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@markuplint/ml-core",
3
- "version": "5.0.0-rc.0",
3
+ "version": "5.0.0-rc.2",
4
4
  "description": "The core module of markuplint",
5
- "repository": "git@github.com:markuplint/markuplint.git",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/markuplint/markuplint.git",
8
+ "directory": "packages/@markuplint/ml-core"
9
+ },
6
10
  "author": "Yusuke Hirao <yusukehirao@me.com>",
7
11
  "license": "MIT",
8
12
  "engines": {
@@ -30,20 +34,20 @@
30
34
  "./lib/configs.js": "./lib/configs.browser.js"
31
35
  },
32
36
  "dependencies": {
33
- "@markuplint/config-presets": "5.0.0-rc.0",
34
- "@markuplint/html-parser": "5.0.0-rc.0",
35
- "@markuplint/html-spec": "5.0.0-rc.0",
36
- "@markuplint/i18n": "5.0.0-rc.0",
37
- "@markuplint/ml-ast": "5.0.0-rc.0",
38
- "@markuplint/ml-config": "5.0.0-rc.0",
39
- "@markuplint/ml-spec": "5.0.0-rc.0",
40
- "@markuplint/parser-utils": "5.0.0-rc.0",
41
- "@markuplint/selector": "5.0.0-rc.0",
42
- "@markuplint/shared": "5.0.0-rc.0",
43
- "@types/debug": "4.1.12",
37
+ "@markuplint/config-presets": "5.0.0-rc.2",
38
+ "@markuplint/html-parser": "5.0.0-rc.2",
39
+ "@markuplint/html-spec": "5.0.0-rc.2",
40
+ "@markuplint/i18n": "5.0.0-rc.2",
41
+ "@markuplint/ml-ast": "5.0.0-rc.2",
42
+ "@markuplint/ml-config": "5.0.0-rc.2",
43
+ "@markuplint/ml-spec": "5.0.0-rc.2",
44
+ "@markuplint/parser-utils": "5.0.0-rc.2",
45
+ "@markuplint/selector": "5.0.0-rc.2",
46
+ "@markuplint/shared": "5.0.0-rc.2",
47
+ "@types/debug": "4.1.13",
44
48
  "debug": "4.4.3",
45
49
  "is-plain-object": "5.0.0",
46
- "type-fest": "5.4.4"
50
+ "type-fest": "5.5.0"
47
51
  },
48
- "gitHead": "ebf4d7cfca0c259aead3b292c6b8a202db4cd802"
52
+ "gitHead": "e43763858d9234c417053becc73dbd088c1e7ea6"
49
53
  }
@@ -1,20 +0,0 @@
1
- import type { MLNode } from '../node/node.js';
2
- import type { MLText } from '../node/text.js';
3
- /**
4
- * Computes the indentation preceding the given node by analyzing
5
- * the whitespace in adjacent text nodes.
6
- *
7
- * @deprecated
8
- * @param node - The node whose indentation to determine
9
- * @returns An indentation object describing the whitespace, or null if no indentation is found
10
- */
11
- export declare function getIndent(node: MLNode<any, any>): MLDOMIndentation | null;
12
- declare class MLDOMIndentation {
13
- #private;
14
- readonly line: number;
15
- constructor(originTextNode: MLText<any, any>, raw: string, line: number, parentNode: MLNode<any, any>);
16
- get raw(): string;
17
- get type(): 'tab' | 'space' | 'mixed' | 'none';
18
- get width(): number;
19
- }
20
- export {};
@@ -1,90 +0,0 @@
1
- /**
2
- * Computes the indentation preceding the given node by analyzing
3
- * the whitespace in adjacent text nodes.
4
- *
5
- * @deprecated
6
- * @param node - The node whose indentation to determine
7
- * @returns An indentation object describing the whitespace, or null if no indentation is found
8
- */
9
- export function getIndent(
10
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
11
- node) {
12
- const prevToken = node.prevToken;
13
- if (!prevToken) {
14
- return null;
15
- }
16
- if (node.is(node.TEXT_NODE)) {
17
- if (node.isRawTextElementContent()) {
18
- return null;
19
- }
20
- const matched = node.raw.match(/^([\t\v\f\r \u00A0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*\n\s*)\S+/);
21
- if (matched) {
22
- const spaces = matched[1];
23
- if (spaces) {
24
- const spaceLines = spaces.split(/\r?\n/);
25
- const line = spaceLines.length + node.startLine - 1;
26
- const lastSpace = spaceLines.pop();
27
- if (lastSpace != null) {
28
- return new MLDOMIndentation(node, lastSpace, line, node);
29
- }
30
- }
31
- }
32
- return null;
33
- }
34
- if (!prevToken.is(node.TEXT_NODE)) {
35
- return null;
36
- }
37
- // One or more newlines and zero or more spaces or tabs.
38
- // Or, If textNode is first token and that is filled spaces, tabs and newlines only.
39
- const matched = isFirstToken(prevToken)
40
- ? prevToken.raw.match(/^(?:[\t ]*\r?\n)*([\t ]*)$/)
41
- : prevToken.raw.match(/\r?\n([\t ]*)$/);
42
- if (matched) {
43
- // Spaces will include empty string.
44
- const spaces = matched[1];
45
- if (spaces != null) {
46
- return new MLDOMIndentation(prevToken, spaces, node.startLine, node);
47
- }
48
- }
49
- return null;
50
- }
51
- class MLDOMIndentation {
52
- #raw;
53
- line;
54
- #node;
55
- #parent;
56
- constructor(
57
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
58
- originTextNode, raw, line,
59
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
60
- parentNode) {
61
- this.line = line;
62
- this.#node = originTextNode;
63
- this.#parent = parentNode;
64
- this.#raw = raw;
65
- }
66
- get raw() {
67
- if (!this.#parent.is(this.#parent.TEXT_NODE) && this.line !== this.#node.endLine) {
68
- return '';
69
- }
70
- return this.#raw;
71
- }
72
- get type() {
73
- if (!this.#parent.is(this.#parent.TEXT_NODE) && this.line !== this.#node.endLine) {
74
- return 'none';
75
- }
76
- const raw = this.#raw;
77
- return raw === '' ? 'none' : /^\t+$/.test(raw) ? 'tab' : /^[^\t]+$/.test(raw) ? 'space' : 'mixed';
78
- }
79
- get width() {
80
- if (!this.#parent.is(this.#parent.TEXT_NODE) && this.line !== this.#node.endLine) {
81
- return 0;
82
- }
83
- return this.#raw.length;
84
- }
85
- }
86
- function isFirstToken(
87
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
88
- node) {
89
- return !node.prevToken;
90
- }
@@ -1,2 +0,0 @@
1
- export declare class UnexpectedCallError extends Error {
2
- }
@@ -1,2 +0,0 @@
1
- export class UnexpectedCallError extends Error {
2
- }