/
css.dart
144 lines (125 loc) · 4.32 KB
/
css.dart
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Copyright 2018 Google Inc. Use of this source code is governed by an
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'package:charcode/charcode.dart';
import 'package:string_scanner/string_scanner.dart';
import '../ast/sass.dart';
import '../functions.dart';
import '../logger.dart';
import 'scss.dart';
/// The set of all function names disallowed in plain CSS.
final _disallowedFunctionNames =
globalFunctions.map((function) => function.name).toSet()
..add("if")
..remove("rgb")
..remove("rgba")
..remove("hsl")
..remove("hsla")
..remove("grayscale")
..remove("invert")
..remove("alpha")
..remove("opacity")
..remove("saturate");
class CssParser extends ScssParser {
bool get plainCss => true;
CssParser(String contents, {Object? url, Logger? logger})
: super(contents, url: url, logger: logger);
void silentComment() {
var start = scanner.state;
super.silentComment();
error("Silent comments aren't allowed in plain CSS.",
scanner.spanFrom(start));
}
Statement atRule(Statement child(), {bool root = false}) {
// NOTE: this logic is largely duplicated in CssParser.atRule. Most changes
// here should be mirrored there.
var start = scanner.state;
scanner.expectChar($at);
var name = interpolatedIdentifier();
whitespace();
switch (name.asPlain) {
case "at-root":
case "content":
case "debug":
case "each":
case "error":
case "extend":
case "for":
case "function":
case "if":
case "include":
case "mixin":
case "return":
case "warn":
case "while":
almostAnyValue();
error("This at-rule isn't allowed in plain CSS.",
scanner.spanFrom(start));
case "import":
return _cssImportRule(start);
case "media":
return mediaRule(start);
case "-moz-document":
return mozDocumentRule(start, name);
case "supports":
return supportsRule(start);
default:
return unknownAtRule(start, name);
}
}
/// Consumes a plain-CSS `@import` rule that disallows interpolation.
///
/// [start] should point before the `@`.
ImportRule _cssImportRule(LineScannerState start) {
var urlStart = scanner.state;
var next = scanner.peekChar();
Expression url;
if (next == $u || next == $U) {
url = dynamicUrl();
} else {
url =
StringExpression(interpolatedString().asInterpolation(static: true));
}
var urlSpan = scanner.spanFrom(urlStart);
whitespace();
var modifiers = tryImportModifiers();
expectStatementSeparator("@import rule");
return ImportRule([
StaticImport(Interpolation([url], urlSpan), scanner.spanFrom(urlStart),
modifiers: modifiers)
], scanner.spanFrom(start));
}
Expression identifierLike() {
var start = scanner.state;
var identifier = interpolatedIdentifier();
var plain = identifier.asPlain!; // CSS doesn't allow non-plain identifiers
var specialFunction = trySpecialFunction(plain.toLowerCase(), start);
if (specialFunction != null) return specialFunction;
var beforeArguments = scanner.state;
if (!scanner.scanChar($lparen)) return StringExpression(identifier);
var arguments = <Expression>[];
if (!scanner.scanChar($rparen)) {
do {
whitespace();
arguments.add(expression(singleEquals: true));
whitespace();
} while (scanner.scanChar($comma));
scanner.expectChar($rparen);
}
if (_disallowedFunctionNames.contains(plain)) {
error(
"This function isn't allowed in plain CSS.", scanner.spanFrom(start));
}
return InterpolatedFunctionExpression(
// Create a fake interpolation to force the function to be interpreted
// as plain CSS, rather than calling a user-defined function.
Interpolation([StringExpression(identifier)], identifier.span),
ArgumentInvocation(
arguments, const {}, scanner.spanFrom(beforeArguments)),
scanner.spanFrom(start));
}
Expression namespacedExpression(String namespace, LineScannerState start) {
var expression = super.namespacedExpression(namespace, start);
error("Module namespaces aren't allowed in plain CSS.", expression.span);
}
}