From 54a6decfc8dbb7b66bf3f149fd15401007af5891 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 14:18:05 -0700 Subject: [PATCH 01/14] Bump lints from 3.0.0 to 4.0.0 (#2242) Bumps [lints](https://github.com/dart-lang/lints) from 3.0.0 to 4.0.0. - [Release notes](https://github.com/dart-lang/lints/releases) - [Changelog](https://github.com/dart-lang/lints/blob/main/CHANGELOG.md) - [Commits](https://github.com/dart-lang/lints/compare/v3.0.0...v4.0.0) --- updated-dependencies: - dependency-name: lints 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 4ecb69a9a..2bbf7a4ed 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,7 +45,7 @@ dev_dependencies: dartdoc: ">=6.0.0 <9.0.0" grinder: ^0.9.0 node_preamble: ^2.0.2 - lints: ">=2.0.0 <4.0.0" + lints: ">=2.0.0 <5.0.0" protoc_plugin: ">=20.0.0 <22.0.0" pub_api_client: ^2.1.1 pubspec_parse: ^1.0.0 From c9f0d3f62317101776a55b828f4cf8cd67cf24e8 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 15 May 2024 12:49:27 -0700 Subject: [PATCH 02/14] Fix deprecation flags in the CLI and add tests Closes #2244 --- CHANGELOG.md | 9 + lib/src/executable/compile_stylesheet.dart | 4 + lib/src/executable/repl.dart | 12 +- pkg/sass_api/CHANGELOG.md | 4 + pkg/sass_api/pubspec.yaml | 4 +- pubspec.yaml | 2 +- test/cli/dart/deprecations_test.dart | 15 + test/cli/node/deprecations_test.dart | 17 + test/cli/shared/deprecations.dart | 497 +++++++++++++++++++++ 9 files changed, 558 insertions(+), 6 deletions(-) create mode 100644 test/cli/dart/deprecations_test.dart create mode 100644 test/cli/node/deprecations_test.dart create mode 100644 test/cli/shared/deprecations.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c306b1ac..c932688b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 1.77.2 + +### Command-Line Interface + +* Properly handle the `--silence-deprecation` flag. + +* Handle the `--fatal-deprecation` and `--future-deprecation` flags for + `--interactive` mode. + ## 1.77.1 * Fix a crash that could come up with importers in certain contexts. diff --git a/lib/src/executable/compile_stylesheet.dart b/lib/src/executable/compile_stylesheet.dart index cd121a6f5..3dbf3dbe0 100644 --- a/lib/src/executable/compile_stylesheet.dart +++ b/lib/src/executable/compile_stylesheet.dart @@ -110,6 +110,7 @@ Future _compileStylesheetWithoutErrorHandling(ExecutableOptions options, verbose: options.verbose, sourceMap: options.emitSourceMap, charset: options.charset, + silenceDeprecations: options.silenceDeprecations, fatalDeprecations: options.fatalDeprecations, futureDeprecations: options.futureDeprecations) : await compileAsync(source, @@ -121,6 +122,7 @@ Future _compileStylesheetWithoutErrorHandling(ExecutableOptions options, verbose: options.verbose, sourceMap: options.emitSourceMap, charset: options.charset, + silenceDeprecations: options.silenceDeprecations, fatalDeprecations: options.fatalDeprecations, futureDeprecations: options.futureDeprecations); } else { @@ -135,6 +137,7 @@ Future _compileStylesheetWithoutErrorHandling(ExecutableOptions options, verbose: options.verbose, sourceMap: options.emitSourceMap, charset: options.charset, + silenceDeprecations: options.silenceDeprecations, fatalDeprecations: options.fatalDeprecations, futureDeprecations: options.futureDeprecations) : compile(source, @@ -146,6 +149,7 @@ Future _compileStylesheetWithoutErrorHandling(ExecutableOptions options, verbose: options.verbose, sourceMap: options.emitSourceMap, charset: options.charset, + silenceDeprecations: options.silenceDeprecations, fatalDeprecations: options.fatalDeprecations, futureDeprecations: options.futureDeprecations); } diff --git a/lib/src/executable/repl.dart b/lib/src/executable/repl.dart index e2e858a26..f79e2de33 100644 --- a/lib/src/executable/repl.dart +++ b/lib/src/executable/repl.dart @@ -12,6 +12,7 @@ import '../exception.dart'; import '../executable/options.dart'; import '../import_cache.dart'; import '../importer/filesystem.dart'; +import '../logger/deprecation_processing.dart'; import '../logger/tracking.dart'; import '../parse/parser.dart'; import '../utils.dart'; @@ -20,7 +21,12 @@ import '../visitor/evaluate.dart'; /// Runs an interactive SassScript shell according to [options]. Future repl(ExecutableOptions options) async { var repl = Repl(prompt: '>> '); - var logger = TrackingLogger(options.logger); + var trackingLogger = TrackingLogger(options.logger); + var logger = DeprecationProcessingLogger(trackingLogger, + silenceDeprecations: options.silenceDeprecations, + fatalDeprecations: options.fatalDeprecations, + futureDeprecations: options.futureDeprecations, + limitRepetition: !options.verbose); var evaluator = Evaluator( importer: FilesystemImporter.cwd, importCache: ImportCache( @@ -46,8 +52,8 @@ Future repl(ExecutableOptions options) async { print(evaluator.evaluate(Expression.parse(line, logger: logger))); } } on SassException catch (error, stackTrace) { - _logError( - error, getTrace(error) ?? stackTrace, line, repl, options, logger); + _logError(error, getTrace(error) ?? stackTrace, line, repl, options, + trackingLogger); } } } diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index 63e2b58f8..c0839df75 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 10.4.2 + +* No user-visible changes. + ## 10.4.1 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 0db04d6b3..08eda2c49 100644 --- a/pkg/sass_api/pubspec.yaml +++ b/pkg/sass_api/pubspec.yaml @@ -2,7 +2,7 @@ name: sass_api # Note: Every time we add a new Sass AST node, we need to bump the *major* # version because it's a breaking change for anyone who's implementing the # visitor interface(s). -version: 10.4.1 +version: 10.4.2 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: - sass: 1.77.1 + sass: 1.77.2 dev_dependencies: dartdoc: ^6.0.0 diff --git a/pubspec.yaml b/pubspec.yaml index 4ecb69a9a..47cec4e93 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.77.1 +version: 1.77.2 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass diff --git a/test/cli/dart/deprecations_test.dart b/test/cli/dart/deprecations_test.dart new file mode 100644 index 000000000..4b4a4244f --- /dev/null +++ b/test/cli/dart/deprecations_test.dart @@ -0,0 +1,15 @@ +// Copyright 2024 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. + +@TestOn('vm') + +import 'package:test/test.dart'; + +import '../dart_test.dart'; +import '../shared/deprecations.dart'; + +void main() { + setUpAll(ensureSnapshotUpToDate); + sharedTests(runSass); +} diff --git a/test/cli/node/deprecations_test.dart b/test/cli/node/deprecations_test.dart new file mode 100644 index 000000000..88e383d2b --- /dev/null +++ b/test/cli/node/deprecations_test.dart @@ -0,0 +1,17 @@ +// Copyright 2024 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. + +@TestOn('vm') +@Tags(['node']) + +import 'package:test/test.dart'; + +import '../../ensure_npm_package.dart'; +import '../node_test.dart'; +import '../shared/deprecations.dart'; + +void main() { + setUpAll(ensureNpmPackage); + sharedTests(runSass); +} diff --git a/test/cli/shared/deprecations.dart b/test/cli/shared/deprecations.dart new file mode 100644 index 000000000..03f1b4dc2 --- /dev/null +++ b/test/cli/shared/deprecations.dart @@ -0,0 +1,497 @@ +// Copyright 2024 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 'package:test/test.dart'; +import 'package:test_descriptor/test_descriptor.dart' as d; +import 'package:test_process/test_process.dart'; + +/// Defines test that are shared between the Dart and Node.js CLI test suites. +void sharedTests(Future runSass(Iterable arguments)) { + // Test complaining about invalid deprecations, combinations, etc + + group("--silence-deprecation", () { + group("prints a warning", () { + setUp(() => d.file("test.scss", "").create()); + + test("for user-authored", () async { + var sass = + await runSass(["--silence-deprecation=user-authored", "test.scss"]); + expect(sass.stderr, emits(contains("User-authored deprecations"))); + await sass.shouldExit(0); + }); + + test("for an obsolete deprecation", () async { + // TODO: test this when a deprecation is obsoleted + }); + + test("for an inactive future deprecation", () async { + var sass = await runSass(["--silence-deprecation=import", "test.scss"]); + expect(sass.stderr, emits(contains("Future import deprecation"))); + await sass.shouldExit(0); + }); + + test("for an active future deprecation", () async { + var sass = await runSass([ + "--future-deprecation=import", + "--silence-deprecation=import", + "test.scss" + ]); + expect(sass.stderr, emits(contains("Conflicting options for future"))); + await sass.shouldExit(0); + }); + + test("in watch mode", () async { + var sass = await runSass([ + "--watch", + "--poll", + "--silence-deprecation=user-authored", + "test.scss:out.css" + ]); + expect(sass.stderr, emits(contains("User-authored deprecations"))); + + await expectLater(sass.stdout, + emitsThrough(endsWith('Compiled test.scss to out.css.'))); + await sass.kill(); + }); + + test("in repl mode", () async { + var sass = await runSass( + ["--interactive", "--silence-deprecation=user-authored"]); + await expectLater( + sass.stderr, emits(contains("User-authored deprecations"))); + await sass.kill(); + }); + }); + + group("throws an error for an unknown deprecation", () { + setUp(() => d.file("test.scss", "").create()); + + test("in immediate mode", () async { + var sass = + await runSass(["--silence-deprecation=unknown", "test.scss"]); + expect(sass.stdout, emits(contains('Invalid deprecation "unknown".'))); + await sass.shouldExit(64); + }); + + test("in watch mode", () async { + var sass = await runSass([ + "--watch", + "--poll", + "--silence-deprecation=unknown", + "test.scss:out.css" + ]); + expect(sass.stdout, emits(contains('Invalid deprecation "unknown".'))); + await sass.shouldExit(64); + }); + + test("in repl mode", () async { + var sass = + await runSass(["--interactive", "--silence-deprecation=unknown"]); + expect(sass.stdout, emits(contains('Invalid deprecation "unknown".'))); + await sass.shouldExit(64); + }); + }); + + group("silences", () { + group("a parse-time deprecation", () { + setUp( + () => d.file("test.scss", "@if true {} @elseif false {}").create()); + + test("in immediate mode", () async { + var sass = + await runSass(["--silence-deprecation=elseif", "test.scss"]); + expect(sass.stderr, emitsDone); + await sass.shouldExit(0); + }); + + test("in watch mode", () async { + var sass = await runSass([ + "--watch", + "--poll", + "--silence-deprecation=elseif", + "test.scss:out.css" + ]); + expect(sass.stderr, emitsDone); + + await expectLater(sass.stdout, + emitsThrough(endsWith('Compiled test.scss to out.css.'))); + await sass.kill(); + }); + + test("in repl mode", () async { + var sass = await runSass( + ["--interactive", "--silence-deprecation=strict-unary"]); + expect(sass.stderr, emitsDone); + sass.stdin.writeln("4 -(5)"); + await expectLater(sass.stdout, emitsInOrder([">> 4 -(5)", "-1"])); + await sass.kill(); + }); + }); + + group("an evaluation-time deprecation", () { + setUp(() => d.file("test.scss", """ + @use 'sass:math'; + a {b: math.random(1px)} + """).create()); + + test("in immediate mode", () async { + var sass = await runSass( + ["--silence-deprecation=function-units", "test.scss"]); + expect(sass.stderr, emitsDone); + await sass.shouldExit(0); + }); + + test("in watch mode", () async { + var sass = await runSass([ + "--watch", + "--poll", + "--silence-deprecation=function-units", + "test.scss:out.css" + ]); + expect(sass.stderr, emitsDone); + + await expectLater(sass.stdout, + emitsThrough(endsWith('Compiled test.scss to out.css.'))); + await sass.kill(); + }); + + test("in repl mode", () async { + var sass = await runSass( + ["--interactive", "--silence-deprecation=function-units"]); + expect(sass.stderr, emitsDone); + sass.stdin.writeln("@use 'sass:math'"); + await expectLater(sass.stdout, emits(">> @use 'sass:math'")); + sass.stdin.writeln("math.random(1px)"); + await expectLater( + sass.stdout, emitsInOrder([">> math.random(1px)", "1"])); + await sass.kill(); + }); + }); + }); + }); + + group("--fatal-deprecation", () { + group("prints a warning", () { + setUp(() => d.file("test.scss", "").create()); + + test("for an obsolete deprecation", () async { + // TODO: test this when a deprecation is obsoleted + }); + + test("for an inactive future deprecation", () async { + var sass = await runSass(["--fatal-deprecation=import", "test.scss"]); + expect(sass.stderr, emits(contains("Future import deprecation"))); + await sass.shouldExit(0); + }); + + test("for a silent deprecation", () async { + var sass = await runSass([ + "--fatal-deprecation=elseif", + "--silence-deprecation=elseif", + "test.scss" + ]); + expect(sass.stderr, emits(contains("Ignoring setting to silence"))); + await sass.shouldExit(0); + }); + + test("in watch mode", () async { + var sass = await runSass([ + "--watch", + "--poll", + "--fatal-deprecation=elseif", + "--silence-deprecation=elseif", + "test.scss:out.css" + ]); + expect(sass.stderr, emits(contains("Ignoring setting to silence"))); + + await expectLater(sass.stdout, + emitsThrough(endsWith('Compiled test.scss to out.css.'))); + await sass.kill(); + }); + + test("in repl mode", () async { + var sass = await runSass([ + "--interactive", + "--fatal-deprecation=elseif", + "--silence-deprecation=elseif" + ]); + await expectLater( + sass.stderr, emits(contains("Ignoring setting to silence"))); + await sass.kill(); + }); + }); + + group("throws an error for", () { + group("an unknown deprecation", () { + setUp(() => d.file("test.scss", "").create()); + + test("in immediate mode", () async { + var sass = + await runSass(["--fatal-deprecation=unknown", "test.scss"]); + expect( + sass.stdout, emits(contains('Invalid deprecation "unknown".'))); + await sass.shouldExit(64); + }); + + test("in watch mode", () async { + var sass = await runSass([ + "--watch", + "--poll", + "--fatal-deprecation=unknown", + "test.scss:out.css" + ]); + expect( + sass.stdout, emits(contains('Invalid deprecation "unknown".'))); + await sass.shouldExit(64); + }); + + test("in repl mode", () async { + var sass = + await runSass(["--interactive", "--fatal-deprecation=unknown"]); + expect( + sass.stdout, emits(contains('Invalid deprecation "unknown".'))); + await sass.shouldExit(64); + }); + }); + + group("a parse-time deprecation", () { + setUp( + () => d.file("test.scss", "@if true {} @elseif false {}").create()); + + test("in immediate mode", () async { + var sass = await runSass(["--fatal-deprecation=elseif", "test.scss"]); + expect(sass.stderr, emits(startsWith("Error: "))); + await sass.shouldExit(65); + }); + + test("in watch mode", () async { + var sass = await runSass([ + "--watch", + "--poll", + "--fatal-deprecation=elseif", + "test.scss:out.css" + ]); + await expectLater(sass.stderr, emits(startsWith("Error: "))); + await expectLater( + sass.stdout, + emitsInOrder( + ["Sass is watching for changes. Press Ctrl-C to stop.", ""])); + await sass.kill(); + }); + + test("in repl mode", () async { + var sass = await runSass( + ["--interactive", "--fatal-deprecation=strict-unary"]); + sass.stdin.writeln("4 -(5)"); + await expectLater( + sass.stdout, + emitsInOrder([ + ">> 4 -(5)", + emitsThrough(startsWith("Error: ")), + emitsThrough(contains("Remove this setting")) + ])); + + // Verify that there's no output written for the previous line. + sass.stdin.writeln("1"); + await expectLater(sass.stdout, emitsInOrder([">> 1", "1"])); + await sass.kill(); + }); + }); + + group("an evaluation-time deprecation", () { + setUp(() => d.file("test.scss", """ + @use 'sass:math'; + a {b: math.random(1px)} + """).create()); + + test("in immediate mode", () async { + var sass = await runSass( + ["--fatal-deprecation=function-units", "test.scss"]); + expect(sass.stderr, emits(startsWith("Error: "))); + await sass.shouldExit(65); + }); + + test("in watch mode", () async { + var sass = await runSass([ + "--watch", + "--poll", + "--fatal-deprecation=function-units", + "test.scss:out.css" + ]); + await expectLater(sass.stderr, emits(startsWith("Error: "))); + await expectLater( + sass.stdout, + emitsInOrder( + ["Sass is watching for changes. Press Ctrl-C to stop.", ""])); + await sass.kill(); + }); + + test("in repl mode", () async { + var sass = await runSass( + ["--interactive", "--fatal-deprecation=function-units"]); + sass.stdin.writeln("@use 'sass:math'"); + await expectLater(sass.stdout, emits(">> @use 'sass:math'")); + sass.stdin.writeln("math.random(1px)"); + await expectLater( + sass.stdout, + emitsInOrder([ + ">> math.random(1px)", + emitsThrough(startsWith("Error: ")), + emitsThrough(contains("Remove this setting")) + ])); + + // Verify that there's no output written for the previous line. + sass.stdin.writeln("1"); + await expectLater(sass.stdout, emitsInOrder([">> 1", "1"])); + await sass.kill(); + }); + }); + }); + }); + + group("--future-deprecation", () { + group("prints a warning for", () { + group("an active deprecation", () { + setUp(() => d.file("test.scss", "").create()); + + test("in immediate mode", () async { + var sass = await runSass( + ["--future-deprecation=function-units", "test.scss"]); + expect(sass.stderr, + emits(contains("function-units is not a future deprecation"))); + await sass.shouldExit(0); + }); + + test("in watch mode", () async { + var sass = await runSass([ + "--watch", + "--poll", + "--future-deprecation=function-units", + "test.scss:out.css" + ]); + expect(sass.stderr, + emits(contains("function-units is not a future deprecation"))); + + await expectLater(sass.stdout, + emitsThrough(endsWith('Compiled test.scss to out.css.'))); + await sass.kill(); + }); + + test("in repl mode", () async { + // TODO: test this when there's an expression-level future deprecation + }); + }); + + group("an obsolete deprecation", () { + // TODO: test this when there are obsolete deprecations + }); + + group("a parse-time deprecation", () { + setUp(() async { + await d.file("test.scss", "@import 'other';").create(); + await d.file("_other.scss", "").create(); + }); + + test("in immediate mode", () async { + var sass = + await runSass(["--future-deprecation=import", "test.scss"]); + expect(sass.stderr, emits(startsWith("DEPRECATION WARNING"))); + await sass.shouldExit(0); + }); + + test("in watch mode", () async { + var sass = await runSass([ + "--watch", + "--poll", + "--future-deprecation=import", + "test.scss:out.css" + ]); + + await expectLater( + sass.stderr, emits(startsWith("DEPRECATION WARNING"))); + await sass.kill(); + }); + + test("in repl mode", () async { + // TODO: test this when there's an expression-level future deprecation + }); + }); + + group("an evaluation-time deprecation", () { + // TODO: test this when there's an evaluation-time future deprecation + }); + }); + + group("throws an error for", () { + group("an unknown deprecation", () { + setUp(() => d.file("test.scss", "").create()); + + test("in immediate mode", () async { + var sass = + await runSass(["--future-deprecation=unknown", "test.scss"]); + expect( + sass.stdout, emits(contains('Invalid deprecation "unknown".'))); + await sass.shouldExit(64); + }); + + test("in watch mode", () async { + var sass = await runSass([ + "--watch", + "--poll", + "--future-deprecation=unknown", + "test.scss:out.css" + ]); + expect( + sass.stdout, emits(contains('Invalid deprecation "unknown".'))); + await sass.shouldExit(64); + }); + + test("in repl mode", () async { + var sass = + await runSass(["--interactive", "--future-deprecation=unknown"]); + expect( + sass.stdout, emits(contains('Invalid deprecation "unknown".'))); + await sass.shouldExit(64); + }); + }); + + group("a fatal deprecation", () { + setUp(() async { + await d.file("test.scss", "@import 'other';").create(); + await d.file("_other.scss", "").create(); + }); + + test("in immediate mode", () async { + var sass = await runSass([ + "--fatal-deprecation=import", + "--future-deprecation=import", + "test.scss" + ]); + expect(sass.stderr, emits(startsWith("Error: "))); + await sass.shouldExit(65); + }); + + test("in watch mode", () async { + var sass = await runSass([ + "--watch", + "--poll", + "--fatal-deprecation=import", + "--future-deprecation=import", + "test.scss:out.css" + ]); + await expectLater(sass.stderr, emits(startsWith("Error: "))); + await expectLater( + sass.stdout, + emitsInOrder( + ["Sass is watching for changes. Press Ctrl-C to stop.", ""])); + await sass.kill(); + }); + + test("in repl mode", () async { + // TODO: test this when there's an expression-level future deprecation + }); + }); + }); + }); +} From 8c48a013617f96a6582b9c0b069bdab81a83a929 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 15 May 2024 14:16:31 -0700 Subject: [PATCH 03/14] Expand dartdoc range for sass_api to match sass --- pkg/sass_api/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 08eda2c49..e11f303a5 100644 --- a/pkg/sass_api/pubspec.yaml +++ b/pkg/sass_api/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: sass: 1.77.2 dev_dependencies: - dartdoc: ^6.0.0 + dartdoc: ">=6.0.0 <9.0.0" dependency_overrides: sass: { path: ../.. } From 9a9e48342cccd8f40acc36980a22ffa1c62af3a7 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 15 May 2024 14:34:21 -0700 Subject: [PATCH 04/14] Add a test to ensure the sass_api dartdoc version matches sass --- test/double_check_test.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/double_check_test.dart b/test/double_check_test.dart index 5cc2a2411..ee87b922d 100644 --- a/test/double_check_test.dart +++ b/test/double_check_test.dart @@ -114,6 +114,17 @@ void main() { expect(pkgPubspec.environment!["sdk"], equals(sassPubspec.environment!["sdk"])); }); + + test("matches dartdoc version", () { + // TODO(nweiz): Just use equals() once dart-lang/pubspec_parse#127 lands + // and is released. + var sassDep = sassPubspec.devDependencies["dartdoc"]; + var pkgDep = pkgPubspec.devDependencies["dartdoc"]; + expect(pkgDep, isA()); + expect(sassDep, isA()); + expect((pkgDep as HostedDependency).version, + equals((sassDep as HostedDependency).version)); + }); }); } } From 5121eb195dea513a0c59c5d9874bebeccf90069c Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 16 May 2024 15:04:08 -0700 Subject: [PATCH 05/14] Don't treat underscores as hyphens for the purpose of error checking (#2247) --- CHANGELOG.md | 5 ++++ lib/src/ast/sass/expression/function.dart | 15 ++++++----- .../sass/statement/callable_declaration.dart | 10 ++++--- lib/src/ast/sass/statement/include_rule.dart | 9 +++++-- lib/src/parse/stylesheet.dart | 8 +++--- lib/src/visitor/async_evaluate.dart | 25 +++++++++++++++++ lib/src/visitor/evaluate.dart | 27 ++++++++++++++++++- 7 files changed, 81 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c932688b4..9d64f38c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## 1.77.2 +* Don't emit deprecation warnings for functions and mixins beginning with `__`. + +* Allow user-defined functions whose names begin with `_` and otherwise look + like vendor-prefixed functions with special CSS syntax. + ### Command-Line Interface * Properly handle the `--silence-deprecation` flag. diff --git a/lib/src/ast/sass/expression/function.dart b/lib/src/ast/sass/expression/function.dart index 398a2ff03..64c508c19 100644 --- a/lib/src/ast/sass/expression/function.dart +++ b/lib/src/ast/sass/expression/function.dart @@ -23,6 +23,12 @@ final class FunctionExpression /// without a namespace. final String? namespace; + /// The name of the function being invoked, with underscores converted to + /// hyphens. + /// + /// If this function is a plain CSS function, use [originalName] instead. + final String name; + /// The name of the function being invoked, with underscores left as-is. final String originalName; @@ -31,12 +37,6 @@ final class FunctionExpression final FileSpan span; - /// The name of the function being invoked, with underscores converted to - /// hyphens. - /// - /// If this function is a plain CSS function, use [originalName] instead. - String get name => originalName.replaceAll('_', '-'); - FileSpan get nameSpan { if (namespace == null) return span.initialIdentifier(); return span.withoutNamespace().initialIdentifier(); @@ -46,7 +46,8 @@ final class FunctionExpression namespace == null ? null : span.initialIdentifier(); FunctionExpression(this.originalName, this.arguments, this.span, - {this.namespace}); + {this.namespace}) + : name = originalName.replaceAll('_', '-'); T accept(ExpressionVisitor visitor) => visitor.visitFunctionExpression(this); diff --git a/lib/src/ast/sass/statement/callable_declaration.dart b/lib/src/ast/sass/statement/callable_declaration.dart index e39b9c035..3ce0ec9a0 100644 --- a/lib/src/ast/sass/statement/callable_declaration.dart +++ b/lib/src/ast/sass/statement/callable_declaration.dart @@ -18,6 +18,9 @@ abstract base class CallableDeclaration /// The name of this callable, with underscores converted to hyphens. final String name; + /// The callable's original name, without underscores converted to hyphens. + final String originalName; + /// The comment immediately preceding this declaration. final SilentComment? comment; @@ -26,8 +29,9 @@ abstract base class CallableDeclaration final FileSpan span; - CallableDeclaration( - this.name, this.arguments, Iterable children, this.span, + CallableDeclaration(this.originalName, this.arguments, + Iterable children, this.span, {this.comment}) - : super(List.unmodifiable(children)); + : name = originalName.replaceAll('_', '-'), + super(List.unmodifiable(children)); } diff --git a/lib/src/ast/sass/statement/include_rule.dart b/lib/src/ast/sass/statement/include_rule.dart index d3c9ceba6..940716cac 100644 --- a/lib/src/ast/sass/statement/include_rule.dart +++ b/lib/src/ast/sass/statement/include_rule.dart @@ -25,6 +25,10 @@ final class IncludeRule /// hyphens. final String name; + /// The original name of the mixin being invoked, without underscores + /// converted to hyphens. + final String originalName; + /// The arguments to pass to the mixin. final ArgumentInvocation arguments; @@ -55,8 +59,9 @@ final class IncludeRule return startSpan.initialIdentifier(); } - IncludeRule(this.name, this.arguments, this.span, - {this.namespace, this.content}); + IncludeRule(this.originalName, this.arguments, this.span, + {this.namespace, this.content}) + : name = originalName.replaceAll('_', '-'); T accept(StatementVisitor visitor) => visitor.visitIncludeRule(this); diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index fb1f1b614..e72e2527b 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -847,7 +847,7 @@ abstract class StylesheetParser extends Parser { var precedingComment = lastSilentComment; lastSilentComment = null; var beforeName = scanner.state; - var name = identifier(normalize: true); + var name = identifier(); if (name.startsWith('--')) { logger.warnForDeprecation( @@ -1221,8 +1221,6 @@ abstract class StylesheetParser extends Parser { if (scanner.scanChar($dot)) { namespace = name; name = _publicIdentifier(); - } else { - name = name.replaceAll("_", "-"); } whitespace(); @@ -1274,7 +1272,7 @@ abstract class StylesheetParser extends Parser { var precedingComment = lastSilentComment; lastSilentComment = null; var beforeName = scanner.state; - var name = identifier(normalize: true); + var name = identifier(); if (name.startsWith('--')) { logger.warnForDeprecation( @@ -3457,7 +3455,7 @@ abstract class StylesheetParser extends Parser { /// Like [identifier], but rejects identifiers that begin with `_` or `-`. String _publicIdentifier() { var start = scanner.state; - var result = identifier(normalize: true); + var result = identifier(); _assertPublic(result, () => scanner.spanFrom(start)); return result; } diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index b564b8323..b8434ff47 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -1853,6 +1853,18 @@ final class _EvaluateVisitor Future visitIncludeRule(IncludeRule node) async { var mixin = _addExceptionSpan(node, () => _environment.getMixin(node.name, namespace: node.namespace)); + if (node.originalName.startsWith('--') && + mixin is UserDefinedCallable && + !mixin.declaration.originalName.startsWith('--')) { + _warn( + 'Sass @mixin names beginning with -- are deprecated for forward-' + 'compatibility with plain CSS mixins.\n' + '\n' + 'For details, see https://sass-lang.com/d/css-function-mixin', + node.nameSpan, + Deprecation.cssFunctionMixin); + } + var contentCallable = node.content.andThen((content) => UserDefinedCallable( content, _environment.closure(), inDependency: _inDependency)); @@ -2510,6 +2522,19 @@ final class _EvaluateVisitor PlainCssCallable(node.originalName); } + if (node.originalName.startsWith('--') && + function is UserDefinedCallable && + !function.declaration.originalName.startsWith('--')) { + _warn( + 'Sass @function names beginning with -- are deprecated for forward-' + 'compatibility with plain CSS functions.\n' + '\n' + 'For details, see https://sass-lang.com/d/css-function-mixin', + node.nameSpan, + Deprecation.cssFunctionMixin, + ); + } + var oldInFunction = _inFunction; _inFunction = true; var result = await _addErrorSpan( diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 473728c1c..1680be88e 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: 7788c21fd8c721992490ac01d0ef4783dddf3f1f +// Checksum: 116b8079719577ac6e4dad4aebe403282136e611 // // ignore_for_file: unused_import @@ -1845,6 +1845,18 @@ final class _EvaluateVisitor Value? visitIncludeRule(IncludeRule node) { var mixin = _addExceptionSpan(node, () => _environment.getMixin(node.name, namespace: node.namespace)); + if (node.originalName.startsWith('--') && + mixin is UserDefinedCallable && + !mixin.declaration.originalName.startsWith('--')) { + _warn( + 'Sass @mixin names beginning with -- are deprecated for forward-' + 'compatibility with plain CSS mixins.\n' + '\n' + 'For details, see https://sass-lang.com/d/css-function-mixin', + node.nameSpan, + Deprecation.cssFunctionMixin); + } + var contentCallable = node.content.andThen((content) => UserDefinedCallable( content, _environment.closure(), inDependency: _inDependency)); @@ -2486,6 +2498,19 @@ final class _EvaluateVisitor PlainCssCallable(node.originalName); } + if (node.originalName.startsWith('--') && + function is UserDefinedCallable && + !function.declaration.originalName.startsWith('--')) { + _warn( + 'Sass @function names beginning with -- are deprecated for forward-' + 'compatibility with plain CSS functions.\n' + '\n' + 'For details, see https://sass-lang.com/d/css-function-mixin', + node.nameSpan, + Deprecation.cssFunctionMixin, + ); + } + var oldInFunction = _inFunction; _inFunction = true; var result = _addErrorSpan( From fc24fec7e97148780180f5d8030c3f827240ca83 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 28 May 2024 17:21:03 -0700 Subject: [PATCH 06/14] Use `pubspec_parse` dependency equality (#2254) --- pubspec.yaml | 2 +- test/double_check_test.dart | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index bb285238e..c4acb1234 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,7 +48,7 @@ dev_dependencies: lints: ">=2.0.0 <5.0.0" protoc_plugin: ">=20.0.0 <22.0.0" pub_api_client: ^2.1.1 - pubspec_parse: ^1.0.0 + pubspec_parse: ^1.3.0 test: ^1.16.7 test_descriptor: ^2.0.0 test_process: ^2.0.0 diff --git a/test/double_check_test.dart b/test/double_check_test.dart index ee87b922d..1da209885 100644 --- a/test/double_check_test.dart +++ b/test/double_check_test.dart @@ -116,14 +116,8 @@ void main() { }); test("matches dartdoc version", () { - // TODO(nweiz): Just use equals() once dart-lang/pubspec_parse#127 lands - // and is released. - var sassDep = sassPubspec.devDependencies["dartdoc"]; - var pkgDep = pkgPubspec.devDependencies["dartdoc"]; - expect(pkgDep, isA()); - expect(sassDep, isA()); - expect((pkgDep as HostedDependency).version, - equals((sassDep as HostedDependency).version)); + expect(sassPubspec.devDependencies["dartdoc"], + equals(pkgPubspec.devDependencies["dartdoc"])); }); }); } From 1073c7b4121dc292887a3be60fbbdde3af0413f5 Mon Sep 17 00:00:00 2001 From: Jennifer Thakar Date: Wed, 29 May 2024 14:23:43 -0700 Subject: [PATCH 07/14] Generate deprecations list from the language repo (#2253) This updates the Deprecation enum to be generated from spec/deprecations.yaml in the language repo. --- .pubignore | 2 +- CHANGELOG.md | 8 +++ lib/src/deprecation.dart | 63 ++++++++++++-------- lib/src/parse/stylesheet.dart | 4 +- pkg/sass_api/CHANGELOG.md | 4 ++ pkg/sass_api/pubspec.yaml | 4 +- pubspec.yaml | 2 +- test/double_check_test.dart | 30 +++++++--- tool/grind.dart | 30 +++------- tool/grind/double_check.dart | 5 +- tool/grind/generate_deprecations.dart | 82 +++++++++++++++++++++++++++ tool/grind/utils.dart | 17 ++++++ 12 files changed, 189 insertions(+), 62 deletions(-) create mode 100644 tool/grind/generate_deprecations.dart diff --git a/.pubignore b/.pubignore index 2fbab300a..08992ce75 100644 --- a/.pubignore +++ b/.pubignore @@ -1,5 +1,5 @@ # This should be identical to .gitignore except that it doesn't exclude -# generated protobuf files. +# generated Dart files. .buildlog .DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d64f38c1..d06b712fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 1.77.3 + +### Dart API + +* `Deprecation.duplicateVariableFlags` has been deprecated and replaced with + `Deprecation.duplicateVarFlags` to make it consistent with the + `duplicate-var-flags` name used on the command line and in the JS API. + ## 1.77.2 * Don't emit deprecation warnings for functions and mixins beginning with `__`. diff --git a/lib/src/deprecation.dart b/lib/src/deprecation.dart index b13180e10..cec976714 100644 --- a/lib/src/deprecation.dart +++ b/lib/src/deprecation.dart @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC. Use of this source code is governed by an +// Copyright 2024 Google LLC. 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. @@ -10,34 +10,42 @@ import 'util/nullable.dart'; /// A deprecated feature in the language. enum Deprecation { - /// Deprecation for passing a string to `call` instead of `get-function`. + // START AUTOGENERATED CODE + // + // DO NOT EDIT. This section was generated from the language repo. + // See tool/grind/generate_deprecations.dart for details. + // + // Checksum: 22d9bdbe92eb39b3c0d6d64ebe1879a431c0037e + + /// Deprecation for 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`. + /// Deprecation for @elseif. elseif('elseif', deprecatedIn: '1.3.2', description: '@elseif.'), - /// Deprecation for parsing `@-moz-document`. + /// Deprecation for @-moz-document. mozDocument('moz-document', deprecatedIn: '1.7.2', description: '@-moz-document.'), - /// Deprecation for importers using relative canonical URLs. - relativeCanonical('relative-canonical', deprecatedIn: '1.14.2'), + /// Deprecation for 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`. + /// Deprecation for declaring new variables with !global. newGlobal('new-global', deprecatedIn: '1.17.2', description: 'Declaring new variables with !global.'), - /// Deprecation for certain functions in the color module matching the - /// behavior of their global counterparts for compatiblity reasons. + /// 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.'), - /// Deprecation for treating `/` as division. + /// Deprecation for / operator for division. slashDiv('slash-div', deprecatedIn: '1.33.0', description: '/ operator for division.'), @@ -46,46 +54,55 @@ enum Deprecation { deprecatedIn: '1.54.0', description: 'Leading, trailing, and repeated combinators.'), - /// Deprecation for ambiguous `+` and `-` operators. + /// Deprecation for ambiguous + and - operators. strictUnary('strict-unary', deprecatedIn: '1.55.0', description: 'Ambiguous + and - operators.'), - /// Deprecation for passing invalid units to certain built-in functions. + /// Deprecation for passing invalid units to built-in functions. functionUnits('function-units', deprecatedIn: '1.56.0', description: 'Passing invalid units to built-in functions.'), - /// Deprecation for passing percentages to the Sass abs() function. - absPercent('abs-percent', - deprecatedIn: '1.65.0', - description: 'Passing percentages to the Sass abs() function.'), - - duplicateVariableFlags('duplicate-var-flags', + /// 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.'), + /// 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.'), + /// Deprecation for 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.'), + /// Deprecation for function and mixin names beginning with --. cssFunctionMixin('css-function-mixin', deprecatedIn: '1.76.0', description: 'Function and mixin names beginning with --.'), - @Deprecated('This deprecation name was never actually used.') - calcInterp('calc-interp', deprecatedIn: null), - - /// Deprecation for `@import` rules. + /// Deprecation for @import rules. import.future('import', description: '@import rules.'), + // END AUTOGENERATED CODE + /// Used for deprecations coming from user-authored code. - userAuthored('user-authored', deprecatedIn: null); + userAuthored('user-authored', deprecatedIn: null), + + @Deprecated('This deprecation name was never actually used.') + calcInterp('calc-interp', deprecatedIn: null); + + @Deprecated('Use duplicateVarFlags instead.') + static const duplicateVariableFlags = duplicateVarFlags; /// A unique ID for this deprecation in kebab case. /// diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index e72e2527b..e372e3912 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -235,7 +235,7 @@ abstract class StylesheetParser extends Parser { case 'default': if (guarded) { logger.warnForDeprecation( - Deprecation.duplicateVariableFlags, + Deprecation.duplicateVarFlags, '!default should only be written once for each variable.\n' 'This will be an error in Dart Sass 2.0.0.', span: scanner.spanFrom(flagStart)); @@ -248,7 +248,7 @@ abstract class StylesheetParser extends Parser { scanner.spanFrom(flagStart)); } else if (global) { logger.warnForDeprecation( - Deprecation.duplicateVariableFlags, + Deprecation.duplicateVarFlags, '!global should only be written once for each variable.\n' 'This will be an error in Dart Sass 2.0.0.', span: scanner.spanFrom(flagStart)); diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index c0839df75..e82b07c57 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 10.4.3 + +* No user-visible changes. + ## 10.4.2 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index e11f303a5..0943de732 100644 --- a/pkg/sass_api/pubspec.yaml +++ b/pkg/sass_api/pubspec.yaml @@ -2,7 +2,7 @@ name: sass_api # Note: Every time we add a new Sass AST node, we need to bump the *major* # version because it's a breaking change for anyone who's implementing the # visitor interface(s). -version: 10.4.2 +version: 10.4.3 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: - sass: 1.77.2 + sass: 1.77.3 dev_dependencies: dartdoc: ">=6.0.0 <9.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index c4acb1234..5260eba17 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.77.2 +version: 1.77.3 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass diff --git a/test/double_check_test.dart b/test/double_check_test.dart index 1da209885..44def85ae 100644 --- a/test/double_check_test.dart +++ b/test/double_check_test.dart @@ -7,25 +7,39 @@ import 'dart:io'; import 'dart:convert'; +import 'package:crypto/crypto.dart'; import 'package:path/path.dart' as p; import 'package:pub_semver/pub_semver.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:test/test.dart'; +import '../tool/grind/generate_deprecations.dart' as deprecations; import '../tool/grind/synchronize.dart' as synchronize; /// Tests that double-check that everything in the repo looks sensible. void main() { - group("synchronized file is up-to-date:", () { - synchronize.sources.forEach((sourcePath, targetPath) { - test(targetPath, () { - if (File(targetPath).readAsStringSync() != - synchronize.synchronizeFile(sourcePath)) { - fail("$targetPath is out-of-date.\n" - "Run `dart pub run grinder` to update it."); - } + group("up-to-date generated", () { + group("synchronized file:", () { + synchronize.sources.forEach((sourcePath, targetPath) { + test(targetPath, () { + if (File(targetPath).readAsStringSync() != + synchronize.synchronizeFile(sourcePath)) { + fail("$targetPath is out-of-date.\n" + "Run `dart run grinder` to update it."); + } + }); }); }); + + test("deprecations", () { + var inputText = File(deprecations.yamlPath).readAsStringSync(); + var outputText = File(deprecations.dartPath).readAsStringSync(); + var checksum = sha1.convert(utf8.encode(inputText)); + if (!outputText.contains('// Checksum: $checksum')) { + fail('${deprecations.dartPath} is out-of-date.\n' + 'Run `dart run grinder` to update it.'); + } + }); }, // Windows sees different bytes than other OSes, possibly because of // newline normalization issues. diff --git a/tool/grind.dart b/tool/grind.dart index 631bdebdf..7af45b438 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -11,6 +11,7 @@ import 'package:grinder/grinder.dart'; import 'package:path/path.dart' as p; import 'package:source_span/source_span.dart'; +import 'grind/generate_deprecations.dart'; import 'grind/synchronize.dart'; import 'grind/utils.dart'; @@ -18,8 +19,10 @@ export 'grind/bazel.dart'; export 'grind/benchmark.dart'; export 'grind/double_check.dart'; export 'grind/frameworks.dart'; +export 'grind/generate_deprecations.dart'; export 'grind/subpackages.dart'; export 'grind/synchronize.dart'; +export 'grind/utils.dart'; void main(List args) { pkg.humanName.value = "Dart Sass"; @@ -127,7 +130,7 @@ void main(List args) { } @DefaultTask('Compile async code and reformat.') -@Depends(format, synchronize) +@Depends(format, synchronize, deprecations) void all() {} @Task('Run the Dart formatter.') @@ -140,7 +143,7 @@ void npmInstall() => run(Platform.isWindows ? "npm.cmd" : "npm", arguments: ["install"]); @Task('Runs the tasks that are required for running tests.') -@Depends(format, synchronize, protobuf, "pkg-npm-dev", npmInstall, +@Depends(format, synchronize, protobuf, deprecations, "pkg-npm-dev", npmInstall, "pkg-standalone-dev") void beforeTest() {} @@ -213,9 +216,9 @@ String _readAndResolveMarkdown(String path) => File(path) /// Returns a map from JS type declaration file names to their contnets. Map _fetchJSTypes() { - var languageRepo = _updateLanguageRepo(); + updateLanguageRepo(); - var typeRoot = p.join(languageRepo, 'js-api-doc'); + var typeRoot = p.join('build/language', 'js-api-doc'); return { for (var entry in Directory(typeRoot).listSync(recursive: true)) if (entry is File && entry.path.endsWith('.d.ts')) @@ -231,6 +234,7 @@ void _matchError(Match match, String message, {Object? url}) { } @Task('Compile the protocol buffer definition to a Dart library.') +@Depends(updateLanguageRepo) Future protobuf() async { Directory('build').createSync(recursive: true); @@ -250,8 +254,6 @@ dart run protoc_plugin "\$@" run('chmod', arguments: ['a+x', 'build/protoc-gen-dart']); } - _updateLanguageRepo(); - await runAsync("buf", arguments: ["generate"], runOptions: RunOptions(environment: { @@ -321,19 +323,3 @@ String _updateHomebrewLanguageRevision(String formula) { match.group(0)!.replaceFirst(match.group(1)!, languageRepoRevision) + formula.substring(match.end); } - -/// Clones the main branch of `github.com/sass/sass` and returns the path to the -/// clone. -/// -/// If the `UPDATE_SASS_SASS_REPO` environment variable is `false`, this instead -/// assumes the repo that already exists at `build/language/sass`. -/// `UPDATE_SASS_PROTOCOL` is also checked as a deprecated alias for -/// `UPDATE_SASS_SASS_REPO`. -String _updateLanguageRepo() => - // UPDATE_SASS_PROTOCOL is considered deprecated, because it doesn't apply as - // generically to other tasks. - Platform.environment['UPDATE_SASS_SASS_REPO'] != 'false' && - Platform.environment['UPDATE_SASS_PROTOCOL'] != 'false' - ? cloneOrCheckout("https://github.com/sass/sass.git", "main", - name: 'language') - : 'build/language'; diff --git a/tool/grind/double_check.dart b/tool/grind/double_check.dart index 4ec0cd8e7..8b1dca18f 100644 --- a/tool/grind/double_check.dart +++ b/tool/grind/double_check.dart @@ -5,13 +5,12 @@ import 'dart:io'; import 'package:cli_pkg/cli_pkg.dart' as pkg; +import 'package:collection/collection.dart'; import 'package:grinder/grinder.dart'; import 'package:path/path.dart' as p; import 'package:pub_api_client/pub_api_client.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; -import 'package:sass/src/utils.dart'; - import 'utils.dart'; @Task('Verify that the package is in a good state to release.') @@ -21,7 +20,7 @@ Future doubleCheckBeforeRelease() async { fail("GITHUB_REF $ref is different than pubspec version ${pkg.version}."); } - if (listEquals(pkg.version.preRelease, ["dev"])) { + if (const ListEquality().equals(pkg.version.preRelease, ["dev"])) { fail("${pkg.version} is a dev release."); } diff --git a/tool/grind/generate_deprecations.dart b/tool/grind/generate_deprecations.dart new file mode 100644 index 000000000..21ce6f58e --- /dev/null +++ b/tool/grind/generate_deprecations.dart @@ -0,0 +1,82 @@ +// Copyright 2024 Google LLC. 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:convert'; +import 'dart:io'; + +import 'package:crypto/crypto.dart'; +import 'package:dart_style/dart_style.dart'; +import 'package:grinder/grinder.dart'; +import 'package:yaml/yaml.dart'; + +import 'utils.dart'; + +const yamlPath = 'build/language/spec/deprecations.yaml'; +const dartPath = 'lib/src/deprecation.dart'; + +final _blockRegex = + RegExp(r'// START AUTOGENERATED CODE[\s\S]*?// END AUTOGENERATED CODE'); + +@Task('Generate deprecation.g.dart from the list in the language repo.') +@Depends(updateLanguageRepo) +void deprecations() { + var yamlFile = File(yamlPath); + var dartFile = File(dartPath); + var yamlText = yamlFile.readAsStringSync(); + var data = loadYaml(yamlText, sourceUrl: yamlFile.uri) as Map; + var dartText = dartFile.readAsStringSync(); + var buffer = StringBuffer('''// START AUTOGENERATED CODE + // + // DO NOT EDIT. This section was generated from the language repo. + // See tool/grind/generate_deprecations.dart for details. + // + // Checksum: ${sha1.convert(utf8.encode(yamlText))} + +'''); + for (var MapEntry(:String key, :value) in data.entries) { + var camelCase = key.replaceAllMapped( + RegExp(r'-(.)'), (match) => match.group(1)!.toUpperCase()); + var (description, deprecatedIn, obsoleteIn) = switch (value) { + { + 'description': String description, + 'dart-sass': {'status': 'future'}, + } => + (description, null, null), + { + 'description': String description, + 'dart-sass': {'status': 'active', 'deprecated': String deprecatedIn}, + } => + (description, deprecatedIn, null), + { + 'description': String description, + 'dart-sass': { + 'status': 'obsolete', + 'deprecated': String deprecatedIn, + 'obsolete': String obsoleteIn + }, + } => + (description, deprecatedIn, obsoleteIn), + _ => throw Exception('Invalid deprecation $key: $value') + }; + description = + description.replaceAll(r'$PLATFORM', r"${isJS ? 'JS': 'Dart'}"); + var constructorName = deprecatedIn == null ? '.future' : ''; + var deprecatedClause = + deprecatedIn == null ? '' : "deprecatedIn: '$deprecatedIn', "; + var obsoleteClause = + obsoleteIn == null ? '' : "obsoleteIn: '$obsoleteIn', "; + var comment = 'Deprecation for ${description.substring(0, 1).toLowerCase()}' + '${description.substring(1)}'; + buffer.writeln('/// $comment'); + buffer.writeln( + "$camelCase$constructorName('$key', $deprecatedClause$obsoleteClause" + "description: '$description'),"); + } + buffer.write('\n // END AUTOGENERATED CODE'); + if (!dartText.contains(_blockRegex)) { + fail("Couldn't find block for generated code in lib/src/deprecation.dart"); + } + var newCode = dartText.replaceFirst(_blockRegex, buffer.toString()); + dartFile.writeAsStringSync(DartFormatter().format(newCode)); +} diff --git a/tool/grind/utils.dart b/tool/grind/utils.dart index 21d9f66c8..86ea7ed96 100644 --- a/tool/grind/utils.dart +++ b/tool/grind/utils.dart @@ -100,3 +100,20 @@ void afterTask(String taskName, FutureOr callback()) { await callback(); }); } + +/// Clones the main branch of `github.com/sass/sass`. +/// +/// If the `UPDATE_SASS_SASS_REPO` environment variable is `false`, this instead +/// assumes the repo that already exists at `build/language/sass`. +/// `UPDATE_SASS_PROTOCOL` is also checked as a deprecated alias for +/// `UPDATE_SASS_SASS_REPO`. +@Task('Clones the main branch of `github.com/sass/sass` if necessary.') +void updateLanguageRepo() { + // UPDATE_SASS_PROTOCOL is considered deprecated, because it doesn't apply as + // generically to other tasks. + if (Platform.environment['UPDATE_SASS_SASS_REPO'] != 'false' && + Platform.environment['UPDATE_SASS_PROTOCOL'] != 'false') { + cloneOrCheckout("https://github.com/sass/sass.git", "main", + name: 'language'); + } +} From 21eeb4d9428d1808487223a379c036a019f98487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AA=E3=81=A4=E3=81=8D?= Date: Thu, 30 May 2024 16:25:17 -0700 Subject: [PATCH 08/14] Parse Deprecation.forVersion on compiler side (#2248) --- CHANGELOG.md | 10 ++++++ lib/src/embedded/compilation_dispatcher.dart | 38 +++++++++++++------- pkg/sass_api/CHANGELOG.md | 4 +++ pkg/sass_api/pubspec.yaml | 4 +-- pubspec.yaml | 2 +- 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d06b712fd..41b6c0862 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 1.77.4 + +### Embedded Sass + +* Support passing `Version` input for `fatalDeprecations` as string over + embedded protocol. + +* Fix a bug in the JS Embedded Host where `Version` could be incorrectly accepted + as input for `silenceDeprecations` and `futureDeprecations` in pure JS. + ## 1.77.3 ### Dart API diff --git a/lib/src/embedded/compilation_dispatcher.dart b/lib/src/embedded/compilation_dispatcher.dart index 675216022..e8c3764b5 100644 --- a/lib/src/embedded/compilation_dispatcher.dart +++ b/lib/src/embedded/compilation_dispatcher.dart @@ -7,10 +7,10 @@ import 'dart:io'; import 'dart:isolate'; import 'dart:typed_data'; -import 'package:collection/collection.dart'; import 'package:native_synchronization/mailbox.dart'; import 'package:path/path.dart' as p; import 'package:protobuf/protobuf.dart'; +import 'package:pub_semver/pub_semver.dart'; import 'package:sass/sass.dart' as sass; import 'package:sass/src/importer/node_package.dart' as npi; @@ -125,20 +125,34 @@ final class CompilationDispatcher { : EmbeddedLogger(this, color: request.alertColor, ascii: request.alertAscii); - sass.Deprecation? deprecationOrWarn(String id) { - var deprecation = sass.Deprecation.fromId(id); - if (deprecation == null) { - logger.warn('Invalid deprecation "$id".'); - } - return deprecation; + Iterable? parseDeprecationsOrWarn( + Iterable deprecations, + {bool supportVersions = false}) { + return () sync* { + for (var item in deprecations) { + var deprecation = sass.Deprecation.fromId(item); + if (deprecation == null) { + if (supportVersions) { + try { + yield* sass.Deprecation.forVersion(Version.parse(item)); + } on FormatException { + logger.warn('Invalid deprecation id or version "$item".'); + } + } else { + logger.warn('Invalid deprecation id "$item".'); + } + } else { + yield deprecation; + } + } + }(); } - var fatalDeprecations = - request.fatalDeprecation.map(deprecationOrWarn).whereNotNull(); + var fatalDeprecations = parseDeprecationsOrWarn(request.fatalDeprecation, + supportVersions: true); var silenceDeprecations = - request.silenceDeprecation.map(deprecationOrWarn).whereNotNull(); - var futureDeprecations = - request.futureDeprecation.map(deprecationOrWarn).whereNotNull(); + parseDeprecationsOrWarn(request.silenceDeprecation); + var futureDeprecations = parseDeprecationsOrWarn(request.futureDeprecation); try { var importers = request.importers.map((importer) => diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index e82b07c57..0006705c9 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 10.4.4 + +* No user-visible changes. + ## 10.4.3 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 0943de732..c184e5f24 100644 --- a/pkg/sass_api/pubspec.yaml +++ b/pkg/sass_api/pubspec.yaml @@ -2,7 +2,7 @@ name: sass_api # Note: Every time we add a new Sass AST node, we need to bump the *major* # version because it's a breaking change for anyone who's implementing the # visitor interface(s). -version: 10.4.3 +version: 10.4.4 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: - sass: 1.77.3 + sass: 1.77.4 dev_dependencies: dartdoc: ">=6.0.0 <9.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 5260eba17..c8656f802 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.77.3 +version: 1.77.4 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass From 53b9ead5aaf8eb83223bc918eac6e2bb68c41d1c Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 31 May 2024 14:38:56 -0700 Subject: [PATCH 09/14] Regenerate protobuf as part of default task (#2257) --- tool/grind.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/grind.dart b/tool/grind.dart index 7af45b438..5f71995f5 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -130,7 +130,7 @@ void main(List args) { } @DefaultTask('Compile async code and reformat.') -@Depends(format, synchronize, deprecations) +@Depends(format, synchronize, deprecations, protobuf) void all() {} @Task('Run the Dart formatter.') From 5ddd7fc723d5be220a38882010469bbd7390a3e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AA=E3=81=A4=E3=81=8D?= Date: Thu, 6 Jun 2024 13:15:23 -0700 Subject: [PATCH 10/14] Enable AOT build for linux-riscv64-musl (#2258) --- .github/workflows/build-linux-musl.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build-linux-musl.yml b/.github/workflows/build-linux-musl.yml index 5f4ba2639..024820e34 100644 --- a/.github/workflows/build-linux-musl.yml +++ b/.github/workflows/build-linux-musl.yml @@ -26,9 +26,8 @@ jobs: # https://gitlab.com/qemu-project/qemu/-/issues/1729 - arch: arm platform: linux/amd64 # linux/arm/v7 - # There is no docker image for riscv64 dart-sdk, build kernel snapshot instead. - arch: riscv64 - platform: linux/amd64 # linux/riscv64 + platform: linux/riscv64 steps: - uses: actions/checkout@v4 From ecff05dfefc2da3f7e462c2a173ac12beeb51100 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 11 Jun 2024 16:05:41 -0700 Subject: [PATCH 11/14] Remove the heuristic where long selector lists wouldn't be trimmed (#2255) Testing this against the `@extend`-heavy stylesheets in vinceliuice/Colloid-gtk-theme, trimming everywhere actually *improves* performance rather than reducing it. --- CHANGELOG.md | 4 ++++ lib/src/ast/selector/compound.dart | 12 ++++++++++ lib/src/ast/selector/pseudo.dart | 4 ++++ lib/src/ast/selector/simple.dart | 10 ++++++++ lib/src/extend/extension_store.dart | 7 ------ lib/src/extend/functions.dart | 36 ++++++++++++++++++----------- pkg/sass_api/CHANGELOG.md | 4 ++++ pkg/sass_api/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- 9 files changed, 59 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41b6c0862..5f299e042 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.77.5 + +* Fully trim redundant selectors generated by `@extend`. + ## 1.77.4 ### Embedded Sass diff --git a/lib/src/ast/selector/compound.dart b/lib/src/ast/selector/compound.dart index bcc2beb33..9fbc8d397 100644 --- a/lib/src/ast/selector/compound.dart +++ b/lib/src/ast/selector/compound.dart @@ -42,6 +42,18 @@ final class CompoundSelector extends Selector { SimpleSelector? get singleSimple => components.length == 1 ? components.first : null; + /// Whether any simple selector in this contains a selector that requires + /// complex non-local reasoning to determine whether it's a super- or + /// sub-selector. + /// + /// This includes both pseudo-elements and pseudo-selectors that take + /// selectors as arguments. + /// + /// #nodoc + @internal + late final bool hasComplicatedSuperselectorSemantics = components + .any((component) => component.hasComplicatedSuperselectorSemantics); + CompoundSelector(Iterable components, super.span) : components = List.unmodifiable(components) { if (this.components.isEmpty) { diff --git a/lib/src/ast/selector/pseudo.dart b/lib/src/ast/selector/pseudo.dart index 44a263d15..e632c651e 100644 --- a/lib/src/ast/selector/pseudo.dart +++ b/lib/src/ast/selector/pseudo.dart @@ -67,6 +67,10 @@ final class PseudoSelector extends SimpleSelector { bool get isHostContext => isClass && name == 'host-context' && selector != null; + @internal + bool get hasComplicatedSuperselectorSemantics => + isElement || selector != null; + /// The non-selector argument passed to this selector. /// /// This is `null` if there's no argument. If [argument] and [selector] are diff --git a/lib/src/ast/selector/simple.dart b/lib/src/ast/selector/simple.dart index d8ae7864d..5ac2f0e53 100644 --- a/lib/src/ast/selector/simple.dart +++ b/lib/src/ast/selector/simple.dart @@ -34,6 +34,16 @@ abstract base class SimpleSelector extends Selector { /// sequence will contain 1000 simple selectors. int get specificity => 1000; + /// Whether this requires complex non-local reasoning to determine whether + /// it's a super- or sub-selector. + /// + /// This includes both pseudo-elements and pseudo-selectors that take + /// selectors as arguments. + /// + /// #nodoc + @internal + bool get hasComplicatedSuperselectorSemantics => false; + SimpleSelector(super.span); /// Parses a simple selector from [contents]. diff --git a/lib/src/extend/extension_store.dart b/lib/src/extend/extension_store.dart index 3637b5aac..2bbc2a9cb 100644 --- a/lib/src/extend/extension_store.dart +++ b/lib/src/extend/extension_store.dart @@ -901,13 +901,6 @@ class ExtensionStore { // document, and thus should never be trimmed. List _trim(List selectors, bool isOriginal(ComplexSelector complex)) { - // Avoid truly horrific quadratic behavior. - // - // TODO(nweiz): I think there may be a way to get perfect trimming without - // going quadratic by building some sort of trie-like data structure that - // can be used to look up superselectors. - if (selectors.length > 100) return selectors; - // This is n² on the sequences, but only comparing between separate // sequences should limit the quadratic behavior. We iterate from last to // first and reverse the result so that, if two selectors are identical, we diff --git a/lib/src/extend/functions.dart b/lib/src/extend/functions.dart index 01d70d248..2fb1f2555 100644 --- a/lib/src/extend/functions.dart +++ b/lib/src/extend/functions.dart @@ -646,24 +646,28 @@ bool complexIsSuperselector(List complex1, var component1 = complex1[i1]; if (component1.combinators.length > 1) return false; if (remaining1 == 1) { - var parents = complex2.sublist(i2, complex2.length - 1); - if (parents.any((parent) => parent.combinators.length > 1)) return false; - - return compoundIsSuperselector( - component1.selector, complex2.last.selector, - parents: parents); + if (complex2.any((parent) => parent.combinators.length > 1)) { + return false; + } else { + return compoundIsSuperselector( + component1.selector, complex2.last.selector, + parents: component1.selector.hasComplicatedSuperselectorSemantics + ? complex2.sublist(i2, complex2.length - 1) + : null); + } } // Find the first index [endOfSubselector] in [complex2] such that // `complex2.sublist(i2, endOfSubselector + 1)` is a subselector of // [component1.selector]. var endOfSubselector = i2; - List? parents; while (true) { var component2 = complex2[endOfSubselector]; if (component2.combinators.length > 1) return false; if (compoundIsSuperselector(component1.selector, component2.selector, - parents: parents)) { + parents: component1.selector.hasComplicatedSuperselectorSemantics + ? complex2.sublist(i2, endOfSubselector) + : null)) { break; } @@ -675,13 +679,10 @@ bool complexIsSuperselector(List complex1, // to match. return false; } - - parents ??= []; - parents.add(component2); } if (!_compatibleWithPreviousCombinator( - previousCombinator, parents ?? const [])) { + previousCombinator, complex2.take(endOfSubselector).skip(i2))) { return false; } @@ -717,8 +718,8 @@ bool complexIsSuperselector(List complex1, /// Returns whether [parents] are valid intersitial components between one /// complex superselector and another, given that the earlier complex /// superselector had the combinator [previous]. -bool _compatibleWithPreviousCombinator( - CssValue? previous, List parents) { +bool _compatibleWithPreviousCombinator(CssValue? previous, + Iterable parents) { if (parents.isEmpty) return true; if (previous == null) return true; @@ -754,6 +755,13 @@ bool _isSupercombinator( bool compoundIsSuperselector( CompoundSelector compound1, CompoundSelector compound2, {Iterable? parents}) { + if (!compound1.hasComplicatedSuperselectorSemantics && + !compound2.hasComplicatedSuperselectorSemantics) { + if (compound1.components.length > compound2.components.length) return false; + return compound1.components + .every((simple1) => compound2.components.any(simple1.isSuperselector)); + } + // Pseudo elements effectively change the target of a compound selector rather // than narrowing the set of elements to which it applies like other // selectors. As such, if either selector has a pseudo element, they both must diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index 0006705c9..373024d43 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 10.4.5 + +* No user-visible changes. + ## 10.4.4 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index c184e5f24..69c311a02 100644 --- a/pkg/sass_api/pubspec.yaml +++ b/pkg/sass_api/pubspec.yaml @@ -2,7 +2,7 @@ name: sass_api # Note: Every time we add a new Sass AST node, we need to bump the *major* # version because it's a breaking change for anyone who's implementing the # visitor interface(s). -version: 10.4.4 +version: 10.4.5 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: - sass: 1.77.4 + sass: 1.77.5 dev_dependencies: dartdoc: ">=6.0.0 <9.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index c8656f802..668acf389 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.77.4 +version: 1.77.5 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass From a1b372eaf1188e34e1bda2628db45ca17fd621c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AA=E3=81=A4=E3=81=8D?= Date: Tue, 11 Jun 2024 16:39:13 -0700 Subject: [PATCH 12/14] Enable AOT build for linux-riscv64 (#2260) --- .github/workflows/build-linux.yml | 34 ++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index c8f5ddd41..308c5ec58 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -22,10 +22,8 @@ jobs: platform: linux/arm/v7 - arch: arm64 platform: linux/arm64 - # There is no docker image for riscv64 dart-sdk, build kernel snapshot instead. - # https://github.com/dart-lang/dart-docker/issues/96#issuecomment-1669860829 - arch: riscv64 - platform: linux/amd64 # linux/riscv64 + platform: linux/riscv64 steps: - uses: actions/checkout@v4 @@ -46,6 +44,7 @@ jobs: EOF - name: Build + if: matrix.arch != 'riscv64' run: | docker run --rm -i \ --platform ${{ matrix.platform }} \ @@ -57,6 +56,35 @@ jobs: dart run grinder pkg-standalone-linux-${{ matrix.arch }} EOF + # https://github.com/dart-lang/dart-docker/issues/96#issuecomment-1669860829 + # There is no official riscv64 dart container image yet, build on debian:unstable instead. + # The setup is adopted from: https://github.com/dart-lang/dart-docker/blob/main/Dockerfile-debian.template + - name: Build riscv64 + if: matrix.arch == 'riscv64' + 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-linux-${{ matrix.arch }}-release.zip + + docker run --rm -i \ + --platform ${{ matrix.platform }} \ + --volume "$PWD:$PWD" \ + --workdir "$PWD" \ + docker.io/library/debian:unstable-slim <<'EOF' + set -e + apt-get update + apt-get install -y --no-install-recommends ca-certificates curl dnsutils git openssh-client unzip + + export DART_SDK=/usr/lib/dart + export PATH=$DART_SDK/bin:/root/.pub-cache/bin:$PATH + + SDK="dartsdk-linux-${{ matrix.arch }}-release.zip" + unzip "$SDK" && mv dart-sdk "$DART_SDK" && rm "$SDK" + + dart pub get + dart run grinder pkg-standalone-linux-${{ matrix.arch }} + EOF + - name: Upload Artifact uses: actions/upload-artifact@v4 with: From cf6f9d0842fbbc786d8b2908f5948c6d282e1b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AA=E3=81=A4=E3=81=8D?= Date: Wed, 12 Jun 2024 13:49:16 -0700 Subject: [PATCH 13/14] Build AOT releases with native arm64 runners (#2262) --- .github/workflows/build-android.yml | 7 ++++++- .github/workflows/build-linux-musl.yml | 13 +++++++------ .github/workflows/build-linux.yml | 13 +++++++++---- .github/workflows/build-macos.yml | 2 +- .github/workflows/build-windows.yml | 2 +- 5 files changed, 24 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml index e8ccdd7dc..90405bbd0 100644 --- a/.github/workflows/build-android.yml +++ b/.github/workflows/build-android.yml @@ -8,7 +8,7 @@ jobs: build: name: Build - runs-on: ubuntu-latest + runs-on: ${{ matrix.runner }} strategy: fail-fast: false @@ -16,19 +16,24 @@ jobs: include: - arch: x64 lib: lib64 + runner: ubuntu-latest platform: linux/amd64 - arch: ia32 lib: lib + runner: ubuntu-latest platform: linux/amd64 - arch: arm64 lib: lib64 + runner: linux-arm64 platform: linux/arm64 - arch: arm lib: lib + runner: linux-arm64 platform: linux/arm64 # There is no docker image for riscv64 dart-sdk, build kernel snapshot instead. - arch: riscv64 lib: lib64 + runner: ubuntu-latest platform: linux/amd64 # linux/riscv64 steps: diff --git a/.github/workflows/build-linux-musl.yml b/.github/workflows/build-linux-musl.yml index 024820e34..aa33b4eff 100644 --- a/.github/workflows/build-linux-musl.yml +++ b/.github/workflows/build-linux-musl.yml @@ -8,25 +8,26 @@ jobs: build: name: Build - runs-on: ubuntu-latest + runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - arch: x64 + runner: ubuntu-latest platform: linux/amd64 - arch: ia32 + runner: ubuntu-latest platform: linux/386 - arch: arm64 + runner: linux-arm64 platform: linux/arm64 - # There is a bug in qemu's mremap causing pthread_getattr_np in musl to stuck in a loop on arm. - # Unless qemu fixes the bug or we get a real linux-arm runner, we cannot build aot-snapshot - # for arm on CI. So, we create a kernel snapshot for arm build in amd64 container instead. - # https://gitlab.com/qemu-project/qemu/-/issues/1729 - arch: arm - platform: linux/amd64 # linux/arm/v7 + runner: linux-arm64 + platform: linux/arm/v7 - arch: riscv64 + runner: ubuntu-latest platform: linux/riscv64 steps: diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 308c5ec58..bf28d809f 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -8,21 +8,26 @@ jobs: build: name: Build - runs-on: ubuntu-latest + runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - arch: x64 + runner: ubuntu-latest platform: linux/amd64 - arch: ia32 - platform: linux/amd64 - - arch: arm - platform: linux/arm/v7 + runner: ubuntu-latest + platform: linux/386 - arch: arm64 + runner: linux-arm64 platform: linux/arm64 + - arch: arm + runner: linux-arm64 + platform: linux/arm/v7 - arch: riscv64 + runner: ubuntu-latest platform: linux/riscv64 steps: diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 3dff23d3b..660ceadfa 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -17,7 +17,7 @@ jobs: - arch: x64 runner: macos-13 - arch: arm64 - runner: macos-14 + runner: macos-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index ba9ff8bb1..86ea17c22 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -19,7 +19,7 @@ jobs: - arch: ia32 runner: windows-latest - arch: arm64 - runner: windows-latest + runner: windows-arm64 steps: - uses: actions/checkout@v4 From 7aae1e67ed3322c1caab1c8ebb2f5c28ff0b2e0b Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 17 Jun 2024 13:41:49 -0700 Subject: [PATCH 14/14] Fix a bug where comments were incorrectly forbidden in some cases (#2264) --- CHANGELOG.md | 5 +++++ lib/src/parse/stylesheet.dart | 20 ++++++++++++++------ pkg/sass_api/CHANGELOG.md | 4 ++++ pkg/sass_api/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f299e042..5229a0b08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.77.6 + +* Fix a few cases where comments and occasionally even whitespace wasn't allowed + between the end of Sass statements and the following semicolon. + ## 1.77.5 * Fully trim redundant selectors generated by `@extend`. diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index e372e3912..046c56e6f 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -770,10 +770,15 @@ abstract class StylesheetParser extends Parser { scanner.spanFrom(start)); } + var beforeWhitespace = scanner.location; whitespace(); - var arguments = scanner.peekChar() == $lparen - ? _argumentInvocation(mixin: true) - : ArgumentInvocation.empty(scanner.emptySpan); + ArgumentInvocation arguments; + if (scanner.peekChar() == $lparen) { + arguments = _argumentInvocation(mixin: true); + whitespace(); + } else { + arguments = ArgumentInvocation.empty(beforeWhitespace.pointSpan()); + } expectStatementSeparator("@content rule"); return ContentRule(arguments, scanner.spanFrom(start)); @@ -835,7 +840,10 @@ abstract class StylesheetParser extends Parser { var value = almostAnyValue(); var optional = scanner.scanChar($exclamation); - if (optional) expectIdentifier("optional"); + if (optional) { + expectIdentifier("optional"); + whitespace(); + } expectStatementSeparator("@extend rule"); return ExtendRule(value, scanner.spanFrom(start), optional: optional); } @@ -954,6 +962,7 @@ abstract class StylesheetParser extends Parser { } var configuration = _configuration(allowGuarded: true); + whitespace(); expectStatementSeparator("@forward rule"); var span = scanner.spanFrom(start); @@ -1419,8 +1428,7 @@ abstract class StylesheetParser extends Parser { var namespace = _useNamespace(url, start); whitespace(); var configuration = _configuration(); - - expectStatementSeparator("@use rule"); + whitespace(); var span = scanner.spanFrom(start); if (!_isUseAllowed) { diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index 373024d43..3ea002075 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 10.4.6 + +* No user-visible changes. + ## 10.4.5 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 69c311a02..bcd120d20 100644 --- a/pkg/sass_api/pubspec.yaml +++ b/pkg/sass_api/pubspec.yaml @@ -2,7 +2,7 @@ name: sass_api # Note: Every time we add a new Sass AST node, we need to bump the *major* # version because it's a breaking change for anyone who's implementing the # visitor interface(s). -version: 10.4.5 +version: 10.4.6 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: - sass: 1.77.5 + sass: 1.77.6 dev_dependencies: dartdoc: ">=6.0.0 <9.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 668acf389..936b32092 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.77.5 +version: 1.77.6 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass