From 7203d6538981df6422c686198da2497909fa11b5 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 9 Jul 2024 14:31:20 -0700 Subject: [PATCH] Deprecated mixed declarations (#2267) See sass/sass#3885 --- CHANGELOG.md | 6 +++++ lib/src/deprecation.dart | 7 +++++- lib/src/parse/css.dart | 6 ++--- lib/src/visitor/async_evaluate.dart | 37 +++++++++++++++++++++------ lib/src/visitor/evaluate.dart | 39 ++++++++++++++++++++++------- pkg/sass_api/CHANGELOG.md | 4 +++ pkg/sass_api/pubspec.yaml | 4 +-- pubspec.yaml | 2 +- 8 files changed, 81 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edce7c487..3cd1720e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ## 1.77.7 +* Declarations that appear after nested rules are deprecated, because the + semantics Sass has historically used are different from the semantics + specified by CSS. In the future, Sass will adopt the standard CSS semantics. + + See [the Sass website](https://sass-lang.com/d/mixed-decls) for details. + * **Potentially breaking bug fix:** `//` in certain places such as unknown at-rule values was being preserved in the CSS output, leading to potentially invalid CSS. It's now properly parsed as a silent comment and omitted from the diff --git a/lib/src/deprecation.dart b/lib/src/deprecation.dart index cec976714..b71960c47 100644 --- a/lib/src/deprecation.dart +++ b/lib/src/deprecation.dart @@ -15,7 +15,7 @@ enum Deprecation { // DO NOT EDIT. This section was generated from the language repo. // See tool/grind/generate_deprecations.dart for details. // - // Checksum: 22d9bdbe92eb39b3c0d6d64ebe1879a431c0037e + // Checksum: 309e4f1f008f08379b824ab6094e13df2e18e187 /// Deprecation for passing a string directly to meta.call(). callString('call-string', @@ -90,6 +90,11 @@ enum Deprecation { deprecatedIn: '1.76.0', description: 'Function and mixin names beginning with --.'), + /// Deprecation for declarations after or between nested rules. + mixedDecls('mixed-decls', + deprecatedIn: '1.77.7', + description: 'Declarations after or between nested rules.'), + /// Deprecation for @import rules. import.future('import', description: '@import rules.'), diff --git a/lib/src/parse/css.dart b/lib/src/parse/css.dart index 747d22c49..22d380edb 100644 --- a/lib/src/parse/css.dart +++ b/lib/src/parse/css.dart @@ -42,7 +42,7 @@ class CssParser extends ScssParser { } Statement atRule(Statement child(), {bool root = false}) { - // NOTE: this logic is largely duplicated in CssParser.atRule. Most changes + // NOTE: this logic is largely duplicated in StylesheetParser.atRule. Most changes // here should be mirrored there. var start = scanner.state; @@ -65,7 +65,7 @@ class CssParser extends ScssParser { "return" || "warn" || "while" => - _forbiddenAtRoot(start), + _forbiddenAtRule(start), "import" => _cssImportRule(start), "media" => mediaRule(start), "-moz-document" => mozDocumentRule(start, name), @@ -75,7 +75,7 @@ class CssParser extends ScssParser { } /// Throws an error for a forbidden at-rule. - Never _forbiddenAtRoot(LineScannerState start) { + Never _forbiddenAtRule(LineScannerState start) { almostAnyValue(); error("This at-rule isn't allowed in plain CSS.", scanner.spanFrom(start)); } diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index b8434ff47..e4a65d12e 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -1189,6 +1189,22 @@ final class _EvaluateVisitor node.span); } + if (_parent.parent!.children.last case var sibling + when _parent != sibling) { + _warn( + "Sass's behavior for declarations that appear after nested\n" + "rules will be changing to match the behavior specified by CSS in an " + "upcoming\n" + "version. To keep the existing behavior, move the declaration above " + "the nested\n" + "rule. To opt into the new behavior, wrap the declaration in `& " + "{}`.\n" + "\n" + "More info: https://sass-lang.com/d/mixed-decls", + MultiSpan(node.span, 'declaration', {sibling.span: 'nested rule'}), + Deprecation.mixedDecls); + } + var name = await _interpolationToValue(node.name, warnForColor: true); if (_declarationName case var declarationName?) { name = CssValue("$declarationName-${name.value}", name.span); @@ -2065,8 +2081,20 @@ final class _EvaluateVisitor scopeWhen: node.hasDeclarations); _atRootExcludingStyleRule = oldAtRootExcludingStyleRule; + _warnForBogusCombinators(rule); + + if (_styleRule == null && _parent.children.isNotEmpty) { + var lastChild = _parent.children.last; + lastChild.isGroupEnd = true; + } + + return null; + } + + /// Emits deprecation warnings for any bogus combinators in [rule]. + void _warnForBogusCombinators(CssStyleRule rule) { if (!rule.isInvisibleOtherThanBogusCombinators) { - for (var complex in parsedSelector.components) { + for (var complex in rule.selector.components) { if (!complex.isBogus) continue; if (complex.isUseless) { @@ -2110,13 +2138,6 @@ final class _EvaluateVisitor } } } - - if (_styleRule == null && _parent.children.isNotEmpty) { - var lastChild = _parent.children.last; - lastChild.isGroupEnd = true; - } - - return null; } Future visitSupportsRule(SupportsRule node) async { diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 1680be88e..cc2458bab 100644 --- a/lib/src/visitor/evaluate.dart +++ b/lib/src/visitor/evaluate.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_evaluate.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: 116b8079719577ac6e4dad4aebe403282136e611 +// Checksum: ebf292c26dcfdd7f61fd70ce3dc9e0be2b6708b3 // // ignore_for_file: unused_import @@ -1187,6 +1187,22 @@ final class _EvaluateVisitor node.span); } + if (_parent.parent!.children.last case var sibling + when _parent != sibling) { + _warn( + "Sass's behavior for declarations that appear after nested\n" + "rules will be changing to match the behavior specified by CSS in an " + "upcoming\n" + "version. To keep the existing behavior, move the declaration above " + "the nested\n" + "rule. To opt into the new behavior, wrap the declaration in `& " + "{}`.\n" + "\n" + "More info: https://sass-lang.com/d/mixed-decls", + MultiSpan(node.span, 'declaration', {sibling.span: 'nested rule'}), + Deprecation.mixedDecls); + } + var name = _interpolationToValue(node.name, warnForColor: true); if (_declarationName case var declarationName?) { name = CssValue("$declarationName-${name.value}", name.span); @@ -2055,8 +2071,20 @@ final class _EvaluateVisitor scopeWhen: node.hasDeclarations); _atRootExcludingStyleRule = oldAtRootExcludingStyleRule; + _warnForBogusCombinators(rule); + + if (_styleRule == null && _parent.children.isNotEmpty) { + var lastChild = _parent.children.last; + lastChild.isGroupEnd = true; + } + + return null; + } + + /// Emits deprecation warnings for any bogus combinators in [rule]. + void _warnForBogusCombinators(CssStyleRule rule) { if (!rule.isInvisibleOtherThanBogusCombinators) { - for (var complex in parsedSelector.components) { + for (var complex in rule.selector.components) { if (!complex.isBogus) continue; if (complex.isUseless) { @@ -2100,13 +2128,6 @@ final class _EvaluateVisitor } } } - - if (_styleRule == null && _parent.children.isNotEmpty) { - var lastChild = _parent.children.last; - lastChild.isGroupEnd = true; - } - - return null; } Value? visitSupportsRule(SupportsRule node) { diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index 3ea002075..006fbe277 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 10.4.7 + +* No user-visible changes. + ## 10.4.6 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index bcd120d20..cba35a261 100644 --- a/pkg/sass_api/pubspec.yaml +++ b/pkg/sass_api/pubspec.yaml @@ -2,7 +2,7 @@ name: sass_api # Note: Every time we add a new Sass AST node, we need to bump the *major* # version because it's a breaking change for anyone who's implementing the # visitor interface(s). -version: 10.4.6 +version: 10.4.7 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: - sass: 1.77.6 + sass: 1.77.7 dev_dependencies: dartdoc: ">=6.0.0 <9.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index fd753e22b..ad62bb19b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.77.7-dev +version: 1.77.7 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass