@markuplint/rules 4.6.0 → 4.7.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/lib/index.d.ts +15 -15
- package/lib/permitted-contents/choice.d.ts +1 -1
- package/lib/permitted-contents/choice.js +2 -2
- package/lib/permitted-contents/complex-branch.d.ts +2 -2
- package/lib/permitted-contents/complex-branch.js +5 -5
- package/lib/permitted-contents/count-pattern.d.ts +2 -2
- package/lib/permitted-contents/count-pattern.js +3 -3
- package/lib/permitted-contents/index.js +9 -3
- package/lib/permitted-contents/matches-selector.d.ts +1 -1
- package/lib/permitted-contents/matches-selector.js +21 -21
- package/lib/permitted-contents/order.d.ts +2 -2
- package/lib/permitted-contents/order.js +3 -3
- package/lib/permitted-contents/recursive-branch.d.ts +1 -1
- package/lib/permitted-contents/recursive-branch.js +4 -4
- package/lib/permitted-contents/represent-transparent-nodes.d.ts +3 -1
- package/lib/permitted-contents/represent-transparent-nodes.js +73 -52
- package/lib/permitted-contents/start.js +49 -43
- package/lib/permitted-contents/transparent.d.ts +1 -1
- package/lib/permitted-contents/transparent.js +6 -6
- package/lib/permitted-contents/types.d.ts +4 -0
- package/lib/permitted-contents/utils.d.ts +2 -3
- package/lib/permitted-contents/utils.js +0 -14
- package/package.json +9 -9
package/lib/index.d.ts
CHANGED
|
@@ -16,38 +16,38 @@ declare const rules: {
|
|
|
16
16
|
readonly 'id-duplication': Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
|
|
17
17
|
readonly 'ineffective-attr': Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
|
|
18
18
|
readonly 'invalid-attr': Readonly<import("@markuplint/ml-core").RuleSeed<boolean, {
|
|
19
|
-
allowAttrs?:
|
|
19
|
+
allowAttrs?: Record<string, {
|
|
20
|
+
enum: [string, ...string[]];
|
|
21
|
+
} | {
|
|
22
|
+
pattern: string;
|
|
23
|
+
} | {
|
|
24
|
+
type: import("packages/@markuplint/ml-spec/lib/index.js").AttributeType;
|
|
25
|
+
}> | (string | {
|
|
20
26
|
name: string;
|
|
21
|
-
value: ({
|
|
27
|
+
value: import("packages/@markuplint/ml-spec/lib/index.js").AttributeType | ({
|
|
22
28
|
enum: [string, ...string[]];
|
|
23
29
|
} | {
|
|
24
30
|
pattern: string;
|
|
25
31
|
} | {
|
|
26
32
|
type: import("packages/@markuplint/ml-spec/lib/index.js").AttributeType;
|
|
27
|
-
})
|
|
28
|
-
})[] |
|
|
33
|
+
});
|
|
34
|
+
})[] | undefined;
|
|
35
|
+
disallowAttrs?: Record<string, {
|
|
29
36
|
enum: [string, ...string[]];
|
|
30
37
|
} | {
|
|
31
38
|
pattern: string;
|
|
32
39
|
} | {
|
|
33
40
|
type: import("packages/@markuplint/ml-spec/lib/index.js").AttributeType;
|
|
34
|
-
}> |
|
|
35
|
-
disallowAttrs?: (string | {
|
|
41
|
+
}> | (string | {
|
|
36
42
|
name: string;
|
|
37
|
-
value: ({
|
|
43
|
+
value: import("packages/@markuplint/ml-spec/lib/index.js").AttributeType | ({
|
|
38
44
|
enum: [string, ...string[]];
|
|
39
45
|
} | {
|
|
40
46
|
pattern: string;
|
|
41
47
|
} | {
|
|
42
48
|
type: import("packages/@markuplint/ml-spec/lib/index.js").AttributeType;
|
|
43
|
-
})
|
|
44
|
-
})[] |
|
|
45
|
-
enum: [string, ...string[]];
|
|
46
|
-
} | {
|
|
47
|
-
pattern: string;
|
|
48
|
-
} | {
|
|
49
|
-
type: import("packages/@markuplint/ml-spec/lib/index.js").AttributeType;
|
|
50
|
-
}> | undefined;
|
|
49
|
+
});
|
|
50
|
+
})[] | undefined;
|
|
51
51
|
ignoreAttrNamePrefix?: string | string[] | undefined;
|
|
52
52
|
allowToAddPropertiesForPretender?: boolean | undefined;
|
|
53
53
|
attrs?: Record<string, ({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ChildNode, Options, Result, Specs } from './types.js';
|
|
2
2
|
import type { PermittedContentChoice } from '@markuplint/ml-spec';
|
|
3
3
|
import type { ReadonlyDeep } from 'type-fest';
|
|
4
|
-
export declare function choice(pattern: ReadonlyDeep<PermittedContentChoice>,
|
|
4
|
+
export declare function choice(pattern: ReadonlyDeep<PermittedContentChoice>, childNodes: readonly ChildNode[], specs: Specs, options: Options, depth: number): Result;
|
|
@@ -4,9 +4,9 @@ import { Collection, modelLog } from './utils.js';
|
|
|
4
4
|
const indexes = new WeakMap();
|
|
5
5
|
export function choice(pattern,
|
|
6
6
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
7
|
-
|
|
7
|
+
childNodes, specs, options, depth) {
|
|
8
8
|
const choiceLog = cmLog.extend(`choice#${depth}`);
|
|
9
|
-
const collection = new Collection(
|
|
9
|
+
const collection = new Collection(childNodes);
|
|
10
10
|
const unmatchedResults = [];
|
|
11
11
|
let i = 0;
|
|
12
12
|
for (const some of pattern.choice) {
|
|
@@ -5,10 +5,10 @@ import type { ReadonlyDeep } from 'type-fest';
|
|
|
5
5
|
* Check content condition
|
|
6
6
|
*
|
|
7
7
|
* @param pattern
|
|
8
|
-
* @param
|
|
8
|
+
* @param childNodes
|
|
9
9
|
* @param specs
|
|
10
10
|
* @param options
|
|
11
11
|
* @param depth
|
|
12
12
|
* @returns
|
|
13
13
|
*/
|
|
14
|
-
export declare function complexBranch(pattern: ReadonlyDeep<PermittedContentPattern>,
|
|
14
|
+
export declare function complexBranch(pattern: ReadonlyDeep<PermittedContentPattern>, childNodes: readonly ChildNode[], specs: Specs, options: Options, depth: number): Result;
|
|
@@ -6,7 +6,7 @@ import { isChoice, isTransparent } from './utils.js';
|
|
|
6
6
|
* Check content condition
|
|
7
7
|
*
|
|
8
8
|
* @param pattern
|
|
9
|
-
* @param
|
|
9
|
+
* @param childNodes
|
|
10
10
|
* @param specs
|
|
11
11
|
* @param options
|
|
12
12
|
* @param depth
|
|
@@ -14,12 +14,12 @@ import { isChoice, isTransparent } from './utils.js';
|
|
|
14
14
|
*/
|
|
15
15
|
export function complexBranch(pattern,
|
|
16
16
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
17
|
-
|
|
17
|
+
childNodes, specs, options, depth) {
|
|
18
18
|
if (isChoice(pattern)) {
|
|
19
|
-
return choice(pattern,
|
|
19
|
+
return choice(pattern, childNodes, specs, options, depth);
|
|
20
20
|
}
|
|
21
21
|
if (isTransparent(pattern)) {
|
|
22
|
-
return transparent(
|
|
22
|
+
return transparent(childNodes);
|
|
23
23
|
}
|
|
24
|
-
return countPattern(pattern,
|
|
24
|
+
return countPattern(pattern, childNodes, specs, options, depth);
|
|
25
25
|
}
|
|
@@ -5,10 +5,10 @@ import type { ReadonlyDeep } from 'type-fest';
|
|
|
5
5
|
* Check count
|
|
6
6
|
*
|
|
7
7
|
* @param pattern
|
|
8
|
-
* @param
|
|
8
|
+
* @param childNodes
|
|
9
9
|
* @param specs
|
|
10
10
|
* @param options
|
|
11
11
|
* @param depth
|
|
12
12
|
* @returns
|
|
13
13
|
*/
|
|
14
|
-
export declare function countPattern(pattern: ReadonlyDeep<PermittedContentOneOrMore> | ReadonlyDeep<PermittedContentOptional> | ReadonlyDeep<PermittedContentRequire> | ReadonlyDeep<PermittedContentZeroOrMore>,
|
|
14
|
+
export declare function countPattern(pattern: ReadonlyDeep<PermittedContentOneOrMore> | ReadonlyDeep<PermittedContentOptional> | ReadonlyDeep<PermittedContentRequire> | ReadonlyDeep<PermittedContentZeroOrMore>, childNodes: readonly ChildNode[], specs: Specs, options: Options, depth: number): Result;
|
|
@@ -6,7 +6,7 @@ const cLog = cmLog.extend('countCompereResult');
|
|
|
6
6
|
* Check count
|
|
7
7
|
*
|
|
8
8
|
* @param pattern
|
|
9
|
-
* @param
|
|
9
|
+
* @param childNodes
|
|
10
10
|
* @param specs
|
|
11
11
|
* @param options
|
|
12
12
|
* @param depth
|
|
@@ -14,9 +14,9 @@ const cLog = cmLog.extend('countCompereResult');
|
|
|
14
14
|
*/
|
|
15
15
|
export function countPattern(pattern,
|
|
16
16
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
17
|
-
|
|
17
|
+
childNodes, specs, options, depth) {
|
|
18
18
|
const ptLog = cmLog.extend(`countPattern#${depth}`);
|
|
19
|
-
const collection = new Collection(
|
|
19
|
+
const collection = new Collection(childNodes);
|
|
20
20
|
const { model, min, max, repeat, missingType } = normalizeModel(pattern);
|
|
21
21
|
ptLog('Model:\n RegEx: %s\n Schema: %o', modelLog(model, repeat), pattern);
|
|
22
22
|
let prevResult = null;
|
|
@@ -7,12 +7,10 @@ export default createRule({
|
|
|
7
7
|
defaultValue: [],
|
|
8
8
|
defaultOptions: {
|
|
9
9
|
ignoreHasMutableChildren: true,
|
|
10
|
+
evaluateConditionalChildNodes: false,
|
|
10
11
|
},
|
|
11
12
|
async verify({ document, report, t }) {
|
|
12
13
|
await document.walkOn('Element', el => {
|
|
13
|
-
if (el.rule.options.ignoreHasMutableChildren && el.hasMutableChildren()) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
14
|
const results = contentModel(el, el.rule.value, el.rule.options);
|
|
17
15
|
for (const { type, scope, query, hint } of results) {
|
|
18
16
|
let message = '';
|
|
@@ -28,6 +26,10 @@ export default createRule({
|
|
|
28
26
|
break;
|
|
29
27
|
}
|
|
30
28
|
case 'MISSING_NODE_ONE_OR_MORE': {
|
|
29
|
+
if (scope.rule.options.ignoreHasMutableChildren &&
|
|
30
|
+
(!scope.is(scope.ELEMENT_NODE) || scope.hasMutableChildren())) {
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
31
33
|
message =
|
|
32
34
|
message ||
|
|
33
35
|
t('Require {0}', t('one or more elements')) + t('. ') + '(' + t('Need "{0*}"', query) + ')';
|
|
@@ -38,6 +40,10 @@ export default createRule({
|
|
|
38
40
|
break;
|
|
39
41
|
}
|
|
40
42
|
case 'MISSING_NODE_REQUIRED': {
|
|
43
|
+
if (scope.rule.options.ignoreHasMutableChildren &&
|
|
44
|
+
(!scope.is(scope.ELEMENT_NODE) || scope.hasMutableChildren())) {
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
41
47
|
message =
|
|
42
48
|
message ||
|
|
43
49
|
t('Require {0}', t('an {0}', 'element')) + t('. ') + '(' + t('Need "{0*}"', query) + ')';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ChildNode, Result, Specs } from './types.js';
|
|
2
2
|
export type SelectorResult = Result<'UNMATCHED_SELECTOR_BUT_MAY_EMPTY' | 'MISSING_NODE' | 'UNMATCHED_SELECTORS'>;
|
|
3
|
-
export declare function matchesSelector(query: string,
|
|
3
|
+
export declare function matchesSelector(query: string, childNode: ChildNode | undefined, specs: Specs, depth: number): SelectorResult;
|
|
@@ -3,10 +3,10 @@ import { cmLog } from './debug.js';
|
|
|
3
3
|
import { cleanObject, matches } from './utils.js';
|
|
4
4
|
export function matchesSelector(query,
|
|
5
5
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
6
|
-
|
|
6
|
+
childNode, specs, depth) {
|
|
7
7
|
const nodeLog = cmLog.extend(`node#${depth}`);
|
|
8
8
|
const { selector, hasText, hasCustom } = optCondition(query, specs);
|
|
9
|
-
if (
|
|
9
|
+
if (childNode == null) {
|
|
10
10
|
if (hasText) {
|
|
11
11
|
nodeLog('<#text>.matches(%s) => ""', query);
|
|
12
12
|
return {
|
|
@@ -27,23 +27,23 @@ node, specs, depth) {
|
|
|
27
27
|
hint: {},
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
-
if (
|
|
30
|
+
if (childNode.is(childNode.TEXT_NODE)) {
|
|
31
31
|
if (hasText) {
|
|
32
|
-
nodeLog('<#text>.matches(%s) => "%s"', query,
|
|
32
|
+
nodeLog('<#text>.matches(%s) => "%s"', query, childNode.raw.trim());
|
|
33
33
|
return {
|
|
34
34
|
type: 'MATCHED',
|
|
35
|
-
matched: [
|
|
35
|
+
matched: [childNode],
|
|
36
36
|
unmatched: [],
|
|
37
37
|
zeroMatch: true,
|
|
38
38
|
query,
|
|
39
39
|
hint: {},
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
-
if (
|
|
42
|
+
if (childNode.isWhitespace()) {
|
|
43
43
|
nodeLog('<#text>.matches(%s) => WHITESPACE', query);
|
|
44
44
|
return {
|
|
45
45
|
type: 'MATCHED',
|
|
46
|
-
matched: [
|
|
46
|
+
matched: [childNode],
|
|
47
47
|
unmatched: [],
|
|
48
48
|
zeroMatch: true,
|
|
49
49
|
query,
|
|
@@ -54,41 +54,41 @@ node, specs, depth) {
|
|
|
54
54
|
return {
|
|
55
55
|
type: 'UNEXPECTED_EXTRA_NODE',
|
|
56
56
|
matched: [],
|
|
57
|
-
unmatched: [
|
|
57
|
+
unmatched: [childNode],
|
|
58
58
|
zeroMatch: false,
|
|
59
59
|
query,
|
|
60
60
|
hint: {},
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
-
if (
|
|
64
|
-
nodeLog('%s.matches(%s) => PBlock',
|
|
63
|
+
if (childNode.is(childNode.MARKUPLINT_PREPROCESSOR_BLOCK)) {
|
|
64
|
+
nodeLog('%s.matches(%s) => PBlock', childNode.raw, query);
|
|
65
65
|
return {
|
|
66
66
|
type: 'MATCHED',
|
|
67
|
-
matched: [
|
|
67
|
+
matched: [childNode],
|
|
68
68
|
unmatched: [],
|
|
69
69
|
zeroMatch: !!hasText,
|
|
70
70
|
query,
|
|
71
71
|
hint: {},
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
-
if (
|
|
75
|
-
if (
|
|
76
|
-
nodeLog('%s.matches(%s) => CustomElement',
|
|
74
|
+
if (childNode.is(childNode.ELEMENT_NODE)) {
|
|
75
|
+
if (childNode.elementType !== 'html' && hasCustom) {
|
|
76
|
+
nodeLog('%s.matches(%s) => CustomElement', childNode.raw, query);
|
|
77
77
|
return {
|
|
78
78
|
type: 'MATCHED',
|
|
79
|
-
matched: [
|
|
79
|
+
matched: [childNode],
|
|
80
80
|
unmatched: [],
|
|
81
81
|
zeroMatch: !!hasText,
|
|
82
82
|
query,
|
|
83
83
|
hint: {},
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
|
-
const result = matches(selector,
|
|
87
|
-
nodeLog('%s.matches(%s) => %s',
|
|
86
|
+
const result = matches(selector, childNode, specs);
|
|
87
|
+
nodeLog('%s.matches(%s) => %s', childNode.raw, query, result.matched);
|
|
88
88
|
if (result.matched) {
|
|
89
89
|
return {
|
|
90
90
|
type: 'MATCHED',
|
|
91
|
-
matched: [
|
|
91
|
+
matched: [childNode],
|
|
92
92
|
unmatched: [],
|
|
93
93
|
zeroMatch: !!hasText,
|
|
94
94
|
query,
|
|
@@ -99,7 +99,7 @@ node, specs, depth) {
|
|
|
99
99
|
return {
|
|
100
100
|
type: 'UNMATCHED_SELECTOR_BUT_MAY_EMPTY',
|
|
101
101
|
matched: [],
|
|
102
|
-
unmatched: [
|
|
102
|
+
unmatched: [childNode],
|
|
103
103
|
zeroMatch: true,
|
|
104
104
|
query,
|
|
105
105
|
hint: cleanObject({
|
|
@@ -110,7 +110,7 @@ node, specs, depth) {
|
|
|
110
110
|
return {
|
|
111
111
|
type: 'UNMATCHED_SELECTORS',
|
|
112
112
|
matched: [],
|
|
113
|
-
unmatched: [
|
|
113
|
+
unmatched: [childNode],
|
|
114
114
|
zeroMatch: false,
|
|
115
115
|
query,
|
|
116
116
|
hint: cleanObject({
|
|
@@ -120,7 +120,7 @@ node, specs, depth) {
|
|
|
120
120
|
}
|
|
121
121
|
return {
|
|
122
122
|
type: 'MATCHED',
|
|
123
|
-
matched: [
|
|
123
|
+
matched: [childNode],
|
|
124
124
|
unmatched: [],
|
|
125
125
|
zeroMatch: !!hasText,
|
|
126
126
|
query,
|
|
@@ -5,10 +5,10 @@ import type { ReadonlyDeep } from 'type-fest';
|
|
|
5
5
|
* Check ordered array
|
|
6
6
|
*
|
|
7
7
|
* @param contents
|
|
8
|
-
* @param
|
|
8
|
+
* @param childNodes
|
|
9
9
|
* @param specs
|
|
10
10
|
* @param options
|
|
11
11
|
* @param depth
|
|
12
12
|
* @returns
|
|
13
13
|
*/
|
|
14
|
-
export declare function order(contents: ReadonlyDeep<PermittedContentPattern[]>,
|
|
14
|
+
export declare function order(contents: ReadonlyDeep<PermittedContentPattern[]>, childNodes: readonly ChildNode[], specs: Specs, options: Options, depth: number): Result;
|
|
@@ -6,7 +6,7 @@ import { Collection, mergeHints, modelLog } from './utils.js';
|
|
|
6
6
|
* Check ordered array
|
|
7
7
|
*
|
|
8
8
|
* @param contents
|
|
9
|
-
* @param
|
|
9
|
+
* @param childNodes
|
|
10
10
|
* @param specs
|
|
11
11
|
* @param options
|
|
12
12
|
* @param depth
|
|
@@ -14,11 +14,11 @@ import { Collection, mergeHints, modelLog } from './utils.js';
|
|
|
14
14
|
*/
|
|
15
15
|
export function order(contents,
|
|
16
16
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
17
|
-
|
|
17
|
+
childNodes, specs, options, depth) {
|
|
18
18
|
const orderLog = cmLog.extend(`order#${depth}`);
|
|
19
19
|
const btLog = cmLog.extend(`backtrack#${depth}`);
|
|
20
20
|
const patterns = deepCopy(contents);
|
|
21
|
-
const collection = new Collection(
|
|
21
|
+
const collection = new Collection(childNodes);
|
|
22
22
|
orderLog('Model:\n RegEx: %s\n Schema: %o', modelLog(patterns, ''), patterns);
|
|
23
23
|
orderLog('Starts: %s', collection);
|
|
24
24
|
let result = undefined;
|
|
@@ -2,4 +2,4 @@ import type { SelectorResult } from './matches-selector.js';
|
|
|
2
2
|
import type { ChildNode, Options, Specs } from './types.js';
|
|
3
3
|
import type { PermittedContentPattern, Model } from '@markuplint/ml-spec';
|
|
4
4
|
import type { ReadonlyDeep } from 'type-fest';
|
|
5
|
-
export declare function recursiveBranch(model: ReadonlyDeep<Model | PermittedContentPattern[]>,
|
|
5
|
+
export declare function recursiveBranch(model: ReadonlyDeep<Model | PermittedContentPattern[]>, childNodes: readonly ChildNode[], specs: Specs, options: Options, depth: number): SelectorResult;
|
|
@@ -3,14 +3,14 @@ import { order } from './order.js';
|
|
|
3
3
|
import { Collection, isModel } from './utils.js';
|
|
4
4
|
export function recursiveBranch(model,
|
|
5
5
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
6
|
-
|
|
6
|
+
childNodes, specs, options, depth) {
|
|
7
7
|
if (!isModel(model)) {
|
|
8
|
-
return order(model,
|
|
8
|
+
return order(model, childNodes, specs, options, depth + 1);
|
|
9
9
|
}
|
|
10
10
|
if (typeof model === 'string') {
|
|
11
|
-
return matchesSelector(model,
|
|
11
|
+
return matchesSelector(model, childNodes[0], specs, depth);
|
|
12
12
|
}
|
|
13
|
-
const collection = new Collection(
|
|
13
|
+
const collection = new Collection(childNodes);
|
|
14
14
|
let lastUnmatched = null;
|
|
15
15
|
for (const query of model) {
|
|
16
16
|
const result = matchesSelector(query, collection.unmatched[0], specs, depth);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ChildNode, Options, Result, Specs } from './types.js';
|
|
2
2
|
export declare const transparentMode: Map<ChildNode, true>;
|
|
3
|
-
|
|
3
|
+
type TransparentNode = {
|
|
4
4
|
nodes: ChildNode[];
|
|
5
5
|
errors: Result[];
|
|
6
6
|
};
|
|
7
|
+
export declare function representTransparentNodes(childNodes: readonly ChildNode[], specs: Specs, options: Options): TransparentNode[];
|
|
8
|
+
export {};
|
|
@@ -1,73 +1,94 @@
|
|
|
1
1
|
import { getContentModel } from '@markuplint/ml-spec';
|
|
2
|
+
import { branchesToPatterns } from '@markuplint/shared';
|
|
2
3
|
import { order } from './order.js';
|
|
3
|
-
import { Collection,
|
|
4
|
+
import { Collection, isTransparent, matches } from './utils.js';
|
|
4
5
|
export const transparentMode = new Map();
|
|
5
6
|
export function representTransparentNodes(
|
|
6
7
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (!node.is(node.ELEMENT_NODE)) {
|
|
17
|
-
result.push(node);
|
|
8
|
+
childNodes, specs, options) {
|
|
9
|
+
const parentElement = childNodes[0]?.parentElement;
|
|
10
|
+
const parentResults = parentElement
|
|
11
|
+
? representTransparentNodes([parentElement], specs, options)
|
|
12
|
+
: [{ nodes: [], errors: [] }];
|
|
13
|
+
const branches = [];
|
|
14
|
+
for (const childNode of childNodes) {
|
|
15
|
+
if (!childNode.is(childNode.ELEMENT_NODE)) {
|
|
16
|
+
branches.push(childNode);
|
|
18
17
|
continue;
|
|
19
18
|
}
|
|
20
|
-
const models = getContentModel(
|
|
19
|
+
const models = getContentModel(childNode, specs.specs);
|
|
21
20
|
if (models == null || typeof models === 'boolean') {
|
|
22
|
-
|
|
21
|
+
branches.push(childNode);
|
|
23
22
|
continue;
|
|
24
23
|
}
|
|
25
24
|
const noTransparentModels = models.filter(m => !isTransparent(m));
|
|
26
25
|
if (noTransparentModels.length === models.length) {
|
|
27
|
-
|
|
26
|
+
branches.push(childNode);
|
|
28
27
|
continue;
|
|
29
28
|
}
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (!transparent || !isTransparent(transparent)) {
|
|
41
|
-
throw new Error('Unreachable code');
|
|
42
|
-
}
|
|
43
|
-
for (const _child of unmatched) {
|
|
44
|
-
const child = _child;
|
|
45
|
-
if (transparentMode.has(child)) {
|
|
46
|
-
continue;
|
|
29
|
+
const childNodesPatterns = options.evaluateConditionalChildNodes
|
|
30
|
+
? childNode.conditionalChildNodes().map(childNodes => [...childNodes])
|
|
31
|
+
: [[...childNode.childNodes].filter(child => !(child.is(child.TEXT_NODE) && child.isWhitespace()))];
|
|
32
|
+
const representPattern = [];
|
|
33
|
+
for (const childNodes of childNodesPatterns) {
|
|
34
|
+
const collection = new Collection([...childNodes]);
|
|
35
|
+
let unmatched;
|
|
36
|
+
if (noTransparentModels.length > 0) {
|
|
37
|
+
const result = order(noTransparentModels, collection.unmatched, specs, options, Number.POSITIVE_INFINITY);
|
|
38
|
+
unmatched = result.unmatched;
|
|
47
39
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
hint: {
|
|
59
|
-
not: transparentCondMatched.not,
|
|
60
|
-
transparent: node,
|
|
61
|
-
},
|
|
62
|
-
});
|
|
40
|
+
else {
|
|
41
|
+
unmatched = collection.unmatched;
|
|
42
|
+
}
|
|
43
|
+
const transparent = models.find(m => isTransparent(m));
|
|
44
|
+
if (!transparent || !isTransparent(transparent)) {
|
|
45
|
+
throw new Error('Unreachable code');
|
|
46
|
+
}
|
|
47
|
+
for (const _child of unmatched) {
|
|
48
|
+
const child = _child;
|
|
49
|
+
if (transparentMode.has(child)) {
|
|
63
50
|
continue;
|
|
64
51
|
}
|
|
52
|
+
transparentMode.set(child, true);
|
|
53
|
+
if (child.is(child.ELEMENT_NODE)) {
|
|
54
|
+
const transparentCondMatched = matches(transparent.transparent, child, specs);
|
|
55
|
+
if (!transparentCondMatched.matched) {
|
|
56
|
+
representPattern.push({
|
|
57
|
+
type: 'TRANSPARENT_MODEL_DISALLOWS',
|
|
58
|
+
matched: [],
|
|
59
|
+
unmatched: [childNode],
|
|
60
|
+
zeroMatch: false,
|
|
61
|
+
query: transparent.transparent,
|
|
62
|
+
hint: {
|
|
63
|
+
not: transparentCondMatched.not,
|
|
64
|
+
transparent: childNode,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
representPattern.push(child);
|
|
65
71
|
}
|
|
66
|
-
result.push(child);
|
|
67
72
|
}
|
|
73
|
+
branches.push(representPattern);
|
|
68
74
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
const patterns = branchesToPatterns(branches);
|
|
76
|
+
const result = parentResults.flatMap(parentResult => {
|
|
77
|
+
const patternResults = patterns.map(pattern => {
|
|
78
|
+
const nodes = pattern.filter((node) => 'nodeName' in node);
|
|
79
|
+
const errors = pattern.filter((node) => 'type' in node);
|
|
80
|
+
return {
|
|
81
|
+
nodes,
|
|
82
|
+
errors: [...parentResult.errors, ...errors],
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
if (patternResults.length === 0) {
|
|
86
|
+
return {
|
|
87
|
+
nodes: [],
|
|
88
|
+
errors: parentResult.errors,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return patternResults;
|
|
92
|
+
});
|
|
93
|
+
return result;
|
|
73
94
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { order } from './order.js';
|
|
2
2
|
import { representTransparentNodes } from './represent-transparent-nodes.js';
|
|
3
|
-
import { getChildNodesWithoutWhitespaces } from './utils.js';
|
|
4
3
|
/**
|
|
5
4
|
* Check start
|
|
6
5
|
*
|
|
@@ -13,53 +12,60 @@ import { getChildNodesWithoutWhitespaces } from './utils.js';
|
|
|
13
12
|
export function start(contents,
|
|
14
13
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
15
14
|
el, specs, options) {
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const childNodesPatterns = options.evaluateConditionalChildNodes
|
|
16
|
+
? el.conditionalChildNodes().map(childNodes => [...childNodes])
|
|
17
|
+
: [[...el.childNodes].filter(child => !(child.is(child.TEXT_NODE) && child.isWhitespace()))];
|
|
18
|
+
return childNodesPatterns.flatMap(childNodes => {
|
|
19
|
+
if (contents === false) {
|
|
20
|
+
if (childNodes.length > 0) {
|
|
21
|
+
return [
|
|
22
|
+
{
|
|
23
|
+
type: 'NOTHING',
|
|
24
|
+
scope: el,
|
|
25
|
+
query: ':not(*)',
|
|
26
|
+
hint: {},
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
}
|
|
18
30
|
return [
|
|
19
31
|
{
|
|
20
|
-
type: '
|
|
32
|
+
type: 'MATCHED',
|
|
21
33
|
scope: el,
|
|
22
|
-
query: '
|
|
34
|
+
query: '*',
|
|
23
35
|
hint: {},
|
|
24
36
|
},
|
|
25
37
|
];
|
|
26
38
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
type: error.type,
|
|
60
|
-
scope: error.unmatched[0] ?? el,
|
|
61
|
-
query: error.query,
|
|
62
|
-
hint: error.hint,
|
|
63
|
-
})),
|
|
64
|
-
];
|
|
39
|
+
if (contents === true) {
|
|
40
|
+
// Allows all elements
|
|
41
|
+
return [
|
|
42
|
+
{
|
|
43
|
+
type: 'MATCHED',
|
|
44
|
+
scope: el,
|
|
45
|
+
query: '*',
|
|
46
|
+
hint: {},
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
const patterns = representTransparentNodes(childNodes, specs, options);
|
|
51
|
+
return patterns.flatMap(({ nodes, errors }) => {
|
|
52
|
+
const result = order(contents, nodes, specs, options, 0);
|
|
53
|
+
return [
|
|
54
|
+
{
|
|
55
|
+
type: result.type,
|
|
56
|
+
scope: result.type === 'MISSING_NODE_REQUIRED' || result.type === 'MISSING_NODE_ONE_OR_MORE'
|
|
57
|
+
? el
|
|
58
|
+
: result.unmatched[0] ?? el,
|
|
59
|
+
query: result.query,
|
|
60
|
+
hint: result.hint,
|
|
61
|
+
},
|
|
62
|
+
...errors.map(error => ({
|
|
63
|
+
type: error.type,
|
|
64
|
+
scope: error.unmatched[0] ?? el,
|
|
65
|
+
query: error.query,
|
|
66
|
+
hint: error.hint,
|
|
67
|
+
})),
|
|
68
|
+
];
|
|
69
|
+
});
|
|
70
|
+
});
|
|
65
71
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ChildNode, Result } from './types.js';
|
|
2
|
-
export declare function transparent(
|
|
2
|
+
export declare function transparent(childNodes: readonly ChildNode[]): Result;
|
|
@@ -2,14 +2,14 @@ import { cmLog } from './debug.js';
|
|
|
2
2
|
const transparentLog = cmLog.extend('transparent');
|
|
3
3
|
export function transparent(
|
|
4
4
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
5
|
-
|
|
6
|
-
if (
|
|
5
|
+
childNodes) {
|
|
6
|
+
if (childNodes.length === 0 || childNodes[0]?.parentElement?.parentElement) {
|
|
7
7
|
transparentLog('Skipped');
|
|
8
8
|
return {
|
|
9
|
-
type:
|
|
10
|
-
matched: [...
|
|
9
|
+
type: childNodes.length === 0 ? 'MATCHED_ZERO' : 'MATCHED',
|
|
10
|
+
matched: [...childNodes],
|
|
11
11
|
unmatched: [],
|
|
12
|
-
zeroMatch:
|
|
12
|
+
zeroMatch: childNodes.length === 0,
|
|
13
13
|
query: 'transparent',
|
|
14
14
|
hint: {},
|
|
15
15
|
};
|
|
@@ -17,7 +17,7 @@ nodes) {
|
|
|
17
17
|
transparentLog('Transparent model element is component root');
|
|
18
18
|
return {
|
|
19
19
|
type: 'MATCHED',
|
|
20
|
-
matched: [...
|
|
20
|
+
matched: [...childNodes],
|
|
21
21
|
unmatched: [],
|
|
22
22
|
zeroMatch: false,
|
|
23
23
|
query: 'transparent',
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { ChildNode,
|
|
1
|
+
import type { ChildNode, Hints, MissingNodeReason, RepeatSign, Specs } from './types.js';
|
|
2
2
|
import type { PermittedContentPattern, PermittedContentChoice, PermittedContentOneOrMore, PermittedContentOptional, PermittedContentRequire, PermittedContentTransparent, PermittedContentZeroOrMore, Model } from '@markuplint/ml-spec';
|
|
3
3
|
import type { ReadonlyDeep } from 'type-fest';
|
|
4
|
-
export declare function getChildNodesWithoutWhitespaces(el: Element): ChildNode[];
|
|
5
4
|
export declare function isModel(model: ReadonlyDeep<Model | PermittedContentPattern[]>): model is ReadonlyDeep<Model>;
|
|
6
5
|
export declare function matches(selector: string, node: ChildNode, specs: Specs): {
|
|
7
6
|
matched: boolean;
|
|
@@ -30,7 +29,7 @@ export declare function mergeHints(a: Readonly<Hints>, b: Readonly<Hints>): Part
|
|
|
30
29
|
}> | undefined;
|
|
31
30
|
max?: number | undefined;
|
|
32
31
|
not?: ChildNode | undefined;
|
|
33
|
-
transparent?: Element | undefined;
|
|
32
|
+
transparent?: import("./types.js").Element | undefined;
|
|
34
33
|
}>;
|
|
35
34
|
export declare function cleanObject<T extends Object>(object: T): Partial<T>;
|
|
36
35
|
export declare class Collection {
|
|
@@ -13,20 +13,6 @@ var _Collection_locked, _Collection_matched, _Collection_nodes, _Collection_orig
|
|
|
13
13
|
import { createSelector } from '@markuplint/selector';
|
|
14
14
|
import { bgGreen, green, bgRed, bgBlue, blue, bgMagenta, cyan } from './debug.js';
|
|
15
15
|
import { transparentMode } from './represent-transparent-nodes.js';
|
|
16
|
-
const getChildNodesWithoutWhitespacesCaches = new Map();
|
|
17
|
-
export function getChildNodesWithoutWhitespaces(
|
|
18
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
19
|
-
el) {
|
|
20
|
-
let nodes = getChildNodesWithoutWhitespacesCaches.get(el);
|
|
21
|
-
if (nodes) {
|
|
22
|
-
return nodes;
|
|
23
|
-
}
|
|
24
|
-
nodes = [...el.childNodes].filter(node => {
|
|
25
|
-
return !(node.is(node.TEXT_NODE) && node.isWhitespace());
|
|
26
|
-
});
|
|
27
|
-
getChildNodesWithoutWhitespacesCaches.set(el, nodes);
|
|
28
|
-
return nodes;
|
|
29
|
-
}
|
|
30
16
|
export function isModel(model) {
|
|
31
17
|
if (typeof model === 'string') {
|
|
32
18
|
return true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/rules",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"description": "Built-in rules of markuplint",
|
|
5
5
|
"repository": "git@github.com:markuplint/markuplint.git",
|
|
6
6
|
"author": "Yusuke Hirao <yusukehirao@me.com>",
|
|
@@ -25,18 +25,18 @@
|
|
|
25
25
|
"./lib/permitted-contents/debug.js": "./lib/permitted-contents/debug.browser.js"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@markuplint/html-spec": "4.
|
|
29
|
-
"@markuplint/ml-core": "4.
|
|
30
|
-
"@markuplint/ml-spec": "4.
|
|
31
|
-
"@markuplint/selector": "4.
|
|
32
|
-
"@markuplint/shared": "4.
|
|
33
|
-
"@markuplint/types": "4.
|
|
28
|
+
"@markuplint/html-spec": "4.7.0",
|
|
29
|
+
"@markuplint/ml-core": "4.7.0",
|
|
30
|
+
"@markuplint/ml-spec": "4.5.0",
|
|
31
|
+
"@markuplint/selector": "4.6.0",
|
|
32
|
+
"@markuplint/shared": "4.3.0",
|
|
33
|
+
"@markuplint/types": "4.4.0",
|
|
34
34
|
"@types/debug": "4.1.12",
|
|
35
35
|
"@ungap/structured-clone": "1.2.0",
|
|
36
36
|
"ansi-colors": "4.1.3",
|
|
37
37
|
"chrono-node": "2.7.5",
|
|
38
38
|
"debug": "4.3.4",
|
|
39
|
-
"type-fest": "4.
|
|
39
|
+
"type-fest": "4.18.1"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "b8d7bae9bdcdad63ff79abe21b88be12abde3633"
|
|
42
42
|
}
|