forked from sveltejs/svelte
/
style.ts
105 lines (86 loc) · 2.4 KB
/
style.ts
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
// @ts-ignore
import parse from 'css-tree/parser';
import { walk } from 'estree-walker';
import { Parser } from '../index';
import { Node } from 'estree';
import { Style } from '../../interfaces';
import parser_errors from '../errors';
const regex_closing_style_tag = /<\/style\s*>/;
export default function read_style(parser: Parser, start: number, attributes: Node[]): Style {
const content_start = parser.index;
const styles = parser.read_until(regex_closing_style_tag, parser_errors.unclosed_style);
if (parser.index >= parser.template.length) {
parser.error(parser_errors.unclosed_style);
}
const content_end = parser.index;
// discard styles when css is disabled
if (parser.css_mode === 'none') {
parser.read(regex_closing_style_tag);
return null;
}
let ast;
try {
ast = parse(styles, {
positions: true,
offset: content_start,
onParseError(error) {
throw error;
}
});
} catch (err) {
if (err.name === 'SyntaxError') {
parser.error(parser_errors.css_syntax_error(err.message), err.offset);
} else {
throw err;
}
}
ast = JSON.parse(JSON.stringify(ast));
// tidy up AST
walk(ast, {
enter: (node: any) => { // `any` because this isn't an ESTree node
// replace `ref:a` nodes
if (node.type === 'Selector') {
for (let i = 0; i < node.children.length; i += 1) {
const a = node.children[i];
const b = node.children[i + 1];
if (is_ref_selector(a, b)) {
parser.error(parser_errors.invalid_ref_selector, a.loc.start.offset);
}
}
}
if (node.type === 'Declaration' && node.value.type === 'Value' && node.value.children.length === 0) {
parser.error(parser_errors.invalid_declaration, node.start);
}
if (node.type === 'PseudoClassSelector' && node.name === 'global' && node.children === null) {
parser.error(parser_errors.empty_global_selector, node.loc.start.offset);
}
if (node.loc) {
node.start = node.loc.start.offset;
node.end = node.loc.end.offset;
delete node.loc;
}
}
});
parser.read(regex_closing_style_tag);
const end = parser.index;
return {
type: 'Style',
start,
end,
attributes,
children: ast.children,
content: {
start: content_start,
end: content_end,
styles
}
};
}
function is_ref_selector(a: any, b: any) { // TODO add CSS node types
if (!b) return false;
return (
a.type === 'TypeSelector' &&
a.name === 'ref' &&
b.type === 'PseudoClassSelector'
);
}