From f6fea3a25cf69fbc676140db4c4cd31e6eaf6018 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 2 Apr 2025 22:50:59 +0000 Subject: [PATCH 01/36] Fix the sass_api release (#2557) Pub.dev just launched an update that requires GitHub publish actions to be run from a tag that matches the version number of the published package. --- .github/workflows/release-sass-api.yml | 29 ++++++++++++++++++++++++++ .github/workflows/release.yml | 28 +++++++++++-------------- CHANGELOG.md | 4 ++++ pkg/sass-parser/CHANGELOG.md | 4 ++++ pkg/sass-parser/package.json | 2 +- pkg/sass_api/CHANGELOG.md | 4 ++++ pkg/sass_api/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- tool/grind/sass_api.dart | 2 ++ 9 files changed, 59 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/release-sass-api.yml diff --git a/.github/workflows/release-sass-api.yml b/.github/workflows/release-sass-api.yml new file mode 100644 index 000000000..c8b3b8a1a --- /dev/null +++ b/.github/workflows/release-sass-api.yml @@ -0,0 +1,29 @@ +# This has to be a separate workflow to satisfy pub.dev's somewhat draconian +# requirements for when and how GitHub actions are allowed publish packages. +# Specifically, it requires that each publish action be triggered by a tag that +# contains the version number for that action, so we can't just publish sass_api +# as part of the standard sass release flow because that was triggered by a tag +# with the sass version number. +name: Release sass-api + +on: + push: + tags: ['sass-api-[0-9]+.[0-9]+.*'] + +jobs: + deploy_sass_api: + if: github.event.repository.fork == false + name: Deploy sass_api + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: ./.github/util/initialize + with: {github-token: "${{ github.token }}"} + + - name: Deploy + run: dart run grinder deploy-sass-api + env: + PUB_CREDENTIALS: "${{ secrets.PUB_CREDENTIALS }}" + GH_TOKEN: "${{ secrets.GH_TOKEN }}" + GH_USER: sassbot diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3ed4f5f7f..051bc0033 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -95,6 +95,10 @@ jobs: steps: - uses: actions/checkout@v4 + with: + # We have to use this rather than the implicit GitHub token so that + # pushing a new tag triggers another action. + token: ${{ secrets.GH_TOKEN }} - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} @@ -102,22 +106,14 @@ jobs: run: dart run grinder protobuf pkg-pub-deploy env: {PUB_CREDENTIALS: "${{ secrets.PUB_CREDENTIALS }}"} - deploy_sass_api: - name: Deploy sass_api - runs-on: ubuntu-latest - needs: [deploy_pub] - - steps: - - uses: actions/checkout@v4 - - uses: ./.github/util/initialize - with: {github-token: "${{ github.token }}"} - - - name: Deploy - run: dart run grinder deploy-sass-api - env: - PUB_CREDENTIALS: "${{ secrets.PUB_CREDENTIALS }}" - GH_TOKEN: "${{ secrets.GH_TOKEN }}" - GH_USER: sassbot + - name: Get Sass API version + id: sass-api-version + run: | + echo "version=$(cat pkg/sass_api/pubspec.yaml | sed -nE 's/version: (.*)/\1/p')" | tee --append "$GITHUB_OUTPUT" + # This should be /-separated rather than hyphenated, but pub.dev doesn't + # currently allow that (dart-lang/pub-dev#8690). + - run: git tag sass-api-${{ steps.sass-api-version.outputs.version }} + - run: git push --tag deploy_sass_parser: name: Deploy sass-parser diff --git a/CHANGELOG.md b/CHANGELOG.md index e0758da72..5af5dd299 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.86.2 + +* No user-visible changes. + ## 1.86.1 * Improve the performance of `file:` URL case canonicalization on Windows and diff --git a/pkg/sass-parser/CHANGELOG.md b/pkg/sass-parser/CHANGELOG.md index 173162adb..c59a0dfb7 100644 --- a/pkg/sass-parser/CHANGELOG.md +++ b/pkg/sass-parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.18 + +* No user-visible changes. + ## 0.4.17 * No user-visible changes. diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index 0c1e06a0b..661f45396 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -1,6 +1,6 @@ { "name": "sass-parser", - "version": "0.4.17", + "version": "0.4.18", "description": "A PostCSS-compatible wrapper of the official Sass parser", "repository": "sass/sass", "author": "Google Inc.", diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index 08682255c..1c4dfc21c 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 15.3.2 + +* No user-visible changes. + ## 15.3.1 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index a5796b62c..63aedda99 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: 15.3.1 +version: 15.3.2 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.6.0 <4.0.0" dependencies: - sass: 1.86.1 + sass: 1.86.2 dev_dependencies: dartdoc: ^8.0.14 diff --git a/pubspec.yaml b/pubspec.yaml index 5c91f0740..9ca9894ee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.86.1 +version: 1.86.2 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass diff --git a/tool/grind/sass_api.dart b/tool/grind/sass_api.dart index 0008e7fcf..861e794d4 100644 --- a/tool/grind/sass_api.dart +++ b/tool/grind/sass_api.dart @@ -71,6 +71,8 @@ Future deploySassApi() async { fail("dart pub publish ${pubspec.name} failed"); } + // TODO(nweiz): Remove this when we use this tag to trigger the release + // (blocked by dart-lang/pub-dev#8690). var response = await client.post( Uri.parse("https://api.github.com/repos/sass/dart-sass/git/refs"), headers: { From 52221c0e1587c745d073eac961bc665dcbb6075f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AA=E3=81=A4=E3=81=8D?= Date: Thu, 3 Apr 2025 16:59:43 -0700 Subject: [PATCH 02/36] Fix canonicalize paths containing `..` (#2561) --- CHANGELOG.md | 5 +++++ lib/src/io.dart | 2 +- pkg/sass-parser/CHANGELOG.md | 4 ++++ pkg/sass-parser/package.json | 2 +- pkg/sass_api/CHANGELOG.md | 4 ++++ pkg/sass_api/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- 7 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5af5dd299..c18d9972d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.86.3 + +* Fix a bug introduced in 1.86.1 where Sass fails to resolve paths starting with + a `..` segment. + ## 1.86.2 * No user-visible changes. diff --git a/lib/src/io.dart b/lib/src/io.dart index b691d9fa7..ef4a4998a 100644 --- a/lib/src/io.dart +++ b/lib/src/io.dart @@ -25,7 +25,7 @@ bool get _couldBeCaseInsensitive => isWindows || isMacOS; /// Returns the canonical form of `path` on disk. String canonicalize(String path) => _couldBeCaseInsensitive - ? _realCasePath(p.absolute(p.normalize(path))) + ? _realCasePath(p.normalize(p.absolute(path))) : p.canonicalize(path); /// Returns `path` with the case updated to match the path's case on disk. diff --git a/pkg/sass-parser/CHANGELOG.md b/pkg/sass-parser/CHANGELOG.md index c59a0dfb7..09cd98eba 100644 --- a/pkg/sass-parser/CHANGELOG.md +++ b/pkg/sass-parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.19 + +* No user-visible changes. + ## 0.4.18 * No user-visible changes. diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index 661f45396..84e67cef3 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -1,6 +1,6 @@ { "name": "sass-parser", - "version": "0.4.18", + "version": "0.4.19", "description": "A PostCSS-compatible wrapper of the official Sass parser", "repository": "sass/sass", "author": "Google Inc.", diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index 1c4dfc21c..2f4592d8f 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 15.3.3 + +* No user-visible changes. + ## 15.3.2 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 63aedda99..7bf89328f 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: 15.3.2 +version: 15.3.3 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.6.0 <4.0.0" dependencies: - sass: 1.86.2 + sass: 1.86.3 dev_dependencies: dartdoc: ^8.0.14 diff --git a/pubspec.yaml b/pubspec.yaml index 9ca9894ee..4fd65a4ec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.86.2 +version: 1.86.3 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass From 1adf81415c6e7b3d8099bb5bf7953bc9d62e8d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AA=E3=81=A4=E3=81=8D?= Date: Mon, 21 Apr 2025 14:05:02 -0700 Subject: [PATCH 03/36] Switch to OSS windows-11-arm runner (#2563) --- .github/workflows/build-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 1e8dd23c8..f22f976bc 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -23,7 +23,7 @@ jobs: - arch: ia32 runner: windows-latest - arch: arm64 - runner: windows-arm64 + runner: windows-11-arm steps: - uses: actions/checkout@v4 From 087a685ab620f77cc61ac0418e1da9e4326fcf1e Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 21 Apr 2025 15:22:10 -0700 Subject: [PATCH 04/36] Stop releasing the Bazel repo (#2565) See bazelbuild/rules_sass#156 --- .github/workflows/release.yml | 16 ------- tool/grind.dart | 1 - tool/grind/bazel.dart | 84 ----------------------------------- 3 files changed, 101 deletions(-) delete mode 100644 tool/grind/bazel.dart diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 051bc0033..acf9feaf1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -73,22 +73,6 @@ jobs: UPDATE_SASS_SASS_REPO: false NPM_TOKEN: "${{ secrets.NPM_TOKEN }}" - deploy_bazel: - name: Deploy Bazel - runs-on: ubuntu-latest - needs: [deploy_npm] - - steps: - - uses: actions/checkout@v4 - - uses: ./.github/util/initialize - with: {github-token: "${{ github.token }}"} - - - name: Deploy - run: dart run grinder update-bazel - env: - GH_TOKEN: "${{ secrets.GH_TOKEN }}" - GH_USER: sassbot - deploy_pub: name: Deploy Pub runs-on: ubuntu-latest diff --git a/tool/grind.dart b/tool/grind.dart index df47da6bd..6265c803b 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -16,7 +16,6 @@ import 'grind/generate_deprecations.dart'; import 'grind/synchronize.dart'; import 'grind/utils.dart'; -export 'grind/bazel.dart'; export 'grind/benchmark.dart'; export 'grind/double_check.dart'; export 'grind/frameworks.dart'; diff --git a/tool/grind/bazel.dart b/tool/grind/bazel.dart deleted file mode 100644 index 62820145e..000000000 --- a/tool/grind/bazel.dart +++ /dev/null @@ -1,84 +0,0 @@ -// 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 'dart:io'; - -import 'package:cli_pkg/cli_pkg.dart' as pkg; -import 'package:grinder/grinder.dart'; -import 'package:path/path.dart' as p; - -import 'utils.dart'; - -@Task('Update the Bazel rules for the current version.') -Future updateBazel() async { - ensureBuild(); - - run("npm", arguments: ["install", "-g", "yarn"]); - - var repo = cloneOrCheckout( - "https://github.com/bazelbuild/rules_sass.git", - "main", - ); - - var packageFile = File(p.join(repo, "sass", "package.json")); - log("updating ${packageFile.path}"); - packageFile.writeAsStringSync( - packageFile.readAsStringSync().replaceFirst( - RegExp(r'"sass": "[^"]+"'), - '"sass": "${pkg.version}"', - ), - ); - - try { - run("yarn", workingDirectory: p.join(repo, "sass")); - } on ProcessException catch (error) { - if (error.stderr.contains("Couldn't find any versions for \"sass\"")) { - log( - "The new sass version doesn't seem to be available yet, waiting 30s...", - ); - await Future.delayed(Duration(minutes: 2)); - run("yarn", workingDirectory: p.join(repo, "sass")); - } - } - - run( - "git", - arguments: [ - "commit", - "--all", - "--message", - "Update Dart Sass to ${pkg.version}", - ], - workingDirectory: repo, - runOptions: sassBotEnvironment, - ); - - run( - "git", - arguments: ["tag", pkg.version.toString()], - workingDirectory: repo, - runOptions: sassBotEnvironment, - ); - - var username = environment('GH_USER'); - var password = environment('GH_TOKEN'); - await runAsync( - "git", - arguments: [ - "push", - "--tags", - "https://$username:$password@github.com/bazelbuild/rules_sass.git", - ], - workingDirectory: repo, - ); - await runAsync( - "git", - arguments: [ - "push", - "https://$username:$password@github.com/bazelbuild/rules_sass.git", - "HEAD:main", - ], - workingDirectory: repo, - ); -} From 3b8afa2b3e97174499af031d2cfceb47fb84b42a Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 21 Apr 2025 15:49:09 -0700 Subject: [PATCH 05/36] Fix top-level nesting selectors in loaded plain CSS (#2560) See sass/sass#4055 --- CHANGELOG.md | 8 ++++++++ lib/src/ast/selector.dart | 17 ++++++++++++++++- lib/src/visitor/async_evaluate.dart | 12 ++++++++++-- lib/src/visitor/evaluate.dart | 14 +++++++++++--- pkg/sass_api/CHANGELOG.md | 2 +- pkg/sass_api/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- 7 files changed, 49 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c18d9972d..a3456669d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 1.87.0 + +* **Potentially breaking bug fix:** When a plain CSS file with a top-level + nesting selector `&` is loaded into a nested Sass context via + `meta.load-css()` or `@import`, Sass now emits plain CSS nesting rather than + incorrectly combining it with the parent selector using a descendant + combinator. + ## 1.86.3 * Fix a bug introduced in 1.86.1 where Sass fails to resolve paths starting with diff --git a/lib/src/ast/selector.dart b/lib/src/ast/selector.dart index 64420d5c7..40e607b14 100644 --- a/lib/src/ast/selector.dart +++ b/lib/src/ast/selector.dart @@ -14,6 +14,7 @@ import '../visitor/serialize.dart'; import 'node.dart'; import 'selector/complex.dart'; import 'selector/list.dart'; +import 'selector/parent.dart'; import 'selector/placeholder.dart'; import 'selector/pseudo.dart'; @@ -49,6 +50,13 @@ abstract base class Selector implements AstNode { @internal bool get isInvisible => accept(const _IsInvisibleVisitor(includeBogus: true)); + /// Whether this selector contains a [ParentSelector]. + /// + /// @nodoc + @internal + bool get containsParentSelector => + accept(const _ContainsParentSelectorVisitor()); + // Whether this selector would be invisible even if it didn't have bogus // combinators. /// @@ -169,7 +177,7 @@ class _IsBogusVisitor with AnySelectorVisitor { } } -/// The visitor used to implement [Selector.isUseless] +/// The visitor used to implement [Selector.isUseless]. class _IsUselessVisitor with AnySelectorVisitor { const _IsUselessVisitor(); @@ -182,3 +190,10 @@ class _IsUselessVisitor with AnySelectorVisitor { bool visitPseudoSelector(PseudoSelector pseudo) => pseudo.isBogus; } + +/// The visitor used to implement [Selector.containsParentSelector]. +class _ContainsParentSelectorVisitor with AnySelectorVisitor { + const _ContainsParentSelectorVisitor(); + + bool visitParentSelector(ParentSelector _) => true; +} diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 70dfb415a..c677b6b21 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -2373,7 +2373,11 @@ final class _EvaluateVisitor plainCss: _stylesheet.plainCss, ); - var nest = !(_styleRule?.fromPlainCss ?? false); + var nest = switch (_styleRule) { + null => true, + CssStyleRule(fromPlainCss: true) => false, + _ => !(_stylesheet.plainCss && parsedSelector.containsParentSelector) + }; if (nest) { if (_stylesheet.plainCss) { for (var complex in parsedSelector.components) { @@ -4071,7 +4075,11 @@ final class _EvaluateVisitor } var styleRule = _styleRule; - var nest = !(_styleRule?.fromPlainCss ?? false); + var nest = switch (_styleRule) { + null => true, + CssStyleRule(fromPlainCss: true) => false, + _ => !(node.fromPlainCss && node.selector.containsParentSelector) + }; var originalSelector = nest ? node.selector.nestWithin( styleRule?.originalSelector, diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index be4707f2b..5421ec67c 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: 25aa2d050126950ea37dc1c53539f0b041356e8e +// Checksum: 607745b48d0737b3be112d0a8753dd87492fcc31 // // ignore_for_file: unused_import @@ -2380,7 +2380,11 @@ final class _EvaluateVisitor plainCss: _stylesheet.plainCss, ); - var nest = !(_styleRule?.fromPlainCss ?? false); + var nest = switch (_styleRule) { + null => true, + CssStyleRule(fromPlainCss: true) => false, + _ => !(_stylesheet.plainCss && parsedSelector.containsParentSelector) + }; if (nest) { if (_stylesheet.plainCss) { for (var complex in parsedSelector.components) { @@ -4072,7 +4076,11 @@ final class _EvaluateVisitor } var styleRule = _styleRule; - var nest = !(_styleRule?.fromPlainCss ?? false); + var nest = switch (_styleRule) { + null => true, + CssStyleRule(fromPlainCss: true) => false, + _ => !(node.fromPlainCss && node.selector.containsParentSelector) + }; var originalSelector = nest ? node.selector.nestWithin( styleRule?.originalSelector, diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index 2f4592d8f..e6a66514b 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,4 +1,4 @@ -## 15.3.3 +## 15.4.0 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 7bf89328f..344a71c1d 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: 15.3.3 +version: 15.4.0 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.6.0 <4.0.0" dependencies: - sass: 1.86.3 + sass: 1.87.0 dev_dependencies: dartdoc: ^8.0.14 diff --git a/pubspec.yaml b/pubspec.yaml index 4fd65a4ec..7e8800fe4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.86.3 +version: 1.87.0 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass From c4b0138c243414709772e0aa5c9b2a62fd8e7931 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 17:30:43 -0700 Subject: [PATCH 06/36] Bump protoc_plugin from 21.1.2 to 22.0.1 and protobuf to 4.0.0 (#2554) * Bump protoc_plugin from 21.1.2 to 22.0.0 (note, we did 22.0.1 instead) Bumps [protoc_plugin](https://github.com/google/protobuf.dart) from 21.1.2 to 22.0.0. - [Release notes](https://github.com/google/protobuf.dart/releases) - [Commits](https://github.com/google/protobuf.dart/compare/protoc_plugin-v21.1.2...protoc_plugin-v22.0.0) --- updated-dependencies: - dependency-name: protoc_plugin dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * update protoc_plugin to 22.0.1 and protobuf to 4.0.0 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Carlos (Goodwine) <2022649+Goodwine@users.noreply.github.com> --- pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 7e8800fe4..574024df0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,7 +25,7 @@ dependencies: package_config: ^2.0.0 path: ^1.8.0 pool: ^1.5.1 - protobuf: ">=2.0.0 <4.0.0" + protobuf: "^4.0.0" pub_semver: ^2.0.0 source_maps: ^0.10.10 source_span: ^1.10.0 @@ -45,7 +45,7 @@ dev_dependencies: grinder: ^0.9.0 node_preamble: ^2.0.2 lints: ">=4.0.0 <6.0.0" - protoc_plugin: ^21.1.2 + protoc_plugin: "^22.0.1" pub_api_client: ">=2.1.1 <4.0.0" pubspec_parse: ^1.3.0 test: ^1.16.7 From 735109f35fc8f3cf2e1c7ddea5a8dc143a7bfc68 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 30 Apr 2025 15:59:41 -0700 Subject: [PATCH 07/36] Deprecate passing a relative URL to `compileString()` *et al* (#2567) This also clarifies the documentation on how loads work in the entrypoint file. --- CHANGELOG.md | 4 ++++ lib/sass.dart | 13 +++++++++++-- lib/src/async_compile.dart | 10 ++++++++++ lib/src/compile.dart | 12 +++++++++++- lib/src/deprecation.dart | 9 ++++++++- pkg/sass-parser/CHANGELOG.md | 4 ++++ pkg/sass-parser/package.json | 2 +- pkg/sass_api/CHANGELOG.md | 4 ++++ pkg/sass_api/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- test/deprecations_test.dart | 29 +++++++++++++++++++++++++++-- 11 files changed, 83 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3456669d..058eda80d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.88.0 + +* Deprecate passing a relative URL to `compileString()` and related functions. + ## 1.87.0 * **Potentially breaking bug fix:** When a plain CSS file with a top-level diff --git a/lib/sass.dart b/lib/sass.dart index a4e4219eb..91f69d1eb 100644 --- a/lib/sass.dart +++ b/lib/sass.dart @@ -49,7 +49,9 @@ export 'src/evaluation_context.dart' show warn; /// /// Imports are resolved by trying, in order: /// -/// * Loading a file relative to [path]. +/// * **For relative URLs only:** the URL resolved relative to the current +/// stylesheet's canonical URL, passed to the importer that loaded the current +/// stylesheet. /// /// * Each importer in [importers]. /// @@ -147,7 +149,14 @@ CompileResult compileToResult( /// /// Imports are resolved by trying, in order: /// -/// * The given [importer], with the imported URL resolved relative to [url]. +/// * **For relative URLs only:** the URL resolved relative to the current +/// stylesheet's canonical URL, passed to the importer that loaded the current +/// stylesheet. +/// +/// For the entrypoint file, [url] is the canonical URL and [importer] is the +/// importer that loaded it. If [url] is a `file:` URL and [importer] is null, +/// it defaults to a [FilesystemImporter] that loads relative URLs from the +/// filesystem. /// /// * Each importer in [importers]. /// diff --git a/lib/src/async_compile.dart b/lib/src/async_compile.dart index d11e2555c..cfea91a55 100644 --- a/lib/src/async_compile.dart +++ b/lib/src/async_compile.dart @@ -133,6 +133,16 @@ Future compileStringAsync( var stylesheet = Stylesheet.parse(source, syntax ?? Syntax.scss, url: url); + if (stylesheet.span.sourceUrl case Uri(scheme: '') + when nodeImporter == null) { + deprecationLogger.warnForDeprecation( + Deprecation.compileStringRelativeUrl, + 'Passing a relative `url` argument (${stylesheet.span.sourceUrl}) to ' + 'compileString() or related functions is deprecated and will be an error ' + 'in Dart Sass 2.0.0.', + ); + } + var result = await _compileStylesheet( stylesheet, logger, diff --git a/lib/src/compile.dart b/lib/src/compile.dart index bfca2bc4a..785643008 100644 --- a/lib/src/compile.dart +++ b/lib/src/compile.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_compile.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: ef3310e5d44fa5a9411c9416fb36f51741dfe6ad +// Checksum: d305a0f75e329a29f5aff734ac31ce145fd3b8d5 // // ignore_for_file: unused_import @@ -142,6 +142,16 @@ CompileResult compileString( var stylesheet = Stylesheet.parse(source, syntax ?? Syntax.scss, url: url); + if (stylesheet.span.sourceUrl case Uri(scheme: '') + when nodeImporter == null) { + deprecationLogger.warnForDeprecation( + Deprecation.compileStringRelativeUrl, + 'Passing a relative `url` argument (${stylesheet.span.sourceUrl}) to ' + 'compileString() or related functions is deprecated and will be an error ' + 'in Dart Sass 2.0.0.', + ); + } + var result = _compileStylesheet( stylesheet, logger, diff --git a/lib/src/deprecation.dart b/lib/src/deprecation.dart index db1789ac7..b0e98900c 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: 3639e60773866019c018ae16267c8f23e4df86cf + // Checksum: c57ab2eb07ab1df48581b8484ef9bdbad0ddceaa /// Deprecation for passing a string directly to meta.call(). callString( @@ -173,6 +173,13 @@ enum Deprecation { description: 'Functions named "type".', ), + /// Deprecation for passing a relative url to compileString(). + compileStringRelativeUrl( + 'compile-string-relative-url', + deprecatedIn: '1.88.0', + description: 'Passing a relative url to compileString().', + ), + // END AUTOGENERATED CODE /// Used for deprecations coming from user-authored code. diff --git a/pkg/sass-parser/CHANGELOG.md b/pkg/sass-parser/CHANGELOG.md index 09cd98eba..e8166614d 100644 --- a/pkg/sass-parser/CHANGELOG.md +++ b/pkg/sass-parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.20 + +* No user-visible changes. + ## 0.4.19 * No user-visible changes. diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index 84e67cef3..2fe2454f0 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -1,6 +1,6 @@ { "name": "sass-parser", - "version": "0.4.19", + "version": "0.4.20", "description": "A PostCSS-compatible wrapper of the official Sass parser", "repository": "sass/sass", "author": "Google Inc.", diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index e6a66514b..0c31d4580 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 15.5.0 + +* No user-visible changes. + ## 15.4.0 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 344a71c1d..b4689708d 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: 15.4.0 +version: 15.5.0 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.6.0 <4.0.0" dependencies: - sass: 1.87.0 + sass: 1.88.0 dev_dependencies: dartdoc: ^8.0.14 diff --git a/pubspec.yaml b/pubspec.yaml index 574024df0..1c5f1dd00 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.87.0 +version: 1.88.0 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass diff --git a/test/deprecations_test.dart b/test/deprecations_test.dart index f8d352af5..ad29bd3ae 100644 --- a/test/deprecations_test.dart +++ b/test/deprecations_test.dart @@ -88,6 +88,24 @@ void main() { }); }); + group("compileStringRelativeUrl is violated by", () { + test("a fully relative URL", () { + _expectDeprecationCallback( + () => compileStringToResult("a {b: c}", + url: "foo", + fatalDeprecations: {Deprecation.compileStringRelativeUrl}), + Deprecation.compileStringRelativeUrl); + }); + + test("a root-relative URL", () { + _expectDeprecationCallback( + () => compileStringToResult("a {b: c}", + url: "/foo", + fatalDeprecations: {Deprecation.compileStringRelativeUrl}), + Deprecation.compileStringRelativeUrl); + }); + }); + // Deprecated in various Sass versions <=1.56.0 group("functionUnits is violated by", () { test("a hue with a non-angle unit", () { @@ -136,9 +154,16 @@ void main() { } /// Confirms that [source] will error if [deprecation] is fatal. -void _expectDeprecation(String source, Deprecation deprecation) { +void _expectDeprecation(String source, Deprecation deprecation) => + _expectDeprecationCallback( + () => compileStringToResult(source, fatalDeprecations: {deprecation}), + deprecation); + +/// Confirms that [callback] will produce a fatal deprecation error for +/// [deprecation]. +void _expectDeprecationCallback(void callback(), Deprecation deprecation) { try { - compileStringToResult(source, fatalDeprecations: {deprecation}); + callback(); } catch (e) { if (e.toString().contains("$deprecation deprecation to be fatal")) return; fail('Unexpected error: $e'); From 1a5e0eeee44ad023372670313101df2545f74967 Mon Sep 17 00:00:00 2001 From: Jonathan Conder Date: Wed, 7 May 2025 10:19:53 +1200 Subject: [PATCH 08/36] Fix off-by-one error in Interpolation.spanForElement() (#2568) Co-authored-by: Natalie Weizenbaum --- CHANGELOG.md | 4 ++++ lib/src/ast/sass/interpolation.dart | 3 ++- pkg/sass-parser/CHANGELOG.md | 4 ++++ pkg/sass-parser/package.json | 2 +- pkg/sass_api/CHANGELOG.md | 4 ++++ pkg/sass_api/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- 7 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 058eda80d..eec216086 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.88.1-dev + +* Fix a bug when calculating source spans for interpolations. + ## 1.88.0 * Deprecate passing a relative URL to `compileString()` and related functions. diff --git a/lib/src/ast/sass/interpolation.dart b/lib/src/ast/sass/interpolation.dart index d1a599334..d932934ad 100644 --- a/lib/src/ast/sass/interpolation.dart +++ b/lib/src/ast/sass/interpolation.dart @@ -65,7 +65,8 @@ final class Interpolation implements SassNode { FileSpan spanForElement(int index) => switch (contents[index]) { String() => span.file.span( (index == 0 ? span.start : spans[index - 1]!.end).offset, - (index == spans.length ? span.end : spans[index + 1]!.start).offset, + (index + 1 == spans.length ? span.end : spans[index + 1]!.start) + .offset, ), _ => spans[index]!, }; diff --git a/pkg/sass-parser/CHANGELOG.md b/pkg/sass-parser/CHANGELOG.md index e8166614d..f905887e4 100644 --- a/pkg/sass-parser/CHANGELOG.md +++ b/pkg/sass-parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.21-dev + +* No user-visible changes. + ## 0.4.20 * No user-visible changes. diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index 2fe2454f0..afd43e47e 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -1,6 +1,6 @@ { "name": "sass-parser", - "version": "0.4.20", + "version": "0.4.21-dev", "description": "A PostCSS-compatible wrapper of the official Sass parser", "repository": "sass/sass", "author": "Google Inc.", diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index 0c31d4580..5802e4112 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 15.5.1-dev + +* No user-visible changes. + ## 15.5.0 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index b4689708d..b01342ace 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: 15.5.0 +version: 15.5.1-dev description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.6.0 <4.0.0" dependencies: - sass: 1.88.0 + sass: 1.88.1 dev_dependencies: dartdoc: ^8.0.14 diff --git a/pubspec.yaml b/pubspec.yaml index 1c5f1dd00..0da8832a8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.88.0 +version: 1.88.1-dev description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass From 90b6b39fbe13e80e0e07d12f24091d4ae7ce5b83 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Wed, 7 May 2025 07:21:12 +0200 Subject: [PATCH 09/36] Remove useless `_currentCallable` property in the evaluate visitor (#2570) Since #2474, the _currentCallable property is not used anymore (except for restoring its state) --- lib/src/deprecation.dart | 177 ++++++++++------------------ lib/src/visitor/async_evaluate.dart | 6 - lib/src/visitor/evaluate.dart | 8 +- 3 files changed, 65 insertions(+), 126 deletions(-) diff --git a/lib/src/deprecation.dart b/lib/src/deprecation.dart index b0e98900c..dbda68937 100644 --- a/lib/src/deprecation.dart +++ b/lib/src/deprecation.dart @@ -18,167 +18,118 @@ enum Deprecation { // Checksum: c57ab2eb07ab1df48581b8484ef9bdbad0ddceaa /// Deprecation for passing a string directly to meta.call(). - callString( - 'call-string', - deprecatedIn: '0.0.0', - description: 'Passing a string directly to meta.call().', - ), + callString('call-string', + deprecatedIn: '0.0.0', + description: 'Passing a string directly to meta.call().'), /// Deprecation for @elseif. elseif('elseif', deprecatedIn: '1.3.2', description: '@elseif.'), /// Deprecation for @-moz-document. - mozDocument( - 'moz-document', - deprecatedIn: '1.7.2', - description: '@-moz-document.', - ), + mozDocument('moz-document', + deprecatedIn: '1.7.2', description: '@-moz-document.'), /// Deprecation for imports using relative canonical URLs. - relativeCanonical( - 'relative-canonical', - deprecatedIn: '1.14.2', - description: 'Imports using relative canonical URLs.', - ), + relativeCanonical('relative-canonical', + deprecatedIn: '1.14.2', + description: 'Imports using relative canonical URLs.'), /// Deprecation for declaring new variables with !global. - newGlobal( - 'new-global', - deprecatedIn: '1.17.2', - description: 'Declaring new variables with !global.', - ), + newGlobal('new-global', + deprecatedIn: '1.17.2', + description: 'Declaring new variables with !global.'), /// Deprecation for using color module functions in place of plain CSS functions. - colorModuleCompat( - 'color-module-compat', - deprecatedIn: '1.23.0', - description: - 'Using color module functions in place of plain CSS functions.', - ), + colorModuleCompat('color-module-compat', + deprecatedIn: '1.23.0', + description: + 'Using color module functions in place of plain CSS functions.'), /// Deprecation for / operator for division. - slashDiv( - 'slash-div', - deprecatedIn: '1.33.0', - description: '/ operator for division.', - ), + slashDiv('slash-div', + deprecatedIn: '1.33.0', description: '/ operator for division.'), /// Deprecation for leading, trailing, and repeated combinators. - bogusCombinators( - 'bogus-combinators', - deprecatedIn: '1.54.0', - description: 'Leading, trailing, and repeated combinators.', - ), + bogusCombinators('bogus-combinators', + deprecatedIn: '1.54.0', + description: 'Leading, trailing, and repeated combinators.'), /// Deprecation for ambiguous + and - operators. - strictUnary( - 'strict-unary', - deprecatedIn: '1.55.0', - description: 'Ambiguous + and - operators.', - ), + strictUnary('strict-unary', + deprecatedIn: '1.55.0', description: 'Ambiguous + and - operators.'), /// Deprecation for passing invalid units to built-in functions. - functionUnits( - 'function-units', - deprecatedIn: '1.56.0', - description: 'Passing invalid units to built-in functions.', - ), + functionUnits('function-units', + deprecatedIn: '1.56.0', + description: 'Passing invalid units to built-in functions.'), /// Deprecation for using !default or !global multiple times for one variable. - duplicateVarFlags( - 'duplicate-var-flags', - deprecatedIn: '1.62.0', - description: 'Using !default or !global multiple times for one variable.', - ), + duplicateVarFlags('duplicate-var-flags', + deprecatedIn: '1.62.0', + description: + 'Using !default or !global multiple times for one variable.'), /// Deprecation for passing null as alpha in the ${isJS ? 'JS': 'Dart'} API. - nullAlpha( - 'null-alpha', - deprecatedIn: '1.62.3', - description: 'Passing null as alpha in the ${isJS ? 'JS' : 'Dart'} API.', - ), + nullAlpha('null-alpha', + deprecatedIn: '1.62.3', + description: 'Passing null as alpha in the ${isJS ? 'JS' : 'Dart'} API.'), /// Deprecation for passing percentages to the Sass abs() function. - absPercent( - 'abs-percent', - deprecatedIn: '1.65.0', - description: 'Passing percentages to the Sass abs() function.', - ), + absPercent('abs-percent', + deprecatedIn: '1.65.0', + description: 'Passing percentages to the Sass abs() function.'), /// Deprecation for using the current working directory as an implicit load path. - fsImporterCwd( - 'fs-importer-cwd', - deprecatedIn: '1.73.0', - description: - 'Using the current working directory as an implicit load path.', - ), + fsImporterCwd('fs-importer-cwd', + deprecatedIn: '1.73.0', + description: + 'Using the current working directory as an implicit load path.'), /// Deprecation for function and mixin names beginning with --. - cssFunctionMixin( - 'css-function-mixin', - deprecatedIn: '1.76.0', - description: 'Function and mixin names beginning with --.', - ), + cssFunctionMixin('css-function-mixin', + 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.', - ), + mixedDecls('mixed-decls', + deprecatedIn: '1.77.7', + description: 'Declarations after or between nested rules.'), /// Deprecation for meta.feature-exists - featureExists( - 'feature-exists', - deprecatedIn: '1.78.0', - description: 'meta.feature-exists', - ), + featureExists('feature-exists', + deprecatedIn: '1.78.0', description: 'meta.feature-exists'), /// Deprecation for certain uses of built-in sass:color functions. - color4Api( - 'color-4-api', - deprecatedIn: '1.79.0', - description: 'Certain uses of built-in sass:color functions.', - ), + color4Api('color-4-api', + deprecatedIn: '1.79.0', + description: 'Certain uses of built-in sass:color functions.'), /// Deprecation for using global color functions instead of sass:color. - colorFunctions( - 'color-functions', - deprecatedIn: '1.79.0', - description: 'Using global color functions instead of sass:color.', - ), + colorFunctions('color-functions', + deprecatedIn: '1.79.0', + description: 'Using global color functions instead of sass:color.'), /// Deprecation for legacy JS API. - legacyJsApi( - 'legacy-js-api', - deprecatedIn: '1.79.0', - description: 'Legacy JS API.', - ), + legacyJsApi('legacy-js-api', + deprecatedIn: '1.79.0', description: 'Legacy JS API.'), /// Deprecation for @import rules. import('import', deprecatedIn: '1.80.0', description: '@import rules.'), /// Deprecation for global built-in functions that are available in sass: modules. - globalBuiltin( - 'global-builtin', - deprecatedIn: '1.80.0', - description: - 'Global built-in functions that are available in sass: modules.', - ), + globalBuiltin('global-builtin', + deprecatedIn: '1.80.0', + description: + 'Global built-in functions that are available in sass: modules.'), /// Deprecation for functions named "type". - typeFunction( - 'type-function', - deprecatedIn: '1.86.0', - description: 'Functions named "type".', - ), + typeFunction('type-function', + deprecatedIn: '1.86.0', description: 'Functions named "type".'), /// Deprecation for passing a relative url to compileString(). - compileStringRelativeUrl( - 'compile-string-relative-url', - deprecatedIn: '1.88.0', - description: 'Passing a relative url to compileString().', - ), + compileStringRelativeUrl('compile-string-relative-url', + deprecatedIn: '1.88.0', + description: 'Passing a relative url to compileString().'), // END AUTOGENERATED CODE diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index c677b6b21..4540ee68c 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -210,9 +210,6 @@ final class _EvaluateVisitor /// The human-readable name of the current stack frame. var _member = "root stylesheet"; - /// The innermost user-defined callable that's being invoked. - UserDefinedCallable? _currentCallable; - /// The node for the innermost callable that's being invoked. /// /// This is used to produce warnings for function calls. It's stored as an @@ -3383,9 +3380,7 @@ final class _EvaluateVisitor var name = callable.name; if (name != "@content") name += "()"; - var oldCallable = _currentCallable; var oldInDependency = _inDependency; - _currentCallable = callable; _inDependency = callable.inDependency; var result = await _withStackFrame(name, nodeWithSpan, () { // Add an extra closure() call so that modifications to the environment @@ -3473,7 +3468,6 @@ final class _EvaluateVisitor }); }); }); - _currentCallable = oldCallable; _inDependency = oldInDependency; return result; } diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 5421ec67c..491165efc 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: 607745b48d0737b3be112d0a8753dd87492fcc31 +// Checksum: 05d8589b401932198e1f52434066ea4d6cbf3756 // // ignore_for_file: unused_import @@ -218,9 +218,6 @@ final class _EvaluateVisitor /// The human-readable name of the current stack frame. var _member = "root stylesheet"; - /// The innermost user-defined callable that's being invoked. - UserDefinedCallable? _currentCallable; - /// The node for the innermost callable that's being invoked. /// /// This is used to produce warnings for function calls. It's stored as an @@ -3384,9 +3381,7 @@ final class _EvaluateVisitor var name = callable.name; if (name != "@content") name += "()"; - var oldCallable = _currentCallable; var oldInDependency = _inDependency; - _currentCallable = callable; _inDependency = callable.inDependency; var result = _withStackFrame(name, nodeWithSpan, () { // Add an extra closure() call so that modifications to the environment @@ -3474,7 +3469,6 @@ final class _EvaluateVisitor }); }); }); - _currentCallable = oldCallable; _inDependency = oldInDependency; return result; } From 7f54226d1cfcfcf82c3c725fca78ea9ae945f6e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AA=E3=81=A4=E3=81=8D?= Date: Thu, 8 May 2025 15:50:42 -0700 Subject: [PATCH 10/36] Disallow passing functions/mixins across compilations (#2544) Co-authored-by: Natalie Weizenbaum --- CHANGELOG.md | 9 +++++++-- lib/src/value/function.dart | 26 +++++++++++++++++++++++++- lib/src/value/mixin.dart | 24 +++++++++++++++++++++++- lib/src/visitor/async_evaluate.dart | 27 ++++++++++++++++++++------- lib/src/visitor/evaluate.dart | 29 +++++++++++++++++++++-------- pkg/sass_api/CHANGELOG.md | 6 +----- pkg/sass_api/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- 8 files changed, 100 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eec216086..447cb7580 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,13 @@ -## 1.88.1-dev +## 1.88.0-dev * Fix a bug when calculating source spans for interpolations. -## 1.88.0 +### Dart and JS APIs + +* **Potentially breaking bug fix:** Throw an error when passing a function or + mixin object from one compilation to another. + +### Dart API * Deprecate passing a relative URL to `compileString()` and related functions. diff --git a/lib/src/value/function.dart b/lib/src/value/function.dart index a5f949550..31ab6fe1b 100644 --- a/lib/src/value/function.dart +++ b/lib/src/value/function.dart @@ -5,6 +5,7 @@ import 'package:meta/meta.dart'; import '../callable.dart'; +import '../exception.dart'; import '../visitor/interface/value.dart'; import '../value.dart'; @@ -23,7 +24,16 @@ class SassFunction extends Value { /// synchronous evaluate visitor will crash if this isn't a [Callable]. final AsyncCallable callable; - SassFunction(this.callable); + /// The unique compile context for tracking if this [SassFunction] belongs to + /// the current compilation or not. + /// + /// This is `null` for functions defined in plugins' Dart code. + final Object? _compileContext; + + SassFunction(this.callable) : _compileContext = null; + + @internal + SassFunction.withCompileContext(this.callable, this._compileContext); /// @nodoc @internal @@ -31,6 +41,20 @@ class SassFunction extends Value { SassFunction assertFunction([String? name]) => this; + /// Asserts that this SassFunction belongs to [compileContext] and returns it. + /// + /// It's checked before evaluating a SassFunction to prevent execution of + /// SassFunction across different compilations. + @internal + SassFunction assertCompileContext(Object compileContext) { + if (_compileContext != null && _compileContext != compileContext) { + throw SassScriptException( + "$this does not belong to current compilation."); + } + + return this; + } + bool operator ==(Object other) => other is SassFunction && callable == other.callable; diff --git a/lib/src/value/mixin.dart b/lib/src/value/mixin.dart index 79091579d..99b50f187 100644 --- a/lib/src/value/mixin.dart +++ b/lib/src/value/mixin.dart @@ -5,6 +5,7 @@ import 'package:meta/meta.dart'; import '../callable.dart'; +import '../exception.dart'; import '../visitor/interface/value.dart'; import '../value.dart'; @@ -25,7 +26,14 @@ final class SassMixin extends Value { @internal final AsyncCallable callable; - SassMixin(this.callable); + /// The unique compile context for tracking if this [SassMixin] belongs to the + /// current compilation or not. + final Object? _compileContext; + + SassMixin(this.callable) : _compileContext = null; + + @internal + SassMixin.withCompileContext(this.callable, this._compileContext); /// @nodoc @internal @@ -33,6 +41,20 @@ final class SassMixin extends Value { SassMixin assertMixin([String? name]) => this; + /// Asserts that this SassMixin belongs to [compileContext] and returns it. + /// + /// It's checked before evaluating a SassMixin to prevent execution of + /// SassMixin across different compilations. + @internal + SassMixin assertCompileContext(Object compileContext) { + if (_compileContext != null && _compileContext != compileContext) { + throw SassScriptException( + "$this does not belong to current compilation."); + } + + return this; + } + bool operator ==(Object other) => other is SassMixin && callable == other.callable; diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 4540ee68c..617ffc712 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -179,6 +179,10 @@ final class _EvaluateVisitor /// Whether to track source map information. final bool _sourceMap; + /// The unique compile context for tracking if [SassFunction]s and + /// [SassMixin]s belongs to the current compilation or not. + final Object _compileContext = Object(); + /// The current lexical environment. AsyncEnvironment _environment; @@ -433,7 +437,8 @@ final class _EvaluateVisitor return SassMap({ for (var (name, value) in module.functions.pairs) - SassString(name): SassFunction(value), + SassString(name): + SassFunction.withCompileContext(value, _compileContext), }); }, url: "sass:meta"), @@ -446,7 +451,8 @@ final class _EvaluateVisitor return SassMap({ for (var (name, value) in module.mixins.pairs) - SassString(name): SassMixin(value), + SassString(name): + SassMixin.withCompileContext(value, _compileContext), }); }, url: "sass:meta"), @@ -462,7 +468,8 @@ final class _EvaluateVisitor if (module != null) { throw r"$css and $module may not both be passed at once."; } - return SassFunction(PlainCssCallable(name.text)); + return SassFunction.withCompileContext( + PlainCssCallable(name.text), _compileContext); } var callable = _addExceptionSpan(_callableNode!, () { @@ -477,7 +484,7 @@ final class _EvaluateVisitor }); if (callable == null) throw "Function not found: $name"; - return SassFunction(callable); + return SassFunction.withCompileContext(callable, _compileContext); }, url: "sass:meta", ), @@ -497,7 +504,7 @@ final class _EvaluateVisitor ); if (callable == null) throw "Mixin not found: $name"; - return SassMixin(callable); + return SassMixin.withCompileContext(callable, _compileContext); }, url: "sass:meta"), AsyncBuiltInCallable.function("call", r"$function, $args...", ( @@ -541,7 +548,10 @@ final class _EvaluateVisitor return await expression.accept(this); } - var callable = function.assertFunction("function").callable; + var callable = function + .assertFunction("function") + .assertCompileContext(_compileContext) + .callable; // ignore: unnecessary_type_check if (callable is AsyncCallable) { return await _runFunctionCallable( @@ -608,7 +618,10 @@ final class _EvaluateVisitor rest: ValueExpression(args, callableNode.span), ); - var callable = mixin.assertMixin("mixin").callable; + var callable = mixin + .assertMixin("mixin") + .assertCompileContext(_compileContext) + .callable; var content = _environment.content; // ignore: unnecessary_type_check diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 491165efc..5e126d91e 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: 05d8589b401932198e1f52434066ea4d6cbf3756 +// Checksum: 6e5710daa106ed0b9b684af8bc61ce9cc233a10b // // ignore_for_file: unused_import @@ -187,6 +187,10 @@ final class _EvaluateVisitor /// Whether to track source map information. final bool _sourceMap; + /// The unique compile context for tracking if [SassFunction]s and + /// [SassMixin]s belongs to the current compilation or not. + final Object _compileContext = Object(); + /// The current lexical environment. Environment _environment; @@ -441,7 +445,8 @@ final class _EvaluateVisitor return SassMap({ for (var (name, value) in module.functions.pairs) - SassString(name): SassFunction(value), + SassString(name): + SassFunction.withCompileContext(value, _compileContext), }); }, url: "sass:meta"), @@ -454,7 +459,8 @@ final class _EvaluateVisitor return SassMap({ for (var (name, value) in module.mixins.pairs) - SassString(name): SassMixin(value), + SassString(name): + SassMixin.withCompileContext(value, _compileContext), }); }, url: "sass:meta"), @@ -470,7 +476,8 @@ final class _EvaluateVisitor if (module != null) { throw r"$css and $module may not both be passed at once."; } - return SassFunction(PlainCssCallable(name.text)); + return SassFunction.withCompileContext( + PlainCssCallable(name.text), _compileContext); } var callable = _addExceptionSpan(_callableNode!, () { @@ -485,7 +492,7 @@ final class _EvaluateVisitor }); if (callable == null) throw "Function not found: $name"; - return SassFunction(callable); + return SassFunction.withCompileContext(callable, _compileContext); }, url: "sass:meta", ), @@ -505,7 +512,7 @@ final class _EvaluateVisitor ); if (callable == null) throw "Mixin not found: $name"; - return SassMixin(callable); + return SassMixin.withCompileContext(callable, _compileContext); }, url: "sass:meta"), BuiltInCallable.function("call", r"$function, $args...", ( @@ -549,7 +556,10 @@ final class _EvaluateVisitor return expression.accept(this); } - var callable = function.assertFunction("function").callable; + var callable = function + .assertFunction("function") + .assertCompileContext(_compileContext) + .callable; // ignore: unnecessary_type_check if (callable is Callable) { return _runFunctionCallable( @@ -616,7 +626,10 @@ final class _EvaluateVisitor rest: ValueExpression(args, callableNode.span), ); - var callable = mixin.assertMixin("mixin").callable; + var callable = mixin + .assertMixin("mixin") + .assertCompileContext(_compileContext) + .callable; var content = _environment.content; // ignore: unnecessary_type_check diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index 5802e4112..4b7430f33 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,8 +1,4 @@ -## 15.5.1-dev - -* No user-visible changes. - -## 15.5.0 +## 15.5.0-dev * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index b01342ace..44cafd4b8 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: 15.5.1-dev +version: 15.5.0-dev description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.6.0 <4.0.0" dependencies: - sass: 1.88.1 + sass: 1.88.0 dev_dependencies: dartdoc: ^8.0.14 diff --git a/pubspec.yaml b/pubspec.yaml index 0da8832a8..e67c1c6b6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.88.1-dev +version: 1.88.0-dev description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass From a81cd73aea532aab95d0b0e9c4f56baf4ade98fc Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 6 May 2025 15:47:13 -0700 Subject: [PATCH 11/36] Properly handle dev changelogs in bump-version-* tasks Previously this wouldn't update -dev changelogs if we were bumping a more major version than the -dev version bumped. --- tool/grind/bump_version.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tool/grind/bump_version.dart b/tool/grind/bump_version.dart index aa6ca82c5..310c25d33 100644 --- a/tool/grind/bump_version.dart +++ b/tool/grind/bump_version.dart @@ -17,6 +17,9 @@ final _pubspecVersionRegExp = RegExp(r'^version: (.*)$', multiLine: true); /// A regular expression that matches a Sass dependency version in a pubspec. final _sassVersionRegExp = RegExp(r'^( +)sass: (\d.*)$', multiLine: true); +/// A regular expression that matches a CHANGELOG header for a dev version +final _changelogDevHeaderRegExp = RegExp(r'^## .*-dev$', multiLine: true); + /// Adds grinder tasks for bumping package versions. void addBumpVersionTasks() { for (var patch in [false, true]) { @@ -65,9 +68,9 @@ void _bumpVersion(bool patch, bool dev) { void addChangelogEntry(String dir, Version version) { var path = p.join(dir, "CHANGELOG.md"); var text = File(path).readAsStringSync(); - if (!dev && text.startsWith("## $version-dev\n")) { + if (!dev && text.startsWith(_changelogDevHeaderRegExp)) { File(path).writeAsStringSync( - text.replaceFirst("## $version-dev\n", "## $version\n"), + text.replaceFirst(_changelogDevHeaderRegExp, "## $version"), ); } else if (text.startsWith("## $version\n")) { return; From 6edc11ee3b734667d35149bb77ec80ab84d94c0c Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 8 May 2025 16:04:22 -0700 Subject: [PATCH 12/36] Support empty custom properties See sass/sass#4078 --- CHANGELOG.md | 4 +++- lib/src/parse/stylesheet.dart | 4 +++- lib/src/visitor/async_evaluate.dart | 14 ++++++-------- lib/src/visitor/evaluate.dart | 16 +++++++--------- pkg/sass-parser/CHANGELOG.md | 2 +- pkg/sass-parser/package.json | 2 +- pkg/sass_api/CHANGELOG.md | 2 +- pkg/sass_api/pubspec.yaml | 2 +- pubspec.yaml | 2 +- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 447cb7580..2e3693fdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 1.88.0-dev +## 1.88.0 + +* Allow custom properties with empty values (such as `--var:;`). * Fix a bug when calculating source spans for interpolations. diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index ae02a7598..17fdd99e5 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -412,7 +412,9 @@ abstract class StylesheetParser extends Parser { var name = nameBuffer.interpolation(scanner.spanFrom(start, beforeColon)); if (name.initialPlain.startsWith('--')) { var value = StringExpression( - _interpolatedDeclarationValue(silentComments: false), + atEndOfStatement() + ? Interpolation(const [], const [], scanner.emptySpan) + : _interpolatedDeclarationValue(silentComments: false), ); expectStatementSeparator("custom property"); return Declaration(name, value, scanner.spanFrom(start)); diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 617ffc712..c1db0e72e 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -1381,9 +1381,12 @@ final class _EvaluateVisitor if (node.value case var expression?) { var value = await expression.accept(this); - // If the value is an empty list, preserve it, because converting it to CSS - // will throw an error that we want the user to see. - if (!value.isBlank || _isEmptyList(value)) { + if (!value.isBlank || + // If the value is an empty list, preserve it, because converting it + // to CSS will throw an error that we want the user to see. + _isEmptyList(value) || + // Custom properties are allowed to have empty values, per spec. + name.value.startsWith('--')) { _parent.addChild( ModifiableCssDeclaration( name, @@ -1396,11 +1399,6 @@ final class _EvaluateVisitor _sourceMap ? node.value.andThen(_expressionNode)?.span : null, ), ); - } else if (name.value.startsWith('--')) { - throw _exception( - "Custom property values may not be empty.", - expression.span, - ); } } diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 5e126d91e..5727e56aa 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: 6e5710daa106ed0b9b684af8bc61ce9cc233a10b +// Checksum: a3068d04660dd2bed34b884aa6e1a21d423dc4e5 // // ignore_for_file: unused_import @@ -1389,9 +1389,12 @@ final class _EvaluateVisitor if (node.value case var expression?) { var value = expression.accept(this); - // If the value is an empty list, preserve it, because converting it to CSS - // will throw an error that we want the user to see. - if (!value.isBlank || _isEmptyList(value)) { + if (!value.isBlank || + // If the value is an empty list, preserve it, because converting it + // to CSS will throw an error that we want the user to see. + _isEmptyList(value) || + // Custom properties are allowed to have empty values, per spec. + name.value.startsWith('--')) { _parent.addChild( ModifiableCssDeclaration( name, @@ -1404,11 +1407,6 @@ final class _EvaluateVisitor _sourceMap ? node.value.andThen(_expressionNode)?.span : null, ), ); - } else if (name.value.startsWith('--')) { - throw _exception( - "Custom property values may not be empty.", - expression.span, - ); } } diff --git a/pkg/sass-parser/CHANGELOG.md b/pkg/sass-parser/CHANGELOG.md index f905887e4..ccf8f716c 100644 --- a/pkg/sass-parser/CHANGELOG.md +++ b/pkg/sass-parser/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.4.21-dev +## 0.4.21 * No user-visible changes. diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index afd43e47e..c34bb4dba 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -1,6 +1,6 @@ { "name": "sass-parser", - "version": "0.4.21-dev", + "version": "0.4.21", "description": "A PostCSS-compatible wrapper of the official Sass parser", "repository": "sass/sass", "author": "Google Inc.", diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index 4b7430f33..0c31d4580 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,4 +1,4 @@ -## 15.5.0-dev +## 15.5.0 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 44cafd4b8..b4689708d 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: 15.5.0-dev +version: 15.5.0 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass diff --git a/pubspec.yaml b/pubspec.yaml index e67c1c6b6..1c5f1dd00 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.88.0-dev +version: 1.88.0 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass From 4e9e2062f691359a8da582eb4e600ad5dc931c93 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 15 May 2025 17:35:15 -0700 Subject: [PATCH 13/36] Allow the Node.js pkg importer to return the same URL multiple times (#2575) --- CHANGELOG.md | 6 ++++++ lib/src/importer/node_package.dart | 1 + pkg/sass-parser/CHANGELOG.md | 4 ++++ pkg/sass-parser/package.json | 2 +- pkg/sass_api/CHANGELOG.md | 4 ++++ pkg/sass_api/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- 7 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e3693fdb..f7226993f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.89.0 + +* Allow the Node package importer to load files even when there are multiple + potential resolutions, as long as those resolutions all point to the same + file. + ## 1.88.0 * Allow custom properties with empty values (such as `--var:;`). diff --git a/lib/src/importer/node_package.dart b/lib/src/importer/node_package.dart index 3a1f4e2b4..3769e292d 100644 --- a/lib/src/importer/node_package.dart +++ b/lib/src/importer/node_package.dart @@ -271,6 +271,7 @@ class NodePackageImporter extends Importer { return null; }) .nonNulls + .toSet() .toList(); return switch (matches) { diff --git a/pkg/sass-parser/CHANGELOG.md b/pkg/sass-parser/CHANGELOG.md index ccf8f716c..746bd40db 100644 --- a/pkg/sass-parser/CHANGELOG.md +++ b/pkg/sass-parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.22 + +* No user-visible changes. + ## 0.4.21 * No user-visible changes. diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index c34bb4dba..add3ca1b0 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -1,6 +1,6 @@ { "name": "sass-parser", - "version": "0.4.21", + "version": "0.4.22", "description": "A PostCSS-compatible wrapper of the official Sass parser", "repository": "sass/sass", "author": "Google Inc.", diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index 0c31d4580..a5edae7cd 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 15.6.0 + +* No user-visible changes. + ## 15.5.0 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index b4689708d..47a3946d8 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: 15.5.0 +version: 15.6.0 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.6.0 <4.0.0" dependencies: - sass: 1.88.0 + sass: 1.89.0 dev_dependencies: dartdoc: ^8.0.14 diff --git a/pubspec.yaml b/pubspec.yaml index 1c5f1dd00..3054e31ee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.88.0 +version: 1.89.0 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass From e7b70e2558be0053de37313a7d3fba5a8adfa866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AA=E3=81=A4=E3=81=8D?= Date: Wed, 21 May 2025 13:36:49 -0700 Subject: [PATCH 14/36] Remove ia32 support on dart 3.8 (#2579) --- .github/workflows/build-linux.yml | 9 --------- .github/workflows/build-windows.yml | 2 -- 2 files changed, 11 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 9b1f5ff38..cc83a4885 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -21,9 +21,6 @@ jobs: - image: docker.io/library/dart platform: linux/amd64 target: linux-x64 - - image: docker.io/library/dart - platform: linux/amd64 - target: linux-ia32 - image: docker.io/library/dart platform: linux/arm64 target: linux-arm64 @@ -36,9 +33,6 @@ jobs: - image: ghcr.io/dart-musl/dart platform: linux/amd64 target: linux-x64-musl - - image: ghcr.io/dart-musl/dart - platform: linux/amd64 - target: linux-ia32-musl - image: ghcr.io/dart-musl/dart platform: linux/arm64 target: linux-arm64-musl @@ -51,9 +45,6 @@ jobs: - image: ghcr.io/dart-android/dart platform: linux/amd64 target: android-x64 - - image: ghcr.io/dart-android/dart - platform: linux/amd64 - target: android-ia32 - image: ghcr.io/dart-android/dart platform: linux/arm64 target: android-arm64 diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index f22f976bc..c72e2bbaa 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -20,8 +20,6 @@ jobs: include: - arch: x64 runner: windows-latest - - arch: ia32 - runner: windows-latest - arch: arm64 runner: windows-11-arm From 9a5b4f5941255b6a4c4e23b969b097c6ddf3e1be Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 30 May 2025 15:14:09 -0700 Subject: [PATCH 15/36] Properly add `ParenthesizedExpression` for comma-separated lists --- CHANGELOG.md | 4 ++++ lib/src/parse/stylesheet.dart | 8 +++++--- pkg/sass-parser/CHANGELOG.md | 5 +++++ pkg/sass-parser/package.json | 2 +- pkg/sass_api/CHANGELOG.md | 5 +++++ pkg/sass_api/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- 7 files changed, 23 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7226993f..019cdb504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.89.1 + +* No user-visible changes. + ## 1.89.0 * Allow the Node package importer to load files even when there are multiple diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 17fdd99e5..efee015a4 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -2396,6 +2396,7 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; scanner.expectChar($lparen); whitespace(consumeNewlines: true); + var inside = scanner.state; if (!_lookingAtExpression()) { scanner.expectChar($rparen); return ListExpression( @@ -2425,12 +2426,13 @@ abstract class StylesheetParser extends Parser { whitespace(consumeNewlines: true); } - scanner.expectChar($rparen); - return ListExpression( + var list = ListExpression( expressions, ListSeparator.comma, - scanner.spanFrom(start), + scanner.spanFrom(inside), ); + scanner.expectChar($rparen); + return ParenthesizedExpression(list, scanner.spanFrom(start)); } finally { _inParentheses = wasInParentheses; } diff --git a/pkg/sass-parser/CHANGELOG.md b/pkg/sass-parser/CHANGELOG.md index 746bd40db..1f7722f80 100644 --- a/pkg/sass-parser/CHANGELOG.md +++ b/pkg/sass-parser/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.4.23 + +* **Potentially-breaking bug fix**: parenthesized, comma-separated lists are now + correctly wrapped in a `ParenthesizedExpression`. + ## 0.4.22 * No user-visible changes. diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index add3ca1b0..4baff4fa1 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -1,6 +1,6 @@ { "name": "sass-parser", - "version": "0.4.22", + "version": "0.4.23", "description": "A PostCSS-compatible wrapper of the official Sass parser", "repository": "sass/sass", "author": "Google Inc.", diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index a5edae7cd..94b28dcc3 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,8 @@ +## 15.7.0 + +* **Potentially-breaking bug fix**: parenthesized, comma-separated lists are now + correctly wrapped in a `ParenthesizedExpression`. + ## 15.6.0 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 47a3946d8..9d70a0149 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: 15.6.0 +version: 15.7.0 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.6.0 <4.0.0" dependencies: - sass: 1.89.0 + sass: 1.89.1 dev_dependencies: dartdoc: ^8.0.14 diff --git a/pubspec.yaml b/pubspec.yaml index 3054e31ee..f5f848a90 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.89.0 +version: 1.89.1 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass From 3c9a5019e1404d36fcdb4274ee2897be66f71c9f Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 30 May 2025 15:31:26 -0700 Subject: [PATCH 16/36] Fix sass-parser compatibility with the latest PostCSS --- pkg/sass-parser/CHANGELOG.md | 2 ++ pkg/sass-parser/lib/src/node.d.ts | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/sass-parser/CHANGELOG.md b/pkg/sass-parser/CHANGELOG.md index 1f7722f80..f67944eec 100644 --- a/pkg/sass-parser/CHANGELOG.md +++ b/pkg/sass-parser/CHANGELOG.md @@ -1,5 +1,7 @@ ## 0.4.23 +* Update types for compatibility with the latest PostCSS. + * **Potentially-breaking bug fix**: parenthesized, comma-separated lists are now correctly wrapped in a `ParenthesizedExpression`. diff --git a/pkg/sass-parser/lib/src/node.d.ts b/pkg/sass-parser/lib/src/node.d.ts index a89bb6137..b4026e362 100644 --- a/pkg/sass-parser/lib/src/node.d.ts +++ b/pkg/sass-parser/lib/src/node.d.ts @@ -121,7 +121,12 @@ declare abstract class Node opts?: Pick, ): postcss.Position; positionInside(index: number): postcss.Position; - rangeBy(opts?: Pick): { + rangeBy( + opts?: Pick< + postcss.WarningOptions, + 'end' | 'endIndex' | 'index' | 'start' | 'word' + >, + ): { start: postcss.Position; end: postcss.Position; }; From 449800725282223aaaf29ee1c1e986baf350424e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 13:55:11 -0700 Subject: [PATCH 17/36] Bump jest-extended from 4.0.2 to 5.0.3 in /pkg/sass-parser (#2582) Bumps [jest-extended](https://github.com/jest-community/jest-extended) from 4.0.2 to 5.0.3. - [Release notes](https://github.com/jest-community/jest-extended/releases) - [Changelog](https://github.com/jest-community/jest-extended/blob/main/CHANGELOG.md) - [Commits](https://github.com/jest-community/jest-extended/compare/v4.0.2...v5.0.3) --- updated-dependencies: - dependency-name: jest-extended dependency-version: 5.0.3 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkg/sass-parser/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index 4baff4fa1..bfe9183f5 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -40,7 +40,7 @@ "expect": "^29.7.0", "gts": "^6.0.2", "jest": "^29.4.1", - "jest-extended": "^4.0.2", + "jest-extended": "^5.0.3", "npm-run-all": "^4.1.5", "rimraf": "^6.0.1", "ts-jest": "^29.0.5", From c2532cc048a90ea1a7050f4a3d5ff2002fdb8db6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 13:55:24 -0700 Subject: [PATCH 18/36] Bump lints from 5.1.1 to 6.0.0 (#2581) Bumps [lints](https://github.com/dart-lang/core/tree/main/pkgs) from 5.1.1 to 6.0.0. - [Release notes](https://github.com/dart-lang/core/releases) - [Commits](https://github.com/dart-lang/core/commits/lints-v6.0.0/pkgs) --- updated-dependencies: - dependency-name: lints dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index f5f848a90..363b97c78 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,7 +44,7 @@ dev_dependencies: dartdoc: ^8.0.14 grinder: ^0.9.0 node_preamble: ^2.0.2 - lints: ">=4.0.0 <6.0.0" + lints: ">=4.0.0 <7.0.0" protoc_plugin: "^22.0.1" pub_api_client: ">=2.1.1 <4.0.0" pubspec_parse: ^1.3.0 From 1250380bc56d1feecc5fead0752731a313b97e2e Mon Sep 17 00:00:00 2001 From: "Carlos (Goodwine)" <2022649+Goodwine@users.noreply.github.com> Date: Mon, 9 Jun 2025 11:18:29 -0700 Subject: [PATCH 19/36] release 1.89.2 for an embedded-host-node fix (#2585) * release 1.89.2 for an embedded-host-node fix --- CHANGELOG.md | 6 ++++++ pkg/sass-parser/CHANGELOG.md | 4 ++++ pkg/sass-parser/package.json | 2 +- pkg/sass_api/CHANGELOG.md | 4 ++++ pkg/sass_api/pubspec.yaml | 4 ++-- pubspec.yaml | 3 +-- 6 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 019cdb504..8b2ccc65f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.89.2 + +### Embedded Host + +* Fixed a compilation error caused by an outdated `buf` dependency. + ## 1.89.1 * No user-visible changes. diff --git a/pkg/sass-parser/CHANGELOG.md b/pkg/sass-parser/CHANGELOG.md index f67944eec..ef9e27284 100644 --- a/pkg/sass-parser/CHANGELOG.md +++ b/pkg/sass-parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.24 + +* No user-visible changes. + ## 0.4.23 * Update types for compatibility with the latest PostCSS. diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index bfe9183f5..f3a7f420f 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -1,6 +1,6 @@ { "name": "sass-parser", - "version": "0.4.23", + "version": "0.4.24", "description": "A PostCSS-compatible wrapper of the official Sass parser", "repository": "sass/sass", "author": "Google Inc.", diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index 94b28dcc3..e034f36f9 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 15.7.1 + +* No user-visible changes. + ## 15.7.0 * **Potentially-breaking bug fix**: parenthesized, comma-separated lists are now diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 9d70a0149..a0ad29aca 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: 15.7.0 +version: 15.7.1 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.6.0 <4.0.0" dependencies: - sass: 1.89.1 + sass: 1.89.2 dev_dependencies: dartdoc: ^8.0.14 diff --git a/pubspec.yaml b/pubspec.yaml index 363b97c78..039be9860 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.89.1 +version: 1.89.2 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass @@ -53,4 +53,3 @@ dev_dependencies: test_process: ^2.0.0 yaml: ^3.1.0 cli_util: ^0.4.0 - From a42380fcf817e8765d1237c1bb5bfbd98ee73ad9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 13:50:31 -0700 Subject: [PATCH 20/36] Bump jest-extended from 5.0.3 to 6.0.0 in /pkg/sass-parser (#2587) Bumps [jest-extended](https://github.com/jest-community/jest-extended) from 5.0.3 to 6.0.0. - [Release notes](https://github.com/jest-community/jest-extended/releases) - [Changelog](https://github.com/jest-community/jest-extended/blob/main/CHANGELOG.md) - [Commits](https://github.com/jest-community/jest-extended/compare/v5.0.3...v6.0.0) --- updated-dependencies: - dependency-name: jest-extended dependency-version: 6.0.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkg/sass-parser/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index f3a7f420f..f24a295dd 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -40,7 +40,7 @@ "expect": "^29.7.0", "gts": "^6.0.2", "jest": "^29.4.1", - "jest-extended": "^5.0.3", + "jest-extended": "^6.0.0", "npm-run-all": "^4.1.5", "rimraf": "^6.0.1", "ts-jest": "^29.0.5", From b27c3ae9415ba83e3f44c34f6c2847bff1bc3e5d Mon Sep 17 00:00:00 2001 From: "Carlos (Goodwine)" <2022649+Goodwine@users.noreply.github.com> Date: Wed, 9 Jul 2025 10:52:18 -0700 Subject: [PATCH 21/36] Fix CI by pinning postcss version to 8.5.5 and disable app armor for browser tests (#2606) * Pin postcss version to 8.5.5 Not using 8.5.6 yet because it needs additional changes for type compatibility * disable app armor for browser tests See https://github.com/puppeteer/puppeteer/issues/12818 Ubuntu 23+ doesn't like running puppeteer without disabling AppArmor. --- .github/workflows/test.yml | 9 +++++---- pkg/sass-parser/package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b4bb2f801..4c9b7b3be 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -309,13 +309,14 @@ jobs: - run: dart run grinder pkg-npm-dev env: {UPDATE_SASS_SASS_REPO: false} - - run: sudo chmod 4755 /opt/google/chrome/chrome-sandbox + # See https://github.com/puppeteer/puppeteer/issues/12818 + # Ubuntu 23+ doesn't like running puppeteer without disabling AppArmor. + - name: Disable AppArmor + run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 - name: Run tests run: dart run test -p chrome -j 2 env: CHROME_EXECUTABLE: chrome - # See https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md#option-3_the-safest-way - CHROME_DEVEL_SANDBOX: /opt/google/chrome/chrome-sandbox sass_parser_tests: name: "sass-parser Tests | Dart ${{ matrix.dart_channel }} | Node ${{ matrix.node-version }}" @@ -328,7 +329,7 @@ jobs: node-version: ['lts/*'] include: # Test older LTS versions - # + # # TODO: Test on lts/-2 and lts/-3 once they support # `structuredClone()` (that is, once they're v18 or later). - os: ubuntu-latest diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index f24a295dd..ee1b563fb 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -31,7 +31,7 @@ "test": "jest" }, "dependencies": { - "postcss": ">=8.4.41 <8.6.0", + "postcss": "8.5.5", "sass": "file:../../build/npm" }, "devDependencies": { From d3e1abf24fdf0663067c9d2750497dbc23bce5a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 14:08:18 -0700 Subject: [PATCH 22/36] Bump jest from 29.7.0 to 30.0.4 in /pkg/sass-parser (#2603) Bumps [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) from 29.7.0 to 30.0.4. - [Release notes](https://github.com/jestjs/jest/releases) - [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jestjs/jest/commits/v30.0.4/packages/jest) --- updated-dependencies: - dependency-name: jest dependency-version: 30.0.4 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkg/sass-parser/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index ee1b563fb..f3d816655 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -39,7 +39,7 @@ "copyfiles": "^2.4.1", "expect": "^29.7.0", "gts": "^6.0.2", - "jest": "^29.4.1", + "jest": "^30.0.4", "jest-extended": "^6.0.0", "npm-run-all": "^4.1.5", "rimraf": "^6.0.1", From 008bbefad0f5c1dd75fcbd853693b26c7e89b126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AA=E3=81=A4=E3=81=8D?= Date: Mon, 21 Jul 2025 16:34:37 -0700 Subject: [PATCH 23/36] Update embedded-host-node release pipeline (#2609) --- .github/workflows/release.yml | 3 ++- .github/workflows/test.yml | 10 ++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index acf9feaf1..4bdc66b89 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -216,7 +216,8 @@ jobs: for dir in $(ls npm); do cat "npm/$dir/package.json" | jq --arg version ${{ steps.version.outputs.version }} ' - .version |= $version + .version |= $version | + if (.dependencies.sass) then .dependencies.sass |= $version end ' > package.json.tmp && mv package.json.tmp "npm/$dir/package.json" done diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4c9b7b3be..5e7cb5ec3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -170,17 +170,11 @@ jobs: npm install npm run init -- --compiler-path=.. --language-path=../build/language npm run compile - mv {`pwd`/,dist/}lib/src/vendor/dart-sass working-directory: embedded-host-node - name: Version info - run: | - path=embedded-host-node/dist/lib/src/vendor/dart-sass/sass - if [[ -f "$path.cmd" ]]; then "./$path.cmd" --version - elif [[ -f "$path.bat" ]]; then "./$path.bat" --version - elif [[ -f "$path.exe" ]]; then "./$path.exe" --version - else "./$path" --version - fi + run: node dist/bin/sass.js --version + working-directory: embedded-host-node - name: Run tests run: npm run js-api-spec -- --sassPackage ../embedded-host-node --sassSassRepo ../build/language From 02fa098afa81bbd3ae9bef2549fabab28a6e285a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Aug 2025 13:47:51 -0700 Subject: [PATCH 24/36] Bump @types/jest from 29.5.14 to 30.0.0 in /pkg/sass-parser (#2591) Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.5.14 to 30.0.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest) --- updated-dependencies: - dependency-name: "@types/jest" dependency-version: 30.0.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkg/sass-parser/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index f3d816655..e19c7816a 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -35,7 +35,7 @@ "sass": "file:../../build/npm" }, "devDependencies": { - "@types/jest": "^29.5.12", + "@types/jest": "^30.0.0", "copyfiles": "^2.4.1", "expect": "^29.7.0", "gts": "^6.0.2", From d6f4f17eef454d870feae0b302d870ffa75beb76 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 5 Aug 2025 14:48:49 -0700 Subject: [PATCH 25/36] Allow unused configurations to go past `@forward` (#2600) Closes #2598 --- CHANGELOG.md | 6 +++ lib/src/async_environment.dart | 76 +++++++++++++++++++++------- lib/src/configuration.dart | 4 +- lib/src/environment.dart | 78 +++++++++++++++++++++-------- lib/src/module.dart | 7 ++- lib/src/module/built_in.dart | 2 + lib/src/module/forwarded_view.dart | 25 +++++++++ lib/src/module/shadowed_view.dart | 8 +++ lib/src/visitor/async_evaluate.dart | 15 ++++-- lib/src/visitor/evaluate.dart | 17 +++++-- pkg/sass-parser/CHANGELOG.md | 4 ++ pkg/sass-parser/package.json | 2 +- pkg/sass_api/CHANGELOG.md | 4 ++ pkg/sass_api/pubspec.yaml | 4 +- pubspec.yaml | 2 +- 15 files changed, 200 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b2ccc65f..eaf6dcc39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.90.0 + +* Allow a `@forward`ed module to be loaded with a configuration when that module + has already been loaded with a different configuration *and* the module + doesn't define any variables that would have been configured anyway. + ## 1.89.2 ### Embedded Host diff --git a/lib/src/async_environment.dart b/lib/src/async_environment.dart index b2b9f6521..fa8458ab4 100644 --- a/lib/src/async_environment.dart +++ b/lib/src/async_environment.dart @@ -121,6 +121,10 @@ final class AsyncEnvironment { UserDefinedCallable? get content => _content; UserDefinedCallable? _content; + /// The set of variable names that could be configured when loading the + /// module. + final Set _configurableVariables; + /// Whether the environment is lexically at the root of the document. bool get atRoot => _variables.length == 1; @@ -160,27 +164,28 @@ final class AsyncEnvironment { _functions = [{}], _functionIndices = {}, _mixins = [{}], - _mixinIndices = {}; + _mixinIndices = {}, + _configurableVariables = {}; AsyncEnvironment._( - this._modules, - this._namespaceNodes, - this._globalModules, - this._importedModules, - this._forwardedModules, - this._nestedForwardedModules, - this._allModules, - this._variables, - this._variableNodes, - this._functions, - this._mixins, - this._content, - ) - // Lazily fill in the indices rather than eagerly copying them from the - // existing environment in closure() because the copying took a lot of - // time and was rarely helpful. This saves a bunch of time on Susy's - // tests. - : _variableIndices = {}, + this._modules, + this._namespaceNodes, + this._globalModules, + this._importedModules, + this._forwardedModules, + this._nestedForwardedModules, + this._allModules, + this._variables, + this._variableNodes, + this._functions, + this._mixins, + this._content, + this._configurableVariables) + // Lazily fill in the indices rather than eagerly copying them from the + // existing environment in closure() because the copying took a lot of + // time and was rarely helpful. This saves a bunch of time on Susy's + // tests. + : _variableIndices = {}, _functionIndices = {}, _mixinIndices = {}; @@ -202,6 +207,9 @@ final class AsyncEnvironment { _functions.toList(), _mixins.toList(), _content, + // Closures are always in nested contexts where configurable variables + // are never added. + const {}, ); /// Returns a new environment to use for an imported file. @@ -222,6 +230,7 @@ final class AsyncEnvironment { _functions.toList(), _mixins.toList(), _content, + _configurableVariables, ); /// Adds [module] to the set of modules visible in this environment. @@ -640,6 +649,15 @@ final class AsyncEnvironment { _variableNodes[index][name] = nodeWithSpan; } + /// Records that [name] is a variable that could have been configured for this + /// module, whether or not it actually was. + /// + /// This is used to determine whether to throw an error when passing a new + /// configuration through `@forward` to an already-loaded module. + void markVariableConfigurable(String name) { + _configurableVariables.add(name); + } + /// Returns the value of the function named [name], optionally with the given /// [namespace], or `null` if no such variable is declared. /// @@ -1070,6 +1088,26 @@ final class _EnvironmentModule implements Module { return module == null ? this : module.variableIdentity(name); } + bool couldHaveBeenConfigured(Set variables) => + // Check if this module defines a configurable variable with any of the + // given names. + (variables.length < _environment._configurableVariables.length + ? variables.any(_environment._configurableVariables.contains) + : _environment._configurableVariables.any(variables.contains)) || + // Find forwarded modules whose variables overlap with [variables] and + // check if they define configurable variables with any of the given + // names. + (variables.length < _modulesByVariable.length + ? { + for (var variable in variables) + if (_modulesByVariable[variable] case var module?) module + } + : { + for (var (variable, module) in _modulesByVariable.pairs) + if (variables.contains(variable)) module + }) + .any((module) => module.couldHaveBeenConfigured(variables)); + Module cloneCss() { if (!transitivelyContainsCss) return this; diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index fb4138481..3cc5b05fd 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -50,7 +50,7 @@ final class Configuration { /// will be considered to have the same original config if they were created /// as a copy from the same base configuration. bool sameOriginal(Configuration that) => - _originalConfiguration == that._originalConfiguration; + identical(_originalConfiguration, that._originalConfiguration); /// The empty configuration, which indicates that the module has not been /// configured. @@ -70,7 +70,7 @@ final class Configuration { /// Creates a new configuration from this one based on a `@forward` rule. Configuration throughForward(ForwardRule forward) { - if (isEmpty) return const Configuration.empty(); + if (isEmpty) return this; var newValues = _values; // Only allow variables that are visible through the `@forward` to be diff --git a/lib/src/environment.dart b/lib/src/environment.dart index 5312ad71f..63c82ae53 100644 --- a/lib/src/environment.dart +++ b/lib/src/environment.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_environment.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: add8a3972aec53ef29de3639af2bc84edcbde9e7 +// Checksum: 72b802e4004aae8a84f7aa78ec728861339b846b // // ignore_for_file: unused_import @@ -128,6 +128,10 @@ final class Environment { UserDefinedCallable? get content => _content; UserDefinedCallable? _content; + /// The set of variable names that could be configured when loading the + /// module. + final Set _configurableVariables; + /// Whether the environment is lexically at the root of the document. bool get atRoot => _variables.length == 1; @@ -167,27 +171,28 @@ final class Environment { _functions = [{}], _functionIndices = {}, _mixins = [{}], - _mixinIndices = {}; + _mixinIndices = {}, + _configurableVariables = {}; Environment._( - this._modules, - this._namespaceNodes, - this._globalModules, - this._importedModules, - this._forwardedModules, - this._nestedForwardedModules, - this._allModules, - this._variables, - this._variableNodes, - this._functions, - this._mixins, - this._content, - ) - // Lazily fill in the indices rather than eagerly copying them from the - // existing environment in closure() because the copying took a lot of - // time and was rarely helpful. This saves a bunch of time on Susy's - // tests. - : _variableIndices = {}, + this._modules, + this._namespaceNodes, + this._globalModules, + this._importedModules, + this._forwardedModules, + this._nestedForwardedModules, + this._allModules, + this._variables, + this._variableNodes, + this._functions, + this._mixins, + this._content, + this._configurableVariables) + // Lazily fill in the indices rather than eagerly copying them from the + // existing environment in closure() because the copying took a lot of + // time and was rarely helpful. This saves a bunch of time on Susy's + // tests. + : _variableIndices = {}, _functionIndices = {}, _mixinIndices = {}; @@ -209,6 +214,9 @@ final class Environment { _functions.toList(), _mixins.toList(), _content, + // Closures are always in nested contexts where configurable variables + // are never added. + const {}, ); /// Returns a new environment to use for an imported file. @@ -229,6 +237,7 @@ final class Environment { _functions.toList(), _mixins.toList(), _content, + _configurableVariables, ); /// Adds [module] to the set of modules visible in this environment. @@ -648,6 +657,15 @@ final class Environment { _variableNodes[index][name] = nodeWithSpan; } + /// Records that [name] is a variable that could have been configured for this + /// module, whether or not it actually was. + /// + /// This is used to determine whether to throw an error when passing a new + /// configuration through `@forward` to an already-loaded module. + void markVariableConfigurable(String name) { + _configurableVariables.add(name); + } + /// Returns the value of the function named [name], optionally with the given /// [namespace], or `null` if no such variable is declared. /// @@ -1080,6 +1098,26 @@ final class _EnvironmentModule implements Module { return module == null ? this : module.variableIdentity(name); } + bool couldHaveBeenConfigured(Set variables) => + // Check if this module defines a configurable variable with any of the + // given names. + (variables.length < _environment._configurableVariables.length + ? variables.any(_environment._configurableVariables.contains) + : _environment._configurableVariables.any(variables.contains)) || + // Find forwarded modules whose variables overlap with [variables] and + // check if they define configurable variables with any of the given + // names. + (variables.length < _modulesByVariable.length + ? { + for (var variable in variables) + if (_modulesByVariable[variable] case var module?) module + } + : { + for (var (variable, module) in _modulesByVariable.pairs) + if (variables.contains(variable)) module + }) + .any((module) => module.couldHaveBeenConfigured(variables)); + Module cloneCss() { if (!transitivelyContainsCss) return this; diff --git a/lib/src/module.dart b/lib/src/module.dart index b545b2751..5a8dd9a55 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -30,8 +30,7 @@ abstract interface class Module { /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. /// - /// Implementations must ensure that this has the same keys as [variables] if - /// it's not `null`. + /// Implementations must ensure that this has the same keys as [variables]. Map get variableNodes; /// The module's functions. @@ -81,6 +80,10 @@ abstract interface class Module { /// question, as defined by the Sass spec. Object variableIdentity(String name); + /// Whether this module exposes any variables from among [variables] that + /// could have been configured when the module was loaded. + bool couldHaveBeenConfigured(Set variables); + /// Creates a copy of this module with new [css] and [extender]. Module cloneCss(); } diff --git a/lib/src/module/built_in.dart b/lib/src/module/built_in.dart index 991bfcfba..39995db10 100644 --- a/lib/src/module/built_in.dart +++ b/lib/src/module/built_in.dart @@ -62,5 +62,7 @@ final class BuiltInModule implements Module { return this; } + bool couldHaveBeenConfigured(Set _) => false; + Module cloneCss() => this; } diff --git a/lib/src/module/forwarded_view.dart b/lib/src/module/forwarded_view.dart index 2875490f3..445016e1b 100644 --- a/lib/src/module/forwarded_view.dart +++ b/lib/src/module/forwarded_view.dart @@ -140,6 +140,31 @@ class ForwardedModuleView implements Module { return _inner.variableIdentity(name); } + bool couldHaveBeenConfigured(Set variables) { + assert(_rule.shownVariables == null || _rule.hiddenVariables == null); + if (_rule.prefix == null && + _rule.shownVariables == null && + (_rule.hiddenVariables?.isEmpty ?? true)) { + return _inner.couldHaveBeenConfigured(variables); + } + + if (_rule.prefix case var prefix?) { + variables = { + for (var name in variables) + if (name.startsWith(prefix)) name.substring(prefix.length) + }; + } + + if (_rule.shownVariables case var safelist?) { + return _inner.couldHaveBeenConfigured(variables.intersection(safelist)); + } else if (_rule.hiddenVariables case var blocklist? + when blocklist.isNotEmpty) { + return _inner.couldHaveBeenConfigured(variables.difference(blocklist)); + } else { + return _inner.couldHaveBeenConfigured(variables); + } + } + bool operator ==(Object other) => other is ForwardedModuleView && _inner == other._inner && diff --git a/lib/src/module/shadowed_view.dart b/lib/src/module/shadowed_view.dart index 32144b402..cf84455a0 100644 --- a/lib/src/module/shadowed_view.dart +++ b/lib/src/module/shadowed_view.dart @@ -107,6 +107,14 @@ final class ShadowedModuleView implements Module { return _inner.variableIdentity(name); } + bool couldHaveBeenConfigured(Set variables) => + this.variables == _inner.variables + ? _inner.couldHaveBeenConfigured(variables) + : _inner.couldHaveBeenConfigured({ + for (var name in this.variables.keys) + if (variables.contains(name)) name + }); + bool operator ==(Object other) => other is ShadowedModuleView && _inner == other._inner && diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index c1db0e72e..8e31e7280 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -854,10 +854,18 @@ final class _EvaluateVisitor }) async { var url = stylesheet.span.sourceUrl; + var currentConfiguration = configuration ?? _configuration; if (_modules[url] case var alreadyLoaded?) { - var currentConfiguration = configuration ?? _configuration; if (!_moduleConfigurations[url]!.sameOriginal(currentConfiguration) && - currentConfiguration is ExplicitConfiguration) { + currentConfiguration is ExplicitConfiguration && + // Don't throw an error if the module being loaded doesn't expose any + // configurable variables that could have been affected by the + // configuration in the first place. If the configuration defines a + // value that's not in the module, it'll still throw an error, but + // this avoids throwing confusing errors for `@forward`ed modules + // without configuration. + alreadyLoaded.couldHaveBeenConfigured( + MapKeySet(currentConfiguration.values))) { var message = namesInErrors ? "${p.prettyUri(url)} was already loaded, so it can't be " "configured using \"with\"." @@ -946,7 +954,7 @@ final class _EvaluateVisitor ); if (url != null) { _modules[url] = module; - _moduleConfigurations[url] = _configuration; + _moduleConfigurations[url] = currentConfiguration; if (nodeWithSpan != null) _moduleNodes[url] = nodeWithSpan; } @@ -2599,6 +2607,7 @@ final class _EvaluateVisitor Future visitVariableDeclaration(VariableDeclaration node) async { if (node.isGuarded) { if (node.namespace == null && _environment.atRoot) { + _environment.markVariableConfigurable(node.name); if (_configuration.remove(node.name) case var override? when override.value != sassNull) { _addExceptionSpan(node, () { diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 5727e56aa..11420063c 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: a3068d04660dd2bed34b884aa6e1a21d423dc4e5 +// Checksum: 70d926fd13a7d35cd805bf7053f073cc123b260f // // ignore_for_file: unused_import @@ -862,10 +862,18 @@ final class _EvaluateVisitor }) { var url = stylesheet.span.sourceUrl; + var currentConfiguration = configuration ?? _configuration; if (_modules[url] case var alreadyLoaded?) { - var currentConfiguration = configuration ?? _configuration; if (!_moduleConfigurations[url]!.sameOriginal(currentConfiguration) && - currentConfiguration is ExplicitConfiguration) { + currentConfiguration is ExplicitConfiguration && + // Don't throw an error if the module being loaded doesn't expose any + // configurable variables that could have been affected by the + // configuration in the first place. If the configuration defines a + // value that's not in the module, it'll still throw an error, but + // this avoids throwing confusing errors for `@forward`ed modules + // without configuration. + alreadyLoaded.couldHaveBeenConfigured( + MapKeySet(currentConfiguration.values))) { var message = namesInErrors ? "${p.prettyUri(url)} was already loaded, so it can't be " "configured using \"with\"." @@ -954,7 +962,7 @@ final class _EvaluateVisitor ); if (url != null) { _modules[url] = module; - _moduleConfigurations[url] = _configuration; + _moduleConfigurations[url] = currentConfiguration; if (nodeWithSpan != null) _moduleNodes[url] = nodeWithSpan; } @@ -2604,6 +2612,7 @@ final class _EvaluateVisitor Value? visitVariableDeclaration(VariableDeclaration node) { if (node.isGuarded) { if (node.namespace == null && _environment.atRoot) { + _environment.markVariableConfigurable(node.name); if (_configuration.remove(node.name) case var override? when override.value != sassNull) { _addExceptionSpan(node, () { diff --git a/pkg/sass-parser/CHANGELOG.md b/pkg/sass-parser/CHANGELOG.md index ef9e27284..1c1862ee3 100644 --- a/pkg/sass-parser/CHANGELOG.md +++ b/pkg/sass-parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.25 + +* No user-visible changes. + ## 0.4.24 * No user-visible changes. diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index e19c7816a..186cb5fcf 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -1,6 +1,6 @@ { "name": "sass-parser", - "version": "0.4.24", + "version": "0.4.25", "description": "A PostCSS-compatible wrapper of the official Sass parser", "repository": "sass/sass", "author": "Google Inc.", diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index e034f36f9..aed8ba3e8 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 15.8.0 + +* No user-visible changes. + ## 15.7.1 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index a0ad29aca..729f75849 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: 15.7.1 +version: 15.8.0 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.6.0 <4.0.0" dependencies: - sass: 1.89.2 + sass: 1.90.0 dev_dependencies: dartdoc: ^8.0.14 diff --git a/pubspec.yaml b/pubspec.yaml index 039be9860..41e3a07bc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.89.2 +version: 1.90.0 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass From 8f1c24a25f2c3b7198492554ec34db05dfd0ef30 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Aug 2025 11:54:58 -0700 Subject: [PATCH 26/36] Bump expect from 29.7.0 to 30.0.5 in /pkg/sass-parser (#2611) Bumps [expect](https://github.com/jestjs/jest/tree/HEAD/packages/expect) from 29.7.0 to 30.0.5. - [Release notes](https://github.com/jestjs/jest/releases) - [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jestjs/jest/commits/v30.0.5/packages/expect) --- updated-dependencies: - dependency-name: expect dependency-version: 30.0.5 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkg/sass-parser/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index 186cb5fcf..bc75673cc 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -37,7 +37,7 @@ "devDependencies": { "@types/jest": "^30.0.0", "copyfiles": "^2.4.1", - "expect": "^29.7.0", + "expect": "^30.0.5", "gts": "^6.0.2", "jest": "^30.0.4", "jest-extended": "^6.0.0", From e55398a03829a8a229db84ca4052dfbfd22dcd00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 13:53:18 -0700 Subject: [PATCH 27/36] Bump actions/checkout from 4 to 5 in /.github/util/initialize (#2621) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/util/initialize/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/util/initialize/action.yml b/.github/util/initialize/action.yml index ec6557570..d686b6b44 100644 --- a/.github/util/initialize/action.yml +++ b/.github/util/initialize/action.yml @@ -45,7 +45,7 @@ runs: # downloading repo via GitHub API. - name: Check out the language repo if: github.event_name != 'pull_request' - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: {repository: sass/sass, path: build/language} - name: Generate Dart from protobuf From e0b2a50437acc858c980ea8cb1aac4bff02aed30 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 13:53:27 -0700 Subject: [PATCH 28/36] Bump actions/checkout from 4 to 5 (#2620) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-linux.yml | 2 +- .github/workflows/build-macos.yml | 2 +- .github/workflows/build-windows.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/release-sass-api.yml | 2 +- .github/workflows/release.yml | 16 ++++++++-------- .github/workflows/test-vendor.yml | 8 ++++---- .github/workflows/test.yml | 26 +++++++++++++------------- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index cc83a4885..d447ed835 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -56,7 +56,7 @@ jobs: target: android-riscv64 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 0eac465b2..aeafedc93 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -24,7 +24,7 @@ jobs: runner: macos-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index c72e2bbaa..931ac08f0 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -24,7 +24,7 @@ jobs: runner: windows-11-arm steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4893095b4..24f32c3c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: if: "github.ref_type == 'tag' && github.event.repository.fork == false" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} diff --git a/.github/workflows/release-sass-api.yml b/.github/workflows/release-sass-api.yml index c8b3b8a1a..20d7a4efc 100644 --- a/.github/workflows/release-sass-api.yml +++ b/.github/workflows/release-sass-api.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4bdc66b89..9b077fd95 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: needs: [build_linux, build_macos, build_windows] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} @@ -63,7 +63,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} @@ -78,7 +78,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: # We have to use this rather than the implicit GitHub token so that # pushing a new tag triggers another action. @@ -105,7 +105,7 @@ jobs: needs: [deploy_npm] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: token: ${{ secrets.GH_TOKEN }} # Set up .npmrc file to publish to npm @@ -142,7 +142,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: dart-lang/setup-dart@v1 - run: dart pub get @@ -157,7 +157,7 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} @@ -171,7 +171,7 @@ jobs: needs: [deploy_npm] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: repository: sass/sass-site token: ${{ secrets.SASS_SITE_TOKEN }} @@ -199,7 +199,7 @@ jobs: needs: [deploy_github] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: repository: sass/embedded-host-node token: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/test-vendor.yml b/.github/workflows/test-vendor.yml index 5e71017a1..778e0ea38 100644 --- a/.github/workflows/test-vendor.yml +++ b/.github/workflows/test-vendor.yml @@ -15,7 +15,7 @@ jobs: bootstrap_version: [4, 5] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} @@ -45,7 +45,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} @@ -62,7 +62,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5e7cb5ec3..336e569e1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} - run: dart format . @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} @@ -37,7 +37,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: {github-token: "${{ github.token }}"} @@ -66,7 +66,7 @@ jobs: async_args: '--cmd-args --async' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: dart-sdk: ${{ matrix.dart_channel }} @@ -105,7 +105,7 @@ jobs: node-version: 'lts/*' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: dart-sdk: ${{ matrix.dart_channel }} @@ -150,7 +150,7 @@ jobs: node-version: lts/-3 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: github-token: ${{ github.token }} @@ -190,7 +190,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: browser-actions/setup-chrome@v1 - uses: ./.github/util/initialize with: @@ -233,7 +233,7 @@ jobs: # include: [{os: ubuntu-latest, dart_channel: dev}] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: dart-sdk: ${{ matrix.dart_channel }} @@ -272,7 +272,7 @@ jobs: node-version: 'lts/*' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: dart-sdk: ${{ matrix.dart_channel }} @@ -294,7 +294,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: browser-actions/setup-chrome@v1 - uses: ./.github/util/initialize with: @@ -335,7 +335,7 @@ jobs: node-version: 'lts/*' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/util/initialize with: dart-sdk: ${{ matrix.dart_channel }} @@ -355,7 +355,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-node@v4 with: {node-version: 'lts/*'} - uses: ./.github/util/initialize @@ -379,7 +379,7 @@ jobs: # runs-on: ubuntu-latest # # steps: - # - uses: actions/checkout@v4 + # - uses: actions/checkout@v5 # - uses: actions/setup-node@v4 # with: {node-version: 'lts/*'} # - run: npm install From 1df71a5a6856ade82cc71093bc2f13350f6b7fbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AA=E3=81=A4=E3=81=8D?= Date: Wed, 20 Aug 2025 16:30:27 -0700 Subject: [PATCH 29/36] Print non-integer number at full precision in inspect mode (#2615) Co-authored-by: Natalie Weizenbaum --- CHANGELOG.md | 9 +++++++++ lib/src/functions/color.dart | 9 +++++---- lib/src/visitor/serialize.dart | 11 ++++++++++- pkg/sass-parser/CHANGELOG.md | 4 ++++ pkg/sass-parser/package.json | 2 +- pkg/sass_api/CHANGELOG.md | 4 ++++ pkg/sass_api/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- 8 files changed, 36 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eaf6dcc39..32b1bdd06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 1.91.0 + +* **Potentially breaking change:** `meta.inspect()` (as well as other systems + that use it such as `@debug` and certain error messages) now emits numbers + with as high precision as is available instead of rounding to the nearest + 1e⁻¹⁰ as we do when serializing to CSS. This better fits the purpose of + `meta.inspect()`, which is to provide full information about the structure of + a Sass value. + ## 1.90.0 * Allow a `@forward`ed module to be loaded with a configuration when that module diff --git a/lib/src/functions/color.dart b/lib/src/functions/color.dart index 1cb3640c0..afffa341d 100644 --- a/lib/src/functions/color.dart +++ b/lib/src/functions/color.dart @@ -170,7 +170,7 @@ final global = UnmodifiableListView([ warnForDeprecation( "adjust-hue() is deprecated. Suggestion:\n" "\n" - "color.adjust(\$color, \$hue: $suggestedValue)\n" + "color.adjust(\$color, \$hue: ${suggestedValue.toCssString()})\n" "\n" "More info: https://sass-lang.com/d/color-functions", Deprecation.colorFunctions, @@ -2019,7 +2019,7 @@ String _suggestScaleAndAdjust( var factorNumber = SassNumber(factor * 100, '%'); suggestion += "s:\n" "\n" - "color.scale(\$color, \$$channelName: $factorNumber)\n"; + "color.scale(\$color, \$$channelName: ${factorNumber.toCssString()})\n"; } else { suggestion += ":\n\n"; } @@ -2028,7 +2028,8 @@ String _suggestScaleAndAdjust( adjustment, channel == ColorChannel.alpha ? null : '%', ); - return suggestion + "color.adjust(\$color, \$$channelName: $difference)"; + return suggestion + + "color.adjust(\$color, \$$channelName: ${difference.toCssString()})"; } /// Throws an error indicating that a missing channel named [name] can't be @@ -2037,7 +2038,7 @@ Never _missingChannelError(SassColor color, String channel) => throw SassScriptException( "Because the CSS working group is still deciding on the best behavior, " "Sass doesn't currently support modifying missing channels (color: " - "$color).", + "${color.toCssString()}).", channel, ); diff --git a/lib/src/visitor/serialize.dart b/lib/src/visitor/serialize.dart index 2856a24fa..f0a1908ef 100644 --- a/lib/src/visitor/serialize.dart +++ b/lib/src/visitor/serialize.dart @@ -1192,7 +1192,10 @@ final class _SerializeVisitor // Dart always converts integers to strings in the obvious way, so all we // have to do is clamp doubles that are close to being integers. - if (fuzzyAsInt(number) case var integer?) { + if (fuzzyAsInt(number) case var integer? + // In inspect mode, we want to show the full precision of every number, + // so we only write them as integers when they're precisely equal. + when !_inspect || number == integer) { // JS still uses exponential notation for integers, so we have to handle // it here. buffer.write(_removeExponent(integer.toString())); @@ -1201,6 +1204,12 @@ final class _SerializeVisitor var text = _removeExponent(number.toString()); + // Write the number at full precision in inspect mode. + if (_inspect) { + buffer.write(text); + return; + } + // Any double that's less than `SassNumber.precision + 2` digits long is // guaranteed to be safe to emit directly, since it'll contain at most `0.` // followed by [SassNumber.precision] digits. diff --git a/pkg/sass-parser/CHANGELOG.md b/pkg/sass-parser/CHANGELOG.md index 1c1862ee3..73013a86a 100644 --- a/pkg/sass-parser/CHANGELOG.md +++ b/pkg/sass-parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.26 + +* No user-visible changes. + ## 0.4.25 * No user-visible changes. diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index bc75673cc..8da5ff799 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -1,6 +1,6 @@ { "name": "sass-parser", - "version": "0.4.25", + "version": "0.4.26", "description": "A PostCSS-compatible wrapper of the official Sass parser", "repository": "sass/sass", "author": "Google Inc.", diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index aed8ba3e8..2357c7988 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 15.9.0 + +* No user-visible changes. + ## 15.8.0 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 729f75849..ca8c298cb 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: 15.8.0 +version: 15.9.0 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.6.0 <4.0.0" dependencies: - sass: 1.90.0 + sass: 1.91.0 dev_dependencies: dartdoc: ^8.0.14 diff --git a/pubspec.yaml b/pubspec.yaml index 41e3a07bc..efbcd7329 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.90.0 +version: 1.91.0 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass From 6607204f2d6df85d7c6ac6150184805c2ee85f6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AA=E3=81=A4=E3=81=8D?= Date: Mon, 25 Aug 2025 13:55:37 -0700 Subject: [PATCH 30/36] Use official dart riscv64 docker image (#2619) --- .github/workflows/build-linux.yml | 33 ++----------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index d447ed835..ffba068bb 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -27,7 +27,7 @@ jobs: - image: docker.io/library/dart platform: linux/arm/v7 target: linux-arm - - image: docker.io/library/debian:trixie-slim + - image: docker.io/library/dart platform: linux/riscv64 target: linux-riscv64 - image: ghcr.io/dart-musl/dart @@ -65,7 +65,7 @@ jobs: run: docker run --privileged --rm registry.fedoraproject.org/fedora-minimal /bin/sh -c "microdnf install --assumeyes --nodocs --setopt=install_weak_deps=False qemu-user-static systemd-udev && mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc && /usr/lib/systemd/systemd-binfmt --unregister && /usr/lib/systemd/systemd-binfmt" - name: Build - if: matrix.image != 'ghcr.io/dart-android/dart' && matrix.image != 'docker.io/library/debian:trixie-slim' + if: matrix.image != 'ghcr.io/dart-android/dart' run: | docker run --rm -i \ --platform ${{ matrix.platform }} \ @@ -93,35 +93,6 @@ jobs: dart run grinder pkg-standalone-${{ matrix.target }} EOF - # https://github.com/dart-lang/dart-docker/issues/96#issuecomment-1669860829 - # There is no official riscv64 dart container image yet, build on debian:trixie instead. - # The setup is adopted from: https://github.com/dart-lang/dart-docker/blob/main/Dockerfile-debian.template - - name: Build - if: matrix.image == 'docker.io/library/debian:trixie-slim' - run: | - DART_CHANNEL=stable - DART_VERSION=$(curl -fsSL https://storage.googleapis.com/dart-archive/channels/$DART_CHANNEL/release/latest/VERSION | yq .version) - curl -fsSLO "https://storage.googleapis.com/dart-archive/channels/$DART_CHANNEL/release/$DART_VERSION/sdk/dartsdk-${{ matrix.target }}-release.zip" - - docker run --rm -i \ - --platform ${{ matrix.platform }} \ - --volume "$PWD:$PWD" \ - --workdir "$PWD" \ - ${{ matrix.image }} <<'EOF' - set -e - apt-get update - apt-get install -y --no-install-recommends bind9-dnsutils ca-certificates curl git openssh-client unzip - - export DART_SDK=/usr/lib/dart - export PATH=$DART_SDK/bin:/root/.pub-cache/bin:$PATH - - SDK="dartsdk-${{ matrix.target }}-release.zip" - unzip "$SDK" && mv dart-sdk "$DART_SDK" && rm "$SDK" - - dart pub get - dart run grinder pkg-standalone-${{ matrix.target }} - EOF - - name: Generate artifact attestation if: github.ref_type == 'tag' uses: actions/attest-build-provenance@v2 From 68787090890e03186423f006ba2222853902fdd2 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 25 Aug 2025 14:11:13 -0700 Subject: [PATCH 31/36] Deprecate misplaced rest arguments (#2623) See sass/sass-spec#2072 --- CHANGELOG.md | 6 ++++++ lib/src/deprecation.dart | 7 ++++++- lib/src/parse/stylesheet.dart | 26 ++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32b1bdd06..6e1819d81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ `meta.inspect()`, which is to provide full information about the structure of a Sass value. +* Passing a rest argument (`$arg...`) before a positional or named argument when + calling a function or mixin is now deprecated. This was always outside the + specified syntax, but it was historically treated the same as passing the rest + argument at the end of the argument list whether or not that matched the + visual order of the arguments. + ## 1.90.0 * Allow a `@forward`ed module to be loaded with a configuration when that module diff --git a/lib/src/deprecation.dart b/lib/src/deprecation.dart index dbda68937..17142dd36 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: c57ab2eb07ab1df48581b8484ef9bdbad0ddceaa + // Checksum: 1c1efc2604729aea755453fefd1e8f56e100698e /// Deprecation for passing a string directly to meta.call(). callString('call-string', @@ -131,6 +131,11 @@ enum Deprecation { deprecatedIn: '1.88.0', description: 'Passing a relative url to compileString().'), + /// Deprecation for a rest parameter before a positional or named parameter. + misplacedRest('misplaced-rest', + deprecatedIn: '1.91.0', + description: 'A rest parameter before a positional or named parameter.'), + // END AUTOGENERATED CODE /// Used for deprecations coming from user-authored code. diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index efee015a4..93fcf03c1 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -15,6 +15,7 @@ import '../exception.dart'; import '../interpolation_buffer.dart'; import '../util/character.dart'; import '../utils.dart'; +import '../util/multi_span.dart'; import '../util/nullable.dart'; import '../value.dart'; import 'parser.dart'; @@ -1832,6 +1833,7 @@ abstract class StylesheetParser extends Parser { var named = {}; Expression? rest; Expression? keywordRest; + var emittedRestDeprecation = false; while (_lookingAtExpression()) { var expression = expressionUntilComma(singleEquals: !mixin); whitespace(consumeNewlines: true); @@ -1842,6 +1844,19 @@ abstract class StylesheetParser extends Parser { error("Duplicate argument.", expression.span); } named[expression.name] = expressionUntilComma(singleEquals: !mixin); + + if (rest != null && !emittedRestDeprecation) { + emittedRestDeprecation = true; + warnings.add(( + deprecation: Deprecation.misplacedRest, + message: 'Named arguments must come before rest arguments.\n' + 'This will be an error in Dart Sass 2.0.0.', + span: MultiSpan( + scanner.spanFromPosition(expression.span.start.offset), + 'named argument', + {rest.span: 'rest argument'}) + )); + } } else if (scanner.scanChar($dot)) { scanner.expectChar($dot); scanner.expectChar($dot); @@ -1860,6 +1875,17 @@ abstract class StylesheetParser extends Parser { ); } else { positional.add(expression); + + if (rest != null && !emittedRestDeprecation) { + emittedRestDeprecation = true; + warnings.add(( + deprecation: Deprecation.misplacedRest, + message: 'Positional arguments must come before rest arguments.\n' + 'This will be an error in Dart Sass 2.0.0.', + span: MultiSpan(expression.span, 'positional argument', + {rest.span: 'rest argument'}) + )); + } } whitespace(consumeNewlines: true); From dc923edbc1bd6c64c002082aa8c19b464e947e53 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 28 Aug 2025 11:29:41 -0700 Subject: [PATCH 32/36] Update Jest snapshot URLs (#2628) --- .../lib/src/__snapshots__/argument-list.test.ts.snap | 2 +- pkg/sass-parser/lib/src/__snapshots__/argument.test.ts.snap | 2 +- .../lib/src/__snapshots__/configured-variable.test.ts.snap | 2 +- .../lib/src/__snapshots__/dynamic-import.test.ts.snap | 2 +- pkg/sass-parser/lib/src/__snapshots__/import-list.test.ts.snap | 2 +- .../lib/src/__snapshots__/interpolation.test.ts.snap | 2 +- .../lib/src/__snapshots__/parameter-list.test.ts.snap | 2 +- pkg/sass-parser/lib/src/__snapshots__/parameter.test.ts.snap | 2 +- .../lib/src/__snapshots__/static-import.test.ts.snap | 2 +- .../src/expression/__snapshots__/binary-operation.test.ts.snap | 2 +- .../lib/src/expression/__snapshots__/boolean.test.ts.snap | 2 +- .../lib/src/expression/__snapshots__/color.test.ts.snap | 2 +- .../lib/src/expression/__snapshots__/function.test.ts.snap | 2 +- .../expression/__snapshots__/interpolated-function.test.ts.snap | 2 +- .../lib/src/expression/__snapshots__/list.test.ts.snap | 2 +- .../lib/src/expression/__snapshots__/map-entry.test.ts.snap | 2 +- .../lib/src/expression/__snapshots__/map.test.ts.snap | 2 +- .../lib/src/expression/__snapshots__/null.test.ts.snap | 2 +- .../lib/src/expression/__snapshots__/number.test.ts.snap | 2 +- .../lib/src/expression/__snapshots__/parenthesized.test.ts.snap | 2 +- .../lib/src/expression/__snapshots__/selector.test.ts.snap | 2 +- .../lib/src/expression/__snapshots__/string.test.ts.snap | 2 +- .../src/expression/__snapshots__/unary-operation.test.ts.snap | 2 +- .../lib/src/expression/__snapshots__/variable.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/content-rule.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/css-comment.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/debug-rule.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/declaration.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/each-rule.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/else-rule.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/error-rule.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/for-rule.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/function-rule.test.ts.snap | 2 +- .../src/statement/__snapshots__/generic-at-rule.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/if-rule.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/import-rule.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/include-rule.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/mixin-rule.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/return-rule.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/root.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/rule.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/sass-comment.test.ts.snap | 2 +- .../statement/__snapshots__/variable-declaration.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/warn-rule.test.ts.snap | 2 +- .../lib/src/statement/__snapshots__/while-rule.test.ts.snap | 2 +- 45 files changed, 45 insertions(+), 45 deletions(-) diff --git a/pkg/sass-parser/lib/src/__snapshots__/argument-list.test.ts.snap b/pkg/sass-parser/lib/src/__snapshots__/argument-list.test.ts.snap index dd74492f2..3964a67a5 100644 --- a/pkg/sass-parser/lib/src/__snapshots__/argument-list.test.ts.snap +++ b/pkg/sass-parser/lib/src/__snapshots__/argument-list.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`an argument list toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/__snapshots__/argument.test.ts.snap b/pkg/sass-parser/lib/src/__snapshots__/argument.test.ts.snap index 7572bff1c..8ea2bb5b2 100644 --- a/pkg/sass-parser/lib/src/__snapshots__/argument.test.ts.snap +++ b/pkg/sass-parser/lib/src/__snapshots__/argument.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`an argument toJSON with a name 1`] = ` { diff --git a/pkg/sass-parser/lib/src/__snapshots__/configured-variable.test.ts.snap b/pkg/sass-parser/lib/src/__snapshots__/configured-variable.test.ts.snap index ec5132c40..e4601c75c 100644 --- a/pkg/sass-parser/lib/src/__snapshots__/configured-variable.test.ts.snap +++ b/pkg/sass-parser/lib/src/__snapshots__/configured-variable.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a configured variable toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/__snapshots__/dynamic-import.test.ts.snap b/pkg/sass-parser/lib/src/__snapshots__/dynamic-import.test.ts.snap index 7ee82d952..67af69d90 100644 --- a/pkg/sass-parser/lib/src/__snapshots__/dynamic-import.test.ts.snap +++ b/pkg/sass-parser/lib/src/__snapshots__/dynamic-import.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a dynamic import toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/__snapshots__/import-list.test.ts.snap b/pkg/sass-parser/lib/src/__snapshots__/import-list.test.ts.snap index 016e78095..4421c8ff7 100644 --- a/pkg/sass-parser/lib/src/__snapshots__/import-list.test.ts.snap +++ b/pkg/sass-parser/lib/src/__snapshots__/import-list.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`an import list toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/__snapshots__/interpolation.test.ts.snap b/pkg/sass-parser/lib/src/__snapshots__/interpolation.test.ts.snap index 4f8fa453c..fa38ce9e5 100644 --- a/pkg/sass-parser/lib/src/__snapshots__/interpolation.test.ts.snap +++ b/pkg/sass-parser/lib/src/__snapshots__/interpolation.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`an interpolation toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/__snapshots__/parameter-list.test.ts.snap b/pkg/sass-parser/lib/src/__snapshots__/parameter-list.test.ts.snap index 96fc0f068..fcea1f6cd 100644 --- a/pkg/sass-parser/lib/src/__snapshots__/parameter-list.test.ts.snap +++ b/pkg/sass-parser/lib/src/__snapshots__/parameter-list.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a parameter list toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/__snapshots__/parameter.test.ts.snap b/pkg/sass-parser/lib/src/__snapshots__/parameter.test.ts.snap index 56d9b6793..06e21092c 100644 --- a/pkg/sass-parser/lib/src/__snapshots__/parameter.test.ts.snap +++ b/pkg/sass-parser/lib/src/__snapshots__/parameter.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a parameter toJSON with a default 1`] = ` { diff --git a/pkg/sass-parser/lib/src/__snapshots__/static-import.test.ts.snap b/pkg/sass-parser/lib/src/__snapshots__/static-import.test.ts.snap index d5a5b833b..34cfcca6e 100644 --- a/pkg/sass-parser/lib/src/__snapshots__/static-import.test.ts.snap +++ b/pkg/sass-parser/lib/src/__snapshots__/static-import.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a static import toJSON with modifiers 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/binary-operation.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/binary-operation.test.ts.snap index ea2511ded..872115569 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/binary-operation.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/binary-operation.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a binary operation toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/boolean.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/boolean.test.ts.snap index 1b68fedcf..7bbc6afa0 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/boolean.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/boolean.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a boolean expression toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/color.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/color.test.ts.snap index 4a9f5852b..525850524 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/color.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/color.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a color expression toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/function.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/function.test.ts.snap index 87bcba7a1..cdb8cb94f 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/function.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/function.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a function expression toJSON if() 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/interpolated-function.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/interpolated-function.test.ts.snap index 98d2fbc07..e3252d5ea 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/interpolated-function.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/interpolated-function.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`an interpolated function expression toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/list.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/list.test.ts.snap index bb4b44815..d100b8af6 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/list.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/list.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a list expression toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/map-entry.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/map-entry.test.ts.snap index 784298934..989bc6108 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/map-entry.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/map-entry.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a map entry toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/map.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/map.test.ts.snap index d3345b161..d0febb863 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/map.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/map.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a map expression toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/null.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/null.test.ts.snap index d112aed4b..540d01c80 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/null.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/null.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a null expression toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/number.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/number.test.ts.snap index 6af882031..026a10d68 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/number.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/number.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a number expression toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/parenthesized.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/parenthesized.test.ts.snap index 2bc093251..8578811a4 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/parenthesized.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/parenthesized.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a parenthesized expression toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/selector.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/selector.test.ts.snap index ce91ef60d..22aba2dbc 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/selector.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/selector.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a selector expression toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/string.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/string.test.ts.snap index 441739a47..539e6d1b2 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/string.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/string.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a string expression toJSON a quoted string 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/unary-operation.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/unary-operation.test.ts.snap index 6f3a397f1..2dbcdb6d6 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/unary-operation.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/unary-operation.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a unary operation toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/expression/__snapshots__/variable.test.ts.snap b/pkg/sass-parser/lib/src/expression/__snapshots__/variable.test.ts.snap index cba3b0060..1052ed6a9 100644 --- a/pkg/sass-parser/lib/src/expression/__snapshots__/variable.test.ts.snap +++ b/pkg/sass-parser/lib/src/expression/__snapshots__/variable.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a variable expression toJSON with a namespace 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/content-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/content-rule.test.ts.snap index 9d50b5b26..720be1e6f 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/content-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/content-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a @content rule toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/css-comment.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/css-comment.test.ts.snap index 1e19f31d6..571a0f7c4 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/css-comment.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/css-comment.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a CSS-style comment toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/debug-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/debug-rule.test.ts.snap index 621628a23..d86d4c974 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/debug-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/debug-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a @debug rule toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/declaration.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/declaration.test.ts.snap index 5a198d734..f8786436b 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/declaration.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/declaration.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a property declaration toJSON with expression and no nodes 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/each-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/each-rule.test.ts.snap index 75dd15404..e9c94a516 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/each-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/each-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`an @each rule toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/else-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/else-rule.test.ts.snap index 79e74f6b8..298a465f2 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/else-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/else-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`an @else rule toJSON with an expression 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/error-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/error-rule.test.ts.snap index 9ed3f5667..f3dcfedbc 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/error-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/error-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a @error rule toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/for-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/for-rule.test.ts.snap index f96b0007f..9476bd00a 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/for-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/for-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`an @for rule toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/function-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/function-rule.test.ts.snap index fa1ae47d1..0c7250791 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/function-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/function-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a @function rule toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/generic-at-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/generic-at-rule.test.ts.snap index 769b88951..b42d87a3c 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/generic-at-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/generic-at-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a generic @-rule toJSON with a child 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/if-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/if-rule.test.ts.snap index cbb1fc044..66316362a 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/if-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/if-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`an @if rule toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/import-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/import-rule.test.ts.snap index 07a9595e9..39209291d 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/import-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/import-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`an @import rule toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/include-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/include-rule.test.ts.snap index 74fed5c7a..33ee3bdbb 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/include-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/include-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a @include rule toJSON with a child 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/mixin-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/mixin-rule.test.ts.snap index 916294fda..f3122238f 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/mixin-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/mixin-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a @mixin rule toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/return-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/return-rule.test.ts.snap index e7880d8b5..1429cf39a 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/return-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/return-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a @return rule toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/root.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/root.test.ts.snap index ee16e41cf..74c9c0bc8 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/root.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/root.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a root node toJSON with children 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/rule.test.ts.snap index 9e431db88..76782d464 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a style rule toJSON with a child 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/sass-comment.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/sass-comment.test.ts.snap index dc289b9ae..2e602c1ff 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/sass-comment.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/sass-comment.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a Sass-style comment toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/variable-declaration.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/variable-declaration.test.ts.snap index 3c700a383..90cc8bae5 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/variable-declaration.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/variable-declaration.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a variable declaration toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/warn-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/warn-rule.test.ts.snap index b072acb85..da5838cad 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/warn-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/warn-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a @warn rule toJSON 1`] = ` { diff --git a/pkg/sass-parser/lib/src/statement/__snapshots__/while-rule.test.ts.snap b/pkg/sass-parser/lib/src/statement/__snapshots__/while-rule.test.ts.snap index 792686aa3..3d1dcef2a 100644 --- a/pkg/sass-parser/lib/src/statement/__snapshots__/while-rule.test.ts.snap +++ b/pkg/sass-parser/lib/src/statement/__snapshots__/while-rule.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`a @while rule toJSON 1`] = ` { From c61b098b39194c88844c951327c23d38cbe3d4e6 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 28 Aug 2025 11:51:28 -0700 Subject: [PATCH 33/36] Fix `@extend` through mixed `@import` and `@use` (#2627) Closes #2616 --- CHANGELOG.md | 5 +++++ lib/src/extend/extension_store.dart | 8 +++++++- lib/src/util/box.dart | 4 ++++ pkg/sass-parser/CHANGELOG.md | 4 ++++ pkg/sass-parser/package.json | 2 +- pkg/sass_api/CHANGELOG.md | 4 ++++ pkg/sass_api/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- 8 files changed, 28 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e1819d81..0d86694ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.91.1-dev + +* Fix a bug where `@extend` rules loaded through a mixture of `@import` and + `@use` rules could fail to apply correctly. + ## 1.91.0 * **Potentially breaking change:** `meta.inspect()` (as well as other systems diff --git a/lib/src/extend/extension_store.dart b/lib/src/extend/extension_store.dart index 379e45d73..1a724dba3 100644 --- a/lib/src/extend/extension_store.dart +++ b/lib/src/extend/extension_store.dart @@ -1102,12 +1102,18 @@ class ExtensionStore { var newMediaContexts = , List>{}; var oldToNewSelectors = Map>.identity(); + // A map from the old to the new selector boxes. This ensures that if a + // single box is referenced by multiple simple selectors, we only create a + // single new box for it in the cloned structure. + var newBoxes = , ModifiableBox>{}; + _selectors.forEach((simple, selectors) { var newSelectorSet = >{}; newSelectors[simple] = newSelectorSet; for (var selector in selectors) { - var newSelector = ModifiableBox(selector.value); + var newSelector = + newBoxes.putIfAbsent(selector, () => ModifiableBox(selector.value)); newSelectorSet.add(newSelector); oldToNewSelectors[selector.value] = newSelector.seal(); diff --git a/lib/src/util/box.dart b/lib/src/util/box.dart index 50a9eb750..f1d652013 100644 --- a/lib/src/util/box.dart +++ b/lib/src/util/box.dart @@ -16,6 +16,8 @@ class Box { bool operator ==(Object other) => other is Box && other._inner == _inner; int get hashCode => _inner.hashCode; + + String toString() => ""; } /// A mutable reference to a (presumably immutable) value. @@ -31,4 +33,6 @@ class ModifiableBox { /// /// The underlying modifiable box may still be modified. Box seal() => Box._(this); + + String toString() => ""; } diff --git a/pkg/sass-parser/CHANGELOG.md b/pkg/sass-parser/CHANGELOG.md index 73013a86a..826535e10 100644 --- a/pkg/sass-parser/CHANGELOG.md +++ b/pkg/sass-parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.27-dev + +* No user-visible changes. + ## 0.4.26 * No user-visible changes. diff --git a/pkg/sass-parser/package.json b/pkg/sass-parser/package.json index 8da5ff799..6daaa9e06 100644 --- a/pkg/sass-parser/package.json +++ b/pkg/sass-parser/package.json @@ -1,6 +1,6 @@ { "name": "sass-parser", - "version": "0.4.26", + "version": "0.4.27-dev", "description": "A PostCSS-compatible wrapper of the official Sass parser", "repository": "sass/sass", "author": "Google Inc.", diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index 2357c7988..a042ec616 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 15.9.1-dev + +* No user-visible changes. + ## 15.9.0 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index ca8c298cb..d463e648f 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: 15.9.0 +version: 15.9.1-dev description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.6.0 <4.0.0" dependencies: - sass: 1.91.0 + sass: 1.91.1 dev_dependencies: dartdoc: ^8.0.14 diff --git a/pubspec.yaml b/pubspec.yaml index efbcd7329..d5a1afcc7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.91.0 +version: 1.91.1-dev description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass From 6d3d9283dde97c1ec891beba56fc322955e3a8bb Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 28 Aug 2025 12:03:35 -0700 Subject: [PATCH 34/36] Delete source maps when source files are deleted in watch mode (#2626) Closes #2613 --- CHANGELOG.md | 5 +++++ lib/src/executable/watch.dart | 5 ++++- pkg/sass_api/pubspec.yaml | 2 +- test/cli/shared/watch.dart | 23 +++++++++++++++++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d86694ad..d68e44e6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ * Fix a bug where `@extend` rules loaded through a mixture of `@import` and `@use` rules could fail to apply correctly. +### Command-Line Interface + +* In `--watch` mode, delete the source map when the associated source file is + deleted. + ## 1.91.0 * **Potentially breaking change:** `meta.inspect()` (as well as other systems diff --git a/lib/src/executable/watch.dart b/lib/src/executable/watch.dart index 902b2ed46..36ad81f5e 100644 --- a/lib/src/executable/watch.dart +++ b/lib/src/executable/watch.dart @@ -166,7 +166,10 @@ final class _Watcher { var url = _canonicalize(path); if (_graph.nodes.containsKey(url)) { - if (_destinationFor(path) case var destination?) _delete(destination); + if (_destinationFor(path) case var destination?) { + _delete(destination); + _delete("$destination.map"); + } } var downstream = _graph.remove(FilesystemImporter.cwd, url); diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index d463e648f..8b74fe073 100644 --- a/pkg/sass_api/pubspec.yaml +++ b/pkg/sass_api/pubspec.yaml @@ -10,7 +10,7 @@ environment: sdk: ">=3.6.0 <4.0.0" dependencies: - sass: 1.91.1 + sass: 1.91.1-dev dev_dependencies: dartdoc: ^8.0.14 diff --git a/test/cli/shared/watch.dart b/test/cli/shared/watch.dart index 0958fd4f8..6f04e852e 100644 --- a/test/cli/shared/watch.dart +++ b/test/cli/shared/watch.dart @@ -976,6 +976,29 @@ void sharedTests(Future runSass(Iterable arguments)) { await d.dir("dir", [d.nothing("out.css")]).validate(); }); + + test("and deletes the source map", () async { + await d.file("test.scss", "a {b: c}").create(); + + var sass = await watch(["--source-map", "test.scss:out.css"]); + await expectLater( + sass.stdout, + emits(endsWith('Compiled test.scss to out.css.')), + ); + await expectLater(sass.stdout, _watchingForChanges); + await tickIfPoll(); + + d.file("test.scss").io.deleteSync(); + await expectLater( + sass.stdout, + emitsInOrder([ + 'Deleted out.css.', + 'Deleted out.css.map.', + ])); + await sass.kill(); + + await d.nothing("out.css").validate(); + }); }); test("creates a new CSS file when a Sass file is added", () async { From 9009a64c19a753ebb3399dc6b6a4e01491cbfd75 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 28 Aug 2025 16:26:10 -0700 Subject: [PATCH 35/36] Implement the new mixed declaration/comment/at-rule behavior (#2629) --- CHANGELOG.md | 6 +- lib/src/ast/css/declaration.dart | 12 ---- lib/src/ast/css/modifiable/declaration.dart | 8 +-- lib/src/deprecation.dart | 8 ++- lib/src/js/deprecations.dart | 2 +- lib/src/visitor/async_evaluate.dart | 65 +++++++------------- lib/src/visitor/evaluate.dart | 67 ++++++++------------- lib/src/visitor/serialize.dart | 49 +-------------- pkg/sass_api/CHANGELOG.md | 2 +- pkg/sass_api/pubspec.yaml | 4 +- pubspec.yaml | 2 +- test/cli/shared/deprecations.dart | 26 ++++++-- test/output_test.dart | 36 ++++++----- tool/grind/bump_version.dart | 4 +- 14 files changed, 111 insertions(+), 180 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d68e44e6f..17f526ac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ -## 1.91.1-dev +## 1.92.0-dev + +* **Breaking change:** Emit declarations, childless at-rules, and comments in + the order they appear in the source even when they're interleaved with nested + rules. This obsoletes the `mixed-decls` deprecation. * Fix a bug where `@extend` rules loaded through a mixture of `@import` and `@use` rules could fail to apply correctly. diff --git a/lib/src/ast/css/declaration.dart b/lib/src/ast/css/declaration.dart index 2d8785dbf..1c7f4cfa3 100644 --- a/lib/src/ast/css/declaration.dart +++ b/lib/src/ast/css/declaration.dart @@ -2,13 +2,11 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. -import 'package:meta/meta.dart'; import 'package:source_span/source_span.dart'; import 'package:stack_trace/stack_trace.dart'; import '../../value.dart'; import 'node.dart'; -import 'style_rule.dart'; import 'value.dart'; /// A plain CSS declaration (that is, a `name: value` pair). @@ -19,16 +17,6 @@ abstract interface class CssDeclaration implements CssNode { /// The value of this declaration. CssValue get value; - /// A list of style rules that appeared before this declaration in the Sass - /// input but after it in the CSS output. - /// - /// These are used to emit mixed declaration deprecation warnings during - /// serialization, so we can check based on specificity whether the warnings - /// are really necessary without worrying about `@extend` potentially changing - /// things up. - @internal - List get interleavedRules; - /// The stack trace indicating where this node was created. /// /// This is used to emit interleaved declaration warnings, and is only set if diff --git a/lib/src/ast/css/modifiable/declaration.dart b/lib/src/ast/css/modifiable/declaration.dart index 479403dfd..90660b279 100644 --- a/lib/src/ast/css/modifiable/declaration.dart +++ b/lib/src/ast/css/modifiable/declaration.dart @@ -9,7 +9,6 @@ import '../../../value.dart'; import '../../../visitor/interface/modifiable_css.dart'; import '../declaration.dart'; import '../value.dart'; -import '../style_rule.dart'; import 'node.dart'; /// A modifiable version of [CssDeclaration] for use in the evaluation step. @@ -18,7 +17,6 @@ final class ModifiableCssDeclaration extends ModifiableCssNode final CssValue name; final CssValue value; final bool parsedAsCustomProperty; - final List interleavedRules; final Trace? trace; final FileSpan valueSpanForMap; final FileSpan span; @@ -31,13 +29,9 @@ final class ModifiableCssDeclaration extends ModifiableCssNode this.value, this.span, { required this.parsedAsCustomProperty, - Iterable? interleavedRules, this.trace, FileSpan? valueSpanForMap, - }) : interleavedRules = interleavedRules == null - ? const [] - : List.unmodifiable(interleavedRules), - valueSpanForMap = valueSpanForMap ?? value.span { + }) : valueSpanForMap = valueSpanForMap ?? value.span { if (parsedAsCustomProperty) { if (!isCustomProperty) { throw ArgumentError( diff --git a/lib/src/deprecation.dart b/lib/src/deprecation.dart index 17142dd36..f45036890 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: 1c1efc2604729aea755453fefd1e8f56e100698e + // Checksum: 2a2a94a3dd8ab2f7e3880b24e8e7850d66998b33 /// Deprecation for passing a string directly to meta.call(). callString('call-string', @@ -93,6 +93,7 @@ enum Deprecation { /// Deprecation for declarations after or between nested rules. mixedDecls('mixed-decls', deprecatedIn: '1.77.7', + obsoleteIn: '1.92.0', description: 'Declarations after or between nested rules.'), /// Deprecation for meta.feature-exists @@ -189,9 +190,10 @@ enum Deprecation { Version? get obsoleteIn => _obsoleteIn?.andThen(Version.parse); /// Constructs a regular deprecation. - const Deprecation(this.id, {required String? deprecatedIn, this.description}) + const Deprecation(this.id, + {required String? deprecatedIn, this.description, String? obsoleteIn}) : _deprecatedIn = deprecatedIn, - _obsoleteIn = null, + _obsoleteIn = obsoleteIn, isFuture = false; /// Constructs a future deprecation. diff --git a/lib/src/js/deprecations.dart b/lib/src/js/deprecations.dart index acde8b6e6..9a40ccf86 100644 --- a/lib/src/js/deprecations.dart +++ b/lib/src/js/deprecations.dart @@ -42,7 +42,7 @@ final Map deprecations = { })(), description: deprecation.description, deprecatedIn: deprecation.deprecatedIn, - obsoleteIn: deprecation.deprecatedIn, + obsoleteIn: deprecation.obsoleteIn, ), }; diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 8e31e7280..1eda35134 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -1342,46 +1342,6 @@ final class _EvaluateVisitor ); } - var siblings = _parent.parent!.children; - var interleavedRules = []; - if (siblings.last != _parent && - // Reproduce this condition from [_warn] so that we don't add anything to - // [interleavedRules] for declarations in dependencies. - !(_quietDeps && _inDependency)) { - loop: - for (var sibling in siblings.skip(siblings.indexOf(_parent) + 1)) { - switch (sibling) { - case CssComment(): - continue loop; - - case CssStyleRule rule: - interleavedRules.add(rule); - - case _: - // Always warn for siblings that aren't style rules, because they - // add no specificity and they're nested in the same parent as this - // declaration. - _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, - ); - interleavedRules.clear(); - break; - } - } - } - var name = await _interpolationToValue(node.name, warnForColor: true); if (_declarationName case var declarationName?) { name = CssValue("$declarationName-${name.value}", name.span); @@ -1395,14 +1355,14 @@ final class _EvaluateVisitor _isEmptyList(value) || // Custom properties are allowed to have empty values, per spec. name.value.startsWith('--')) { + _copyParentAfterSibling(); _parent.addChild( ModifiableCssDeclaration( name, CssValue(value, expression.span), node.span, parsedAsCustomProperty: node.isCustomProperty, - interleavedRules: interleavedRules, - trace: interleavedRules.isEmpty ? null : _stackTrace(node.span), + trace: _stackTrace(node.span), valueSpanForMap: _sourceMap ? node.value.andThen(_expressionNode)?.span : null, ), @@ -1565,6 +1525,7 @@ final class _EvaluateVisitor var children = node.children; if (children == null) { + _copyParentAfterSibling(); _parent.addChild( ModifiableCssAtRule(name, node.span, childless: true, value: value), ); @@ -2081,6 +2042,7 @@ final class _EvaluateVisitor ); if (_parent != _root) { + _copyParentAfterSibling(); _parent.addChild(node); } else if (_endOfImports == _root.children.length) { _root.addChild(node); @@ -2231,6 +2193,8 @@ final class _EvaluateVisitor var text = await _performInterpolation(node.text); // Indented syntax doesn't require */ if (!text.endsWith("*/")) text += " */"; + + _copyParentAfterSibling(); _parent.addChild(ModifiableCssComment(text, node.span)); return null; } @@ -3920,6 +3884,7 @@ final class _EvaluateVisitor } if (node.isChildless) { + _copyParentAfterSibling(); _parent.addChild( ModifiableCssAtRule( node.name, @@ -3966,10 +3931,12 @@ final class _EvaluateVisitor _endOfImports++; } + _copyParentAfterSibling(); _parent.addChild(ModifiableCssComment(node.text, node.span)); } Future visitCssDeclaration(CssDeclaration node) async { + _copyParentAfterSibling(); _parent.addChild( ModifiableCssDeclaration( node.name, @@ -3991,6 +3958,7 @@ final class _EvaluateVisitor modifiers: node.modifiers, ); if (_parent != _root) { + _copyParentAfterSibling(); _parent.addChild(modifiableNode); } else if (_endOfImports == _root.children.length) { _root.addChild(modifiableNode); @@ -4377,6 +4345,19 @@ final class _EvaluateVisitor return result; } + /// If the current [_parent] is not the last child of its grandparent, makes a + /// new childless copy of it and sets [_parent] to that. + /// + /// Otherwise, leaves [_parent] as-is. + void _copyParentAfterSibling() { + if (_parent.parent case var grandparent? + when grandparent.children.last != _parent) { + var newParent = _parent.copyWithoutChildren(); + grandparent.addChild(newParent); + _parent = newParent; + } + } + /// Adds [node] as a child of the current parent. /// /// If [through] is passed, [node] is added as a child of the first parent for diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 11420063c..2d2176399 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: 70d926fd13a7d35cd805bf7053f073cc123b260f +// Checksum: 100082e5b65dc126357c027e76f34ae97b3f9e48 // // ignore_for_file: unused_import @@ -1350,46 +1350,6 @@ final class _EvaluateVisitor ); } - var siblings = _parent.parent!.children; - var interleavedRules = []; - if (siblings.last != _parent && - // Reproduce this condition from [_warn] so that we don't add anything to - // [interleavedRules] for declarations in dependencies. - !(_quietDeps && _inDependency)) { - loop: - for (var sibling in siblings.skip(siblings.indexOf(_parent) + 1)) { - switch (sibling) { - case CssComment(): - continue loop; - - case CssStyleRule rule: - interleavedRules.add(rule); - - case _: - // Always warn for siblings that aren't style rules, because they - // add no specificity and they're nested in the same parent as this - // declaration. - _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, - ); - interleavedRules.clear(); - break; - } - } - } - var name = _interpolationToValue(node.name, warnForColor: true); if (_declarationName case var declarationName?) { name = CssValue("$declarationName-${name.value}", name.span); @@ -1403,14 +1363,14 @@ final class _EvaluateVisitor _isEmptyList(value) || // Custom properties are allowed to have empty values, per spec. name.value.startsWith('--')) { + _copyParentAfterSibling(); _parent.addChild( ModifiableCssDeclaration( name, CssValue(value, expression.span), node.span, parsedAsCustomProperty: node.isCustomProperty, - interleavedRules: interleavedRules, - trace: interleavedRules.isEmpty ? null : _stackTrace(node.span), + trace: _stackTrace(node.span), valueSpanForMap: _sourceMap ? node.value.andThen(_expressionNode)?.span : null, ), @@ -1573,6 +1533,7 @@ final class _EvaluateVisitor var children = node.children; if (children == null) { + _copyParentAfterSibling(); _parent.addChild( ModifiableCssAtRule(name, node.span, childless: true, value: value), ); @@ -2089,6 +2050,7 @@ final class _EvaluateVisitor ); if (_parent != _root) { + _copyParentAfterSibling(); _parent.addChild(node); } else if (_endOfImports == _root.children.length) { _root.addChild(node); @@ -2238,6 +2200,8 @@ final class _EvaluateVisitor var text = _performInterpolation(node.text); // Indented syntax doesn't require */ if (!text.endsWith("*/")) text += " */"; + + _copyParentAfterSibling(); _parent.addChild(ModifiableCssComment(text, node.span)); return null; } @@ -3921,6 +3885,7 @@ final class _EvaluateVisitor } if (node.isChildless) { + _copyParentAfterSibling(); _parent.addChild( ModifiableCssAtRule( node.name, @@ -3967,10 +3932,12 @@ final class _EvaluateVisitor _endOfImports++; } + _copyParentAfterSibling(); _parent.addChild(ModifiableCssComment(node.text, node.span)); } void visitCssDeclaration(CssDeclaration node) { + _copyParentAfterSibling(); _parent.addChild( ModifiableCssDeclaration( node.name, @@ -3992,6 +3959,7 @@ final class _EvaluateVisitor modifiers: node.modifiers, ); if (_parent != _root) { + _copyParentAfterSibling(); _parent.addChild(modifiableNode); } else if (_endOfImports == _root.children.length) { _root.addChild(modifiableNode); @@ -4378,6 +4346,19 @@ final class _EvaluateVisitor return result; } + /// If the current [_parent] is not the last child of its grandparent, makes a + /// new childless copy of it and sets [_parent] to that. + /// + /// Otherwise, leaves [_parent] as-is. + void _copyParentAfterSibling() { + if (_parent.parent case var grandparent? + when grandparent.children.last != _parent) { + var newParent = _parent.copyWithoutChildren(); + grandparent.addChild(newParent); + _parent = newParent; + } + } + /// Adds [node] as a child of the current parent. /// /// If [through] is passed, [node] is added as a child of the first parent for diff --git a/lib/src/visitor/serialize.dart b/lib/src/visitor/serialize.dart index f0a1908ef..fac8a6ef7 100644 --- a/lib/src/visitor/serialize.dart +++ b/lib/src/visitor/serialize.dart @@ -6,7 +6,6 @@ import 'dart:math' as math; import 'dart:typed_data'; import 'package:charcode/charcode.dart'; -import 'package:collection/collection.dart'; import 'package:source_maps/source_maps.dart'; import 'package:string_scanner/string_scanner.dart'; @@ -14,13 +13,11 @@ import '../ast/css.dart'; import '../ast/node.dart'; import '../ast/selector.dart'; import '../color_names.dart'; -import '../deprecation.dart'; import '../exception.dart'; import '../logger.dart'; import '../parse/parser.dart'; import '../utils.dart'; import '../util/character.dart'; -import '../util/multi_span.dart'; import '../util/no_source_map_buffer.dart'; import '../util/nullable.dart'; import '../util/number.dart'; @@ -145,6 +142,9 @@ final class _SerializeVisitor /// /// This should only be used for statement-level serialization. It's not /// guaranteed to be the main user-provided logger for expressions. + // We include this even when it's unused to reduce the churn as deprecations + // are added and removed. + // ignore: unused_field final Logger _logger; /// Whether we're emitting compressed output. @@ -355,33 +355,6 @@ final class _SerializeVisitor } void visitCssDeclaration(CssDeclaration node) { - if (node.interleavedRules.isNotEmpty) { - var declSpecificities = _specificities(node.parent!); - for (var rule in node.interleavedRules) { - var ruleSpecificities = _specificities(rule); - - // If the declaration can never match with the same specificity as one - // of its sibling rules, then ordering will never matter and there's no - // need to warn about the declaration being re-ordered. - if (!declSpecificities.any(ruleSpecificities.contains)) continue; - - _logger.warnForDeprecation( - Deprecation.mixedDecls, - "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", - span: MultiSpan(node.span, 'declaration', {rule.span: 'nested rule'}), - trace: node.trace, - ); - } - } - _writeIndentation(); _write(node.name); @@ -426,22 +399,6 @@ final class _SerializeVisitor } } - /// Returns the set of possible specificities which which [node] might match. - Set _specificities(CssParentNode node) { - if (node case CssStyleRule rule) { - // Plain CSS style rule nesting implicitly wraps parent selectors in - // `:is()`, so they all match with the highest specificity among any of - // them. - var parent = node.parent.andThen(_specificities)?.max ?? 0; - return { - for (var selector in rule.selector.components) - parent + selector.specificity, - }; - } else { - return node.parent.andThen(_specificities) ?? const {0}; - } - } - /// Emits the value of [node], with all newlines followed by whitespace void _writeFoldedValue(CssDeclaration node) { var scanner = StringScanner((node.value.value as SassString).text); diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index a042ec616..f3046f9c1 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,4 +1,4 @@ -## 15.9.1-dev +## 15.10.0-dev * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 8b74fe073..5b997da20 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: 15.9.1-dev +version: 15.10.0-dev description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.6.0 <4.0.0" dependencies: - sass: 1.91.1-dev + sass: 1.92.0 dev_dependencies: dartdoc: ^8.0.14 diff --git a/pubspec.yaml b/pubspec.yaml index d5a1afcc7..f19177dfe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.91.1-dev +version: 1.92.0-dev description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass diff --git a/test/cli/shared/deprecations.dart b/test/cli/shared/deprecations.dart index ff31318d6..afa72fd3e 100644 --- a/test/cli/shared/deprecations.dart +++ b/test/cli/shared/deprecations.dart @@ -24,7 +24,13 @@ void sharedTests(Future runSass(Iterable arguments)) { }); test("for an obsolete deprecation", () async { - // TODO: test this when a deprecation is obsoleted + var sass = await runSass([ + "--silence-deprecation=mixed-decls", + "test.scss", + ]); + expect(sass.stderr, + emits(contains("mixed-decls deprecation is obsolete"))); + await sass.shouldExit(0); }); test("for an inactive future deprecation", () async { @@ -205,7 +211,13 @@ void sharedTests(Future runSass(Iterable arguments)) { setUp(() => d.file("test.scss", "").create()); test("for an obsolete deprecation", () async { - // TODO: test this when a deprecation is obsoleted + var sass = await runSass([ + "--fatal-deprecation=mixed-decls", + "test.scss", + ]); + expect(sass.stderr, + emits(contains("mixed-decls deprecation is obsolete"))); + await sass.shouldExit(0); }); test("for an inactive future deprecation", () async { @@ -451,8 +463,14 @@ void sharedTests(Future runSass(Iterable arguments)) { }); }); - group("an obsolete deprecation", () { - // TODO: test this when there are obsolete deprecations + test("an obsolete deprecation", () async { + var sass = await runSass([ + "--future-deprecation=mixed-decls", + "test.scss", + ]); + expect(sass.stderr, + emits(contains("mixed-decls deprecation is obsolete"))); + await sass.shouldExit(0); }); group("a parse-time deprecation", () { diff --git a/test/output_test.dart b/test/output_test.dart index 30d4eba0f..0178c37de 100644 --- a/test/output_test.dart +++ b/test/output_test.dart @@ -430,25 +430,29 @@ selector { equals(""" foo { /* foo */ padding: 1px; /* foo padding */ - /* bar end */ - margin: 1px; /* foo margin */ } foo bar { /* bar */ padding: 2px; /* bar padding */ - /* baz end */ - /* biz end */ - margin: 2px; /* bar margin */ } foo bar baz { /* baz */ padding: 3px; /* baz padding */ margin: 3px; /* baz margin */ } +foo bar { + /* baz end */ +} foo bar biz { /* biz */ padding: 3px; /* biz padding */ margin: 3px; /* biz margin */ } - -/* foo end */"""), +foo bar { + /* biz end */ + margin: 2px; /* bar margin */ +} +foo { + /* bar end */ + margin: 1px; /* foo margin */ +} /* foo end */"""), ); }); @@ -465,17 +469,19 @@ foo bar biz { /* biz */ } /* foo end */ """), equals(""" -foo { /* foo */ - /* bar end */ -} -foo bar { /* bar */ +foo { /* foo */ } +foo bar { /* bar */ } +foo bar baz { /* baz */ } +foo bar { /* baz end */ - /* biz end */ } -foo bar baz { /* baz */ } foo bar biz { /* biz */ } - -/* foo end */"""), +foo bar { + /* biz end */ +} +foo { + /* bar end */ +} /* foo end */"""), ); }); }); diff --git a/tool/grind/bump_version.dart b/tool/grind/bump_version.dart index 310c25d33..3acabad7d 100644 --- a/tool/grind/bump_version.dart +++ b/tool/grind/bump_version.dart @@ -68,7 +68,7 @@ void _bumpVersion(bool patch, bool dev) { void addChangelogEntry(String dir, Version version) { var path = p.join(dir, "CHANGELOG.md"); var text = File(path).readAsStringSync(); - if (!dev && text.startsWith(_changelogDevHeaderRegExp)) { + if (text.startsWith(_changelogDevHeaderRegExp)) { File(path).writeAsStringSync( text.replaceFirst(_changelogDevHeaderRegExp, "## $version"), ); @@ -83,7 +83,7 @@ void _bumpVersion(bool patch, bool dev) { // Bumps the current version of [pubspec] to the next [patch] version, with // `-dev` if [dev] is true. - // + //c // If [sassVersion] is passed, this bumps the `sass` dependency to that version. // // Returns the new version of this package. From 87e2e1c3df5193a1fdc3e14e3c3580cc2516e764 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 28 Aug 2025 18:11:26 -0700 Subject: [PATCH 36/36] Add support for the plain CSS type() function (#2630) Closes #2539 --- CHANGELOG.md | 5 +++ lib/src/deprecation.dart | 6 ++-- lib/src/parse/stylesheet.dart | 67 +++++++++++++++++------------------ 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17f526ac8..800d15beb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ the order they appear in the source even when they're interleaved with nested rules. This obsoletes the `mixed-decls` deprecation. +* **Breaking change:** The function name `type()` is now fully reserved for the + plain CSS function. This means that `@function` definitions with the name + `type` will produce errors, while function calls will be parsed as special + function strings. + * Fix a bug where `@extend` rules loaded through a mixture of `@import` and `@use` rules could fail to apply correctly. diff --git a/lib/src/deprecation.dart b/lib/src/deprecation.dart index f45036890..a3f01d961 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: 2a2a94a3dd8ab2f7e3880b24e8e7850d66998b33 + // Checksum: 247ee9ef1df8665b759064856492831661b08985 /// Deprecation for passing a string directly to meta.call(). callString('call-string', @@ -125,7 +125,9 @@ enum Deprecation { /// Deprecation for functions named "type". typeFunction('type-function', - deprecatedIn: '1.86.0', description: 'Functions named "type".'), + deprecatedIn: '1.86.0', + obsoleteIn: '1.92.0', + description: 'Functions named "type".'), /// Deprecation for passing a relative url to compileString(). compileStringRelativeUrl('compile-string-relative-url', diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 93fcf03c1..4b653b6c1 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -932,14 +932,8 @@ abstract class StylesheetParser extends Parser { span: scanner.spanFrom(beforeName), )); } else if (equalsIgnoreCase(name, 'type')) { - warnings.add(( - deprecation: Deprecation.typeFunction, - message: 'Sass @functions named "type" are deprecated for forward-' - 'compatibility with the plain CSS type() function.\n' - '\n' - 'For details, see https://sass-lang.com/d/type-function', - span: scanner.spanFrom(beforeName), - )); + error('This name is reserved for the plain-CSS function.', + scanner.spanFrom(beforeName)); } whitespace(consumeNewlines: true); @@ -2966,35 +2960,40 @@ abstract class StylesheetParser extends Parser { /// [name]. @protected Expression? trySpecialFunction(String name, LineScannerState start) { - var normalized = unvendor(name); - InterpolationBuffer buffer; - switch (normalized) { - case "calc" when normalized != name && scanner.scanChar($lparen): - case "element" || "expression" when scanner.scanChar($lparen): - buffer = InterpolationBuffer() - ..write(name) - ..writeCharCode($lparen); - - case "progid" when scanner.scanChar($colon): - buffer = InterpolationBuffer() - ..write(name) - ..writeCharCode($colon); - var next = scanner.peekChar(); - while (next != null && (next.isAlphabetic || next == $dot)) { - buffer.writeCharCode(scanner.readChar()); - next = scanner.peekChar(); - } - scanner.expectChar($lparen); - buffer.writeCharCode($lparen); + if (name == "type" && scanner.scanChar($lparen)) { + buffer = InterpolationBuffer() + ..write(name) + ..writeCharCode($lparen); + } else { + var normalized = unvendor(name); + switch (normalized) { + case "calc" when normalized != name && scanner.scanChar($lparen): + case "element" || "expression" when scanner.scanChar($lparen): + buffer = InterpolationBuffer() + ..write(name) + ..writeCharCode($lparen); + + case "progid" when scanner.scanChar($colon): + buffer = InterpolationBuffer() + ..write(name) + ..writeCharCode($colon); + var next = scanner.peekChar(); + while (next != null && (next.isAlphabetic || next == $dot)) { + buffer.writeCharCode(scanner.readChar()); + next = scanner.peekChar(); + } + scanner.expectChar($lparen); + buffer.writeCharCode($lparen); - case "url": - return _tryUrlContents( - start, - ).andThen((contents) => StringExpression(contents)); + case "url": + return _tryUrlContents( + start, + ).andThen((contents) => StringExpression(contents)); - case _: - return null; + case _: + return null; + } } buffer.addInterpolation(_interpolatedDeclarationValue(allowEmpty: true));