@@ -962,6 +962,25 @@ abstract class StylesheetParser extends Parser {
962962 spanFrom (beforeName));
963963 }
964964
965+ if (switch (name) {
966+ "expression" || "url" || "and" || "or" || "not" => true ,
967+ _ => unvendor (name) == "element"
968+ }) {
969+ error ("Invalid function name." , spanFrom (beforeName));
970+ } else if (switch (name.toLowerCase ()) {
971+ "expression" || "url" => true ,
972+ var name => unvendor (name) == "element"
973+ }) {
974+ warnings.add ((
975+ deprecation: Deprecation .functionName,
976+ message: "Custom functions with this name are deprecated and will be "
977+ "removed in a future\n "
978+ "release. Please choose a different name.\n "
979+ "More info: https://sass-lang.com/d/function-name" ,
980+ span: spanFrom (beforeName)
981+ ));
982+ }
983+
965984 whitespace (consumeNewlines: true );
966985 var parameters = _parameterList ();
967986
@@ -977,18 +996,6 @@ abstract class StylesheetParser extends Parser {
977996 );
978997 }
979998
980- if (unvendor (name)
981- case "calc" ||
982- "element" ||
983- "expression" ||
984- "url" ||
985- "and" ||
986- "or" ||
987- "not" ||
988- "clamp" ) {
989- error ("Invalid function name." , spanFrom (start));
990- }
991-
992999 whitespace (consumeNewlines: false );
9931000 return _withChildren (
9941001 _functionChild,
@@ -3324,9 +3331,54 @@ abstract class StylesheetParser extends Parser {
33243331 ..writeCharCode ($lparen);
33253332 } else {
33263333 var normalized = unvendor (name);
3334+ var vendored = normalized != name;
33273335 switch (normalized) {
3328- case "calc" when normalized != name && scanner.scanChar ($lparen):
3329- case "element" || "expression" when scanner.scanChar ($lparen):
3336+ case "expression" when vendored && scanner.scanChar ($lparen):
3337+ buffer = InterpolationBuffer ()
3338+ ..write (name)
3339+ ..writeCharCode ($lparen);
3340+
3341+ var beforeArg = scanner.state;
3342+ var invalidSassScript = false ;
3343+ var nonCssSassScript = false ;
3344+ try {
3345+ var argument = _expression ();
3346+ nonCssSassScript = ! argument.isPlainCss (allowInterpolation: true );
3347+ } on StringScannerException {
3348+ invalidSassScript = true ;
3349+ }
3350+ scanner.state = beforeArg;
3351+
3352+ var value = _interpolatedDeclarationValue (allowEmpty: true );
3353+ buffer.addInterpolation (value);
3354+ scanner.expectChar ($rparen);
3355+ buffer.writeCharCode ($rparen);
3356+
3357+ if (invalidSassScript || nonCssSassScript) {
3358+ var suggestion =
3359+ StringExpression (value, quotes: true ).asInterpolation ();
3360+ warnings.add ((
3361+ deprecation: Deprecation .functionName,
3362+ message: "Vendor-prefixed $normalized () functions will no longer "
3363+ "have special parsing in a future release of Dart Sass. "
3364+ "Once that happens, this argument will " +
3365+ (invalidSassScript
3366+ ? "be parsed as SassScript. "
3367+ : "no longer be valid syntax. " ) +
3368+ "To preserve current behavior:\n "
3369+ "\n "
3370+ "$name (#{$suggestion })\n "
3371+ "\n "
3372+ "More info: https://sass-lang.com/d/function-name" ,
3373+ span: spanFrom (start)
3374+ ));
3375+ }
3376+
3377+ return StringExpression (buffer.interpolation (spanFrom (start)));
3378+
3379+ case "calc" when vendored && scanner.scanChar ($lparen):
3380+ case "expression" when ! vendored && scanner.scanChar ($lparen):
3381+ case "element" when scanner.scanChar ($lparen):
33303382 buffer = InterpolationBuffer ()
33313383 ..write (name)
33323384 ..writeCharCode ($lparen);
@@ -3343,9 +3395,36 @@ abstract class StylesheetParser extends Parser {
33433395 scanner.expectChar ($lparen);
33443396 buffer.writeCharCode ($lparen);
33453397
3398+ buffer.addInterpolation (
3399+ _interpolatedDeclarationValue (allowEmpty: true ));
3400+ scanner.expectChar ($rparen);
3401+ buffer.writeCharCode ($rparen);
3402+
3403+ if (vendored) {
3404+ var suggestion = StringExpression (
3405+ buffer.interpolation (spanFrom (start)),
3406+ quotes: true )
3407+ .asInterpolation ();
3408+ warnings.add ((
3409+ deprecation: Deprecation .functionName,
3410+ message:
3411+ "Vendor-prefixed progid:...() functions will no longer be "
3412+ "supported in a future release of Dart Sass. To preserve "
3413+ "current behavior:\n "
3414+ "\n "
3415+ "#{$suggestion }\n "
3416+ "\n "
3417+ "More info: https://sass-lang.com/d/function-name" ,
3418+ span: spanFrom (start)
3419+ ));
3420+ }
3421+
3422+ return StringExpression (buffer.interpolation (spanFrom (start)));
3423+
33463424 case "url" :
33473425 return _tryUrlContents (
33483426 start,
3427+ vendored: vendored,
33493428 ).andThen ((contents) => StringExpression (contents));
33503429
33513430 case _:
@@ -3363,13 +3442,28 @@ abstract class StylesheetParser extends Parser {
33633442 /// Like [_urlContents] , but returns `null` if the URL fails to parse.
33643443 ///
33653444 /// [start] is the position before the beginning of the name. [name] is the
3366- /// function's name; it defaults to `"url"` .
3367- Interpolation ? _tryUrlContents (LineScannerState start, {String ? name}) {
3445+ /// function's name; it defaults to `"url"` . [vendored] is true if this is
3446+ /// being parsed in an expression context as a deprecated vendor-prefixed
3447+ /// `url()` expression.
3448+ Interpolation ? _tryUrlContents (LineScannerState start,
3449+ {String ? name, bool vendored = false }) {
33683450 // NOTE: this logic is largely duplicated in Parser.tryUrl. Most changes
33693451 // here should be mirrored there.
33703452
33713453 var beginningOfContents = scanner.state;
33723454 if (! scanner.scanChar ($lparen)) return null ;
3455+
3456+ var invalidSassScript = false ;
3457+ if (vendored) {
3458+ var beforeArg = scanner.state;
3459+ try {
3460+ _expression ();
3461+ } on StringScannerException {
3462+ invalidSassScript = true ;
3463+ }
3464+ scanner.state = beforeArg;
3465+ }
3466+
33733467 whitespaceWithoutComments (consumeNewlines: true );
33743468
33753469 // Match Ruby Sass's behavior: parse a raw URL() if possible, and if not
@@ -3391,8 +3485,6 @@ abstract class StylesheetParser extends Parser {
33913485 $percent ||
33923486 $ampersand ||
33933487 $hash ||
3394- // dart-lang/sdk#52740
3395- // ignore: non_constant_relational_pattern_expression
33963488 (>= $asterisk && <= $tilde) ||
33973489 >= 0x80 :
33983490 buffer.writeCharCode (scanner.readChar ());
@@ -3401,6 +3493,26 @@ abstract class StylesheetParser extends Parser {
34013493 if (scanner.peekChar () != $rparen) break loop;
34023494 case $rparen:
34033495 buffer.writeCharCode (scanner.readChar ());
3496+
3497+ if (vendored && invalidSassScript) {
3498+ var suggestion = StringExpression (
3499+ buffer.interpolation (spanFrom (start)),
3500+ quotes: true )
3501+ .asInterpolation ();
3502+ warnings.add ((
3503+ deprecation: Deprecation .functionName,
3504+ message: "Vendor-prefixed url() functions will no longer have "
3505+ "special parsing in a future release of Dart Sass. Once "
3506+ "that happens, this argument will be parsed as SassScript. "
3507+ "To preserve current behavior:\n "
3508+ "\n "
3509+ "$name (#{$suggestion })\n "
3510+ "\n "
3511+ "More info: https://sass-lang.com/d/function-name" ,
3512+ span: spanFrom (start)
3513+ ));
3514+ }
3515+
34043516 return buffer.interpolation (spanFrom (start));
34053517 case _:
34063518 break loop;
0 commit comments