• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

csstree / validator / 11292848853

11 Oct 2024 01:02PM UTC coverage: 95.658% (-0.1%) from 95.807%
11292848853

push

github

lahmatiy
Wrap tests in describe()

121 of 136 branches covered (88.97%)

Branch coverage included in aggregate %.

540 of 555 relevant lines covered (97.3%)

21.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.71
/lib/validate.js
1
import { lexer, parse, walk, property as propertyName } from 'css-tree';
17✔
2

17✔
3
const syntax = lexer;
17✔
4

17✔
5
function isTargetError(error) {
144✔
6
    if (!error) {
144✔
7
        return null;
64✔
8
    }
64✔
9

80✔
10
    if (error.name !== 'SyntaxError' &&
132✔
11
        error.name !== 'SyntaxMatchError' &&
144✔
12
        error.name !== 'SyntaxReferenceError') {
144!
13
        return null;
×
14
    }
×
15

80✔
16
    return error;
80✔
17
}
144✔
18

17✔
19
function locFromIdentStart(ident, loc) {
36✔
20
    if (!loc) {
36!
21
        return null;
×
22
    }
×
23

36✔
24
    const { source, start } = loc;
36✔
25

36✔
26
    return {
36✔
27
        source,
36✔
28
        start,
36✔
29
        end: {
36✔
30
            offset: start.offset + ident.length,
36✔
31
            line: start.line,
36✔
32
            column: start.column + ident.length
36✔
33
        }
36✔
34
    };
36✔
35
}
36✔
36

17✔
37
function makeErrorEntry(error, override) {
80✔
38
    Object.assign(error, override);
80✔
39

80✔
40
    if (error.loc) {
80✔
41
        const { source, start, end } = error.loc;
80✔
42

80✔
43
        // recreate loc to ensure shape and avoid sharing
80✔
44
        error.loc = {
80✔
45
            source,
80✔
46
            start: {
80✔
47
                offset: start.offset,
80✔
48
                line: start.line,
80✔
49
                column: start.column
80✔
50
            },
80✔
51
            end: {
80✔
52
                offset: end.offset,
80✔
53
                line: end.line,
80✔
54
                column: end.column
80✔
55
            }
80✔
56
        };
80✔
57

80✔
58
        // map loc.start values on error
80✔
59
        Object.assign(error, error.loc.start);
80✔
60
    }
80✔
61

80✔
62
    return error;
80✔
63
}
80✔
64

17✔
65
export function validateAtrule(node) {
17✔
66
    const atrule = node.name;
9✔
67
    const errors = [];
9✔
68
    let error;
9✔
69

9✔
70
    if (error = isTargetError(syntax.checkAtruleName(atrule))) {
9✔
71
        errors.push(makeErrorEntry(error, {
1✔
72
            atrule,
1✔
73
            loc: locFromIdentStart('@' + atrule, node.loc)
1✔
74
        }));
1✔
75

1✔
76
        return errors;
1✔
77
    }
1✔
78

8✔
79
    errors.push(...validateAtrulePrelude(
8✔
80
        atrule,
8✔
81
        node.prelude,
8✔
82
        node.loc
8✔
83
    ));
8✔
84

8✔
85
    if (node.block && node.block.children) {
9✔
86
        node.block.children.forEach(child => {
8✔
87
            if (child.type === 'Declaration') {
6✔
88
                errors.push(...validateAtruleDescriptor(
6✔
89
                    atrule,
6✔
90
                    child.property,
6✔
91
                    child.value,
6✔
92
                    child.loc
6✔
93
                ));
6✔
94
            }
6✔
95
        });
8✔
96
    }
8✔
97

8✔
98
    return errors;
8✔
99
}
9✔
100

17✔
101
export function validateAtrulePrelude(atrule, prelude, atruleLoc) {
17✔
102
    const errors = [];
8✔
103
    let error;
8✔
104

8✔
105
    if (error = isTargetError(syntax.checkAtrulePrelude(atrule, prelude))) {
8✔
106
        errors.push(makeErrorEntry(error, {
3✔
107
            atrule,
3✔
108
            loc: prelude ? prelude.loc : locFromIdentStart('@' + atrule, atruleLoc)
3✔
109
        }));
3✔
110
    } else if (error = isTargetError(syntax.matchAtrulePrelude(atrule, prelude).error)) {
8✔
111
        errors.push(makeErrorEntry(error, {
1✔
112
            atrule,
1✔
113
            ...error.rawMessage === 'Mismatch' &&
1✔
114
                { details: error.message, message: 'Invalid value for `@' + atrule + '` prelude' }
1✔
115
        }));
1✔
116
    }
1✔
117

8✔
118
    return errors;
8✔
119
}
8✔
120

17✔
121
export function validateAtruleDescriptor(atrule, descriptor, value, descriptorLoc) {
17✔
122
    const errors = [];
6✔
123
    let error;
6✔
124

6✔
125
    if (error = isTargetError(syntax.checkAtruleDescriptorName(atrule, descriptor))) {
6✔
126
        errors.push(makeErrorEntry(error, {
4✔
127
            atrule,
4✔
128
            descriptor,
4✔
129
            loc: locFromIdentStart(descriptor, descriptorLoc)
4✔
130
        }));
4✔
131
    } else {
6✔
132
        if (error = isTargetError(syntax.matchAtruleDescriptor(atrule, descriptor, value).error)) {
2✔
133
            errors.push(makeErrorEntry(error, {
1✔
134
                atrule,
1✔
135
                descriptor,
1✔
136
                ...error.rawMessage === 'Mismatch' &&
1✔
137
                    { details: error.message, message: 'Invalid value for `' + descriptor + '` descriptor' }
1✔
138
            }));
1✔
139
        }
1✔
140
    }
2✔
141

6✔
142
    return errors;
6✔
143
}
6✔
144

17✔
145
export function validateDeclaration(property, value, declarationLoc) {
17✔
146
    const errors = [];
77✔
147
    let error;
77✔
148

77✔
149
    if (propertyName(property).custom) {
77✔
150
        return errors;
5✔
151
    }
5✔
152

72✔
153
    if (error = isTargetError(syntax.checkPropertyName(property))) {
77✔
154
        errors.push(makeErrorEntry(error, {
30✔
155
            property,
30✔
156
            loc: locFromIdentStart(property, declarationLoc)
30✔
157
        }));
30✔
158
    } else if (error = isTargetError(syntax.matchProperty(property, value).error)) {
65✔
159
        errors.push(makeErrorEntry(error, {
40✔
160
            property,
40✔
161
            ...error.rawMessage === 'Mismatch' &&
40✔
162
                { details: error.message, message: 'Invalid value for `' + property + '` property' }
40✔
163
        }));
40✔
164
    }
40✔
165

72✔
166
    return errors;
72✔
167
}
77✔
168

17✔
169
export function validateRule(node) {
17✔
170
    const errors = [];
49✔
171

49✔
172
    if (node.block && node.block.children) {
49✔
173
        node.block.children.forEach(child => {
49✔
174
            if (child.type === 'Declaration') {
83✔
175
                errors.push(...validateDeclaration(
77✔
176
                    child.property,
77✔
177
                    child.value,
77✔
178
                    child.loc
77✔
179
                ));
77✔
180
            }
77✔
181
        });
49✔
182
    }
49✔
183

49✔
184
    return errors;
49✔
185
}
49✔
186

17✔
187
export function validate(css, filename) {
17✔
188
    const errors = [];
55✔
189
    const ast = typeof css !== 'string'
55✔
190
        ? css
55✔
191
        : parse(css, {
55✔
192
            filename,
54✔
193
            positions: true,
54✔
194
            parseAtrulePrelude: false,
54✔
195
            parseRulePrelude: false,
54✔
196
            parseValue: false,
54✔
197
            parseCustomProperty: false,
54✔
198
            onParseError(error) {
54✔
199
                errors.push(error);
7✔
200
            }
7✔
201
        });
55✔
202

55✔
203
    walk(ast, {
55✔
204
        visit: 'Atrule',
55✔
205
        enter(node) {
55✔
206
            errors.push(...validateAtrule(node));
9✔
207
        }
9✔
208
    });
55✔
209

55✔
210
    walk(ast, {
55✔
211
        visit: 'Rule',
55✔
212
        enter(node) {
55✔
213
            errors.push(...validateRule(node));
49✔
214
        }
49✔
215
    });
55✔
216

55✔
217
    return errors;
55✔
218
};
17✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc