Skip to content

Commit 7203d65

Browse files
authored
Deprecated mixed declarations (#2267)
See sass/sass#3885
1 parent 1edc247 commit 7203d65

File tree

8 files changed

+81
-24
lines changed

8 files changed

+81
-24
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
## 1.77.7
22

3+
* Declarations that appear after nested rules are deprecated, because the
4+
semantics Sass has historically used are different from the semantics
5+
specified by CSS. In the future, Sass will adopt the standard CSS semantics.
6+
7+
See [the Sass website](https://sass-lang.com/d/mixed-decls) for details.
8+
39
* **Potentially breaking bug fix:** `//` in certain places such as unknown
410
at-rule values was being preserved in the CSS output, leading to potentially
511
invalid CSS. It's now properly parsed as a silent comment and omitted from the

lib/src/deprecation.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ enum Deprecation {
1515
// DO NOT EDIT. This section was generated from the language repo.
1616
// See tool/grind/generate_deprecations.dart for details.
1717
//
18-
// Checksum: 22d9bdbe92eb39b3c0d6d64ebe1879a431c0037e
18+
// Checksum: 309e4f1f008f08379b824ab6094e13df2e18e187
1919

2020
/// Deprecation for passing a string directly to meta.call().
2121
callString('call-string',
@@ -90,6 +90,11 @@ enum Deprecation {
9090
deprecatedIn: '1.76.0',
9191
description: 'Function and mixin names beginning with --.'),
9292

93+
/// Deprecation for declarations after or between nested rules.
94+
mixedDecls('mixed-decls',
95+
deprecatedIn: '1.77.7',
96+
description: 'Declarations after or between nested rules.'),
97+
9398
/// Deprecation for @import rules.
9499
import.future('import', description: '@import rules.'),
95100

lib/src/parse/css.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class CssParser extends ScssParser {
4242
}
4343

4444
Statement atRule(Statement child(), {bool root = false}) {
45-
// NOTE: this logic is largely duplicated in CssParser.atRule. Most changes
45+
// NOTE: this logic is largely duplicated in StylesheetParser.atRule. Most changes
4646
// here should be mirrored there.
4747

4848
var start = scanner.state;
@@ -65,7 +65,7 @@ class CssParser extends ScssParser {
6565
"return" ||
6666
"warn" ||
6767
"while" =>
68-
_forbiddenAtRoot(start),
68+
_forbiddenAtRule(start),
6969
"import" => _cssImportRule(start),
7070
"media" => mediaRule(start),
7171
"-moz-document" => mozDocumentRule(start, name),
@@ -75,7 +75,7 @@ class CssParser extends ScssParser {
7575
}
7676

7777
/// Throws an error for a forbidden at-rule.
78-
Never _forbiddenAtRoot(LineScannerState start) {
78+
Never _forbiddenAtRule(LineScannerState start) {
7979
almostAnyValue();
8080
error("This at-rule isn't allowed in plain CSS.", scanner.spanFrom(start));
8181
}

lib/src/visitor/async_evaluate.dart

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,22 @@ final class _EvaluateVisitor
11891189
node.span);
11901190
}
11911191

1192+
if (_parent.parent!.children.last case var sibling
1193+
when _parent != sibling) {
1194+
_warn(
1195+
"Sass's behavior for declarations that appear after nested\n"
1196+
"rules will be changing to match the behavior specified by CSS in an "
1197+
"upcoming\n"
1198+
"version. To keep the existing behavior, move the declaration above "
1199+
"the nested\n"
1200+
"rule. To opt into the new behavior, wrap the declaration in `& "
1201+
"{}`.\n"
1202+
"\n"
1203+
"More info: https://sass-lang.com/d/mixed-decls",
1204+
MultiSpan(node.span, 'declaration', {sibling.span: 'nested rule'}),
1205+
Deprecation.mixedDecls);
1206+
}
1207+
11921208
var name = await _interpolationToValue(node.name, warnForColor: true);
11931209
if (_declarationName case var declarationName?) {
11941210
name = CssValue("$declarationName-${name.value}", name.span);
@@ -2065,8 +2081,20 @@ final class _EvaluateVisitor
20652081
scopeWhen: node.hasDeclarations);
20662082
_atRootExcludingStyleRule = oldAtRootExcludingStyleRule;
20672083

2084+
_warnForBogusCombinators(rule);
2085+
2086+
if (_styleRule == null && _parent.children.isNotEmpty) {
2087+
var lastChild = _parent.children.last;
2088+
lastChild.isGroupEnd = true;
2089+
}
2090+
2091+
return null;
2092+
}
2093+
2094+
/// Emits deprecation warnings for any bogus combinators in [rule].
2095+
void _warnForBogusCombinators(CssStyleRule rule) {
20682096
if (!rule.isInvisibleOtherThanBogusCombinators) {
2069-
for (var complex in parsedSelector.components) {
2097+
for (var complex in rule.selector.components) {
20702098
if (!complex.isBogus) continue;
20712099

20722100
if (complex.isUseless) {
@@ -2110,13 +2138,6 @@ final class _EvaluateVisitor
21102138
}
21112139
}
21122140
}
2113-
2114-
if (_styleRule == null && _parent.children.isNotEmpty) {
2115-
var lastChild = _parent.children.last;
2116-
lastChild.isGroupEnd = true;
2117-
}
2118-
2119-
return null;
21202141
}
21212142

21222143
Future<Value?> visitSupportsRule(SupportsRule node) async {

lib/src/visitor/evaluate.dart

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// DO NOT EDIT. This file was generated from async_evaluate.dart.
66
// See tool/grind/synchronize.dart for details.
77
//
8-
// Checksum: 116b8079719577ac6e4dad4aebe403282136e611
8+
// Checksum: ebf292c26dcfdd7f61fd70ce3dc9e0be2b6708b3
99
//
1010
// ignore_for_file: unused_import
1111

@@ -1187,6 +1187,22 @@ final class _EvaluateVisitor
11871187
node.span);
11881188
}
11891189

1190+
if (_parent.parent!.children.last case var sibling
1191+
when _parent != sibling) {
1192+
_warn(
1193+
"Sass's behavior for declarations that appear after nested\n"
1194+
"rules will be changing to match the behavior specified by CSS in an "
1195+
"upcoming\n"
1196+
"version. To keep the existing behavior, move the declaration above "
1197+
"the nested\n"
1198+
"rule. To opt into the new behavior, wrap the declaration in `& "
1199+
"{}`.\n"
1200+
"\n"
1201+
"More info: https://sass-lang.com/d/mixed-decls",
1202+
MultiSpan(node.span, 'declaration', {sibling.span: 'nested rule'}),
1203+
Deprecation.mixedDecls);
1204+
}
1205+
11901206
var name = _interpolationToValue(node.name, warnForColor: true);
11911207
if (_declarationName case var declarationName?) {
11921208
name = CssValue("$declarationName-${name.value}", name.span);
@@ -2055,8 +2071,20 @@ final class _EvaluateVisitor
20552071
scopeWhen: node.hasDeclarations);
20562072
_atRootExcludingStyleRule = oldAtRootExcludingStyleRule;
20572073

2074+
_warnForBogusCombinators(rule);
2075+
2076+
if (_styleRule == null && _parent.children.isNotEmpty) {
2077+
var lastChild = _parent.children.last;
2078+
lastChild.isGroupEnd = true;
2079+
}
2080+
2081+
return null;
2082+
}
2083+
2084+
/// Emits deprecation warnings for any bogus combinators in [rule].
2085+
void _warnForBogusCombinators(CssStyleRule rule) {
20582086
if (!rule.isInvisibleOtherThanBogusCombinators) {
2059-
for (var complex in parsedSelector.components) {
2087+
for (var complex in rule.selector.components) {
20602088
if (!complex.isBogus) continue;
20612089

20622090
if (complex.isUseless) {
@@ -2100,13 +2128,6 @@ final class _EvaluateVisitor
21002128
}
21012129
}
21022130
}
2103-
2104-
if (_styleRule == null && _parent.children.isNotEmpty) {
2105-
var lastChild = _parent.children.last;
2106-
lastChild.isGroupEnd = true;
2107-
}
2108-
2109-
return null;
21102131
}
21112132

21122133
Value? visitSupportsRule(SupportsRule node) {

pkg/sass_api/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 10.4.7
2+
3+
* No user-visible changes.
4+
15
## 10.4.6
26

37
* No user-visible changes.

pkg/sass_api/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ name: sass_api
22
# Note: Every time we add a new Sass AST node, we need to bump the *major*
33
# version because it's a breaking change for anyone who's implementing the
44
# visitor interface(s).
5-
version: 10.4.6
5+
version: 10.4.7
66
description: Additional APIs for Dart Sass.
77
homepage: https://github.com/sass/dart-sass
88

99
environment:
1010
sdk: ">=3.0.0 <4.0.0"
1111

1212
dependencies:
13-
sass: 1.77.6
13+
sass: 1.77.7
1414

1515
dev_dependencies:
1616
dartdoc: ">=6.0.0 <9.0.0"

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: sass
2-
version: 1.77.7-dev
2+
version: 1.77.7
33
description: A Sass implementation in Dart.
44
homepage: https://github.com/sass/dart-sass
55

0 commit comments

Comments
 (0)