5
5
// https://opensource.org/licenses/MIT.
6
6
7
7
import 'package:args/args.dart' ;
8
+ import 'package:sass/sass.dart' ;
8
9
9
10
// The sass package's API is not necessarily stable. It is being imported with
10
11
// the Sass team's explicit knowledge and approval. See
@@ -49,6 +50,13 @@ class _DivisionMigrationVisitor extends MigrationVisitor {
49
50
/// True when the current node is expected to evaluate to a number.
50
51
var _expectsNumericResult = false ;
51
52
53
+ /// Allows division within this argument invocation.
54
+ @override
55
+ void visitArgumentInvocation (ArgumentInvocation invocation) {
56
+ _withContext (() => super .visitArgumentInvocation (invocation),
57
+ isDivisionAllowed: true );
58
+ }
59
+
52
60
/// If this is a division operation, migrates it.
53
61
///
54
62
/// If this is any other operator, allows division within its left and right
@@ -65,6 +73,15 @@ class _DivisionMigrationVisitor extends MigrationVisitor {
65
73
}
66
74
}
67
75
76
+ /// Allows division within a function call's arguments, with special handling
77
+ /// for new-syntax color functions.
78
+ @override
79
+ void visitFunctionExpression (FunctionExpression node) {
80
+ visitInterpolation (node.name);
81
+ if (_tryColorFunction (node)) return ;
82
+ visitArgumentInvocation (node.arguments);
83
+ }
84
+
68
85
/// Disallows division within this list.
69
86
@override
70
87
void visitListExpression (ListExpression node) {
@@ -105,6 +122,48 @@ class _DivisionMigrationVisitor extends MigrationVisitor {
105
122
isDivisionAllowed: true );
106
123
}
107
124
125
+ /// Migrates [node] and returns true if it is a new-syntax color function or
126
+ /// returns false if it is any other function.
127
+ bool _tryColorFunction (FunctionExpression node) {
128
+ if (! ["rgb" , "rgba" , "hsl" , "hsla" ].contains (node.name.asPlain)) {
129
+ return false ;
130
+ }
131
+
132
+ ListExpression channels;
133
+ if (node.arguments.positional.length == 1 &&
134
+ node.arguments.named.isEmpty &&
135
+ node.arguments.positional.first is ListExpression ) {
136
+ channels = node.arguments.positional.first;
137
+ } else if (node.arguments.positional.isEmpty &&
138
+ node.arguments.named.containsKey (r'$channels' ) &&
139
+ node.arguments.named.length == 1 &&
140
+ node.arguments.named[r'$channels' ] is ListExpression ) {
141
+ channels = node.arguments.named[r'$channels' ];
142
+ }
143
+ if (channels == null ||
144
+ channels.hasBrackets ||
145
+ channels.separator != ListSeparator .space ||
146
+ channels.contents.length != 3 ||
147
+ channels.contents.last is ! BinaryOperationExpression ) {
148
+ return false ;
149
+ }
150
+
151
+ var last = channels.contents.last as BinaryOperationExpression ;
152
+ if (last.left is ! NumberExpression || last.right is ! NumberExpression ) {
153
+ // Handles cases like `rgb(10 20 30/2 / 0.5)`, since converting `30/2` to
154
+ // `divide(30, 20)` would cause `/ 0.5` to be interpreted as division.
155
+ _patchSpacesToCommas (channels);
156
+ _patchOperatorToComma (last);
157
+ }
158
+ _withContext (() {
159
+ channels.contents[0 ].accept (this );
160
+ channels.contents[1 ].accept (this );
161
+ last.left.accept (this );
162
+ }, isDivisionAllowed: true );
163
+ last.right.accept (this );
164
+ return true ;
165
+ }
166
+
108
167
/// Visits a `/` operation [node] and migrates it to either the `division`
109
168
/// function or the `slash-list` function.
110
169
///
@@ -115,9 +174,14 @@ class _DivisionMigrationVisitor extends MigrationVisitor {
115
174
if ((! _isDivisionAllowed && _onlySlash (node)) ||
116
175
_isDefinitelyNotNumber (node)) {
117
176
// Definitely not division
118
- addPatch (patchBefore (node, "slash-list(" ));
119
- addPatch (patchAfter (node, ")" ));
120
- _visitSlashListArguments (node);
177
+ if (_isDivisionAllowed || _containsInterpolation (node)) {
178
+ // We only want to convert a non-division slash operation to a
179
+ // slash-list call when it's in a non-plain-CSS context to avoid
180
+ // unnecessary function calls within plain CSS.
181
+ addPatch (patchBefore (node, "slash-list(" ));
182
+ addPatch (patchAfter (node, ")" ));
183
+ _visitSlashListArguments (node);
184
+ }
121
185
return true ;
122
186
} else if (_expectsNumericResult ||
123
187
_isDefinitelyNumber (node) ||
@@ -126,7 +190,7 @@ class _DivisionMigrationVisitor extends MigrationVisitor {
126
190
addPatch (patchBefore (node, "divide(" ));
127
191
addPatch (patchAfter (node, ")" ));
128
192
_patchParensIfAny (node.left);
129
- _patchSlashToComma (node);
193
+ _patchOperatorToComma (node);
130
194
_patchParensIfAny (node.right);
131
195
_withContext (() => super .visitBinaryOperationExpression (node),
132
196
expectsNumericResult: true );
@@ -145,7 +209,7 @@ class _DivisionMigrationVisitor extends MigrationVisitor {
145
209
if (node is BinaryOperationExpression &&
146
210
node.operator == BinaryOperator .dividedBy) {
147
211
_visitSlashListArguments (node.left);
148
- _patchSlashToComma (node);
212
+ _patchOperatorToComma (node);
149
213
_visitSlashListArguments (node.right);
150
214
} else if (node is StringExpression &&
151
215
node.text.contents.length == 1 &&
@@ -218,8 +282,27 @@ class _DivisionMigrationVisitor extends MigrationVisitor {
218
282
node is StringExpression ;
219
283
}
220
284
285
+ /// Returns true if [node] contains an interpolation.
286
+ bool _containsInterpolation (Expression node) {
287
+ if (node is ParenthesizedExpression ) return _containsInterpolation (node);
288
+ if (node is BinaryOperationExpression ) {
289
+ return _containsInterpolation (node.left) ||
290
+ _containsInterpolation (node.right);
291
+ }
292
+ return node is StringExpression && node.text.asPlain == null ;
293
+ }
294
+
295
+ /// Converts a space-separated list [node] to a comma-separated list.
296
+ void _patchSpacesToCommas (ListExpression node) {
297
+ for (var i = 0 ; i < node.contents.length - 1 ; i++ ) {
298
+ var start = node.contents[i].span.end;
299
+ var end = node.contents[i + 1 ].span.start;
300
+ addPatch (Patch (start.file.span (start.offset, end.offset), ", " ));
301
+ }
302
+ }
303
+
221
304
/// Adds a patch replacing the operator of [node] with ", ".
222
- void _patchSlashToComma (BinaryOperationExpression node) {
305
+ void _patchOperatorToComma (BinaryOperationExpression node) {
223
306
var start = node.left.span.end;
224
307
var end = node.right.span.start;
225
308
addPatch (Patch (start.file.span (start.offset, end.offset), ", " ));
0 commit comments