@markuplint/ml-core 5.0.0-rc.1 → 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.
- package/CHANGELOG.md +18 -0
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/lib/ml-core.js +31 -1
- package/lib/ml-dom/node/document.js +66 -0
- package/lib/ruleset/index.d.ts +13 -1
- package/lib/ruleset/index.js +14 -1
- package/package.json +17 -13
- package/lib/ml-dom/helper/get-indent.d.ts +0 -20
- package/lib/ml-dom/helper/get-indent.js +0 -90
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,24 @@
|
|
|
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
|
+
|
|
6
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)
|
|
7
25
|
|
|
8
26
|
### Bug Fixes
|
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
|
|
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
|
*
|
|
@@ -2399,6 +2399,24 @@ export class MLDocument extends MLParentNode {
|
|
|
2399
2399
|
if (rule == null) {
|
|
2400
2400
|
continue;
|
|
2401
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
|
+
}
|
|
2402
2420
|
const convertedRule = exchangeValueOnRule(rule, matches.data ?? {});
|
|
2403
2421
|
if (convertedRule === undefined) {
|
|
2404
2422
|
continue;
|
|
@@ -2411,6 +2429,19 @@ export class MLDocument extends MLParentNode {
|
|
|
2411
2429
|
specificity: matches.specificity,
|
|
2412
2430
|
rule: mergedRule,
|
|
2413
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
|
+
}
|
|
2414
2445
|
}
|
|
2415
2446
|
}
|
|
2416
2447
|
// overwrite rule to child node
|
|
@@ -2442,6 +2473,26 @@ export class MLDocument extends MLParentNode {
|
|
|
2442
2473
|
if (rule == null) {
|
|
2443
2474
|
continue;
|
|
2444
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
|
+
}
|
|
2445
2496
|
const convertedRule = exchangeValueOnRule(rule, matches.data ?? {});
|
|
2446
2497
|
if (convertedRule === undefined) {
|
|
2447
2498
|
continue;
|
|
@@ -2456,6 +2507,21 @@ export class MLDocument extends MLParentNode {
|
|
|
2456
2507
|
rule: mergedRule,
|
|
2457
2508
|
});
|
|
2458
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
|
+
}
|
|
2459
2525
|
}
|
|
2460
2526
|
}
|
|
2461
2527
|
}
|
package/lib/ruleset/index.d.ts
CHANGED
|
@@ -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
|
}
|
package/lib/ruleset/index.js
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "5.0.0-rc.2",
|
|
4
4
|
"description": "The core module of markuplint",
|
|
5
|
-
"repository":
|
|
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.
|
|
34
|
-
"@markuplint/html-parser": "5.0.0-rc.
|
|
35
|
-
"@markuplint/html-spec": "5.0.0-rc.
|
|
36
|
-
"@markuplint/i18n": "5.0.0-rc.
|
|
37
|
-
"@markuplint/ml-ast": "5.0.0-rc.
|
|
38
|
-
"@markuplint/ml-config": "5.0.0-rc.
|
|
39
|
-
"@markuplint/ml-spec": "5.0.0-rc.
|
|
40
|
-
"@markuplint/parser-utils": "5.0.0-rc.
|
|
41
|
-
"@markuplint/selector": "5.0.0-rc.
|
|
42
|
-
"@markuplint/shared": "5.0.0-rc.
|
|
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",
|
|
43
47
|
"@types/debug": "4.1.13",
|
|
44
48
|
"debug": "4.4.3",
|
|
45
49
|
"is-plain-object": "5.0.0",
|
|
46
50
|
"type-fest": "5.5.0"
|
|
47
51
|
},
|
|
48
|
-
"gitHead": "
|
|
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
|
-
}
|