-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcollectSemanticWarnings.ts
More file actions
108 lines (99 loc) · 4.53 KB
/
Copy pathcollectSemanticWarnings.ts
File metadata and controls
108 lines (99 loc) · 4.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// Static semantic checks derived from the documented constraints in
// `packages/interact/rules/*.md`. These run with no DOM and no runtime — they
// inspect the parsed config shape only. Warning/info checks are collected by
// `collectSemanticWarnings` (consumed by the schema `transform`).
import type { Path, SemanticIssue, AnyConfig, Visitors } from '../types';
import { checkCSSPropertyNames, checkInvalidInset } from './cssSyntax';
import { checkSameElementRetrigger, checkHitAreaShift } from './fouc';
import {
checkListItemSelectorWithoutContainer,
checkRedundantSelector,
checkPointerAxisIgnored,
} from './ignored';
import {
checkScrollPresetRange,
checkEmptyStyleProperties,
checkStateRemoveWithoutEffectId,
} from './partialData';
import { checkRecommendedFill } from './recommendedPatterns';
import { checkReduceGatedScrub } from './reducedMotion';
import { findAnimationEndWarnings } from './animationEndGraph';
// Single traversal of top-level registry effects/sequences and per-interaction
// effects/sequences, supplying each node's `path`, whether it is a top-level
// registry definition, and its owning interaction (when known).
export function walkConfig(config: AnyConfig, visitors: Visitors): void {
const { onInteraction, onEffect, onSequence } = visitors;
Object.entries(config.effects ?? {}).forEach(([id, effect]) => {
onEffect(['effects', id], effect, true, undefined);
});
Object.entries(config.sequences ?? {}).forEach(([id, sequence]) => {
onSequence(['sequences', id], sequence, true, undefined);
sequence.effects?.forEach((effect, ei) => {
onEffect(['sequences', id, 'effects', ei], effect, false, undefined);
});
});
config.interactions.forEach((interaction, i) => {
onInteraction?.(['interactions', i], interaction);
interaction.effects?.forEach((effect, ei) => {
onEffect(['interactions', i, 'effects', ei], effect, false, interaction);
});
interaction.sequences?.forEach((sequence, si) => {
const seqPath: Path = ['interactions', i, 'sequences', si];
onSequence(seqPath, sequence, false, interaction);
sequence.effects?.forEach((effect, ei) => {
onEffect([...seqPath, 'effects', ei], effect, false, interaction);
});
});
});
}
// Collect every warning/info-level semantic issue (consumed by `transform`).
export function collectSemanticWarnings(config: AnyConfig): SemanticIssue[] {
const warnings: SemanticIssue[] = [];
const configConditions = config.conditions ?? {};
walkConfig(config, {
onInteraction: (path, interaction) => {
warnings.push(...checkListItemSelectorWithoutContainer(path, interaction));
warnings.push(...checkRedundantSelector(path, interaction));
warnings.push(...checkInvalidInset(path, interaction));
warnings.push(
...checkReduceGatedScrub(
path,
interaction.conditions,
configConditions,
interaction.trigger,
),
);
},
// checks that only look at depth>1 properties in effects/sequences do not need resolving
onEffect: (path, effect, isTopLevel, owner) => {
const { effectId } = effect;
const resolvedEffect =
!isTopLevel && effectId
? { ...((config.effects ?? {})[effectId] ?? {}), ...effect }
: effect;
warnings.push(...checkSameElementRetrigger(path, resolvedEffect, owner));
warnings.push(...checkHitAreaShift(path, resolvedEffect, owner));
warnings.push(...checkScrollPresetRange(path, resolvedEffect, owner));
warnings.push(...checkListItemSelectorWithoutContainer(path, resolvedEffect));
warnings.push(...checkRedundantSelector(path, resolvedEffect));
warnings.push(...checkEmptyStyleProperties(path, effect));
warnings.push(...checkStateRemoveWithoutEffectId(path, effect));
warnings.push(...checkRecommendedFill(path, resolvedEffect, owner));
warnings.push(...checkPointerAxisIgnored(path, resolvedEffect, owner));
warnings.push(...checkCSSPropertyNames(path, effect));
warnings.push(
...checkReduceGatedScrub(path, resolvedEffect.conditions, configConditions, owner?.trigger),
);
},
onSequence: (path, sequence, isTopLevel, owner) => {
const { sequenceId } = sequence;
const resolvedSequence =
!isTopLevel && sequenceId
? { ...((config.sequences ?? {})[sequenceId] ?? {}), ...sequence }
: sequence;
warnings.push(...checkSameElementRetrigger(path, resolvedSequence, owner));
},
});
warnings.push(...findAnimationEndWarnings(config));
return warnings;
}