From 247cb3d8167986c3d19e2efd50770adb93c5d807 Mon Sep 17 00:00:00 2001 From: Jennifer Thakar Date: Tue, 9 Mar 2021 14:36:48 -0800 Subject: [PATCH 01/19] Update dependencies to null-safe versions This also replaces package_resolver with package_config, since package_resolver is archived and is incompatible with null-safe Dart packages. --- lib/sass.dart | 38 ++++++--------- lib/src/async_compile.dart | 2 - lib/src/async_import_cache.dart | 20 ++++---- lib/src/compile.dart | 4 +- lib/src/import_cache.dart | 22 ++++----- lib/src/importer/package.dart | 16 +++--- lib/src/sync_package_resolver.dart | 2 - lib/src/sync_package_resolver/node.dart | 15 ------ pubspec.yaml | 65 +++++++++++++------------ test/cli/dart_test.dart | 4 +- test/cli/node_test.dart | 7 ++- test/dart_api_test.dart | 30 ++++++------ test/double_check_test.dart | 2 +- 13 files changed, 101 insertions(+), 126 deletions(-) delete mode 100644 lib/src/sync_package_resolver.dart delete mode 100644 lib/src/sync_package_resolver/node.dart diff --git a/lib/sass.dart b/lib/sass.dart index e45a610dd..855f20f74 100644 --- a/lib/sass.dart +++ b/lib/sass.dart @@ -5,6 +5,7 @@ /// We strongly recommend importing this library with the prefix `sass`. library sass; +import 'package:package_config/package_config_types.dart'; import 'package:source_maps/source_maps.dart'; import 'src/async_import_cache.dart'; @@ -14,7 +15,6 @@ import 'src/exception.dart'; import 'src/import_cache.dart'; import 'src/importer.dart'; import 'src/logger.dart'; -import 'src/sync_package_resolver.dart'; import 'src/syntax.dart'; import 'src/visitor/serialize.dart'; @@ -48,11 +48,11 @@ export 'src/warn.dart' show warn; /// * Each load path specified in the `SASS_PATH` environment variable, which /// should be semicolon-separated on Windows and colon-separated elsewhere. /// -/// * `package:` resolution using [packageResolver], which is a -/// [`SyncPackageResolver`][] from the `package_resolver` package. Note that +/// * `package:` resolution using [packageConfig], which is a +/// [`PackageConfig`][] from the `package_resolver` package. Note that /// this is a shorthand for adding a [PackageImporter] to [importers]. /// -/// [`SyncPackageResolver`]: https://www.dartdocs.org/documentation/package_resolver/latest/package_resolver/SyncPackageResolver-class.html +/// [`PackageConfig`]: https://pub.dev/documentation/package_config/latest/package_config.package_config/PackageConfig-class.html /// /// Dart functions that can be called from Sass may be passed using [functions]. /// Each [Callable] defines a top-level function that will be invoked when the @@ -90,7 +90,7 @@ String compile(String path, Logger logger, Iterable importers, Iterable loadPaths, - SyncPackageResolver packageResolver, + PackageConfig packageConfig, Iterable functions, OutputStyle style, void sourceMap(SingleMapping map), @@ -99,9 +99,7 @@ String compile(String path, var result = c.compile(path, logger: logger, importCache: ImportCache(importers, - logger: logger, - loadPaths: loadPaths, - packageResolver: packageResolver), + logger: logger, loadPaths: loadPaths, packageConfig: packageConfig), functions: functions, style: style, sourceMap: sourceMap != null, @@ -132,11 +130,11 @@ String compile(String path, /// * Each load path specified in the `SASS_PATH` environment variable, which /// should be semicolon-separated on Windows and colon-separated elsewhere. /// -/// * `package:` resolution using [packageResolver], which is a -/// [`SyncPackageResolver`][] from the `package_resolver` package. Note that +/// * `package:` resolution using [packageConfig], which is a +/// [`PackageConfig`][] from the `package_resolver` package. Note that /// this is a shorthand for adding a [PackageImporter] to [importers]. /// -/// [`SyncPackageResolver`]: https://www.dartdocs.org/documentation/package_resolver/latest/package_resolver/SyncPackageResolver-class.html +/// [`PackageConfig`]: https://pub.dev/documentation/package_config/latest/package_config.package_config/PackageConfig-class.html /// /// Dart functions that can be called from Sass may be passed using [functions]. /// Each [Callable] defines a top-level function that will be invoked when the @@ -178,7 +176,7 @@ String compileString(String source, bool color = false, Logger logger, Iterable importers, - SyncPackageResolver packageResolver, + PackageConfig packageConfig, Iterable loadPaths, Iterable functions, OutputStyle style, @@ -192,9 +190,7 @@ String compileString(String source, syntax: syntax ?? (indented ? Syntax.sass : Syntax.scss), logger: logger, importCache: ImportCache(importers, - logger: logger, - packageResolver: packageResolver, - loadPaths: loadPaths), + logger: logger, packageConfig: packageConfig, loadPaths: loadPaths), functions: functions, style: style, importer: importer, @@ -214,7 +210,7 @@ Future compileAsync(String path, {bool color = false, Logger logger, Iterable importers, - SyncPackageResolver packageResolver, + PackageConfig packageConfig, Iterable loadPaths, Iterable functions, OutputStyle style, @@ -223,9 +219,7 @@ Future compileAsync(String path, var result = await c.compileAsync(path, logger: logger, importCache: AsyncImportCache(importers, - logger: logger, - loadPaths: loadPaths, - packageResolver: packageResolver), + logger: logger, loadPaths: loadPaths, packageConfig: packageConfig), functions: functions, style: style, sourceMap: sourceMap != null); @@ -243,7 +237,7 @@ Future compileStringAsync(String source, bool color = false, Logger logger, Iterable importers, - SyncPackageResolver packageResolver, + PackageConfig packageConfig, Iterable loadPaths, Iterable functions, OutputStyle style, @@ -257,9 +251,7 @@ Future compileStringAsync(String source, syntax: syntax ?? (indented ? Syntax.sass : Syntax.scss), logger: logger, importCache: AsyncImportCache(importers, - logger: logger, - packageResolver: packageResolver, - loadPaths: loadPaths), + logger: logger, packageConfig: packageConfig, loadPaths: loadPaths), functions: functions, style: style, importer: importer, diff --git a/lib/src/async_compile.dart b/lib/src/async_compile.dart index b1996b646..64d4fe151 100644 --- a/lib/src/async_compile.dart +++ b/lib/src/async_compile.dart @@ -15,7 +15,6 @@ import 'importer.dart'; import 'importer/node.dart'; import 'io.dart'; import 'logger.dart'; -import 'sync_package_resolver.dart'; import 'syntax.dart'; import 'utils.dart'; import 'visitor/async_evaluate.dart'; @@ -77,7 +76,6 @@ Future compileStringAsync(String source, NodeImporter nodeImporter, Iterable importers, Iterable loadPaths, - SyncPackageResolver packageResolver, AsyncImporter importer, Iterable functions, OutputStyle style, diff --git a/lib/src/async_import_cache.dart b/lib/src/async_import_cache.dart index 58e5a1b6d..d26c685ad 100644 --- a/lib/src/async_import_cache.dart +++ b/lib/src/async_import_cache.dart @@ -3,6 +3,7 @@ // https://opensource.org/licenses/MIT. import 'package:collection/collection.dart'; +import 'package:package_config/package_config_types.dart'; import 'package:path/path.dart' as p; import 'package:tuple/tuple.dart'; @@ -11,7 +12,6 @@ import 'importer.dart'; import 'importer/utils.dart'; import 'io.dart'; import 'logger.dart'; -import 'sync_package_resolver.dart'; import 'utils.dart'; // ignore: unused_import /// An in-memory cache of parsed stylesheets that have been imported by Sass. @@ -52,16 +52,14 @@ class AsyncImportCache { /// * Each load path specified in the `SASS_PATH` environment variable, which /// should be semicolon-separated on Windows and colon-separated elsewhere. /// - /// * `package:` resolution using [packageResolver], which is a - /// [`SyncPackageResolver`][] from the `package_resolver` package. Note that + /// * `package:` resolution using [packageConfig], which is a + /// [`PackageConfig`][] from the `package_config` package. Note that /// this is a shorthand for adding a [PackageImporter] to [importers]. /// - /// [`SyncPackageResolver`]: https://www.dartdocs.org/documentation/package_resolver/latest/package_resolver/SyncPackageResolver-class.html + /// [`PackageConfig`]: https://pub.dev/documentation/package_config/latest/package_config.package_config/PackageConfig-class.html AsyncImportCache(Iterable importers, - {Iterable loadPaths, - SyncPackageResolver packageResolver, - Logger logger}) - : _importers = _toImporters(importers, loadPaths, packageResolver), + {Iterable loadPaths, PackageConfig packageConfig, Logger logger}) + : _importers = _toImporters(importers, loadPaths, packageConfig), _logger = logger ?? const Logger.stderr(), _canonicalizeCache = {}, _importCache = {}, @@ -75,10 +73,10 @@ class AsyncImportCache { _importCache = {}, _resultsCache = {}; - /// Converts the user's [importers], [loadPaths], and [packageResolver] + /// Converts the user's [importers], [loadPaths], and [packageConfig] /// options into a single list of importers. static List _toImporters(Iterable importers, - Iterable loadPaths, SyncPackageResolver packageResolver) { + Iterable loadPaths, PackageConfig packageConfig) { var sassPath = getEnvironmentVariable('SASS_PATH'); return [ ...?importers, @@ -87,7 +85,7 @@ class AsyncImportCache { if (sassPath != null) for (var path in sassPath.split(isWindows ? ';' : ':')) FilesystemImporter(path), - if (packageResolver != null) PackageImporter(packageResolver) + if (packageConfig != null) PackageImporter(packageConfig) ]; } diff --git a/lib/src/compile.dart b/lib/src/compile.dart index d2b495657..72e2655d9 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: b2cd6037efa37e300daa45ebed20cb4b61526161 +// Checksum: ef27d750f1d5305373fe9e712cf99697a21e0689 // // ignore_for_file: unused_import @@ -25,7 +25,6 @@ import 'importer.dart'; import 'importer/node.dart'; import 'io.dart'; import 'logger.dart'; -import 'sync_package_resolver.dart'; import 'syntax.dart'; import 'utils.dart'; import 'visitor/evaluate.dart'; @@ -87,7 +86,6 @@ CompileResult compileString(String source, NodeImporter nodeImporter, Iterable importers, Iterable loadPaths, - SyncPackageResolver packageResolver, Importer importer, Iterable functions, OutputStyle style, diff --git a/lib/src/import_cache.dart b/lib/src/import_cache.dart index 96c5d628a..57b726abf 100644 --- a/lib/src/import_cache.dart +++ b/lib/src/import_cache.dart @@ -5,11 +5,12 @@ // DO NOT EDIT. This file was generated from async_import_cache.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: 6ac1ee07d6b46134f1616d82782180f1cc3b6d81 +// Checksum: 31432610e32afefcc7adcda592b811ec50e9b47f // // ignore_for_file: unused_import import 'package:collection/collection.dart'; +import 'package:package_config/package_config_types.dart'; import 'package:path/path.dart' as p; import 'package:tuple/tuple.dart'; @@ -18,7 +19,6 @@ import 'importer.dart'; import 'importer/utils.dart'; import 'io.dart'; import 'logger.dart'; -import 'sync_package_resolver.dart'; import 'utils.dart'; // ignore: unused_import /// An in-memory cache of parsed stylesheets that have been imported by Sass. @@ -58,16 +58,14 @@ class ImportCache { /// * Each load path specified in the `SASS_PATH` environment variable, which /// should be semicolon-separated on Windows and colon-separated elsewhere. /// - /// * `package:` resolution using [packageResolver], which is a - /// [`SyncPackageResolver`][] from the `package_resolver` package. Note that + /// * `package:` resolution using [packageConfig], which is a + /// [`PackageConfig`][] from the `package_config` package. Note that /// this is a shorthand for adding a [PackageImporter] to [importers]. /// - /// [`SyncPackageResolver`]: https://www.dartdocs.org/documentation/package_resolver/latest/package_resolver/SyncPackageResolver-class.html + /// [`PackageConfig`]: https://pub.dev/documentation/package_config/latest/package_config.package_config/PackageConfig-class.html ImportCache(Iterable importers, - {Iterable loadPaths, - SyncPackageResolver packageResolver, - Logger logger}) - : _importers = _toImporters(importers, loadPaths, packageResolver), + {Iterable loadPaths, PackageConfig packageConfig, Logger logger}) + : _importers = _toImporters(importers, loadPaths, packageConfig), _logger = logger ?? const Logger.stderr(), _canonicalizeCache = {}, _importCache = {}, @@ -81,10 +79,10 @@ class ImportCache { _importCache = {}, _resultsCache = {}; - /// Converts the user's [importers], [loadPaths], and [packageResolver] + /// Converts the user's [importers], [loadPaths], and [packageConfig] /// options into a single list of importers. static List _toImporters(Iterable importers, - Iterable loadPaths, SyncPackageResolver packageResolver) { + Iterable loadPaths, PackageConfig packageConfig) { var sassPath = getEnvironmentVariable('SASS_PATH'); return [ ...?importers, @@ -93,7 +91,7 @@ class ImportCache { if (sassPath != null) for (var path in sassPath.split(isWindows ? ';' : ':')) FilesystemImporter(path), - if (packageResolver != null) PackageImporter(packageResolver) + if (packageConfig != null) PackageImporter(packageConfig) ]; } diff --git a/lib/src/importer/package.dart b/lib/src/importer/package.dart index 79eded476..770968750 100644 --- a/lib/src/importer/package.dart +++ b/lib/src/importer/package.dart @@ -3,9 +3,9 @@ // https://opensource.org/licenses/MIT. import 'package:meta/meta.dart'; +import 'package:package_config/package_config_types.dart'; import '../importer.dart'; -import '../sync_package_resolver.dart'; /// A filesystem importer to use when resolving the results of `package:` URLs. /// @@ -17,24 +17,24 @@ final _filesystemImporter = FilesystemImporter('.'); @sealed class PackageImporter extends Importer { /// The resolver that converts `package:` imports to `file:`. - final SyncPackageResolver _packageResolver; + final PackageConfig _packageConfig; /// Creates an importer that loads stylesheets from `package:` URLs according - /// to [packageResolver], which is a [SyncPackageResolver][] from the - /// `package_resolver` package. + /// to [packageConfig], which is a [PackageConfig][] from the `package_config` + /// package. /// - /// [SyncPackageResolver]: https://www.dartdocs.org/documentation/package_resolver/latest/package_resolver/SyncPackageResolver-class.html - PackageImporter(this._packageResolver); + /// [`PackageConfig`]: https://pub.dev/documentation/package_config/latest/package_config.package_config/PackageConfig-class.html + PackageImporter(this._packageConfig); Uri canonicalize(Uri url) { if (url.scheme == 'file') return _filesystemImporter.canonicalize(url); if (url.scheme != 'package') return null; - var resolved = _packageResolver.resolveUri(url); + var resolved = _packageConfig.resolve(url); if (resolved == null) throw "Unknown package."; if (resolved.scheme.isNotEmpty && resolved.scheme != 'file') { - throw "Unsupported URL ${resolved}."; + throw "Unsupported URL $resolved."; } return _filesystemImporter.canonicalize(resolved); diff --git a/lib/src/sync_package_resolver.dart b/lib/src/sync_package_resolver.dart deleted file mode 100644 index f61ae7be2..000000000 --- a/lib/src/sync_package_resolver.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'package:package_resolver/package_resolver.dart' - if (dart.library.js) 'sync_package_resolver/node.dart'; diff --git a/lib/src/sync_package_resolver/node.dart b/lib/src/sync_package_resolver/node.dart deleted file mode 100644 index c0984dced..000000000 --- a/lib/src/sync_package_resolver/node.dart +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -class SyncPackageResolver { - static final _error = - UnsupportedError('SyncPackageResolver is not supported in JS.'); - - static Future get current => throw _error; - - Uri resolveUri(Object packageUri) => throw _error; - - factory SyncPackageResolver.config(Map configMap) => - throw _error; -} diff --git a/pubspec.yaml b/pubspec.yaml index edd4de456..a76c7a9ef 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,37 +12,38 @@ environment: sdk: '>=2.6.0 <3.0.0' dependencies: - args: ">=1.4.0 <3.0.0" - async: ">=1.10.0 <3.0.0" - charcode: "^1.1.0" - cli_repl: ">=0.1.3 <0.3.0" - collection: "^1.8.0" - meta: "^1.1.7" - node_interop: "^1.1.0" - js: "^0.6.0" - package_resolver: "^1.0.0" - path: "^1.6.0" - source_maps: "^0.10.5" - source_span: "^1.6.0" - stack_trace: ">=0.9.0 <2.0.0" - stream_transform: ">=0.0.20 <3.0.0" - string_scanner: ">=0.1.5 <2.0.0" - term_glyph: "^1.0.0" - tuple: "^1.0.0" - watcher: ">=0.9.6 <2.0.0" + args: ^2.0.0 + async: ^2.5.0 + charcode: ^1.2.0 + cli_repl: ^0.2.1-nullsafety + collection: ^1.15.0 + meta: ^1.3.0 + node_interop: ^2.0.0 + js: ^0.6.3 + path: ^1.8.0 + source_maps: ^0.10.10 + source_span: ^1.8.1 + stack_trace: ^1.10.0 + stream_transform: ^2.0.0 + string_scanner: ^1.1.0 + term_glyph: ^1.2.0 + tuple: ^2.0.0-nullsafety.0 + watcher: ^1.0.0 + +publish_to: none dev_dependencies: - archive: ">=1.0.0 <3.0.0" - analyzer: "^0.40.0" - cli_pkg: "^1.1.0" - crypto: ">=0.9.2 <3.0.0" - dart_style: "^1.2.0" - grinder: "^0.8.0" - node_preamble: "^1.1.0" - pedantic: "^1.0.0" - pub_semver: "^1.0.0" - stream_channel: ">=1.0.0 <3.0.0" - test_descriptor: "^1.1.0" - test_process: "^1.0.0-rc.1" - test: ">=0.12.42 <2.0.0" - yaml: "^2.0.0" + archive: ^3.1.2 + analyzer: ^1.1.0 + cli_pkg: ^1.3.0 + crypto: ^3.0.0 + dart_style: ^2.0.0 + grinder: ^0.9.0-nullsafety.0 + node_preamble: ^2.0.0 + pedantic: ^1.11.0 + pub_semver: ^2.0.0 + stream_channel: ^2.1.0 + test_descriptor: ^2.0.0 + test_process: ^2.0.0 + test: ^1.16.7 + yaml: ^3.1.0 diff --git a/test/cli/dart_test.dart b/test/cli/dart_test.dart index 639d08865..5e1f654d2 100644 --- a/test/cli/dart_test.dart +++ b/test/cli/dart_test.dart @@ -4,6 +4,8 @@ @TestOn('vm') +import 'dart:convert'; + import 'package:cli_pkg/testing.dart' as pkg; import 'package:test/test.dart'; import 'package:test_descriptor/test_descriptor.dart' as d; @@ -30,4 +32,4 @@ void ensureSnapshotUpToDate() => pkg.ensureExecutableUpToDate("sass"); Future runSass(Iterable arguments, {Map environment}) => pkg.start("sass", arguments, - environment: environment, workingDirectory: d.sandbox); + environment: environment, workingDirectory: d.sandbox, encoding: utf8); diff --git a/test/cli/node_test.dart b/test/cli/node_test.dart index e27bb4fc5..7d1e4f751 100644 --- a/test/cli/node_test.dart +++ b/test/cli/node_test.dart @@ -5,6 +5,8 @@ @TestOn('vm') @Tags(['node']) +import 'dart:convert'; + import 'package:cli_pkg/testing.dart' as pkg; import 'package:test_process/test_process.dart'; import 'package:test_descriptor/test_descriptor.dart' as d; @@ -31,4 +33,7 @@ void main() { Future runSass(Iterable arguments, {Map environment}) => pkg.start("sass", arguments, - environment: environment, workingDirectory: d.sandbox, node: true); + environment: environment, + workingDirectory: d.sandbox, + encoding: utf8, + node: true); diff --git a/test/dart_api_test.dart b/test/dart_api_test.dart index 7d628b4f4..5ccb36961 100644 --- a/test/dart_api_test.dart +++ b/test/dart_api_test.dart @@ -4,7 +4,7 @@ @TestOn('vm') -import 'package:package_resolver/package_resolver.dart'; +import 'package:package_config/package_config.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'package:test_descriptor/test_descriptor.dart' as d; @@ -93,10 +93,10 @@ void main() { await d .file("test.scss", '@import "package:fake_package/test";') .create(); - var resolver = SyncPackageResolver.config( - {"fake_package": p.toUri(d.path('subdir'))}); + var config = + PackageConfig([Package('fake_package', p.toUri(d.path('subdir/')))]); - var css = compile(d.path("test.scss"), packageResolver: resolver); + var css = compile(d.path("test.scss"), packageConfig: config); expect(css, equals("a {\n b: 3;\n}")); }); @@ -109,10 +109,10 @@ void main() { await d .file("test.scss", '@import "package:fake_package/test";') .create(); - var resolver = SyncPackageResolver.config( - {"fake_package": p.toUri(d.path('subdir'))}); + var config = + PackageConfig([Package('fake_package', p.toUri(d.path('subdir/')))]); - var css = compile(d.path("test.scss"), packageResolver: resolver); + var css = compile(d.path("test.scss"), packageConfig: config); expect(css, equals("a {\n b: 3;\n}")); }); @@ -120,9 +120,9 @@ void main() { await d .file("test.scss", '@import "package:fake_package/test_aux";') .create(); - var resolver = SyncPackageResolver.config({}); - expect(() => compile(d.path("test.scss"), packageResolver: resolver), + expect( + () => compile(d.path("test.scss"), packageConfig: PackageConfig([])), throwsA(const TypeMatcher())); }); }); @@ -166,9 +166,9 @@ void main() { expect(css, equals("a {\n b: from-importer;\n}")); }); - test("importers take precedence over packageResolver", () async { + test("importers take precedence over packageConfig", () async { await d.dir("package", - [d.file("other.scss", "a {b: from-package-resolver}")]).create(); + [d.file("other.scss", "a {b: from-package-config}")]).create(); await d.dir( "importer", [d.file("other.scss", "a {b: from-importer}")]).create(); await d @@ -177,11 +177,11 @@ void main() { var css = compile(d.path("test.scss"), importers: [ - PackageImporter(SyncPackageResolver.config( - {"fake_package": p.toUri(d.path('importer'))})) + PackageImporter(PackageConfig( + [Package('fake_package', p.toUri(d.path('importer/')))])) ], - packageResolver: SyncPackageResolver.config( - {"fake_package": p.toUri(d.path('package'))})); + packageConfig: PackageConfig( + [Package('fake_package', p.toUri(d.path('package/')))])); expect(css, equals("a {\n b: from-importer;\n}")); }); }); diff --git a/test/double_check_test.dart b/test/double_check_test.dart index b18502cdb..23983a27f 100644 --- a/test/double_check_test.dart +++ b/test/double_check_test.dart @@ -44,7 +44,7 @@ void main() { var changelogVersion = firstLine.substring(3); var pubspec = loadYaml(File("pubspec.yaml").readAsStringSync(), - sourceUrl: "pubspec.yaml") as Map; + sourceUrl: Uri(path: "pubspec.yaml")) as Map; expect(pubspec, containsPair("version", isA())); var pubspecVersion = pubspec["version"] as String; From 3fa2b1d98adcece874078dc74dbc94bc14c8b908 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 9 Mar 2021 18:43:58 -0800 Subject: [PATCH 02/19] Fix hints and lints --- lib/src/ast/sass/argument_declaration.dart | 2 +- lib/src/ast/sass/expression/number.dart | 2 +- lib/src/ast/sass/statement/declaration.dart | 2 +- lib/src/ast/sass/supports_condition/operation.dart | 2 +- lib/src/async_import_cache.dart | 2 +- lib/src/import_cache.dart | 4 ++-- lib/src/node.dart | 2 +- lib/src/parse/parser.dart | 3 --- lib/src/value.dart | 2 +- lib/src/visitor/async_evaluate.dart | 2 +- lib/src/visitor/evaluate.dart | 2 +- 11 files changed, 11 insertions(+), 14 deletions(-) diff --git a/lib/src/ast/sass/argument_declaration.dart b/lib/src/ast/sass/argument_declaration.dart index 9f08ae07f..34aeba0f6 100644 --- a/lib/src/ast/sass/argument_declaration.dart +++ b/lib/src/ast/sass/argument_declaration.dart @@ -113,7 +113,7 @@ class ArgumentDeclaration implements SassNode { "Only ${arguments.length} " "${names.isEmpty ? '' : 'positional '}" "${pluralize('argument', arguments.length)} allowed, but " - "${positional} ${pluralize('was', positional, plural: 'were')} " + "$positional ${pluralize('was', positional, plural: 'were')} " "passed.", "invocation", {spanWithName: "declaration"}); diff --git a/lib/src/ast/sass/expression/number.dart b/lib/src/ast/sass/expression/number.dart index 7868ceff9..909655b8d 100644 --- a/lib/src/ast/sass/expression/number.dart +++ b/lib/src/ast/sass/expression/number.dart @@ -22,5 +22,5 @@ class NumberExpression implements Expression { T accept(ExpressionVisitor visitor) => visitor.visitNumberExpression(this); - String toString() => "${value}${unit ?? ''}"; + String toString() => "$value${unit ?? ''}"; } diff --git a/lib/src/ast/sass/statement/declaration.dart b/lib/src/ast/sass/statement/declaration.dart index 6d6240ece..9dfe1b5bd 100644 --- a/lib/src/ast/sass/statement/declaration.dart +++ b/lib/src/ast/sass/statement/declaration.dart @@ -36,7 +36,7 @@ class Declaration extends ParentStatement { if (isCustomProperty && value is! StringExpression) { throw ArgumentError( 'Declarations whose names begin with "--" must have StringExpression ' - 'values (was `${value}` of type ${value.runtimeType}).'); + 'values (was `$value` of type ${value.runtimeType}).'); } } diff --git a/lib/src/ast/sass/supports_condition/operation.dart b/lib/src/ast/sass/supports_condition/operation.dart index 0bebbc447..41b5c5218 100644 --- a/lib/src/ast/sass/supports_condition/operation.dart +++ b/lib/src/ast/sass/supports_condition/operation.dart @@ -31,7 +31,7 @@ class SupportsOperation implements SupportsCondition { } String toString() => - "${_parenthesize(left)} ${operator} ${_parenthesize(right)}"; + "${_parenthesize(left)} $operator ${_parenthesize(right)}"; String _parenthesize(SupportsCondition condition) => condition is SupportsNegation || diff --git a/lib/src/async_import_cache.dart b/lib/src/async_import_cache.dart index d26c685ad..d70beed16 100644 --- a/lib/src/async_import_cache.dart +++ b/lib/src/async_import_cache.dart @@ -12,7 +12,7 @@ import 'importer.dart'; import 'importer/utils.dart'; import 'io.dart'; import 'logger.dart'; -import 'utils.dart'; // ignore: unused_import +import 'utils.dart'; /// An in-memory cache of parsed stylesheets that have been imported by Sass. class AsyncImportCache { diff --git a/lib/src/import_cache.dart b/lib/src/import_cache.dart index 57b726abf..cf18d48e7 100644 --- a/lib/src/import_cache.dart +++ b/lib/src/import_cache.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_import_cache.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: 31432610e32afefcc7adcda592b811ec50e9b47f +// Checksum: 6cda9ea7b0ce46a5194d5179843daa83c16dfc05 // // ignore_for_file: unused_import @@ -19,7 +19,7 @@ import 'importer.dart'; import 'importer/utils.dart'; import 'io.dart'; import 'logger.dart'; -import 'utils.dart'; // ignore: unused_import +import 'utils.dart'; /// An in-memory cache of parsed stylesheets that have been imported by Sass. class ImportCache { diff --git a/lib/src/node.dart b/lib/src/node.dart index 065e5543b..ebb63aa37 100644 --- a/lib/src/node.dart +++ b/lib/src/node.dart @@ -197,7 +197,7 @@ List _parseFunctions(RenderOptions options, DateTime start, tuple = ScssParser(signature as String).parseSignature(); } on SassFormatException catch (error) { throw SassFormatException( - 'Invalid signature "${signature}": ${error.message}', error.span); + 'Invalid signature "$signature": ${error.message}', error.span); } var context = _contextWithOptions(options, start); diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index 1a8102cde..17c0a2441 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -445,7 +445,6 @@ class Parser { return ""; } else if (isNewline(first)) { scanner.error("Expected escape sequence."); - return null; } else if (isHex(first)) { for (var i = 0; i < 6; i++) { var next = scanner.peekChar(); @@ -465,7 +464,6 @@ class Parser { } on RangeError { scanner.error("Invalid Unicode code point.", position: start, length: scanner.position - start); - return ''; } } else if (value <= 0x1F || value == 0x7F || @@ -491,7 +489,6 @@ class Parser { return 0xFFFD; } else if (isNewline(first)) { scanner.error("Expected escape sequence."); - return 0; } else if (isHex(first)) { var value = 0; for (var i = 0; i < 6; i++) { diff --git a/lib/src/value.dart b/lib/src/value.dart index 1bd532caf..92cd7e67f 100644 --- a/lib/src/value.dart +++ b/lib/src/value.dart @@ -79,7 +79,7 @@ abstract class Value implements ext.Value { if (index == 0) throw _exception("List index may not be 0.", name); if (index.abs() > lengthAsList) { throw _exception( - "Invalid index $sassIndex for a list with ${lengthAsList} elements.", + "Invalid index $sassIndex for a list with $lengthAsList elements.", name); } diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index b62955c0f..a58b005ef 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -584,7 +584,7 @@ class _EvaluateVisitor } try { - await callback(module); + callback(module); } on SassRuntimeException { rethrow; } on MultiSpanSassException catch (error) { diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index aa2df383e..258e00d78 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: 0e9e7b6b99be25500cfd6469b0366190e92c43f4 +// Checksum: 53b9e481e4baa9aac2024f07ab79bfe79ac738ab // // ignore_for_file: unused_import From 5d0ad6957bbd2984e76ce799d7cbc336e574468a Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 9 Mar 2021 18:33:06 -0800 Subject: [PATCH 03/19] Split IfClause into ElseClause This allows us to be statically explicit about when the expression does or doesn't exist. --- lib/src/ast/sass/statement/if_rule.dart | 57 +++++++++++++----------- lib/src/parse/stylesheet.dart | 4 +- lib/src/visitor/async_evaluate.dart | 2 +- lib/src/visitor/evaluate.dart | 4 +- lib/src/visitor/recursive_statement.dart | 19 ++++---- 5 files changed, 47 insertions(+), 39 deletions(-) diff --git a/lib/src/ast/sass/statement/if_rule.dart b/lib/src/ast/sass/statement/if_rule.dart index 84050d58d..459b135e8 100644 --- a/lib/src/ast/sass/statement/if_rule.dart +++ b/lib/src/ast/sass/statement/if_rule.dart @@ -27,55 +27,62 @@ class IfRule implements Statement { /// The final, unconditional `@else` clause. /// /// This is `null` if there is no unconditional `@else`. - final IfClause lastClause; + final ElseClause lastClause; final FileSpan span; IfRule(Iterable clauses, this.span, {this.lastClause}) - : clauses = List.unmodifiable(clauses) { - assert(clauses.every((clause) => clause.expression != null)); - assert(lastClause?.expression == null); - } + : clauses = List.unmodifiable(clauses); T accept(StatementVisitor visitor) => visitor.visitIfRule(this); String toString() { var first = true; - return clauses.map((clause) { - var name = first ? 'if' : 'else'; - first = false; - return '@$name ${clause.expression} {${clause.children.join(" ")}}'; - }).join(' '); + var result = clauses + .map((clause) => + "@${first ? 'if' : 'else if'} {${clause.children.join(' ')}}") + .join(' '); + + var lastClause = this.lastClause; + if (lastClause != null) result += " $lastClause"; + return result; } } -/// A single clause in an `@if` rule. -class IfClause { - /// The expression to evaluate to determine whether to run this rule, or - /// `null` if this is the final unconditional `@else` clause. - final Expression expression; - +/// The superclass of `@if` and `@else` clauses. +abstract class IfRuleClause { /// The statements to evaluate if this clause matches. final List children; /// Whether any of [children] is a variable, function, or mixin declaration. final bool hasDeclarations; - IfClause(Expression expression, Iterable children) - : this._(expression, List.unmodifiable(children)); - - IfClause.last(Iterable children) - : this._(null, List.unmodifiable(children)); + IfRuleClause(Iterable children) + : this._(List.unmodifiable(children)); - IfClause._(this.expression, this.children) + IfRuleClause._(this.children) : hasDeclarations = children.any((child) => child is VariableDeclaration || child is FunctionRule || child is MixinRule || (child is ImportRule && child.imports.any((import) => import is DynamicImport))); +} + +/// An `@if` or `@else if` clause in an `@if` rule. +class IfClause extends IfRuleClause { + /// The expression to evaluate to determine whether to run this rule. + final Expression expression; + + IfClause(this.expression, Iterable children) + : super(children); + + String toString() => "@if $expression {${children.join(' ')}}"; +} + +/// An `@else` clause in an `@if` rule. +class ElseClause extends IfRuleClause { + ElseClause(Iterable children) : super(children); - String toString() => - (expression == null ? "@else" : "@if $expression") + - " {${children.join(' ')}}"; + String toString() => "@else {${children.join(' ')}}"; } diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index a0b1a8398..e4fea189d 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -1038,7 +1038,7 @@ abstract class StylesheetParser extends Parser { whitespaceWithoutComments(); var clauses = [IfClause(condition, children)]; - IfClause lastClause; + ElseClause lastClause; while (scanElse(ifIndentation)) { whitespace(); @@ -1046,7 +1046,7 @@ abstract class StylesheetParser extends Parser { whitespace(); clauses.add(IfClause(expression(), this.children(child))); } else { - lastClause = IfClause.last(this.children(child)); + lastClause = ElseClause(this.children(child)); break; } } diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index a58b005ef..97bfd566a 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -1326,7 +1326,7 @@ class _EvaluateVisitor } Future visitIfRule(IfRule node) async { - var clause = node.lastClause; + IfRuleClause clause = node.lastClause; for (var clauseToCheck in node.clauses) { if ((await clauseToCheck.expression.accept(this)).isTruthy) { clause = clauseToCheck; diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 258e00d78..91ae7ec6e 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: 53b9e481e4baa9aac2024f07ab79bfe79ac738ab +// Checksum: 919e6c70df46bb19149017bea4bac888da37c388 // // ignore_for_file: unused_import @@ -1324,7 +1324,7 @@ class _EvaluateVisitor } Value visitIfRule(IfRule node) { - var clause = node.lastClause; + IfRuleClause clause = node.lastClause; for (var clauseToCheck in node.clauses) { if (clauseToCheck.expression.accept(this).isTruthy) { clause = clauseToCheck; diff --git a/lib/src/visitor/recursive_statement.dart b/lib/src/visitor/recursive_statement.dart index ca09c8c2c..11264acbb 100644 --- a/lib/src/visitor/recursive_statement.dart +++ b/lib/src/visitor/recursive_statement.dart @@ -78,18 +78,19 @@ abstract class RecursiveStatementVisitor implements StatementVisitor { T visitIfRule(IfRule node) { for (var clause in node.clauses) { - _visitIfClause(clause); + visitExpression(clause.expression); + for (var child in clause.children) { + child.accept(this); + } } - if (node.lastClause != null) _visitIfClause(node.lastClause); - return null; - } - /// Visits [clause]'s expression and children. - void _visitIfClause(IfClause clause) { - if (clause.expression != null) visitExpression(clause.expression); - for (var child in clause.children) { - child.accept(this); + if (node.lastClause != null) { + for (var child in node.lastClause.children) { + child.accept(this); + } } + + return null; } T visitImportRule(ImportRule node) { From ca3c6f5ccc02dc83f3973e5ca3c4730d8ce66356 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Sat, 13 Mar 2021 00:47:17 -0800 Subject: [PATCH 04/19] Refactor how style rules interact with at-root This is more friendly to null-safe code, since we no longer rely on `_inStyleRule` implying that `_styleRule != null`. --- lib/src/visitor/async_evaluate.dart | 64 ++++++++++++++++------------ lib/src/visitor/evaluate.dart | 66 +++++++++++++++++------------ 2 files changed, 75 insertions(+), 55 deletions(-) diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 97bfd566a..c797075fc 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -156,7 +156,10 @@ class _EvaluateVisitor AsyncEnvironment _environment; /// The style rule that defines the current parent selector, if any. - ModifiableCssStyleRule _styleRule; + /// + /// This doesn't take into consideration any intermediate `@at-root` rules. In + /// the common case where those rules are relevant, use [_styleRule] instead. + ModifiableCssStyleRule _styleRuleIgnoringAtRoot; /// The current media queries, if any. List _mediaQueries; @@ -189,8 +192,8 @@ class _EvaluateVisitor /// Whether we're currently building the output of an unknown at rule. var _inUnknownAtRule = false; - /// Whether we're currently building the output of a style rule. - bool get _inStyleRule => _styleRule != null && !_atRootExcludingStyleRule; + ModifiableCssStyleRule get _styleRule => + _atRootExcludingStyleRule ? null : _styleRuleIgnoringAtRoot; /// Whether we're directly within an `@at-root` rule that excludes style /// rules. @@ -664,7 +667,7 @@ class _EvaluateVisitor _endOfImports = 0; _outOfOrderImports = null; _extender = extender; - _styleRule = null; + _styleRuleIgnoringAtRoot = null; _mediaQueries = null; _declarationName = null; _inUnknownAtRule = false; @@ -684,7 +687,7 @@ class _EvaluateVisitor _endOfImports = oldEndOfImports; _outOfOrderImports = oldOutOfOrderImports; _extender = oldExtender; - _styleRule = oldStyleRule; + _styleRuleIgnoringAtRoot = oldStyleRule; _mediaQueries = oldMediaQueries; _declarationName = oldDeclarationName; _inUnknownAtRule = oldInUnknownAtRule; @@ -1021,7 +1024,7 @@ class _EvaluateVisitor } Future visitDeclaration(Declaration node) async { - if (!_inStyleRule && !_inUnknownAtRule && !_inKeyframes) { + if (_styleRule == null && !_inUnknownAtRule && !_inKeyframes) { throw _exception( "Declarations may only be used within style rules.", node.span); } @@ -1101,7 +1104,8 @@ class _EvaluateVisitor } Future visitExtendRule(ExtendRule node) async { - if (!_inStyleRule || _declarationName != null) { + var styleRule = _styleRule; + if (styleRule == null || _declarationName != null) { throw _exception( "@extend may only be used within style rules.", node.span); } @@ -1135,7 +1139,7 @@ class _EvaluateVisitor } _extender.addExtension( - _styleRule.selector, compound.components.first, node, _mediaQueries); + styleRule.selector, compound.components.first, node, _mediaQueries); } return null; @@ -1173,7 +1177,8 @@ class _EvaluateVisitor await _withParent(ModifiableCssAtRule(name, node.span, value: value), () async { - if (!_inStyleRule || _inKeyframes) { + var styleRule = _styleRule; + if (styleRule == null || _inKeyframes) { for (var child in node.children) { await child.accept(this); } @@ -1182,7 +1187,7 @@ class _EvaluateVisitor // declarations immediately inside it have somewhere to go. // // For example, "a {@foo {b: c}}" should produce "@foo {a {b: c}}". - await _withParent(_styleRule.copyWithoutChildren(), () async { + await _withParent(styleRule.copyWithoutChildren(), () async { for (var child in node.children) { await child.accept(this); } @@ -1628,7 +1633,8 @@ class _EvaluateVisitor await _withParent( ModifiableCssMediaRule(mergedQueries ?? queries, node.span), () async { await _withMediaQueries(mergedQueries ?? queries, () async { - if (!_inStyleRule) { + var styleRule = _styleRule; + if (styleRule == null) { for (var child in node.children) { await child.accept(this); } @@ -1638,7 +1644,7 @@ class _EvaluateVisitor // // For example, "a {@media screen {b: c}}" should produce // "@media screen {a {b: c}}". - await _withParent(_styleRule.copyWithoutChildren(), () async { + await _withParent(styleRule.copyWithoutChildren(), () async { for (var child in node.children) { await child.accept(this); } @@ -1732,7 +1738,7 @@ class _EvaluateVisitor parsedSelector = _addExceptionSpan( node.selector, () => parsedSelector.resolveParentSelectors( - _styleRule?.originalSelector, + _styleRuleIgnoringAtRoot?.originalSelector, implicitParent: !_atRootExcludingStyleRule)); var selector = _extender.addSelector( @@ -1752,7 +1758,7 @@ class _EvaluateVisitor scopeWhen: node.hasDeclarations); _atRootExcludingStyleRule = oldAtRootExcludingStyleRule; - if (!_inStyleRule && _parent.children.isNotEmpty) { + if (_styleRule == null && _parent.children.isNotEmpty) { var lastChild = _parent.children.last; lastChild.isGroupEnd = true; } @@ -1774,7 +1780,8 @@ class _EvaluateVisitor await _visitSupportsCondition(node.condition), node.condition.span); await _withParent(ModifiableCssSupportsRule(condition, node.span), () async { - if (!_inStyleRule) { + var styleRule = _styleRule; + if (styleRule == null) { for (var child in node.children) { await child.accept(this); } @@ -1784,7 +1791,7 @@ class _EvaluateVisitor // // For example, "a {@supports (a: b) {b: c}}" should produce "@supports // (a: b) {a {b: c}}". - await _withParent(_styleRule.copyWithoutChildren(), () async { + await _withParent(styleRule.copyWithoutChildren(), () async { for (var child in node.children) { await child.accept(this); } @@ -2487,8 +2494,8 @@ class _EvaluateVisitor nodeWithSpan, () => arguments.verify(positional, MapKeySet(named))); Future visitSelectorExpression(SelectorExpression node) async { - if (_styleRule == null) return sassNull; - return _styleRule.originalSelector.asSassList; + if (_styleRuleIgnoringAtRoot == null) return sassNull; + return _styleRuleIgnoringAtRoot.originalSelector.asSassList; } Future visitStringExpression(StringExpression node) async { @@ -2620,7 +2627,8 @@ class _EvaluateVisitor ModifiableCssMediaRule(mergedQueries ?? node.queries, node.span), () async { await _withMediaQueries(mergedQueries ?? node.queries, () async { - if (!_inStyleRule) { + var styleRule = _styleRule; + if (styleRule == null) { for (var child in node.children) { await child.accept(this); } @@ -2630,7 +2638,7 @@ class _EvaluateVisitor // // For example, "a {@media screen {b: c}}" should produce // "@media screen {a {b: c}}". - await _withParent(_styleRule.copyWithoutChildren(), () async { + await _withParent(styleRule.copyWithoutChildren(), () async { for (var child in node.children) { await child.accept(this); } @@ -2653,8 +2661,9 @@ class _EvaluateVisitor "Style rules may not be used within nested declarations.", node.span); } + var styleRule = _styleRule; var originalSelector = node.selector.value.resolveParentSelectors( - _styleRule?.originalSelector, + styleRule?.originalSelector, implicitParent: !_atRootExcludingStyleRule); var selector = _extender.addSelector( originalSelector, node.selector.span, _mediaQueries); @@ -2671,7 +2680,7 @@ class _EvaluateVisitor }, through: (node) => node is CssStyleRule, scopeWhen: false); _atRootExcludingStyleRule = oldAtRootExcludingStyleRule; - if (!_inStyleRule && _parent.children.isNotEmpty) { + if (styleRule == null && _parent.children.isNotEmpty) { var lastChild = _parent.children.last; lastChild.isGroupEnd = true; } @@ -2695,7 +2704,8 @@ class _EvaluateVisitor await _withParent(ModifiableCssSupportsRule(node.condition, node.span), () async { - if (!_inStyleRule) { + var styleRule = _styleRule; + if (styleRule == null) { for (var child in node.children) { await child.accept(this); } @@ -2705,7 +2715,7 @@ class _EvaluateVisitor // // For example, "a {@supports (a: b) {b: c}}" should produce "@supports // (a: b) {a {b: c}}". - await _withParent(_styleRule.copyWithoutChildren(), () async { + await _withParent(styleRule.copyWithoutChildren(), () async { for (var child in node.children) { await child.accept(this); } @@ -2873,10 +2883,10 @@ class _EvaluateVisitor /// Runs [callback] with [rule] as the current style rule. Future _withStyleRule( ModifiableCssStyleRule rule, Future callback()) async { - var oldRule = _styleRule; - _styleRule = rule; + var oldRule = _styleRuleIgnoringAtRoot; + _styleRuleIgnoringAtRoot = rule; var result = await callback(); - _styleRule = oldRule; + _styleRuleIgnoringAtRoot = oldRule; return result; } diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 91ae7ec6e..30e28aea4 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: 919e6c70df46bb19149017bea4bac888da37c388 +// Checksum: e129cd22922df1f120a08d5a4ef022f6648ba0dd // // ignore_for_file: unused_import @@ -164,7 +164,10 @@ class _EvaluateVisitor Environment _environment; /// The style rule that defines the current parent selector, if any. - ModifiableCssStyleRule _styleRule; + /// + /// This doesn't take into consideration any intermediate `@at-root` rules. In + /// the common case where those rules are relevant, use [_styleRule] instead. + ModifiableCssStyleRule _styleRuleIgnoringAtRoot; /// The current media queries, if any. List _mediaQueries; @@ -197,8 +200,8 @@ class _EvaluateVisitor /// Whether we're currently building the output of an unknown at rule. var _inUnknownAtRule = false; - /// Whether we're currently building the output of a style rule. - bool get _inStyleRule => _styleRule != null && !_atRootExcludingStyleRule; + ModifiableCssStyleRule get _styleRule => + _atRootExcludingStyleRule ? null : _styleRuleIgnoringAtRoot; /// Whether we're directly within an `@at-root` rule that excludes style /// rules. @@ -667,7 +670,7 @@ class _EvaluateVisitor _endOfImports = 0; _outOfOrderImports = null; _extender = extender; - _styleRule = null; + _styleRuleIgnoringAtRoot = null; _mediaQueries = null; _declarationName = null; _inUnknownAtRule = false; @@ -687,7 +690,7 @@ class _EvaluateVisitor _endOfImports = oldEndOfImports; _outOfOrderImports = oldOutOfOrderImports; _extender = oldExtender; - _styleRule = oldStyleRule; + _styleRuleIgnoringAtRoot = oldStyleRule; _mediaQueries = oldMediaQueries; _declarationName = oldDeclarationName; _inUnknownAtRule = oldInUnknownAtRule; @@ -1023,7 +1026,7 @@ class _EvaluateVisitor } Value visitDeclaration(Declaration node) { - if (!_inStyleRule && !_inUnknownAtRule && !_inKeyframes) { + if (_styleRule == null && !_inUnknownAtRule && !_inKeyframes) { throw _exception( "Declarations may only be used within style rules.", node.span); } @@ -1102,7 +1105,8 @@ class _EvaluateVisitor } Value visitExtendRule(ExtendRule node) { - if (!_inStyleRule || _declarationName != null) { + var styleRule = _styleRule; + if (styleRule == null || _declarationName != null) { throw _exception( "@extend may only be used within style rules.", node.span); } @@ -1135,7 +1139,7 @@ class _EvaluateVisitor } _extender.addExtension( - _styleRule.selector, compound.components.first, node, _mediaQueries); + styleRule.selector, compound.components.first, node, _mediaQueries); } return null; @@ -1171,7 +1175,8 @@ class _EvaluateVisitor } _withParent(ModifiableCssAtRule(name, node.span, value: value), () { - if (!_inStyleRule || _inKeyframes) { + var styleRule = _styleRule; + if (styleRule == null || _inKeyframes) { for (var child in node.children) { child.accept(this); } @@ -1180,7 +1185,7 @@ class _EvaluateVisitor // declarations immediately inside it have somewhere to go. // // For example, "a {@foo {b: c}}" should produce "@foo {a {b: c}}". - _withParent(_styleRule.copyWithoutChildren(), () { + _withParent(styleRule.copyWithoutChildren(), () { for (var child in node.children) { child.accept(this); } @@ -1623,7 +1628,8 @@ class _EvaluateVisitor _withParent(ModifiableCssMediaRule(mergedQueries ?? queries, node.span), () { _withMediaQueries(mergedQueries ?? queries, () { - if (!_inStyleRule) { + var styleRule = _styleRule; + if (styleRule == null) { for (var child in node.children) { child.accept(this); } @@ -1633,7 +1639,7 @@ class _EvaluateVisitor // // For example, "a {@media screen {b: c}}" should produce // "@media screen {a {b: c}}". - _withParent(_styleRule.copyWithoutChildren(), () { + _withParent(styleRule.copyWithoutChildren(), () { for (var child in node.children) { child.accept(this); } @@ -1724,7 +1730,7 @@ class _EvaluateVisitor parsedSelector = _addExceptionSpan( node.selector, () => parsedSelector.resolveParentSelectors( - _styleRule?.originalSelector, + _styleRuleIgnoringAtRoot?.originalSelector, implicitParent: !_atRootExcludingStyleRule)); var selector = _extender.addSelector( @@ -1744,7 +1750,7 @@ class _EvaluateVisitor scopeWhen: node.hasDeclarations); _atRootExcludingStyleRule = oldAtRootExcludingStyleRule; - if (!_inStyleRule && _parent.children.isNotEmpty) { + if (_styleRule == null && _parent.children.isNotEmpty) { var lastChild = _parent.children.last; lastChild.isGroupEnd = true; } @@ -1765,7 +1771,8 @@ class _EvaluateVisitor var condition = CssValue(_visitSupportsCondition(node.condition), node.condition.span); _withParent(ModifiableCssSupportsRule(condition, node.span), () { - if (!_inStyleRule) { + var styleRule = _styleRule; + if (styleRule == null) { for (var child in node.children) { child.accept(this); } @@ -1775,7 +1782,7 @@ class _EvaluateVisitor // // For example, "a {@supports (a: b) {b: c}}" should produce "@supports // (a: b) {a {b: c}}". - _withParent(_styleRule.copyWithoutChildren(), () { + _withParent(styleRule.copyWithoutChildren(), () { for (var child in node.children) { child.accept(this); } @@ -2470,8 +2477,8 @@ class _EvaluateVisitor nodeWithSpan, () => arguments.verify(positional, MapKeySet(named))); Value visitSelectorExpression(SelectorExpression node) { - if (_styleRule == null) return sassNull; - return _styleRule.originalSelector.asSassList; + if (_styleRuleIgnoringAtRoot == null) return sassNull; + return _styleRuleIgnoringAtRoot.originalSelector.asSassList; } SassString visitStringExpression(StringExpression node) { @@ -2601,7 +2608,8 @@ class _EvaluateVisitor _withParent( ModifiableCssMediaRule(mergedQueries ?? node.queries, node.span), () { _withMediaQueries(mergedQueries ?? node.queries, () { - if (!_inStyleRule) { + var styleRule = _styleRule; + if (styleRule == null) { for (var child in node.children) { child.accept(this); } @@ -2611,7 +2619,7 @@ class _EvaluateVisitor // // For example, "a {@media screen {b: c}}" should produce // "@media screen {a {b: c}}". - _withParent(_styleRule.copyWithoutChildren(), () { + _withParent(styleRule.copyWithoutChildren(), () { for (var child in node.children) { child.accept(this); } @@ -2634,8 +2642,9 @@ class _EvaluateVisitor "Style rules may not be used within nested declarations.", node.span); } + var styleRule = _styleRule; var originalSelector = node.selector.value.resolveParentSelectors( - _styleRule?.originalSelector, + styleRule?.originalSelector, implicitParent: !_atRootExcludingStyleRule); var selector = _extender.addSelector( originalSelector, node.selector.span, _mediaQueries); @@ -2652,7 +2661,7 @@ class _EvaluateVisitor }, through: (node) => node is CssStyleRule, scopeWhen: false); _atRootExcludingStyleRule = oldAtRootExcludingStyleRule; - if (!_inStyleRule && _parent.children.isNotEmpty) { + if (styleRule == null && _parent.children.isNotEmpty) { var lastChild = _parent.children.last; lastChild.isGroupEnd = true; } @@ -2675,7 +2684,8 @@ class _EvaluateVisitor } _withParent(ModifiableCssSupportsRule(node.condition, node.span), () { - if (!_inStyleRule) { + var styleRule = _styleRule; + if (styleRule == null) { for (var child in node.children) { child.accept(this); } @@ -2685,7 +2695,7 @@ class _EvaluateVisitor // // For example, "a {@supports (a: b) {b: c}}" should produce "@supports // (a: b) {a {b: c}}". - _withParent(_styleRule.copyWithoutChildren(), () { + _withParent(styleRule.copyWithoutChildren(), () { for (var child in node.children) { child.accept(this); } @@ -2847,10 +2857,10 @@ class _EvaluateVisitor /// Runs [callback] with [rule] as the current style rule. T _withStyleRule(ModifiableCssStyleRule rule, T callback()) { - var oldRule = _styleRule; - _styleRule = rule; + var oldRule = _styleRuleIgnoringAtRoot; + _styleRuleIgnoringAtRoot = rule; var result = callback(); - _styleRule = oldRule; + _styleRuleIgnoringAtRoot = oldRule; return result; } From 21e7ec19dcd5dbc655f2fdbb14f4971948286619 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Sat, 13 Mar 2021 01:31:19 -0800 Subject: [PATCH 05/19] Split Declaration's constructor into two This doesn't explicitly help null-safety, but it makes the relationship between `Declaration.value` and `Declaration.children` more obvious. --- lib/src/ast/sass/statement/declaration.dart | 21 ++++++++++++++++++--- lib/src/parse/stylesheet.dart | 16 ++++++++-------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/lib/src/ast/sass/statement/declaration.dart b/lib/src/ast/sass/statement/declaration.dart index 9dfe1b5bd..172218b5b 100644 --- a/lib/src/ast/sass/statement/declaration.dart +++ b/lib/src/ast/sass/statement/declaration.dart @@ -17,6 +17,9 @@ class Declaration extends ParentStatement { final Interpolation name; /// The value of this declaration. + /// + /// If [children] is `null`, this is never `null`. Otherwise, it may or may + /// not be `null`. final Expression value; final FileSpan span; @@ -30,9 +33,9 @@ class Declaration extends ParentStatement { /// If this is `true`, then `value` will be a [StringExpression]. bool get isCustomProperty => name.initialPlain.startsWith('--'); - Declaration(this.name, this.span, {this.value, Iterable children}) - : super( - children = children == null ? null : List.unmodifiable(children)) { + Declaration(this.name, Expression /*!*/ value, this.span) + : value = value, + super(null) { if (isCustomProperty && value is! StringExpression) { throw ArgumentError( 'Declarations whose names begin with "--" must have StringExpression ' @@ -40,5 +43,17 @@ class Declaration extends ParentStatement { } } + /// Creates a declaration with children. + /// + /// For these declaraions, a value is optional. + Declaration.nested(this.name, Iterable children, this.span, + {this.value}) + : super(List.unmodifiable(children)) { + if (isCustomProperty && value is! StringExpression) { + throw ArgumentError( + 'Declarations whose names begin with "--" may not be nested.'); + } + } + T accept(StatementVisitor visitor) => visitor.visitDeclaration(this); } diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index e4fea189d..f279f4570 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -373,7 +373,7 @@ abstract class StylesheetParser extends Parser { if (name.initialPlain.startsWith('--')) { var value = StringExpression(_interpolatedDeclarationValue()); expectStatementSeparator("custom property"); - return Declaration(name, scanner.spanFrom(start), value: value); + return Declaration(name, value, scanner.spanFrom(start)); } if (scanner.scanChar($colon)) { @@ -389,7 +389,7 @@ abstract class StylesheetParser extends Parser { var postColonWhitespace = rawText(whitespace); if (lookingAtChildren()) { return _withChildren(_declarationChild, start, - (children, span) => Declaration(name, span, children: children)); + (children, span) => Declaration.nested(name, children, span)); } midBuffer.write(postColonWhitespace); @@ -433,10 +433,10 @@ abstract class StylesheetParser extends Parser { _declarationChild, start, (children, span) => - Declaration(name, span, value: value, children: children)); + Declaration.nested(name, children, span, value: value)); } else { expectStatementSeparator(); - return Declaration(name, scanner.spanFrom(start), value: value); + return Declaration(name, value, scanner.spanFrom(start)); } } @@ -539,7 +539,7 @@ abstract class StylesheetParser extends Parser { if (parseCustomProperties && name.initialPlain.startsWith('--')) { var value = StringExpression(_interpolatedDeclarationValue()); expectStatementSeparator("custom property"); - return Declaration(name, scanner.spanFrom(start), value: value); + return Declaration(name, value, scanner.spanFrom(start)); } whitespace(); @@ -549,7 +549,7 @@ abstract class StylesheetParser extends Parser { scanner.error("Nested declarations aren't allowed in plain CSS."); } return _withChildren(_declarationChild, start, - (children, span) => Declaration(name, span, children: children)); + (children, span) => Declaration.nested(name, children, span)); } var value = expression(); @@ -561,10 +561,10 @@ abstract class StylesheetParser extends Parser { _declarationChild, start, (children, span) => - Declaration(name, span, value: value, children: children)); + Declaration.nested(name, children, span, value: value)); } else { expectStatementSeparator(); - return Declaration(name, scanner.spanFrom(start), value: value); + return Declaration(name, value, scanner.spanFrom(start)); } } From 984e76ef77d41ec357ec28651e6804aac3aae5ee Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 15 Mar 2021 17:56:36 -0700 Subject: [PATCH 06/19] Remove type parameters from recursive AST visitors --- lib/src/visitor/find_dependencies.dart | 2 +- lib/src/visitor/recursive_ast.dart | 52 +++++-------- lib/src/visitor/recursive_statement.dart | 98 ++++++++++-------------- 3 files changed, 64 insertions(+), 88 deletions(-) diff --git a/lib/src/visitor/find_dependencies.dart b/lib/src/visitor/find_dependencies.dart index d2963ec50..80a876fa5 100644 --- a/lib/src/visitor/find_dependencies.dart +++ b/lib/src/visitor/find_dependencies.dart @@ -16,7 +16,7 @@ Tuple2, List> findDependencies(Stylesheet stylesheet) => /// A visitor that traverses a stylesheet and records, all `@import`, `@use`, /// and `@forward` rules it contains. -class _FindDependenciesVisitor extends RecursiveStatementVisitor { +class _FindDependenciesVisitor extends RecursiveStatementVisitor { final _usesAndForwards = []; final _imports = []; diff --git a/lib/src/visitor/recursive_ast.dart b/lib/src/visitor/recursive_ast.dart index 3900253ad..018d70fce 100644 --- a/lib/src/visitor/recursive_ast.dart +++ b/lib/src/visitor/recursive_ast.dart @@ -11,82 +11,72 @@ import 'recursive_statement.dart'; /// /// This extends [RecursiveStatementVisitor] to traverse each expression in /// addition to each statement. -/// -/// The default implementation of the visit methods all return `null`. -abstract class RecursiveAstVisitor extends RecursiveStatementVisitor - implements ExpressionVisitor { +abstract class RecursiveAstVisitor extends RecursiveStatementVisitor + implements ExpressionVisitor { void visitExpression(Expression expression) { expression.accept(this); } - T visitBinaryOperationExpression(BinaryOperationExpression node) { + void visitBinaryOperationExpression(BinaryOperationExpression node) { node.left.accept(this); node.right.accept(this); - return null; } - T visitBooleanExpression(BooleanExpression node) => null; + void visitBooleanExpression(BooleanExpression node) {} - T visitColorExpression(ColorExpression node) => null; + void visitColorExpression(ColorExpression node) {} - T visitForwardRule(ForwardRule node) { + void visitForwardRule(ForwardRule node) { for (var variable in node.configuration) { variable.expression.accept(this); } - return null; } - T visitFunctionExpression(FunctionExpression node) { + void visitFunctionExpression(FunctionExpression node) { visitInterpolation(node.name); visitArgumentInvocation(node.arguments); - return null; } - T visitIfExpression(IfExpression node) { + void visitIfExpression(IfExpression node) { visitArgumentInvocation(node.arguments); - return null; } - T visitListExpression(ListExpression node) { + void visitListExpression(ListExpression node) { for (var item in node.contents) { item.accept(this); } - return null; } - T visitMapExpression(MapExpression node) { + void visitMapExpression(MapExpression node) { for (var pair in node.pairs) { pair.item1.accept(this); pair.item2.accept(this); } - return null; } - T visitNullExpression(NullExpression node) => null; + void visitNullExpression(NullExpression node) {} - T visitNumberExpression(NumberExpression node) => null; + void visitNumberExpression(NumberExpression node) {} - T visitParenthesizedExpression(ParenthesizedExpression node) => - node.expression.accept(this); + void visitParenthesizedExpression(ParenthesizedExpression node) {} - T visitSelectorExpression(SelectorExpression node) => null; + void visitSelectorExpression(SelectorExpression node) {} - T visitStringExpression(StringExpression node) { + void visitStringExpression(StringExpression node) { visitInterpolation(node.text); - return null; } - T visitUnaryOperationExpression(UnaryOperationExpression node) => - node.operand.accept(this); + void visitUnaryOperationExpression(UnaryOperationExpression node) { + node.operand.accept(this); + } - T visitUseRule(UseRule node) { + void visitUseRule(UseRule node) { for (var variable in node.configuration) { variable.expression.accept(this); } - return null; } - T visitValueExpression(ValueExpression node) => null; + void visitValueExpression(ValueExpression node) {} - T visitVariableExpression(VariableExpression node) => null; + void visitVariableExpression(VariableExpression node) {} } diff --git a/lib/src/visitor/recursive_statement.dart b/lib/src/visitor/recursive_statement.dart index 11264acbb..caddfad91 100644 --- a/lib/src/visitor/recursive_statement.dart +++ b/lib/src/visitor/recursive_statement.dart @@ -19,64 +19,58 @@ import 'interface/statement.dart'; /// * [visitChildren] /// * [visitInterpolation] /// * [visitExpression] -/// -/// The default implementation of the visit methods all return `null`. -abstract class RecursiveStatementVisitor implements StatementVisitor { - T visitAtRootRule(AtRootRule node) { +abstract class RecursiveStatementVisitor implements StatementVisitor { + void visitAtRootRule(AtRootRule node) { if (node.query != null) visitInterpolation(node.query); - return visitChildren(node); + visitChildren(node); } - T visitAtRule(AtRule node) { + void visitAtRule(AtRule node) { visitInterpolation(node.name); if (node.value != null) visitInterpolation(node.value); - return node.children == null ? null : visitChildren(node); + if (node.children != null) visitChildren(node); } - T visitContentBlock(ContentBlock node) => visitCallableDeclaration(node); + void visitContentBlock(ContentBlock node) => visitCallableDeclaration(node); - T visitContentRule(ContentRule node) { + void visitContentRule(ContentRule node) { visitArgumentInvocation(node.arguments); - return null; } - T visitDebugRule(DebugRule node) { + void visitDebugRule(DebugRule node) { visitExpression(node.expression); - return null; } - T visitDeclaration(Declaration node) { + void visitDeclaration(Declaration node) { visitInterpolation(node.name); if (node.value != null) visitExpression(node.value); - return node.children == null ? null : visitChildren(node); + if (node.children != null) visitChildren(node); } - T visitEachRule(EachRule node) { + void visitEachRule(EachRule node) { visitExpression(node.list); - return visitChildren(node); + visitChildren(node); } - T visitErrorRule(ErrorRule node) { + void visitErrorRule(ErrorRule node) { visitExpression(node.expression); - return null; } - T visitExtendRule(ExtendRule node) { + void visitExtendRule(ExtendRule node) { visitInterpolation(node.selector); - return null; } - T visitForRule(ForRule node) { + void visitForRule(ForRule node) { visitExpression(node.from); visitExpression(node.to); - return visitChildren(node); + visitChildren(node); } - T visitForwardRule(ForwardRule node) => null; + void visitForwardRule(ForwardRule node) {} - T visitFunctionRule(FunctionRule node) => visitCallableDeclaration(node); + void visitFunctionRule(FunctionRule node) => visitCallableDeclaration(node); - T visitIfRule(IfRule node) { + void visitIfRule(IfRule node) { for (var clause in node.clauses) { visitExpression(clause.expression); for (var child in clause.children) { @@ -89,11 +83,9 @@ abstract class RecursiveStatementVisitor implements StatementVisitor { child.accept(this); } } - - return null; } - T visitImportRule(ImportRule node) { + void visitImportRule(ImportRule node) { for (var import in node.imports) { if (import is StaticImport) { visitInterpolation(import.url); @@ -101,60 +93,55 @@ abstract class RecursiveStatementVisitor implements StatementVisitor { if (import.media != null) visitInterpolation(import.media); } } - return null; } - T visitIncludeRule(IncludeRule node) { + void visitIncludeRule(IncludeRule node) { visitArgumentInvocation(node.arguments); - return node.content == null ? null : visitContentBlock(node.content); + if (node.content != null) visitContentBlock(node.content); } - T visitLoudComment(LoudComment node) { + void visitLoudComment(LoudComment node) { visitInterpolation(node.text); - return null; } - T visitMediaRule(MediaRule node) { + void visitMediaRule(MediaRule node) { visitInterpolation(node.query); - return visitChildren(node); + visitChildren(node); } - T visitMixinRule(MixinRule node) => visitCallableDeclaration(node); + void visitMixinRule(MixinRule node) => visitCallableDeclaration(node); - T visitReturnRule(ReturnRule node) { + void visitReturnRule(ReturnRule node) { visitExpression(node.expression); - return null; } - T visitSilentComment(SilentComment node) => null; + void visitSilentComment(SilentComment node) {} - T visitStyleRule(StyleRule node) { + void visitStyleRule(StyleRule node) { visitInterpolation(node.selector); - return visitChildren(node); + visitChildren(node); } - T visitStylesheet(Stylesheet node) => visitChildren(node); + void visitStylesheet(Stylesheet node) => visitChildren(node); - T visitSupportsRule(SupportsRule node) { + void visitSupportsRule(SupportsRule node) { visitSupportsCondition(node.condition); - return visitChildren(node); + visitChildren(node); } - T visitUseRule(UseRule node) => null; + void visitUseRule(UseRule node) {} - T visitVariableDeclaration(VariableDeclaration node) { + void visitVariableDeclaration(VariableDeclaration node) { visitExpression(node.expression); - return null; } - T visitWarnRule(WarnRule node) { + void visitWarnRule(WarnRule node) { visitExpression(node.expression); - return null; } - T visitWhileRule(WhileRule node) { + void visitWhileRule(WhileRule node) { visitExpression(node.condition); - return visitChildren(node); + visitChildren(node); } /// Visits each of [node]'s expressions and children. @@ -162,11 +149,11 @@ abstract class RecursiveStatementVisitor implements StatementVisitor { /// The default implementations of [visitFunctionRule] and [visitMixinRule] /// call this. @protected - T visitCallableDeclaration(CallableDeclaration node) { + void visitCallableDeclaration(CallableDeclaration node) { for (var argument in node.arguments.arguments) { if (argument.defaultValue != null) visitExpression(argument.defaultValue); } - return visitChildren(node); + visitChildren(node); } /// Visits each expression in an [invocation]. @@ -211,13 +198,12 @@ abstract class RecursiveStatementVisitor implements StatementVisitor { /// Visits each of [node]'s children. /// /// The default implementation of the visit methods for all [ParentStatement]s - /// call this and return its result. + /// call this. @protected - T visitChildren(ParentStatement node) { + void visitChildren(ParentStatement node) { for (var child in node.children) { child.accept(this); } - return null; } /// Visits each expression in an [interpolation]. From 8fdb281e832c247bc71979d1f13b868b06da7aac Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 16 Mar 2021 15:22:00 -0700 Subject: [PATCH 07/19] Make the first parameter to ImportCache() named, not mandatory It was always valid to pass in `null`, this just makes it more clear. --- bin/sass.dart | 2 +- lib/sass.dart | 28 +++++++++++++++------- lib/src/async_import_cache.dart | 7 ++++-- lib/src/executable/compile_stylesheet.dart | 2 +- lib/src/executable/repl.dart | 3 +-- lib/src/import_cache.dart | 9 ++++--- 6 files changed, 34 insertions(+), 17 deletions(-) diff --git a/bin/sass.dart b/bin/sass.dart index d74f39c6c..16e322efd 100644 --- a/bin/sass.dart +++ b/bin/sass.dart @@ -52,7 +52,7 @@ Future main(List args) async { } var graph = StylesheetGraph( - ImportCache([], loadPaths: options.loadPaths, logger: options.logger)); + ImportCache(loadPaths: options.loadPaths, logger: options.logger)); if (options.watch) { await watch(options, graph); return; diff --git a/lib/sass.dart b/lib/sass.dart index 855f20f74..ff142d06e 100644 --- a/lib/sass.dart +++ b/lib/sass.dart @@ -98,8 +98,11 @@ String compile(String path, logger ??= Logger.stderr(color: color); var result = c.compile(path, logger: logger, - importCache: ImportCache(importers, - logger: logger, loadPaths: loadPaths, packageConfig: packageConfig), + importCache: ImportCache( + importers: importers, + logger: logger, + loadPaths: loadPaths, + packageConfig: packageConfig), functions: functions, style: style, sourceMap: sourceMap != null, @@ -189,8 +192,11 @@ String compileString(String source, var result = c.compileString(source, syntax: syntax ?? (indented ? Syntax.sass : Syntax.scss), logger: logger, - importCache: ImportCache(importers, - logger: logger, packageConfig: packageConfig, loadPaths: loadPaths), + importCache: ImportCache( + importers: importers, + logger: logger, + packageConfig: packageConfig, + loadPaths: loadPaths), functions: functions, style: style, importer: importer, @@ -218,8 +224,11 @@ Future compileAsync(String path, logger ??= Logger.stderr(color: color); var result = await c.compileAsync(path, logger: logger, - importCache: AsyncImportCache(importers, - logger: logger, loadPaths: loadPaths, packageConfig: packageConfig), + importCache: AsyncImportCache( + importers: importers, + logger: logger, + loadPaths: loadPaths, + packageConfig: packageConfig), functions: functions, style: style, sourceMap: sourceMap != null); @@ -250,8 +259,11 @@ Future compileStringAsync(String source, var result = await c.compileStringAsync(source, syntax: syntax ?? (indented ? Syntax.sass : Syntax.scss), logger: logger, - importCache: AsyncImportCache(importers, - logger: logger, packageConfig: packageConfig, loadPaths: loadPaths), + importCache: AsyncImportCache( + importers: importers, + logger: logger, + packageConfig: packageConfig, + loadPaths: loadPaths), functions: functions, style: style, importer: importer, diff --git a/lib/src/async_import_cache.dart b/lib/src/async_import_cache.dart index d70beed16..70b869d3a 100644 --- a/lib/src/async_import_cache.dart +++ b/lib/src/async_import_cache.dart @@ -57,8 +57,11 @@ class AsyncImportCache { /// this is a shorthand for adding a [PackageImporter] to [importers]. /// /// [`PackageConfig`]: https://pub.dev/documentation/package_config/latest/package_config.package_config/PackageConfig-class.html - AsyncImportCache(Iterable importers, - {Iterable loadPaths, PackageConfig packageConfig, Logger logger}) + AsyncImportCache( + {Iterable importers, + Iterable loadPaths, + PackageConfig packageConfig, + Logger logger}) : _importers = _toImporters(importers, loadPaths, packageConfig), _logger = logger ?? const Logger.stderr(), _canonicalizeCache = {}, diff --git a/lib/src/executable/compile_stylesheet.dart b/lib/src/executable/compile_stylesheet.dart index 6e799118f..fe1e37df9 100644 --- a/lib/src/executable/compile_stylesheet.dart +++ b/lib/src/executable/compile_stylesheet.dart @@ -58,7 +58,7 @@ Future compileStylesheet(ExecutableOptions options, StylesheetGraph graph, CompileResult result; try { if (options.asynchronous) { - var importCache = AsyncImportCache([], + var importCache = AsyncImportCache( loadPaths: options.loadPaths, logger: options.logger); result = source == null diff --git a/lib/src/executable/repl.dart b/lib/src/executable/repl.dart index cbf6097d3..eb3e4fca4 100644 --- a/lib/src/executable/repl.dart +++ b/lib/src/executable/repl.dart @@ -22,8 +22,7 @@ Future repl(ExecutableOptions options) async { var logger = TrackingLogger(options.logger); var evaluator = Evaluator( importer: FilesystemImporter('.'), - importCache: - ImportCache(const [], loadPaths: options.loadPaths, logger: logger), + importCache: ImportCache(loadPaths: options.loadPaths, logger: logger), logger: logger); await for (String line in repl.runAsync()) { if (line.trim().isEmpty) continue; diff --git a/lib/src/import_cache.dart b/lib/src/import_cache.dart index cf18d48e7..908369674 100644 --- a/lib/src/import_cache.dart +++ b/lib/src/import_cache.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_import_cache.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: 6cda9ea7b0ce46a5194d5179843daa83c16dfc05 +// Checksum: 181b14d1635a824cb4387d7b5ce70bbf5a30c7df // // ignore_for_file: unused_import @@ -63,8 +63,11 @@ class ImportCache { /// this is a shorthand for adding a [PackageImporter] to [importers]. /// /// [`PackageConfig`]: https://pub.dev/documentation/package_config/latest/package_config.package_config/PackageConfig-class.html - ImportCache(Iterable importers, - {Iterable loadPaths, PackageConfig packageConfig, Logger logger}) + ImportCache( + {Iterable importers, + Iterable loadPaths, + PackageConfig packageConfig, + Logger logger}) : _importers = _toImporters(importers, loadPaths, packageConfig), _logger = logger ?? const Logger.stderr(), _canonicalizeCache = {}, From dfbdae0d71911a518d04eeb38c90aad0857995ff Mon Sep 17 00:00:00 2001 From: Jennifer Thakar Date: Tue, 9 Mar 2021 17:04:09 -0800 Subject: [PATCH 08/19] Prepare for null-safety migration --- bin/sass.dart | 14 +- lib/sass.dart | 13 +- lib/src/ast/css/declaration.dart | 2 +- lib/src/ast/css/modifiable/node.dart | 16 +- lib/src/ast/css/modifiable/supports_rule.dart | 2 +- lib/src/ast/css/node.dart | 2 +- lib/src/ast/css/stylesheet.dart | 4 +- lib/src/ast/css/value.dart | 4 +- lib/src/ast/sass/argument.dart | 7 +- lib/src/ast/sass/argument_declaration.dart | 12 +- lib/src/ast/sass/argument_invocation.dart | 6 +- lib/src/ast/sass/at_root_query.dart | 2 +- lib/src/ast/sass/configured_variable.dart | 2 +- lib/src/ast/sass/expression.dart | 2 +- .../ast/sass/expression/binary_operation.dart | 4 +- lib/src/ast/sass/expression/color.dart | 2 +- lib/src/ast/sass/expression/list.dart | 2 +- lib/src/ast/sass/expression/map.dart | 3 +- .../ast/sass/expression/parenthesized.dart | 2 +- lib/src/ast/sass/expression/string.dart | 8 +- .../ast/sass/expression/unary_operation.dart | 4 +- lib/src/ast/sass/expression/value.dart | 2 +- lib/src/ast/sass/interpolation.dart | 5 +- lib/src/ast/sass/statement.dart | 2 +- lib/src/ast/sass/statement/at_root_rule.dart | 4 +- lib/src/ast/sass/statement/at_rule.dart | 3 +- .../sass/statement/callable_declaration.dart | 9 +- lib/src/ast/sass/statement/content_block.dart | 6 +- lib/src/ast/sass/statement/debug_rule.dart | 2 +- lib/src/ast/sass/statement/each_rule.dart | 8 +- lib/src/ast/sass/statement/error_rule.dart | 2 +- lib/src/ast/sass/statement/for_rule.dart | 12 +- lib/src/ast/sass/statement/forward_rule.dart | 13 +- lib/src/ast/sass/statement/function_rule.dart | 2 +- lib/src/ast/sass/statement/if_rule.dart | 2 +- lib/src/ast/sass/statement/media_rule.dart | 4 +- lib/src/ast/sass/statement/mixin_rule.dart | 4 +- lib/src/ast/sass/statement/parent.dart | 10 +- lib/src/ast/sass/statement/return_rule.dart | 2 +- lib/src/ast/sass/statement/style_rule.dart | 4 +- lib/src/ast/sass/statement/stylesheet.dart | 9 +- lib/src/ast/sass/statement/supports_rule.dart | 4 +- lib/src/ast/sass/statement/use_rule.dart | 2 +- .../sass/statement/variable_declaration.dart | 2 +- lib/src/ast/sass/statement/warn_rule.dart | 2 +- lib/src/ast/sass/statement/while_rule.dart | 8 +- .../sass/supports_condition/declaration.dart | 4 +- lib/src/ast/selector/complex.dart | 15 +- lib/src/ast/selector/compound.dart | 12 +- lib/src/ast/selector/list.dart | 34 +- lib/src/ast/selector/pseudo.dart | 22 +- lib/src/ast/selector/simple.dart | 4 +- lib/src/async_compile.dart | 8 +- lib/src/async_environment.dart | 144 +++--- lib/src/async_import_cache.dart | 14 +- lib/src/callable.dart | 10 +- lib/src/callable/async.dart | 2 + lib/src/callable/async_built_in.dart | 7 +- lib/src/callable/built_in.dart | 24 +- lib/src/callable/plain_css.dart | 2 +- lib/src/callable/user_defined.dart | 4 +- lib/src/compile.dart | 10 +- lib/src/configuration.dart | 87 ++-- lib/src/configured_value.dart | 9 +- lib/src/environment.dart | 145 +++--- lib/src/exception.dart | 42 +- lib/src/executable/compile_stylesheet.dart | 2 + lib/src/executable/options.dart | 133 ++++-- lib/src/executable/watch.dart | 56 ++- lib/src/extend/empty_extender.dart | 2 +- lib/src/extend/extender.dart | 44 +- lib/src/extend/functions.dart | 4 +- lib/src/functions/color.dart | 40 +- lib/src/functions/list.dart | 1 + lib/src/functions/map.dart | 8 +- lib/src/functions/math.dart | 19 +- lib/src/functions/meta.dart | 1 + lib/src/functions/selector.dart | 1 + lib/src/functions/string.dart | 1 + lib/src/import_cache.dart | 17 +- lib/src/importer.dart | 4 +- lib/src/importer/async.dart | 4 +- lib/src/importer/filesystem.dart | 10 +- lib/src/importer/no_op.dart | 4 +- lib/src/importer/node/implementation.dart | 71 +-- lib/src/importer/node/interface.dart | 11 +- lib/src/importer/package.dart | 4 +- lib/src/importer/utils.dart | 2 + lib/src/interpolation_buffer.dart | 2 +- lib/src/io.dart | 2 +- lib/src/io/interface.dart | 43 +- lib/src/io/node.dart | 8 +- lib/src/logger.dart | 4 +- lib/src/logger/stderr.dart | 4 +- lib/src/logger/tracking.dart | 2 +- lib/src/module.dart | 12 +- lib/src/module/forwarded_view.dart | 42 +- lib/src/module/shadowed_view.dart | 18 +- lib/src/node.dart | 104 ++-- lib/src/node/chokidar.dart | 4 +- lib/src/node/importer_result.dart | 4 +- lib/src/node/render_context.dart | 3 +- lib/src/node/render_context_options.dart | 22 +- lib/src/node/render_options.dart | 34 +- lib/src/node/render_result.dart | 16 +- lib/src/node/utils.dart | 17 +- lib/src/node/value/color.dart | 4 +- lib/src/node/value/list.dart | 2 +- lib/src/node/value/map.dart | 6 +- lib/src/node/value/number.dart | 2 +- lib/src/node/value/string.dart | 2 +- lib/src/parse/css.dart | 8 +- lib/src/parse/parser.dart | 6 +- lib/src/parse/sass.dart | 38 +- lib/src/parse/scss.dart | 7 +- lib/src/parse/selector.dart | 3 +- lib/src/parse/stylesheet.dart | 81 ++-- lib/src/stylesheet_graph.dart | 17 +- lib/src/util/character.dart | 3 +- lib/src/util/fixed_length_list_builder.dart | 67 --- lib/src/util/limited_map_view.dart | 3 +- lib/src/util/merged_map_view.dart | 11 +- lib/src/util/multi_dir_watcher.dart | 10 +- lib/src/util/nullable.dart | 44 ++ lib/src/util/public_member_map_view.dart | 2 +- lib/src/util/source_map_buffer.dart | 4 + lib/src/util/unprefixed_map_view.dart | 4 +- lib/src/utils.dart | 27 +- lib/src/value/argument_list.dart | 4 +- lib/src/value/color.dart | 11 +- lib/src/value/external/color.dart | 12 +- lib/src/value/external/list.dart | 3 +- lib/src/value/external/string.dart | 8 +- lib/src/value/external/value.dart | 2 +- lib/src/value/list.dart | 5 +- lib/src/value/map.dart | 3 +- lib/src/value/number.dart | 34 +- lib/src/value/number/single_unit.dart | 12 +- lib/src/value/string.dart | 4 +- lib/src/visitor/async_evaluate.dart | 447 ++++++++++-------- lib/src/visitor/evaluate.dart | 444 +++++++++-------- lib/src/visitor/recursive_statement.dart | 57 ++- lib/src/visitor/serialize.dart | 64 +-- test/cli/shared/source_maps.dart | 4 +- test/dart_api/function_test.dart | 9 +- test/dart_api/importer_test.dart | 10 +- test/dart_api/logger_test.dart | 6 +- test/dart_api/test_importer.dart | 2 +- test/dart_api/value/boolean_test.dart | 4 +- test/dart_api/value/utils.dart | 3 +- test/double_check_test.dart | 12 +- test/hybrid.dart | 3 +- test/node_api/function_test.dart | 2 + test/node_api/importer_test.dart | 4 +- test/node_api/source_map_test.dart | 14 +- test/node_api/utils.dart | 14 +- test/node_api/value/list_test.dart | 2 +- test/node_api/value/utils.dart | 6 +- test/node_api_test.dart | 17 +- test/source_map_test.dart | 1 + test/utils.dart | 2 +- tool/grind.dart | 4 +- tool/grind/benchmark.dart | 14 +- tool/grind/synchronize.dart | 14 +- tool/grind/utils.dart | 4 +- 165 files changed, 1786 insertions(+), 1441 deletions(-) delete mode 100644 lib/src/util/fixed_length_list_builder.dart create mode 100644 lib/src/util/nullable.dart diff --git a/bin/sass.dart b/bin/sass.dart index 16e322efd..9610363f5 100644 --- a/bin/sass.dart +++ b/bin/sass.dart @@ -35,7 +35,7 @@ Future main(List args) async { } } - ExecutableOptions options; + /*late*/ ExecutableOptions options; try { options = ExecutableOptions.parse(args); term_glyph.ascii = !options.unicode; @@ -115,12 +115,14 @@ Future main(List args) async { /// Loads and returns the current version of Sass. Future _loadVersion() async { - var version = const String.fromEnvironment('version'); - if (const bool.fromEnvironment('node')) { - version += " compiled with dart2js " - "${const String.fromEnvironment('dart-version')}"; + if (bool.hasEnvironment('version')) { + var version = const String.fromEnvironment('version'); + if (const bool.fromEnvironment('node')) { + version += " compiled with dart2js " + "${const String.fromEnvironment('dart-version')}"; + } + return version; } - if (version != null) return version; var libDir = p.fromUri(await Isolate.resolvePackageUri(Uri.parse('package:sass/'))); diff --git a/lib/sass.dart b/lib/sass.dart index ff142d06e..60a0a60d4 100644 --- a/lib/sass.dart +++ b/lib/sass.dart @@ -16,6 +16,7 @@ import 'src/import_cache.dart'; import 'src/importer.dart'; import 'src/logger.dart'; import 'src/syntax.dart'; +import 'src/util/nullable.dart'; import 'src/visitor/serialize.dart'; export 'src/callable.dart' show Callable, AsyncCallable; @@ -93,7 +94,7 @@ String compile(String path, PackageConfig packageConfig, Iterable functions, OutputStyle style, - void sourceMap(SingleMapping map), + void sourceMap(SingleMapping /*!*/ map), bool charset = true}) { logger ??= Logger.stderr(color: color); var result = c.compile(path, @@ -107,7 +108,7 @@ String compile(String path, style: style, sourceMap: sourceMap != null, charset: charset); - if (sourceMap != null) sourceMap(result.sourceMap); + result.sourceMap.andThen(sourceMap); return result.css; } @@ -185,7 +186,7 @@ String compileString(String source, OutputStyle style, Importer importer, Object url, - void sourceMap(SingleMapping map), + void sourceMap(SingleMapping /*!*/ map), bool charset = true, @Deprecated("Use syntax instead.") bool indented = false}) { logger ??= Logger.stderr(color: color); @@ -203,7 +204,7 @@ String compileString(String source, url: url, sourceMap: sourceMap != null, charset: charset); - if (sourceMap != null) sourceMap(result.sourceMap); + result.sourceMap.andThen(sourceMap); return result.css; } @@ -232,7 +233,7 @@ Future compileAsync(String path, functions: functions, style: style, sourceMap: sourceMap != null); - if (sourceMap != null) sourceMap(result.sourceMap); + result.sourceMap.andThen(sourceMap); return result.css; } @@ -270,6 +271,6 @@ Future compileStringAsync(String source, url: url, sourceMap: sourceMap != null, charset: charset); - if (sourceMap != null) sourceMap(result.sourceMap); + result.sourceMap.andThen(sourceMap); return result.css; } diff --git a/lib/src/ast/css/declaration.dart b/lib/src/ast/css/declaration.dart index 1bd7279fc..4f6be41ee 100644 --- a/lib/src/ast/css/declaration.dart +++ b/lib/src/ast/css/declaration.dart @@ -15,7 +15,7 @@ abstract class CssDeclaration extends CssNode { CssValue get name; /// The value of this declaration. - CssValue get value; + CssValue get value; /// The span for [value] that should be emitted to the source map. /// diff --git a/lib/src/ast/css/modifiable/node.dart b/lib/src/ast/css/modifiable/node.dart index af737943b..f4d0e1b9c 100644 --- a/lib/src/ast/css/modifiable/node.dart +++ b/lib/src/ast/css/modifiable/node.dart @@ -28,8 +28,9 @@ abstract class ModifiableCssNode extends CssNode { /// Whether this node has a visible sibling after it. bool get hasFollowingSibling { - if (_parent == null) return false; - var siblings = _parent.children; + var parent = _parent; + if (parent == null) return false; + var siblings = parent.children; for (var i = _indexInParent + 1; i < siblings.length; i++) { var sibling = siblings[i]; if (!_isInvisible(sibling)) return true; @@ -43,7 +44,7 @@ abstract class ModifiableCssNode extends CssNode { /// This can return a false negative for a comment node in compressed mode, /// since the AST doesn't know the output style, but that's an extremely /// narrow edge case so we don't worry about it. - bool _isInvisible(CssNode node) { + bool _isInvisible(CssNode /*!*/ node) { if (node is CssParentNode) { // An unknown at-rule is never invisible. Because we don't know the // semantics of unknown rules, we can't guarantee that (for example) @@ -63,13 +64,14 @@ abstract class ModifiableCssNode extends CssNode { /// /// Throws a [StateError] if [parent] is `null`. void remove() { - if (_parent == null) { + var parent = _parent; + if (parent == null) { throw StateError("Can't remove a node without a parent."); } - _parent._children.removeAt(_indexInParent); - for (var i = _indexInParent; i < _parent._children.length; i++) { - _parent._children[i]._indexInParent--; + parent._children.removeAt(_indexInParent); + for (var i = _indexInParent; i < parent._children.length; i++) { + parent._children[i]._indexInParent--; } _parent = null; } diff --git a/lib/src/ast/css/modifiable/supports_rule.dart b/lib/src/ast/css/modifiable/supports_rule.dart index ef921b511..4fd8048f5 100644 --- a/lib/src/ast/css/modifiable/supports_rule.dart +++ b/lib/src/ast/css/modifiable/supports_rule.dart @@ -12,7 +12,7 @@ import 'node.dart'; /// A modifiable version of [CssSupportsRule] for use in the evaluation step. class ModifiableCssSupportsRule extends ModifiableCssParentNode implements CssSupportsRule { - final CssValue condition; + final CssValue condition; final FileSpan span; ModifiableCssSupportsRule(this.condition, this.span); diff --git a/lib/src/ast/css/node.dart b/lib/src/ast/css/node.dart index 0366961ca..3a3eef416 100644 --- a/lib/src/ast/css/node.dart +++ b/lib/src/ast/css/node.dart @@ -23,7 +23,7 @@ abstract class CssNode extends AstNode { /// A [CssNode] that can have child statements. abstract class CssParentNode extends CssNode { /// The child statements of this node. - List get children; + List get children; /// Whether the rule has no children and should be emitted without curly /// braces. diff --git a/lib/src/ast/css/stylesheet.dart b/lib/src/ast/css/stylesheet.dart index 3c92b9c2d..c4b061b8f 100644 --- a/lib/src/ast/css/stylesheet.dart +++ b/lib/src/ast/css/stylesheet.dart @@ -13,13 +13,13 @@ import 'node.dart'; /// /// This is the root plain CSS node. It contains top-level statements. class CssStylesheet extends CssParentNode { - final List children; + final List children; final FileSpan span; bool get isGroupEnd => false; bool get isChildless => false; /// Creates an unmodifiable stylesheet containing [children]. - CssStylesheet(Iterable children, this.span) + CssStylesheet(Iterable children, this.span) // Use [UnmodifiableListView] rather than [List.unmodifiable] because // the underlying nodes are mutable anyway, so it's better to have the // whole thing consistently represent mutation of the underlying data. diff --git a/lib/src/ast/css/value.dart b/lib/src/ast/css/value.dart index 90379398d..6775cae37 100644 --- a/lib/src/ast/css/value.dart +++ b/lib/src/ast/css/value.dart @@ -10,9 +10,9 @@ import '../node.dart'; /// /// This is used to associate a span with a value that doesn't otherwise track /// its span. -class CssValue implements AstNode { +class CssValue implements AstNode { /// The value. - final T value; + final T /*!*/ value; /// The span associated with the value. final FileSpan span; diff --git a/lib/src/ast/sass/argument.dart b/lib/src/ast/sass/argument.dart index 4fb508ecd..43ade4277 100644 --- a/lib/src/ast/sass/argument.dart +++ b/lib/src/ast/sass/argument.dart @@ -23,8 +23,11 @@ class Argument implements SassNode { /// /// This isn't particularly efficient, and should only be used for error /// messages. - String get originalName => - defaultValue == null ? span.text : declarationName(span); + String get originalName { + var span = this.span; + if (span == null) return '\$$name'; + return defaultValue == null ? span.text : declarationName(span); + } Argument(this.name, {this.defaultValue, this.span}); diff --git a/lib/src/ast/sass/argument_declaration.dart b/lib/src/ast/sass/argument_declaration.dart index 34aeba0f6..694b9f5b4 100644 --- a/lib/src/ast/sass/argument_declaration.dart +++ b/lib/src/ast/sass/argument_declaration.dart @@ -26,6 +26,9 @@ class ArgumentDeclaration implements SassNode { /// Returns [span] expanded to include an identifier immediately before the /// declaration, if possible. FileSpan get spanWithName { + var span = this.span; + if (span == null) return null; + var text = span.file.getText(0); // Move backwards through and whitspace between the name and the arguments. @@ -57,6 +60,9 @@ class ArgumentDeclaration implements SassNode { String get originalRestArgument { if (restArgument == null) return null; + var span = this.span; + if (span == null) return '\$$restArgument'; + var text = span.text; var fromDollar = text.substring(text.lastIndexOf("\$")); return fromDollar.substring(0, text.indexOf(".")); @@ -86,7 +92,7 @@ class ArgumentDeclaration implements SassNode { /// Throws a [SassScriptException] if [positional] and [names] aren't valid /// for this argument declaration. - void verify(int positional, Set names) { + void verify(int positional, Set names) { var namedUsed = 0; for (var i = 0; i < arguments.length; i++) { var argument = arguments[i]; @@ -133,7 +139,7 @@ class ArgumentDeclaration implements SassNode { /// Returns the argument named [name] with a leading `$` and its original /// underscores (which are otherwise converted to hyphens). String _originalArgumentName(String name) { - if (name == restArgument) return originalRestArgument; + if (name == restArgument) return originalRestArgument /*!*/; for (var argument in arguments) { if (argument.name == name) return argument.originalName; @@ -144,7 +150,7 @@ class ArgumentDeclaration implements SassNode { /// Returns whether [positional] and [names] are valid for this argument /// declaration. - bool matches(int positional, Set names) { + bool matches(int positional, Set names) { var namedUsed = 0; for (var i = 0; i < arguments.length; i++) { var argument = arguments[i]; diff --git a/lib/src/ast/sass/argument_invocation.dart b/lib/src/ast/sass/argument_invocation.dart index b18fcb07f..2866177ec 100644 --- a/lib/src/ast/sass/argument_invocation.dart +++ b/lib/src/ast/sass/argument_invocation.dart @@ -21,13 +21,13 @@ class ArgumentInvocation implements SassNode { /// The second rest argument, which is expected to only contain a keyword map. final Expression keywordRest; - final FileSpan span; + final FileSpan /*?*/ span; /// Returns whether this invocation passes no arguments. bool get isEmpty => positional.isEmpty && named.isEmpty && rest == null; - ArgumentInvocation( - Iterable positional, Map named, this.span, + ArgumentInvocation(Iterable positional, + Map named, this.span, {this.rest, this.keywordRest}) : positional = List.unmodifiable(positional), named = Map.unmodifiable(named) { diff --git a/lib/src/ast/sass/at_root_query.dart b/lib/src/ast/sass/at_root_query.dart index 3c4e7f73a..4b6d8f222 100644 --- a/lib/src/ast/sass/at_root_query.dart +++ b/lib/src/ast/sass/at_root_query.dart @@ -54,7 +54,7 @@ class AtRootQuery { AtRootQueryParser(contents, url: url, logger: logger).parse(); /// Returns whether [this] excludes [node]. - bool excludes(CssParentNode node) { + bool excludes(CssParentNode /*!*/ node) { if (_all) return !include; if (node is CssStyleRule) return excludesStyleRules; if (node is CssMediaRule) return excludesName("media"); diff --git a/lib/src/ast/sass/configured_variable.dart b/lib/src/ast/sass/configured_variable.dart index 302c4dde2..5f11ce305 100644 --- a/lib/src/ast/sass/configured_variable.dart +++ b/lib/src/ast/sass/configured_variable.dart @@ -13,7 +13,7 @@ class ConfiguredVariable implements SassNode { final String name; /// The variable's value. - final Expression expression; + final Expression /*!*/ expression; /// Whether the variable can be further configured by outer modules. /// diff --git a/lib/src/ast/sass/expression.dart b/lib/src/ast/sass/expression.dart index 579c8482b..e637ec316 100644 --- a/lib/src/ast/sass/expression.dart +++ b/lib/src/ast/sass/expression.dart @@ -10,7 +10,7 @@ import 'node.dart'; /// A SassScript expression in a Sass syntax tree. abstract class Expression implements SassNode { /// Calls the appropriate visit method on [visitor]. - T accept(ExpressionVisitor visitor); + T /*!*/ accept(ExpressionVisitor visitor); /// Parses an expression from [contents]. /// diff --git a/lib/src/ast/sass/expression/binary_operation.dart b/lib/src/ast/sass/expression/binary_operation.dart index b1608288b..3ebb587ff 100644 --- a/lib/src/ast/sass/expression/binary_operation.dart +++ b/lib/src/ast/sass/expression/binary_operation.dart @@ -15,10 +15,10 @@ class BinaryOperationExpression implements Expression { final BinaryOperator operator; /// The left-hand operand. - final Expression left; + final Expression /*!*/ left; /// The right-hand operand. - final Expression right; + final Expression /*!*/ right; /// Whether this is a [BinaryOperator.dividedBy] operation that may be /// interpreted as slash-separated numbers. diff --git a/lib/src/ast/sass/expression/color.dart b/lib/src/ast/sass/expression/color.dart index 49f1d62ae..f8e0fde82 100644 --- a/lib/src/ast/sass/expression/color.dart +++ b/lib/src/ast/sass/expression/color.dart @@ -11,7 +11,7 @@ import '../expression.dart'; /// A color literal. class ColorExpression implements Expression { /// The value of this color. - final SassColor value; + final SassColor /*!*/ value; FileSpan get span => value.originalSpan; diff --git a/lib/src/ast/sass/expression/list.dart b/lib/src/ast/sass/expression/list.dart index ac5a61aa5..e2db171f7 100644 --- a/lib/src/ast/sass/expression/list.dart +++ b/lib/src/ast/sass/expression/list.dart @@ -24,7 +24,7 @@ class ListExpression implements Expression { final FileSpan span; - ListExpression(Iterable contents, ListSeparator separator, + ListExpression(Iterable contents, ListSeparator separator, {bool brackets = false, FileSpan span}) : this._(List.unmodifiable(contents), separator, brackets, span); diff --git a/lib/src/ast/sass/expression/map.dart b/lib/src/ast/sass/expression/map.dart index 6dcdc06df..3bb7a9db6 100644 --- a/lib/src/ast/sass/expression/map.dart +++ b/lib/src/ast/sass/expression/map.dart @@ -18,7 +18,8 @@ class MapExpression implements Expression { final FileSpan span; - MapExpression(Iterable> pairs, this.span) + MapExpression( + Iterable> pairs, this.span) : pairs = List.unmodifiable(pairs); T accept(ExpressionVisitor visitor) => visitor.visitMapExpression(this); diff --git a/lib/src/ast/sass/expression/parenthesized.dart b/lib/src/ast/sass/expression/parenthesized.dart index 72a91708d..451e87ae2 100644 --- a/lib/src/ast/sass/expression/parenthesized.dart +++ b/lib/src/ast/sass/expression/parenthesized.dart @@ -10,7 +10,7 @@ import '../expression.dart'; /// An expression wrapped in parentheses. class ParenthesizedExpression implements Expression { /// The internal expression. - final Expression expression; + final Expression /*!*/ expression; final FileSpan span; diff --git a/lib/src/ast/sass/expression/string.dart b/lib/src/ast/sass/expression/string.dart index 5504cfe2c..b401c9ad1 100644 --- a/lib/src/ast/sass/expression/string.dart +++ b/lib/src/ast/sass/expression/string.dart @@ -26,7 +26,7 @@ class StringExpression implements Expression { /// Returns Sass source for a quoted string that, when evaluated, will have /// [text] as its contents. - static String quoteText(String text) => + static String /*!*/ quoteText(String text) => StringExpression.plain(text, null, quotes: true) .asInterpolation(static: true) .asPlain; @@ -51,9 +51,9 @@ class StringExpression implements Expression { Interpolation asInterpolation({bool static = false, int quote}) { if (!hasQuotes) return text; - quote ??= hasQuotes ? _bestQuote() : null; + quote ??= _bestQuote(); var buffer = InterpolationBuffer(); - if (quote != null) buffer.writeCharCode(quote); + buffer.writeCharCode(quote); for (var value in text.contents) { assert(value is Expression || value is String); if (value is Expression) { @@ -85,7 +85,7 @@ class StringExpression implements Expression { } } } - if (quote != null) buffer.writeCharCode(quote); + buffer.writeCharCode(quote); return buffer.interpolation(text.span); } diff --git a/lib/src/ast/sass/expression/unary_operation.dart b/lib/src/ast/sass/expression/unary_operation.dart index c3ac12642..088c9333a 100644 --- a/lib/src/ast/sass/expression/unary_operation.dart +++ b/lib/src/ast/sass/expression/unary_operation.dart @@ -11,10 +11,10 @@ import '../expression.dart'; /// A unary operator, as in `+$var` or `not fn()`. class UnaryOperationExpression implements Expression { /// The operator being invoked. - final UnaryOperator operator; + final UnaryOperator /*!*/ operator; /// The operand. - final Expression operand; + final Expression /*!*/ operand; final FileSpan span; diff --git a/lib/src/ast/sass/expression/value.dart b/lib/src/ast/sass/expression/value.dart index 1ef46e52e..c4cebc3ab 100644 --- a/lib/src/ast/sass/expression/value.dart +++ b/lib/src/ast/sass/expression/value.dart @@ -14,7 +14,7 @@ import '../expression.dart'; /// constructed dynamically, as for the `call()` function. class ValueExpression implements Expression { /// The embedded value. - final Value value; + final Value /*!*/ value; final FileSpan span; diff --git a/lib/src/ast/sass/interpolation.dart b/lib/src/ast/sass/interpolation.dart index 2fd68f181..2a7462645 100644 --- a/lib/src/ast/sass/interpolation.dart +++ b/lib/src/ast/sass/interpolation.dart @@ -15,7 +15,7 @@ class Interpolation implements SassNode { /// [String]s. final List contents; - final FileSpan span; + final FileSpan /*?*/ span; /// If this contains no interpolated expressions, returns its text contents. /// @@ -33,7 +33,8 @@ class Interpolation implements SassNode { return first is String ? first : ''; } - Interpolation(Iterable contents, this.span) + Interpolation( + Iterable contents, this.span) : contents = List.unmodifiable(contents) { for (var i = 0; i < this.contents.length; i++) { if (this.contents[i] is! String && this.contents[i] is! Expression) { diff --git a/lib/src/ast/sass/statement.dart b/lib/src/ast/sass/statement.dart index cfdffd320..934b7a946 100644 --- a/lib/src/ast/sass/statement.dart +++ b/lib/src/ast/sass/statement.dart @@ -8,5 +8,5 @@ import 'node.dart'; /// A statement in a Sass syntax tree. abstract class Statement implements SassNode { /// Calls the appropriate visit method on [visitor]. - T accept(StatementVisitor visitor); + T /*!*/ accept(StatementVisitor visitor); } diff --git a/lib/src/ast/sass/statement/at_root_rule.dart b/lib/src/ast/sass/statement/at_root_rule.dart index dc433a9ae..dba1309e9 100644 --- a/lib/src/ast/sass/statement/at_root_rule.dart +++ b/lib/src/ast/sass/statement/at_root_rule.dart @@ -12,14 +12,14 @@ import 'parent.dart'; /// An `@at-root` rule. /// /// This moves it contents "up" the tree through parent nodes. -class AtRootRule extends ParentStatement { +class AtRootRule extends ParentStatement /*!*/ > { /// The query specifying which statements this should move its contents /// through. final Interpolation query; final FileSpan span; - AtRootRule(Iterable children, this.span, {this.query}) + AtRootRule(Iterable children, this.span, {this.query}) : super(List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitAtRootRule(this); diff --git a/lib/src/ast/sass/statement/at_rule.dart b/lib/src/ast/sass/statement/at_rule.dart index d35a45210..899762fa7 100644 --- a/lib/src/ast/sass/statement/at_rule.dart +++ b/lib/src/ast/sass/statement/at_rule.dart @@ -19,7 +19,8 @@ class AtRule extends ParentStatement { final FileSpan span; - AtRule(this.name, this.span, {this.value, Iterable children}) + AtRule(this.name, this.span, + {this.value, Iterable children}) : super(children == null ? null : List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitAtRule(this); diff --git a/lib/src/ast/sass/statement/callable_declaration.dart b/lib/src/ast/sass/statement/callable_declaration.dart index f26bb8a82..be7afd648 100644 --- a/lib/src/ast/sass/statement/callable_declaration.dart +++ b/lib/src/ast/sass/statement/callable_declaration.dart @@ -11,22 +11,21 @@ import 'silent_comment.dart'; /// An abstract class for callables (functions or mixins) that are declared in /// user code. -abstract class CallableDeclaration extends ParentStatement { +abstract class CallableDeclaration + extends ParentStatement /*!*/ > { /// The name of this callable, with underscores converted to hyphens. - /// - /// This may be `null` for callables without names. final String name; /// The comment immediately preceding this declaration. final SilentComment comment; /// The declared arguments this callable accepts. - final ArgumentDeclaration arguments; + final ArgumentDeclaration /*!*/ arguments; final FileSpan span; CallableDeclaration( - this.name, this.arguments, Iterable children, this.span, + this.name, this.arguments, Iterable children, this.span, {SilentComment comment}) : comment = comment, super(List.unmodifiable(children)); diff --git a/lib/src/ast/sass/statement/content_block.dart b/lib/src/ast/sass/statement/content_block.dart index 85881d7ed..2d97e1917 100644 --- a/lib/src/ast/sass/statement/content_block.dart +++ b/lib/src/ast/sass/statement/content_block.dart @@ -11,9 +11,9 @@ import 'callable_declaration.dart'; /// An anonymous block of code that's invoked for a [ContentRule]. class ContentBlock extends CallableDeclaration { - ContentBlock(ArgumentDeclaration arguments, Iterable children, - FileSpan span) - : super(null /* name */, arguments, children, span); + ContentBlock(ArgumentDeclaration arguments, + Iterable children, FileSpan span) + : super("@content", arguments, children, span); T accept(StatementVisitor visitor) => visitor.visitContentBlock(this); diff --git a/lib/src/ast/sass/statement/debug_rule.dart b/lib/src/ast/sass/statement/debug_rule.dart index 16c9abad0..e829ec2f5 100644 --- a/lib/src/ast/sass/statement/debug_rule.dart +++ b/lib/src/ast/sass/statement/debug_rule.dart @@ -13,7 +13,7 @@ import '../statement.dart'; /// This prints a Sass value for debugging purposes. class DebugRule implements Statement { /// The expression to print. - final Expression expression; + final Expression /*!*/ expression; final FileSpan span; diff --git a/lib/src/ast/sass/statement/each_rule.dart b/lib/src/ast/sass/statement/each_rule.dart index cd1ad7b77..c80bf322b 100644 --- a/lib/src/ast/sass/statement/each_rule.dart +++ b/lib/src/ast/sass/statement/each_rule.dart @@ -12,17 +12,17 @@ import 'parent.dart'; /// An `@each` rule. /// /// This iterates over values in a list or map. -class EachRule extends ParentStatement { +class EachRule extends ParentStatement /*!*/ > { /// The variables assigned for each iteration. final List variables; /// The expression whose value this iterates through. - final Expression list; + final Expression /*!*/ list; final FileSpan span; - EachRule(Iterable variables, this.list, Iterable children, - this.span) + EachRule(Iterable variables, this.list, + Iterable children, this.span) : variables = List.unmodifiable(variables), super(List.unmodifiable(children)); diff --git a/lib/src/ast/sass/statement/error_rule.dart b/lib/src/ast/sass/statement/error_rule.dart index 1f51f2226..29633991b 100644 --- a/lib/src/ast/sass/statement/error_rule.dart +++ b/lib/src/ast/sass/statement/error_rule.dart @@ -13,7 +13,7 @@ import '../statement.dart'; /// This emits an error and stops execution. class ErrorRule implements Statement { /// The expression to evaluate for the error message. - final Expression expression; + final Expression /*!*/ expression; final FileSpan span; diff --git a/lib/src/ast/sass/statement/for_rule.dart b/lib/src/ast/sass/statement/for_rule.dart index eae380a0e..89378646f 100644 --- a/lib/src/ast/sass/statement/for_rule.dart +++ b/lib/src/ast/sass/statement/for_rule.dart @@ -12,24 +12,24 @@ import 'parent.dart'; /// A `@for` rule. /// /// This iterates a set number of times. -class ForRule extends ParentStatement { +class ForRule extends ParentStatement /*!*/ > { /// The name of the variable that will contain the index value. final String variable; /// The expression for the start index. - final Expression from; + final Expression /*!*/ from; /// The expression for the end index. - final Expression to; + final Expression /*!*/ to; /// Whether [to] is exclusive. final bool isExclusive; final FileSpan span; - ForRule(this.variable, this.from, this.to, Iterable children, - this.span, - {bool exclusive = true}) + ForRule(this.variable, this.from, this.to, + Iterable children, this.span, + {bool /*!*/ exclusive = true}) : isExclusive = exclusive, super(List.unmodifiable(children)); diff --git a/lib/src/ast/sass/statement/forward_rule.dart b/lib/src/ast/sass/statement/forward_rule.dart index 542d0b1b7..80a206371 100644 --- a/lib/src/ast/sass/statement/forward_rule.dart +++ b/lib/src/ast/sass/statement/forward_rule.dart @@ -112,17 +112,20 @@ class ForwardRule implements Statement { var buffer = StringBuffer("@forward ${StringExpression.quoteText(url.toString())}"); + var shownMixinsAndFunctions = this.shownMixinsAndFunctions; + var hiddenMixinsAndFunctions = this.hiddenMixinsAndFunctions; if (shownMixinsAndFunctions != null) { buffer ..write(" show ") - ..write(_memberList(shownMixinsAndFunctions, shownVariables)); + ..write(_memberList(shownMixinsAndFunctions, shownVariables /*!*/)); } else if (hiddenMixinsAndFunctions != null && hiddenMixinsAndFunctions.isNotEmpty) { buffer ..write(" hide ") - ..write(_memberList(hiddenMixinsAndFunctions, hiddenVariables)); + ..write(_memberList(hiddenMixinsAndFunctions, hiddenVariables /*!*/)); } + var prefix = this.prefix; if (prefix != null) buffer.write(" as $prefix*"); if (configuration.isNotEmpty) { @@ -136,9 +139,5 @@ class ForwardRule implements Statement { /// Returns a combined list of names of the given members. String _memberList( Iterable mixinsAndFunctions, Iterable variables) => - [ - if (shownMixinsAndFunctions != null) ...shownMixinsAndFunctions, - if (shownVariables != null) - for (var name in shownVariables) "\$$name" - ].join(", "); + [...mixinsAndFunctions, for (var name in variables) "\$$name"].join(", "); } diff --git a/lib/src/ast/sass/statement/function_rule.dart b/lib/src/ast/sass/statement/function_rule.dart index 78bcd4d94..c69809b36 100644 --- a/lib/src/ast/sass/statement/function_rule.dart +++ b/lib/src/ast/sass/statement/function_rule.dart @@ -15,7 +15,7 @@ import 'silent_comment.dart'; /// This declares a function that's invoked using normal CSS function syntax. class FunctionRule extends CallableDeclaration { FunctionRule(String name, ArgumentDeclaration arguments, - Iterable children, FileSpan span, + Iterable children, FileSpan span, {SilentComment comment}) : super(name, arguments, children, span, comment: comment); diff --git a/lib/src/ast/sass/statement/if_rule.dart b/lib/src/ast/sass/statement/if_rule.dart index 459b135e8..8622e73f2 100644 --- a/lib/src/ast/sass/statement/if_rule.dart +++ b/lib/src/ast/sass/statement/if_rule.dart @@ -72,7 +72,7 @@ abstract class IfRuleClause { /// An `@if` or `@else if` clause in an `@if` rule. class IfClause extends IfRuleClause { /// The expression to evaluate to determine whether to run this rule. - final Expression expression; + final Expression /*!*/ expression; IfClause(this.expression, Iterable children) : super(children); diff --git a/lib/src/ast/sass/statement/media_rule.dart b/lib/src/ast/sass/statement/media_rule.dart index a73b806cd..52a8c1252 100644 --- a/lib/src/ast/sass/statement/media_rule.dart +++ b/lib/src/ast/sass/statement/media_rule.dart @@ -10,7 +10,7 @@ import '../statement.dart'; import 'parent.dart'; /// A `@media` rule. -class MediaRule extends ParentStatement { +class MediaRule extends ParentStatement /*!*/ > { /// The query that determines on which platforms the styles will be in effect. /// /// This is only parsed after the interpolation has been resolved. @@ -18,7 +18,7 @@ class MediaRule extends ParentStatement { final FileSpan span; - MediaRule(this.query, Iterable children, this.span) + MediaRule(this.query, Iterable children, this.span) : super(List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitMediaRule(this); diff --git a/lib/src/ast/sass/statement/mixin_rule.dart b/lib/src/ast/sass/statement/mixin_rule.dart index 7256e92c2..1d86ce6df 100644 --- a/lib/src/ast/sass/statement/mixin_rule.dart +++ b/lib/src/ast/sass/statement/mixin_rule.dart @@ -15,7 +15,7 @@ import 'silent_comment.dart'; /// This declares a mixin that's invoked using `@include`. class MixinRule extends CallableDeclaration { /// Whether the mixin contains a `@content` rule. - final bool hasContent; + final bool /*!*/ hasContent; /// Creates a [MixinRule]. /// @@ -23,7 +23,7 @@ class MixinRule extends CallableDeclaration { /// recursively contains a `@content` rule. Otherwise, invoking this mixin /// won't work correctly. MixinRule(String name, ArgumentDeclaration arguments, - Iterable children, FileSpan span, + Iterable children, FileSpan span, {this.hasContent = false, SilentComment comment}) : super(name, arguments, children, span, comment: comment); diff --git a/lib/src/ast/sass/statement/parent.dart b/lib/src/ast/sass/statement/parent.dart index 06d6560b2..94b0cd3f5 100644 --- a/lib/src/ast/sass/statement/parent.dart +++ b/lib/src/ast/sass/statement/parent.dart @@ -9,10 +9,16 @@ import 'import_rule.dart'; import 'mixin_rule.dart'; import 'variable_declaration.dart'; +// TODO: make this generic over nullable children. + /// A [Statement] that can have child statements. -abstract class ParentStatement implements Statement { +/// +/// This has a generic parameter so that its subclasses can choose whether or +/// not their children lists are nullable. +abstract class ParentStatement /*?*/ > + implements Statement { /// The child statements of this statement. - final List children; + final T children; /// Whether any of [children] is a variable, function, or mixin declaration, /// or a dynamic import rule. diff --git a/lib/src/ast/sass/statement/return_rule.dart b/lib/src/ast/sass/statement/return_rule.dart index f2c29c96c..3a8403249 100644 --- a/lib/src/ast/sass/statement/return_rule.dart +++ b/lib/src/ast/sass/statement/return_rule.dart @@ -13,7 +13,7 @@ import '../statement.dart'; /// This exits from the current function body with a return value. class ReturnRule implements Statement { /// The value to return from this function. - final Expression expression; + final Expression /*!*/ expression; final FileSpan span; diff --git a/lib/src/ast/sass/statement/style_rule.dart b/lib/src/ast/sass/statement/style_rule.dart index 0b207efe9..022545250 100644 --- a/lib/src/ast/sass/statement/style_rule.dart +++ b/lib/src/ast/sass/statement/style_rule.dart @@ -12,7 +12,7 @@ import 'parent.dart'; /// A style rule. /// /// This applies style declarations to elements that match a given selector. -class StyleRule extends ParentStatement { +class StyleRule extends ParentStatement /*!*/ > { /// The selector to which the declaration will be applied. /// /// This is only parsed after the interpolation has been resolved. @@ -20,7 +20,7 @@ class StyleRule extends ParentStatement { final FileSpan span; - StyleRule(this.selector, Iterable children, this.span) + StyleRule(this.selector, Iterable children, this.span) : super(List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitStyleRule(this); diff --git a/lib/src/ast/sass/statement/stylesheet.dart b/lib/src/ast/sass/statement/stylesheet.dart index 2cabd259e..6543b69b1 100644 --- a/lib/src/ast/sass/statement/stylesheet.dart +++ b/lib/src/ast/sass/statement/stylesheet.dart @@ -23,8 +23,8 @@ import 'variable_declaration.dart'; /// A Sass stylesheet. /// /// This is the root Sass node. It contains top-level statements. -class Stylesheet extends ParentStatement { - final FileSpan span; +class Stylesheet extends ParentStatement /*!*/ > { + final FileSpan /*?*/ span; /// Whether this was parsed from a plain CSS stylesheet. final bool plainCss; @@ -37,7 +37,8 @@ class Stylesheet extends ParentStatement { List get forwards => UnmodifiableListView(_forwards); final _forwards = []; - Stylesheet(Iterable children, this.span, {this.plainCss = false}) + Stylesheet(Iterable children, this.span, + {this.plainCss = false}) : super(List.unmodifiable(children)) { for (var child in this.children) { if (child is UseRule) { @@ -57,7 +58,7 @@ class Stylesheet extends ParentStatement { /// If passed, [url] is the name of the file from which [contents] comes. /// /// Throws a [SassFormatException] if parsing fails. - factory Stylesheet.parse(String contents, Syntax syntax, + factory Stylesheet.parse(String /*!*/ contents, Syntax syntax, {Object url, Logger logger}) { switch (syntax) { case Syntax.sass: diff --git a/lib/src/ast/sass/statement/supports_rule.dart b/lib/src/ast/sass/statement/supports_rule.dart index e83957075..8f70ead8c 100644 --- a/lib/src/ast/sass/statement/supports_rule.dart +++ b/lib/src/ast/sass/statement/supports_rule.dart @@ -10,13 +10,13 @@ import '../supports_condition.dart'; import 'parent.dart'; /// A `@supports` rule. -class SupportsRule extends ParentStatement { +class SupportsRule extends ParentStatement /*!*/ > { /// The condition that selects what browsers this rule targets. final SupportsCondition condition; final FileSpan span; - SupportsRule(this.condition, Iterable children, this.span) + SupportsRule(this.condition, Iterable children, this.span) : super(List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitSupportsRule(this); diff --git a/lib/src/ast/sass/statement/use_rule.dart b/lib/src/ast/sass/statement/use_rule.dart index 3a582c089..33932e418 100644 --- a/lib/src/ast/sass/statement/use_rule.dart +++ b/lib/src/ast/sass/statement/use_rule.dart @@ -31,7 +31,7 @@ class UseRule implements Statement { {Iterable configuration}) : configuration = configuration == null ? const [] - : List.unmodifiable(configuration) { + : List.unmodifiable(configuration) { for (var variable in this.configuration) { if (variable.isGuarded) { throw ArgumentError.value(variable, "configured variable", diff --git a/lib/src/ast/sass/statement/variable_declaration.dart b/lib/src/ast/sass/statement/variable_declaration.dart index 81f2b1ea9..3515b1256 100644 --- a/lib/src/ast/sass/statement/variable_declaration.dart +++ b/lib/src/ast/sass/statement/variable_declaration.dart @@ -27,7 +27,7 @@ class VariableDeclaration implements Statement { SilentComment comment; /// The value the variable is being assigned to. - final Expression expression; + final Expression /*!*/ expression; /// Whether this is a guarded assignment. /// diff --git a/lib/src/ast/sass/statement/warn_rule.dart b/lib/src/ast/sass/statement/warn_rule.dart index 2ed5f13c1..ff47ca15e 100644 --- a/lib/src/ast/sass/statement/warn_rule.dart +++ b/lib/src/ast/sass/statement/warn_rule.dart @@ -13,7 +13,7 @@ import '../statement.dart'; /// This prints a Sass value—usually a string—to warn the user of something. class WarnRule implements Statement { /// The expression to print. - final Expression expression; + final Expression /*!*/ expression; final FileSpan span; diff --git a/lib/src/ast/sass/statement/while_rule.dart b/lib/src/ast/sass/statement/while_rule.dart index 69cff0889..121ca5bb2 100644 --- a/lib/src/ast/sass/statement/while_rule.dart +++ b/lib/src/ast/sass/statement/while_rule.dart @@ -13,14 +13,14 @@ import 'parent.dart'; /// /// This repeatedly executes a block of code as long as a statement evaluates to /// `true`. -class WhileRule extends ParentStatement { +class WhileRule extends ParentStatement /*!*/ > { /// The condition that determines whether the block will be executed. - final Expression condition; + final Expression /*!*/ condition; final FileSpan span; - WhileRule(this.condition, Iterable children, this.span) - : super(List.unmodifiable(children)); + WhileRule(this.condition, Iterable children, this.span) + : super(List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitWhileRule(this); diff --git a/lib/src/ast/sass/supports_condition/declaration.dart b/lib/src/ast/sass/supports_condition/declaration.dart index 21db700ae..fa85a0d5a 100644 --- a/lib/src/ast/sass/supports_condition/declaration.dart +++ b/lib/src/ast/sass/supports_condition/declaration.dart @@ -11,10 +11,10 @@ import '../supports_condition.dart'; /// supported. class SupportsDeclaration implements SupportsCondition { /// The name of the declaration being tested. - final Expression name; + final Expression /*!*/ name; /// The value of the declaration being tested. - final Expression value; + final Expression /*!*/ value; final FileSpan span; diff --git a/lib/src/ast/selector/complex.dart b/lib/src/ast/selector/complex.dart index d2503036d..0213e8fa4 100644 --- a/lib/src/ast/selector/complex.dart +++ b/lib/src/ast/selector/complex.dart @@ -31,34 +31,33 @@ class ComplexSelector extends Selector { /// /// Pseudo selectors that contain selectors, like `:not()` and `:matches()`, /// can have a range of possible specificities. - int get minSpecificity { + int /*!*/ get minSpecificity { if (_minSpecificity == null) _computeSpecificity(); return _minSpecificity; } - int _minSpecificity; + /*late final*/ int _minSpecificity; /// The maximum possible specificity that this selector can have. /// /// Pseudo selectors that contain selectors, like `:not()` and `:matches()`, /// can have a range of possible specificities. - int get maxSpecificity { + int /*!*/ get maxSpecificity { if (_maxSpecificity == null) _computeSpecificity(); return _maxSpecificity; } - int _maxSpecificity; + /*late final*/ int _maxSpecificity; + // TODO: make late bool get isInvisible { - if (_isInvisible != null) return _isInvisible; - _isInvisible = components.any( + return _isInvisible ??= components.any( (component) => component is CompoundSelector && component.isInvisible); - return _isInvisible; } bool _isInvisible; - ComplexSelector(Iterable components, + ComplexSelector(Iterable components, {this.lineBreak = false}) : components = List.unmodifiable(components) { if (this.components.isEmpty) { diff --git a/lib/src/ast/selector/compound.dart b/lib/src/ast/selector/compound.dart index e058168e8..d2769acbf 100644 --- a/lib/src/ast/selector/compound.dart +++ b/lib/src/ast/selector/compound.dart @@ -19,27 +19,29 @@ class CompoundSelector extends Selector implements ComplexSelectorComponent { /// This is never empty. final List components; + // TODO: late for specificities + /// The minimum possible specificity that this selector can have. /// /// Pseudo selectors that contain selectors, like `:not()` and `:matches()`, /// can have a range of possible specificities. - int get minSpecificity { + int /*!*/ get minSpecificity { if (_minSpecificity == null) _computeSpecificity(); return _minSpecificity; } - int _minSpecificity; + /*late final*/ int _minSpecificity; /// The maximum possible specificity that this selector can have. /// /// Pseudo selectors that contain selectors, like `:not()` and `:matches()`, /// can have a range of possible specificities. - int get maxSpecificity { + int /*!*/ get maxSpecificity { if (_maxSpecificity == null) _computeSpecificity(); return _maxSpecificity; } - int _maxSpecificity; + /*late final*/ int /*?*/ _maxSpecificity; bool get isInvisible => components.any((component) => component.isInvisible); @@ -70,7 +72,7 @@ class CompoundSelector extends Selector implements ComplexSelectorComponent { /// /// That is, whether this matches every element that [other] matches, as well /// as possibly additional elements. - bool isSuperselector(CompoundSelector other) => + bool isSuperselector(CompoundSelector /*!*/ other) => compoundIsSuperselector(this, other); /// Computes [_minSpecificity] and [_maxSpecificity]. diff --git a/lib/src/ast/selector/list.dart b/lib/src/ast/selector/list.dart index 73f291735..5901ab6c1 100644 --- a/lib/src/ast/selector/list.dart +++ b/lib/src/ast/selector/list.dart @@ -148,11 +148,12 @@ class SelectorList extends Selector { bool _complexContainsParentSelector(ComplexSelector complex) => complex.components.any((component) => component is CompoundSelector && - component.components.any((simple) => - simple is ParentSelector || - (simple is PseudoSelector && - simple.selector != null && - simple.selector._containsParentSelector))); + component.components.any((simple) { + if (simple is ParentSelector) return true; + if (simple is! PseudoSelector) return true; + var selector = (simple as PseudoSelector).selector; + return selector != null && selector._containsParentSelector; + })); /// Returns a new [CompoundSelector] based on [compound] with all /// [ParentSelector]s replaced with [parent]. @@ -160,10 +161,11 @@ class SelectorList extends Selector { /// Returns `null` if [compound] doesn't contain any [ParentSelector]s. Iterable _resolveParentSelectorsCompound( CompoundSelector compound, SelectorList parent) { - var containsSelectorPseudo = compound.components.any((simple) => - simple is PseudoSelector && - simple.selector != null && - simple.selector._containsParentSelector); + var containsSelectorPseudo = compound.components.any((simple) { + if (simple is! PseudoSelector) return false; + var selector = (simple as PseudoSelector).selector; + return selector != null && selector._containsParentSelector; + }); if (!containsSelectorPseudo && compound.components.first is! ParentSelector) { return null; @@ -171,14 +173,12 @@ class SelectorList extends Selector { var resolvedMembers = containsSelectorPseudo ? compound.components.map((simple) { - if (simple is PseudoSelector) { - if (simple.selector == null) return simple; - if (!simple.selector._containsParentSelector) return simple; - return simple.withSelector(simple.selector - .resolveParentSelectors(parent, implicitParent: false)); - } else { - return simple; - } + if (simple is! PseudoSelector) return simple; + var selector = (simple as PseudoSelector).selector; + if (selector == null) return simple; + if (!selector._containsParentSelector) return simple; + return (simple as PseudoSelector).withSelector( + selector.resolveParentSelectors(parent, implicitParent: false)); }) : compound.components; diff --git a/lib/src/ast/selector/pseudo.dart b/lib/src/ast/selector/pseudo.dart index 6b516e11a..c80634eea 100644 --- a/lib/src/ast/selector/pseudo.dart +++ b/lib/src/ast/selector/pseudo.dart @@ -59,6 +59,8 @@ class PseudoSelector extends SimpleSelector { /// both non-`null`, the selector follows the argument. final SelectorList selector; + // TODO: late + int get minSpecificity { if (_minSpecificity == null) _computeSpecificity(); return _minSpecificity; @@ -74,6 +76,7 @@ class PseudoSelector extends SimpleSelector { int _maxSpecificity; bool get isInvisible { + var selector = this.selector; if (selector == null) return false; // We don't consider `:not(%foo)` to be invisible because, semantically, it @@ -158,6 +161,7 @@ class PseudoSelector extends SimpleSelector { return; } + var selector = this.selector; if (selector == null) { _minSpecificity = super.minSpecificity; _maxSpecificity = super.maxSpecificity; @@ -165,21 +169,23 @@ class PseudoSelector extends SimpleSelector { } if (name == 'not') { - _minSpecificity = 0; - _maxSpecificity = 0; + var minSpecificity = 0; + var maxSpecificity = 0; for (var complex in selector.components) { - _minSpecificity = math.max(_minSpecificity, complex.minSpecificity); - _maxSpecificity = math.max(_maxSpecificity, complex.maxSpecificity); + minSpecificity = math.max(_minSpecificity, complex.minSpecificity); + maxSpecificity = math.max(_maxSpecificity, complex.maxSpecificity); } } else { // This is higher than any selector's specificity can actually be. - _minSpecificity = math.pow(super.minSpecificity, 3) as int; - _maxSpecificity = 0; + var minSpecificity = math.pow(super.minSpecificity, 3) as int; + var maxSpecificity = 0; for (var complex in selector.components) { - _minSpecificity = math.min(_minSpecificity, complex.minSpecificity); - _maxSpecificity = math.max(_maxSpecificity, complex.maxSpecificity); + minSpecificity = math.min(_minSpecificity, complex.minSpecificity); + maxSpecificity = math.max(_maxSpecificity, complex.maxSpecificity); } } + _minSpecificity = minSpecificity; + _maxSpecificity = maxSpecificity; } T accept(SelectorVisitor visitor) => visitor.visitPseudoSelector(this); diff --git a/lib/src/ast/selector/simple.dart b/lib/src/ast/selector/simple.dart index f421db7b8..7b3e82261 100644 --- a/lib/src/ast/selector/simple.dart +++ b/lib/src/ast/selector/simple.dart @@ -17,13 +17,13 @@ abstract class SimpleSelector extends Selector { /// Specifity is represented in base 1000. The spec says this should be /// "sufficiently high"; it's extremely unlikely that any single selector /// sequence will contain 1000 simple selectors. - int get minSpecificity => 1000; + int /*!*/ get minSpecificity => 1000; /// The maximum possible specificity that this selector can have. /// /// Pseudo selectors that contain selectors, like `:not()` and `:matches()`, /// can have a range of possible specificities. - int get maxSpecificity => minSpecificity; + int /*!*/ get maxSpecificity => minSpecificity; SimpleSelector(); diff --git a/lib/src/async_compile.dart b/lib/src/async_compile.dart index 64d4fe151..dc6306e11 100644 --- a/lib/src/async_compile.dart +++ b/lib/src/async_compile.dart @@ -24,7 +24,7 @@ import 'visitor/serialize.dart'; /// the node-sass compatible API and the executable. /// /// At most one of `importCache` and `nodeImporter` may be provided at once. -Future compileAsync(String path, +Future compileAsync(String /*!*/ path, {Syntax syntax, Logger logger, AsyncImportCache importCache, @@ -51,6 +51,7 @@ Future compileAsync(String path, } return await _compileStylesheet( + // TODO: no ! stylesheet, logger, importCache, @@ -135,11 +136,12 @@ Future _compileStylesheet( sourceMap: sourceMap, charset: charset); - if (serializeResult.sourceMap != null && importCache != null) { + var resultSourceMap = serializeResult.sourceMap; + if (resultSourceMap != null && importCache != null) { // TODO(nweiz): Don't explicitly use a type parameter when dart-lang/sdk#25490 // is fixed. mapInPlace( - serializeResult.sourceMap.urls, + resultSourceMap.urls, (url) => url == '' ? Uri.dataFromString(stylesheet.span.file.getText(0), encoding: utf8) diff --git a/lib/src/async_environment.dart b/lib/src/async_environment.dart index c8d73acc7..f5d9bec17 100644 --- a/lib/src/async_environment.dart +++ b/lib/src/async_environment.dart @@ -20,6 +20,7 @@ import 'module.dart'; import 'module/forwarded_view.dart'; import 'module/shadowed_view.dart'; import 'util/merged_map_view.dart'; +import 'util/nullable.dart'; import 'util/public_member_map_view.dart'; import 'utils.dart'; import 'value.dart'; @@ -43,7 +44,7 @@ class AsyncEnvironment { /// A map from modules in [_globalModules] to the nodes whose spans /// indicate where those modules were originally loaded. - final Map _globalModuleNodes; + final Map _globalModuleNodes; /// The modules forwarded by this module. /// @@ -54,7 +55,7 @@ class AsyncEnvironment { /// indicate where those modules were originally forwarded. /// /// This is `null` if there are no forwarded modules. - Map _forwardedModuleNodes; + Map _forwardedModuleNodes; /// Modules forwarded by nested imports at each lexical scope level *beneath /// the global scope*. @@ -73,7 +74,7 @@ class AsyncEnvironment { /// /// The first element is the global scope, and each successive element is /// deeper in the tree. - final List> _variables; + final List> _variables; /// The nodes where each variable in [_variables] was defined. /// @@ -82,12 +83,12 @@ class AsyncEnvironment { /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - final List> _variableNodes; + final List> _variableNodes; /// A map of variable names to their indices in [_variables]. /// /// This map is filled in as-needed, and may not be complete. - final Map _variableIndices; + final Map _variableIndices; /// A list of functions defined at each lexical scope level. /// @@ -248,10 +249,11 @@ class AsyncEnvironment { } } else { if (_modules.containsKey(namespace)) { + var span = _namespaceNodes[namespace]?.span; throw MultiSpanSassScriptException( "There's already a module with namespace \"$namespace\".", "new @use", - {_namespaceNodes[namespace].span: "original @use"}); + {if (span != null) span: "original @use"}); } _modules[namespace] = module; @@ -263,16 +265,16 @@ class AsyncEnvironment { /// Exposes the members in [module] to downstream modules as though they were /// defined in this module, according to the modifications defined by [rule]. void forwardModule(Module module, ForwardRule rule) { - _forwardedModules ??= {}; - _forwardedModuleNodes ??= {}; + var forwardedModules = (_forwardedModules ??= {}); + var forwardedModuleNodes = (_forwardedModuleNodes ??= {}); var view = ForwardedModuleView.ifNecessary(module, rule); - for (var other in _forwardedModules) { + for (var other in forwardedModules) { _assertNoConflicts( - view.variables, other.variables, view, other, "variable", rule); + view.variables, other.variables, view, other, "variable"); _assertNoConflicts( - view.functions, other.functions, view, other, "function", rule); - _assertNoConflicts(view.mixins, other.mixins, view, other, "mixin", rule); + view.functions, other.functions, view, other, "function"); + _assertNoConflicts(view.mixins, other.mixins, view, other, "mixin"); } // Add the original module to [_allModules] (rather than the @@ -280,8 +282,8 @@ class AsyncEnvironment { // `==`. This is safe because upstream modules are only used for collating // CSS, not for the members they expose. _allModules.add(module); - _forwardedModules.add(view); - _forwardedModuleNodes[view] = rule; + forwardedModules.add(view); + forwardedModuleNodes[view] = rule; } /// Throws a [SassScriptException] if [newMembers] from [newModule] has any @@ -293,8 +295,7 @@ class AsyncEnvironment { Map oldMembers, Module newModule, Module oldModule, - String type, - AstNode newModuleNodeWithSpan) { + String type) { Map smaller; Map larger; if (newMembers.length < oldMembers.length) { @@ -314,10 +315,11 @@ class AsyncEnvironment { } if (type == "variable") name = "\$$name"; + var span = _forwardedModuleNodes.andGet(oldModule)?.span; throw MultiSpanSassScriptException( 'Two forwarded modules both define a $type named $name.', "new @forward", - {_forwardedModuleNodes[oldModule].span: "original @forward"}); + {if (span != null) span: "original @forward"}); } } @@ -332,17 +334,19 @@ class AsyncEnvironment { // Omit modules from [forwarded] that are already globally available and // forwarded in this module. - if (_forwardedModules != null) { + var forwardedModules = _forwardedModules; + if (forwardedModules != null) { forwarded = { for (var module in forwarded) - if (!_forwardedModules.contains(module) || + if (!forwardedModules.contains(module) || !_globalModules.contains(module)) module }; + forwardedModules = _forwardedModules = {}; } - _forwardedModules ??= {}; - _forwardedModuleNodes ??= {}; + // TODO: var + var forwardedModuleNodes = (_forwardedModuleNodes ??= {}); var forwardedVariableNames = forwarded.expand((module) => module.variables.keys).toSet(); @@ -368,30 +372,35 @@ class AsyncEnvironment { } } } - for (var module in _forwardedModules.toList()) { + + // TODO: no ! + for (var module in forwardedModules.toList()) { var shadowed = ShadowedModuleView.ifNecessary(module, variables: forwardedVariableNames, mixins: forwardedMixinNames, functions: forwardedFunctionNames); if (shadowed != null) { - _forwardedModules.remove(module); + forwardedModules.remove(module); if (!shadowed.isEmpty) { - _forwardedModules.add(shadowed); - _forwardedModuleNodes[shadowed] = - _forwardedModuleNodes.remove(module); + forwardedModules.add(shadowed); + forwardedModuleNodes[shadowed] = + forwardedModuleNodes.remove(module); } } } _globalModules.addAll(forwarded); - _globalModuleNodes.addAll(module._environment._forwardedModuleNodes); - _forwardedModules.addAll(forwarded); - _forwardedModuleNodes.addAll(module._environment._forwardedModuleNodes); + _globalModuleNodes + .addAll(module._environment._forwardedModuleNodes ?? const {}); + forwardedModules.addAll(forwarded); + forwardedModuleNodes + .addAll(module._environment._forwardedModuleNodes ?? const {}); } else { - _nestedForwardedModules ??= - List.generate(_variables.length - 1, (_) => []); - _nestedForwardedModules.last.addAll(forwarded); + (_nestedForwardedModules ??= + List.generate(_variables.length - 1, (_) => [])) + .last + .addAll(forwarded); } // Remove existing member definitions that are now shadowed by the @@ -399,7 +408,7 @@ class AsyncEnvironment { for (var variable in forwardedVariableNames) { _variableIndices.remove(variable); _variables.last.remove(variable); - if (_variableNodes != null) _variableNodes.last.remove(variable); + _variableNodes?.last.remove(variable); } for (var function in forwardedFunctionNames) { _functionIndices.remove(function); @@ -461,10 +470,17 @@ class AsyncEnvironment { /// required, since some nodes need to do real work to manufacture a source /// span. AstNode getVariableNode(String name, {String namespace}) { + var variableNodes = _variableNodes; + if (variableNodes == null) { + throw StateError( + "getVariableNodes() should only be called if sourceMap = true was " + "passed in."); + } + if (namespace != null) return _getModule(namespace).variableNodes[name]; if (_lastVariableName == name) { - return _variableNodes[_lastVariableIndex][name] ?? + return variableNodes[_lastVariableIndex][name] ?? _getVariableNodeFromGlobalModule(name); } @@ -472,7 +488,7 @@ class AsyncEnvironment { if (index != null) { _lastVariableName = name; _lastVariableIndex = index; - return _variableNodes[index][name] ?? + return variableNodes[index][name] ?? _getVariableNodeFromGlobalModule(name); } @@ -482,8 +498,7 @@ class AsyncEnvironment { _lastVariableName = name; _lastVariableIndex = index; _variableIndices[name] = index; - return _variableNodes[index][name] ?? - _getVariableNodeFromGlobalModule(name); + return variableNodes[index][name] ?? _getVariableNodeFromGlobalModule(name); } /// Returns the node for the variable named [name] from a namespaceless @@ -546,7 +561,7 @@ class AsyncEnvironment { /// defined with the given namespace, if no variable with the given [name] is /// defined in module with the given namespace, or if no [namespace] is passed /// and multiple global modules define variables named [name]. - void setVariable(String name, Value value, AstNode nodeWithSpan, + void setVariable(String name, Value /*!*/ value, AstNode /*?*/ nodeWithSpan, {String namespace, bool global = false}) { if (namespace != null) { _getModule(namespace).setVariable(name, value, nodeWithSpan); @@ -574,14 +589,15 @@ class AsyncEnvironment { } _variables.first[name] = value; - if (_variableNodes != null) _variableNodes.first[name] = nodeWithSpan; + if (nodeWithSpan != null) _variableNodes?.first[name] = nodeWithSpan; return; } - if (_nestedForwardedModules != null && + var nestedForwardedModules = _nestedForwardedModules; + if (nestedForwardedModules != null && !_variableIndices.containsKey(name) && _variableIndex(name) == null) { - for (var modules in _nestedForwardedModules.reversed) { + for (var modules in nestedForwardedModules.reversed) { for (var module in modules.reversed) { if (module.variables.containsKey(name)) { module.setVariable(name, value, nodeWithSpan); @@ -592,7 +608,7 @@ class AsyncEnvironment { } var index = _lastVariableName == name - ? _lastVariableIndex + ? _lastVariableIndex /*!*/ : _variableIndices.putIfAbsent( name, () => _variableIndex(name) ?? _variables.length - 1); if (!_inSemiGlobalScope && index == 0) { @@ -603,7 +619,7 @@ class AsyncEnvironment { _lastVariableName = name; _lastVariableIndex = index; _variables[index][name] = value; - if (_variableNodes != null) _variableNodes[index][name] = nodeWithSpan; + _variableNodes?.andGet(index)[name] = nodeWithSpan; } /// Sets the variable named [name] to [value], associated with @@ -615,13 +631,15 @@ class AsyncEnvironment { /// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - void setLocalVariable(String name, Value value, AstNode nodeWithSpan) { + void setLocalVariable(String name, Value value, AstNode /*?*/ nodeWithSpan) { var index = _variables.length - 1; _lastVariableName = name; _lastVariableIndex = index; _variableIndices[name] = index; _variables[index][name] = value; - if (_variableNodes != null) _variableNodes[index][name] = nodeWithSpan; + if (nodeWithSpan != null) { + _variableNodes?.andGet(index)[name] = nodeWithSpan; + } } /// Returns the value of the function named [name], optionally with the given @@ -804,10 +822,12 @@ class AsyncEnvironment { var values = _variables[i]; var nodes = _variableNodes == null ? {} : _variableNodes[i]; - for (var name in values.keys) { + // TODO: var nodes = _variableNodes.andGet(i) ?? {}; + for (var entry in values.entries) { // Implicit configurations are never invalid, making [configurationSpan] // unnecessary, so we pass null here to avoid having to compute it. - configuration[name] = ConfiguredValue(values[name], null, nodes[name]); + configuration[entry.key] = + ConfiguredValue(entry.value, null, nodes[entry.key]); } } return Configuration.implicit(configuration); @@ -858,9 +878,11 @@ class AsyncEnvironment { /// /// The [type] should be the singular name of the value type being returned. /// It's used to format an appropriate error message. - T _fromOneModule(String name, String type, T callback(Module module)) { - if (_nestedForwardedModules != null) { - for (var modules in _nestedForwardedModules.reversed) { + T _fromOneModule( + String name, String type, T /*?*/ callback(Module module)) { + var nestedForwardedModules = _nestedForwardedModules; + if (nestedForwardedModules != null) { + for (var modules in nestedForwardedModules.reversed) { for (var module in modules.reversed) { var value = callback(module); if (value != null) return value; @@ -880,11 +902,15 @@ class AsyncEnvironment { if (identityFromModule == identity) continue; if (value != null) { + // TODO no !, as + var spans = _globalModuleNodes.entries.map( + (entry) => callback(entry.key).andThen((_) => entry.value.span)); + throw MultiSpanSassScriptException( 'This $type is available from multiple global modules.', '$type use', { - for (var entry in _globalModuleNodes.entries) - if (callback(entry.key) != null) entry.value.span: 'includes $type' + for (var span in spans) + if (span != null) span: 'includes $type' }); } @@ -897,7 +923,7 @@ class AsyncEnvironment { /// A module that represents the top-level members defined in an [Environment]. class _EnvironmentModule implements Module { - Uri get url => css.span.sourceUrl; + Uri get url => css?.span.sourceUrl; final List upstream; final Map variables; @@ -931,10 +957,10 @@ class _EnvironmentModule implements Module { _makeModulesByVariable(forwarded), _memberMap(environment._variables.first, forwarded.map((module) => module.variables)), - environment._variableNodes == null - ? null - : _memberMap(environment._variableNodes.first, - forwarded.map((module) => module.variableNodes)), + environment._variableNodes.andThen((nodes) => _memberMap( + // TODO: no ! + nodes.first, + forwarded.map((module) => module.variableNodes /*!*/))), _memberMap(environment._functions.first, forwarded.map((module) => module.functions)), _memberMap(environment._mixins.first, @@ -1009,8 +1035,8 @@ class _EnvironmentModule implements Module { } _environment._variables.first[name] = value; - if (_environment._variableNodes != null) { - _environment._variableNodes.first[name] = nodeWithSpan; + if (nodeWithSpan != null) { + _environment._variableNodes?.first[name] = nodeWithSpan; } return; } diff --git a/lib/src/async_import_cache.dart b/lib/src/async_import_cache.dart index 70b869d3a..76a1c5568 100644 --- a/lib/src/async_import_cache.dart +++ b/lib/src/async_import_cache.dart @@ -31,11 +31,11 @@ class AsyncImportCache { /// /// This cache isn't used for relative imports, because they're /// context-dependent. - final Map, Tuple3> + final Map, Tuple3 /*?*/ > _canonicalizeCache; /// The parsed stylesheets for each canonicalized import URL. - final Map _importCache; + final Map _importCache; /// The import results for each canonicalized import URL. final Map _resultsCache; @@ -107,7 +107,7 @@ class AsyncImportCache { Future> canonicalize(Uri url, {AsyncImporter baseImporter, Uri baseUrl, bool forImport = false}) async { if (baseImporter != null) { - var resolvedUrl = baseUrl != null ? baseUrl.resolveUri(url) : url; + var resolvedUrl = baseUrl?.resolveUri(url) ?? url; var canonicalUrl = await _canonicalize(baseImporter, resolvedUrl, forImport); if (canonicalUrl != null) { @@ -115,6 +115,7 @@ class AsyncImportCache { } } + // TODO: no as return await putIfAbsentAsync(_canonicalizeCache, Tuple2(url, forImport), () async { for (var importer in _importers) { @@ -176,6 +177,7 @@ Relative canonical URLs are deprecated and will eventually be disallowed. /// Caches the result of the import and uses cached results if possible. Future importCanonical(AsyncImporter importer, Uri canonicalUrl, [Uri originalUrl]) async { + // TODO: no as return await putIfAbsentAsync(_importCache, canonicalUrl, () async { var result = await importer.load(canonicalUrl); if (result == null) return null; @@ -198,9 +200,11 @@ Relative canonical URLs are deprecated and will eventually be disallowed. // Display the URL with the shortest path length. var url = minBy( _canonicalizeCache.values - .where((tuple) => tuple?.item2 == canonicalUrl) + .whereNotNull() + .where((tuple) => tuple.item2 == canonicalUrl) .map((tuple) => tuple.item3), - (url) => url.path.length); + // TODO: no Uri + (Uri url) => url.path.length); if (url == null) return canonicalUrl; // Use the canonicalized basename so that we display e.g. diff --git a/lib/src/callable.dart b/lib/src/callable.dart index 89beb9601..d25353ff1 100644 --- a/lib/src/callable.dart +++ b/lib/src/callable.dart @@ -67,7 +67,8 @@ export 'callable/user_defined.dart'; abstract class Callable extends AsyncCallable { @Deprecated('Use `Callable.function` instead.') factory Callable(String name, String arguments, - ext.Value callback(List arguments)) => + ext.Value /*!*/ callback(List arguments)) => + // TODO: no as Callable.function(name, arguments, callback); /// Creates a function with the given [name] and [arguments] that runs @@ -113,7 +114,10 @@ abstract class Callable extends AsyncCallable { /// which provides access to keyword arguments using /// [SassArgumentList.keywords]. factory Callable.function(String name, String arguments, - ext.Value callback(List arguments)) => + ext.Value /*!*/ callback(List arguments)) => BuiltInCallable.function( - name, arguments, (arguments) => callback(arguments) as Value); + // TODO: no as + name, + arguments, + (arguments) => callback(arguments) as Value); } diff --git a/lib/src/callable/async.dart b/lib/src/callable/async.dart index 18ab8b69d..8315eaada 100644 --- a/lib/src/callable/async.dart +++ b/lib/src/callable/async.dart @@ -26,6 +26,7 @@ abstract class AsyncCallable { @Deprecated('Use `AsyncCallable.function` instead.') factory AsyncCallable(String name, String arguments, FutureOr callback(List arguments)) => + // TODO: no as AsyncCallable.function(name, arguments, callback); /// Creates a callable with the given [name] and [arguments] that runs @@ -37,6 +38,7 @@ abstract class AsyncCallable { /// See [new Callable] for more details. factory AsyncCallable.function(String name, String arguments, FutureOr callback(List arguments)) => + // TODO: no as AsyncBuiltInCallable.function(name, arguments, (arguments) { var result = callback(arguments); if (result is ext.Value) return result as Value; diff --git a/lib/src/callable/async_built_in.dart b/lib/src/callable/async_built_in.dart index 5d0ed1ccf..93d9e9ed3 100644 --- a/lib/src/callable/async_built_in.dart +++ b/lib/src/callable/async_built_in.dart @@ -11,7 +11,8 @@ import '../value.dart'; import 'async.dart'; /// An [AsyncBuiltInCallable]'s callback. -typedef _Callback = FutureOr Function(List arguments); +typedef _Callback = FutureOr /*!*/ Function( + List arguments); /// A callable defined in Dart code. /// @@ -37,7 +38,7 @@ class AsyncBuiltInCallable implements AsyncCallable { /// If passed, [url] is the URL of the module in which the function is /// defined. AsyncBuiltInCallable.function(String name, String arguments, - FutureOr callback(List arguments), {Object url}) + FutureOr callback(List arguments), {Object url}) : this.parsed( name, ArgumentDeclaration.parse('@function $name($arguments) {', @@ -73,6 +74,6 @@ class AsyncBuiltInCallable implements AsyncCallable { /// doesn't guarantee that [positional] and [names] are valid for the returned /// [ArgumentDeclaration]. Tuple2 callbackFor( - int positional, Set names) => + int positional, Set names) => Tuple2(_arguments, _callback); } diff --git a/lib/src/callable/built_in.dart b/lib/src/callable/built_in.dart index 58a9c5818..e328236f3 100644 --- a/lib/src/callable/built_in.dart +++ b/lib/src/callable/built_in.dart @@ -8,7 +8,7 @@ import '../ast/sass.dart'; import '../callable.dart'; import '../value.dart'; -typedef _Callback = Value Function(List arguments); +typedef _Callback = Value /*!*/ Function(List arguments); /// A callable defined in Dart code. /// @@ -30,9 +30,8 @@ class BuiltInCallable implements Callable, AsyncBuiltInCallable { /// /// If passed, [url] is the URL of the module in which the function is /// defined. - BuiltInCallable.function( - String name, String arguments, Value callback(List arguments), - {Object url}) + BuiltInCallable.function(String name, String arguments, + Value /*!*/ callback(List arguments), {Object url}) : this.parsed( name, ArgumentDeclaration.parse('@function $name($arguments) {', @@ -47,20 +46,21 @@ class BuiltInCallable implements Callable, AsyncBuiltInCallable { /// /// If passed, [url] is the URL of the module in which the mixin is /// defined. - BuiltInCallable.mixin( - String name, String arguments, void callback(List arguments), + BuiltInCallable.mixin(String name, String arguments, + void callback(List arguments), {Object url}) : this.parsed(name, ArgumentDeclaration.parse('@mixin $name($arguments) {', url: url), (arguments) { callback(arguments); - return null; + return sassNull; }); /// Creates a callable with a single [arguments] declaration and a single /// [callback]. BuiltInCallable.parsed(this.name, ArgumentDeclaration arguments, - Value callback(List arguments)) + Value /*!*/ callback(List arguments)) + // TODO: no as : _overloads = [Tuple2(arguments, callback)]; /// Creates a function with multiple implementations. @@ -73,7 +73,9 @@ class BuiltInCallable implements Callable, AsyncBuiltInCallable { /// If passed, [url] is the URL of the module in which the function is /// defined. BuiltInCallable.overloadedFunction( - this.name, Map overloads, + // TODO: use _Callback + this.name, + Map arguments)> overloads, {Object url}) : _overloads = [ for (var entry in overloads.entries) @@ -92,8 +94,8 @@ class BuiltInCallable implements Callable, AsyncBuiltInCallable { /// doesn't guarantee that [positional] and [names] are valid for the returned /// [ArgumentDeclaration]. Tuple2 callbackFor( - int positional, Set names) { - Tuple2 fuzzyMatch; + int positional, Set names) { + Tuple2 /*!*/ fuzzyMatch; int minMismatchDistance; for (var overload in _overloads) { diff --git a/lib/src/callable/plain_css.dart b/lib/src/callable/plain_css.dart index 9a74ed604..8e3284520 100644 --- a/lib/src/callable/plain_css.dart +++ b/lib/src/callable/plain_css.dart @@ -8,7 +8,7 @@ import '../callable.dart'; /// /// This can't be used for mixins. class PlainCssCallable implements Callable { - final String name; + final String /*!*/ name; PlainCssCallable(this.name); diff --git a/lib/src/callable/user_defined.dart b/lib/src/callable/user_defined.dart index 13a19a30a..72b9633bb 100644 --- a/lib/src/callable/user_defined.dart +++ b/lib/src/callable/user_defined.dart @@ -10,12 +10,12 @@ import '../callable.dart'; /// The type parameter [E] should either be `Environment` or `AsyncEnvironment`. class UserDefinedCallable implements Callable { /// The declaration. - final CallableDeclaration declaration; + final CallableDeclaration /*!*/ declaration; /// The environment in which this callable was declared. final E environment; - String get name => declaration.name; + String /*!*/ get name => declaration.name; UserDefinedCallable(this.declaration, this.environment); } diff --git a/lib/src/compile.dart b/lib/src/compile.dart index 72e2655d9..cdbed09c9 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: ef27d750f1d5305373fe9e712cf99697a21e0689 +// Checksum: a3fcd1e07a08124e0a60ff933944368e49ac0999 // // ignore_for_file: unused_import @@ -34,7 +34,7 @@ import 'visitor/serialize.dart'; /// the node-sass compatible API and the executable. /// /// At most one of `importCache` and `nodeImporter` may be provided at once. -CompileResult compile(String path, +CompileResult compile(String /*!*/ path, {Syntax syntax, Logger logger, ImportCache importCache, @@ -61,6 +61,7 @@ CompileResult compile(String path, } return _compileStylesheet( + // TODO: no ! stylesheet, logger, importCache, @@ -145,11 +146,12 @@ CompileResult _compileStylesheet( sourceMap: sourceMap, charset: charset); - if (serializeResult.sourceMap != null && importCache != null) { + var resultSourceMap = serializeResult.sourceMap; + if (resultSourceMap != null && importCache != null) { // TODO(nweiz): Don't explicitly use a type parameter when dart-lang/sdk#25490 // is fixed. mapInPlace( - serializeResult.sourceMap.urls, + resultSourceMap.urls, (url) => url == '' ? Uri.dataFromString(stylesheet.span.file.getText(0), encoding: utf8) diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index 2f4b29398..fae9c7ae0 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -12,6 +12,12 @@ import 'util/unprefixed_map_view.dart'; /// A set of variables meant to configure a module by overriding its /// `!default` declarations. +/// +/// A configuration may be either *implicit*, meaning that it's either empty or +/// created by importing a file containing a `@forward` rule; or *explicit*, +/// meaning that it's created by passing a `with` clause to a `@use` rule. +/// Explicit configurations have spans associated with them and are represented +/// by the [ExplicitConfiguration] subclass. class Configuration { /// A map from variable names (without `$`) to values. /// @@ -20,45 +26,20 @@ class Configuration { Map get values => UnmodifiableMapView(_values); final Map _values; - /// The node whose span indicates where the configuration was declared. - /// - /// This is `null` for implicit configurations. - final AstNode nodeWithSpan; - - /// Whether or not this configuration is implicit. - /// - /// Implicit configurations are created when a file containing a `@forward` - /// rule is imported, while explicit configurations are created by the - /// `with` clause of a `@use` rule. - /// - /// Both types of configuration pass through `@forward` rules, but explicit - /// configurations will cause an error if attempting to use them on a module - /// that has already been loaded, while implicit configurations will be - /// silently ignored in this case. - final bool isImplicit; - /// Creates an explicit configuration with the given [values]. - Configuration(Map values, this.nodeWithSpan) - : _values = values, - isImplicit = false; + factory Configuration( + Map values, AstNode nodeWithSpan) = + ExplicitConfiguration._; /// Creates an implicit configuration with the given [values]. - /// - /// See [isImplicit] for details. - Configuration.implicit(Map values) - : _values = values, - nodeWithSpan = null, - isImplicit = true; + Configuration.implicit(this._values); /// The empty configuration, which indicates that the module has not been /// configured. /// /// Empty configurations are always considered implicit, since they are /// ignored if the module has already been loaded. - const Configuration.empty() - : _values = const {}, - nodeWithSpan = null, - isImplicit = true; + const Configuration.empty() : _values = const {}; bool get isEmpty => values.isEmpty; @@ -76,16 +57,42 @@ class Configuration { // configured. These views support [Map.remove] so we can mark when a // configuration variable is used by removing it even when the underlying // map is wrapped. - if (forward.prefix != null) { - newValues = UnprefixedMapView(newValues, forward.prefix); - } - if (forward.shownVariables != null) { - newValues = LimitedMapView.safelist(newValues, forward.shownVariables); - } else if (forward.hiddenVariables?.isNotEmpty ?? false) { - newValues = LimitedMapView.blocklist(newValues, forward.hiddenVariables); + var prefix = forward.prefix; + if (prefix != null) newValues = UnprefixedMapView(newValues, prefix); + + var shownVariables = forward.shownVariables; + var hiddenVariables = forward.hiddenVariables; + if (shownVariables != null) { + newValues = LimitedMapView.safelist(newValues, shownVariables); + } else if (hiddenVariables != null && hiddenVariables.isNotEmpty) { + newValues = LimitedMapView.blocklist(newValues, hiddenVariables); } - return isImplicit - ? Configuration.implicit(newValues) - : Configuration(newValues, nodeWithSpan); + return _withValues(newValues); } + + /// Returns a copy of [this] with the given [values] map. + Configuration _withValues(Map values) => + Configuration.implicit(values); +} + +/// A [Configuratoin] that was created with an explicit `with` clause of a +/// `@use` rule. +/// +/// This is as opposed to *implicit* configurations, which are +/// +/// Both types of configuration pass through `@forward` rules, but explicit +/// configurations will cause an error if attempting to use them on a module +/// that has already been loaded, while implicit configurations will be +/// silently ignored in this case. +class ExplicitConfiguration extends Configuration { + /// The node whose span indicates where the configuration was declared. + final AstNode /*!*/ nodeWithSpan; + + ExplicitConfiguration._( + Map values, this.nodeWithSpan) + : super.implicit(values); + + /// Returns a copy of [this] with the given [values] map. + Configuration _withValues(Map values) => + Configuration(values, nodeWithSpan); } diff --git a/lib/src/configured_value.dart b/lib/src/configured_value.dart index beeb76193..bb732a9e2 100644 --- a/lib/src/configured_value.dart +++ b/lib/src/configured_value.dart @@ -10,16 +10,15 @@ import 'value.dart'; /// A variable value that's been configured for a [Configuration]. class ConfiguredValue { /// The value of the variable. - final Value value; + final Value /*!*/ value; /// The span where the variable's configuration was written. final FileSpan configurationSpan; /// The [AstNode] where the variable's value originated. /// - /// This is used to generate source maps and can be `null` if source map - /// generation is disabled. - final AstNode assignmentNode; + /// This is used to generate source maps. + final AstNode /*?*/ assignmentNode; - ConfiguredValue(this.value, this.configurationSpan, [this.assignmentNode]); + ConfiguredValue(this.value, this.configurationSpan, this.assignmentNode); } diff --git a/lib/src/environment.dart b/lib/src/environment.dart index f9d6eaa9e..bd3b682ab 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: 9f4ee98a1c9e90d8d5277e0c2b0355460cda8788 +// Checksum: cd1babbf1959dde9f4a31d720a7a48e613ad6aa9 // // ignore_for_file: unused_import @@ -27,6 +27,7 @@ import 'module.dart'; import 'module/forwarded_view.dart'; import 'module/shadowed_view.dart'; import 'util/merged_map_view.dart'; +import 'util/nullable.dart'; import 'util/public_member_map_view.dart'; import 'utils.dart'; import 'value.dart'; @@ -50,7 +51,7 @@ class Environment { /// A map from modules in [_globalModules] to the nodes whose spans /// indicate where those modules were originally loaded. - final Map, AstNode> _globalModuleNodes; + final Map, AstNode /*!*/ > _globalModuleNodes; /// The modules forwarded by this module. /// @@ -61,7 +62,7 @@ class Environment { /// indicate where those modules were originally forwarded. /// /// This is `null` if there are no forwarded modules. - Map, AstNode> _forwardedModuleNodes; + Map, AstNode /*!*/ > _forwardedModuleNodes; /// Modules forwarded by nested imports at each lexical scope level *beneath /// the global scope*. @@ -80,7 +81,7 @@ class Environment { /// /// The first element is the global scope, and each successive element is /// deeper in the tree. - final List> _variables; + final List> _variables; /// The nodes where each variable in [_variables] was defined. /// @@ -89,12 +90,12 @@ class Environment { /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - final List> _variableNodes; + final List> _variableNodes; /// A map of variable names to their indices in [_variables]. /// /// This map is filled in as-needed, and may not be complete. - final Map _variableIndices; + final Map _variableIndices; /// A list of functions defined at each lexical scope level. /// @@ -256,10 +257,11 @@ class Environment { } } else { if (_modules.containsKey(namespace)) { + var span = _namespaceNodes[namespace]?.span; throw MultiSpanSassScriptException( "There's already a module with namespace \"$namespace\".", "new @use", - {_namespaceNodes[namespace].span: "original @use"}); + {if (span != null) span: "original @use"}); } _modules[namespace] = module; @@ -271,16 +273,16 @@ class Environment { /// Exposes the members in [module] to downstream modules as though they were /// defined in this module, according to the modifications defined by [rule]. void forwardModule(Module module, ForwardRule rule) { - _forwardedModules ??= {}; - _forwardedModuleNodes ??= {}; + var forwardedModules = (_forwardedModules ??= {}); + var forwardedModuleNodes = (_forwardedModuleNodes ??= {}); var view = ForwardedModuleView.ifNecessary(module, rule); - for (var other in _forwardedModules) { + for (var other in forwardedModules) { _assertNoConflicts( - view.variables, other.variables, view, other, "variable", rule); + view.variables, other.variables, view, other, "variable"); _assertNoConflicts( - view.functions, other.functions, view, other, "function", rule); - _assertNoConflicts(view.mixins, other.mixins, view, other, "mixin", rule); + view.functions, other.functions, view, other, "function"); + _assertNoConflicts(view.mixins, other.mixins, view, other, "mixin"); } // Add the original module to [_allModules] (rather than the @@ -288,8 +290,8 @@ class Environment { // `==`. This is safe because upstream modules are only used for collating // CSS, not for the members they expose. _allModules.add(module); - _forwardedModules.add(view); - _forwardedModuleNodes[view] = rule; + forwardedModules.add(view); + forwardedModuleNodes[view] = rule; } /// Throws a [SassScriptException] if [newMembers] from [newModule] has any @@ -301,8 +303,7 @@ class Environment { Map oldMembers, Module newModule, Module oldModule, - String type, - AstNode newModuleNodeWithSpan) { + String type) { Map smaller; Map larger; if (newMembers.length < oldMembers.length) { @@ -322,10 +323,11 @@ class Environment { } if (type == "variable") name = "\$$name"; + var span = _forwardedModuleNodes.andGet(oldModule)?.span; throw MultiSpanSassScriptException( 'Two forwarded modules both define a $type named $name.', "new @forward", - {_forwardedModuleNodes[oldModule].span: "original @forward"}); + {if (span != null) span: "original @forward"}); } } @@ -340,17 +342,19 @@ class Environment { // Omit modules from [forwarded] that are already globally available and // forwarded in this module. - if (_forwardedModules != null) { + var forwardedModules = _forwardedModules; + if (forwardedModules != null) { forwarded = { for (var module in forwarded) - if (!_forwardedModules.contains(module) || + if (!forwardedModules.contains(module) || !_globalModules.contains(module)) module }; + forwardedModules = _forwardedModules = {}; } - _forwardedModules ??= {}; - _forwardedModuleNodes ??= {}; + // TODO: var + var forwardedModuleNodes = (_forwardedModuleNodes ??= {}); var forwardedVariableNames = forwarded.expand((module) => module.variables.keys).toSet(); @@ -376,30 +380,35 @@ class Environment { } } } - for (var module in _forwardedModules.toList()) { + + // TODO: no ! + for (var module in forwardedModules.toList()) { var shadowed = ShadowedModuleView.ifNecessary(module, variables: forwardedVariableNames, mixins: forwardedMixinNames, functions: forwardedFunctionNames); if (shadowed != null) { - _forwardedModules.remove(module); + forwardedModules.remove(module); if (!shadowed.isEmpty) { - _forwardedModules.add(shadowed); - _forwardedModuleNodes[shadowed] = - _forwardedModuleNodes.remove(module); + forwardedModules.add(shadowed); + forwardedModuleNodes[shadowed] = + forwardedModuleNodes.remove(module); } } } _globalModules.addAll(forwarded); - _globalModuleNodes.addAll(module._environment._forwardedModuleNodes); - _forwardedModules.addAll(forwarded); - _forwardedModuleNodes.addAll(module._environment._forwardedModuleNodes); + _globalModuleNodes + .addAll(module._environment._forwardedModuleNodes ?? const {}); + forwardedModules.addAll(forwarded); + forwardedModuleNodes + .addAll(module._environment._forwardedModuleNodes ?? const {}); } else { - _nestedForwardedModules ??= - List.generate(_variables.length - 1, (_) => []); - _nestedForwardedModules.last.addAll(forwarded); + (_nestedForwardedModules ??= + List.generate(_variables.length - 1, (_) => [])) + .last + .addAll(forwarded); } // Remove existing member definitions that are now shadowed by the @@ -407,7 +416,7 @@ class Environment { for (var variable in forwardedVariableNames) { _variableIndices.remove(variable); _variables.last.remove(variable); - if (_variableNodes != null) _variableNodes.last.remove(variable); + _variableNodes?.last.remove(variable); } for (var function in forwardedFunctionNames) { _functionIndices.remove(function); @@ -469,10 +478,17 @@ class Environment { /// required, since some nodes need to do real work to manufacture a source /// span. AstNode getVariableNode(String name, {String namespace}) { + var variableNodes = _variableNodes; + if (variableNodes == null) { + throw StateError( + "getVariableNodes() should only be called if sourceMap = true was " + "passed in."); + } + if (namespace != null) return _getModule(namespace).variableNodes[name]; if (_lastVariableName == name) { - return _variableNodes[_lastVariableIndex][name] ?? + return variableNodes[_lastVariableIndex][name] ?? _getVariableNodeFromGlobalModule(name); } @@ -480,7 +496,7 @@ class Environment { if (index != null) { _lastVariableName = name; _lastVariableIndex = index; - return _variableNodes[index][name] ?? + return variableNodes[index][name] ?? _getVariableNodeFromGlobalModule(name); } @@ -490,8 +506,7 @@ class Environment { _lastVariableName = name; _lastVariableIndex = index; _variableIndices[name] = index; - return _variableNodes[index][name] ?? - _getVariableNodeFromGlobalModule(name); + return variableNodes[index][name] ?? _getVariableNodeFromGlobalModule(name); } /// Returns the node for the variable named [name] from a namespaceless @@ -554,7 +569,7 @@ class Environment { /// defined with the given namespace, if no variable with the given [name] is /// defined in module with the given namespace, or if no [namespace] is passed /// and multiple global modules define variables named [name]. - void setVariable(String name, Value value, AstNode nodeWithSpan, + void setVariable(String name, Value /*!*/ value, AstNode /*?*/ nodeWithSpan, {String namespace, bool global = false}) { if (namespace != null) { _getModule(namespace).setVariable(name, value, nodeWithSpan); @@ -582,14 +597,15 @@ class Environment { } _variables.first[name] = value; - if (_variableNodes != null) _variableNodes.first[name] = nodeWithSpan; + if (nodeWithSpan != null) _variableNodes?.first[name] = nodeWithSpan; return; } - if (_nestedForwardedModules != null && + var nestedForwardedModules = _nestedForwardedModules; + if (nestedForwardedModules != null && !_variableIndices.containsKey(name) && _variableIndex(name) == null) { - for (var modules in _nestedForwardedModules.reversed) { + for (var modules in nestedForwardedModules.reversed) { for (var module in modules.reversed) { if (module.variables.containsKey(name)) { module.setVariable(name, value, nodeWithSpan); @@ -600,7 +616,7 @@ class Environment { } var index = _lastVariableName == name - ? _lastVariableIndex + ? _lastVariableIndex /*!*/ : _variableIndices.putIfAbsent( name, () => _variableIndex(name) ?? _variables.length - 1); if (!_inSemiGlobalScope && index == 0) { @@ -611,7 +627,7 @@ class Environment { _lastVariableName = name; _lastVariableIndex = index; _variables[index][name] = value; - if (_variableNodes != null) _variableNodes[index][name] = nodeWithSpan; + _variableNodes?.andGet(index)[name] = nodeWithSpan; } /// Sets the variable named [name] to [value], associated with @@ -623,13 +639,15 @@ class Environment { /// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - void setLocalVariable(String name, Value value, AstNode nodeWithSpan) { + void setLocalVariable(String name, Value value, AstNode /*?*/ nodeWithSpan) { var index = _variables.length - 1; _lastVariableName = name; _lastVariableIndex = index; _variableIndices[name] = index; _variables[index][name] = value; - if (_variableNodes != null) _variableNodes[index][name] = nodeWithSpan; + if (nodeWithSpan != null) { + _variableNodes?.andGet(index)[name] = nodeWithSpan; + } } /// Returns the value of the function named [name], optionally with the given @@ -810,10 +828,12 @@ class Environment { var values = _variables[i]; var nodes = _variableNodes == null ? {} : _variableNodes[i]; - for (var name in values.keys) { + // TODO: var nodes = _variableNodes.andGet(i) ?? {}; + for (var entry in values.entries) { // Implicit configurations are never invalid, making [configurationSpan] // unnecessary, so we pass null here to avoid having to compute it. - configuration[name] = ConfiguredValue(values[name], null, nodes[name]); + configuration[entry.key] = + ConfiguredValue(entry.value, null, nodes[entry.key]); } } return Configuration.implicit(configuration); @@ -865,9 +885,10 @@ class Environment { /// The [type] should be the singular name of the value type being returned. /// It's used to format an appropriate error message. T _fromOneModule( - String name, String type, T callback(Module module)) { - if (_nestedForwardedModules != null) { - for (var modules in _nestedForwardedModules.reversed) { + String name, String type, T /*?*/ callback(Module module)) { + var nestedForwardedModules = _nestedForwardedModules; + if (nestedForwardedModules != null) { + for (var modules in nestedForwardedModules.reversed) { for (var module in modules.reversed) { var value = callback(module); if (value != null) return value; @@ -887,11 +908,15 @@ class Environment { if (identityFromModule == identity) continue; if (value != null) { + // TODO no !, as + var spans = _globalModuleNodes.entries.map( + (entry) => callback(entry.key).andThen((_) => entry.value.span)); + throw MultiSpanSassScriptException( 'This $type is available from multiple global modules.', '$type use', { - for (var entry in _globalModuleNodes.entries) - if (callback(entry.key) != null) entry.value.span: 'includes $type' + for (var span in spans) + if (span != null) span: 'includes $type' }); } @@ -904,7 +929,7 @@ class Environment { /// A module that represents the top-level members defined in an [Environment]. class _EnvironmentModule implements Module { - Uri get url => css.span.sourceUrl; + Uri get url => css?.span.sourceUrl; final List> upstream; final Map variables; @@ -938,10 +963,10 @@ class _EnvironmentModule implements Module { _makeModulesByVariable(forwarded), _memberMap(environment._variables.first, forwarded.map((module) => module.variables)), - environment._variableNodes == null - ? null - : _memberMap(environment._variableNodes.first, - forwarded.map((module) => module.variableNodes)), + environment._variableNodes.andThen((nodes) => _memberMap( + // TODO: no ! + nodes.first, + forwarded.map((module) => module.variableNodes /*!*/))), _memberMap(environment._functions.first, forwarded.map((module) => module.functions)), _memberMap(environment._mixins.first, @@ -1017,8 +1042,8 @@ class _EnvironmentModule implements Module { } _environment._variables.first[name] = value; - if (_environment._variableNodes != null) { - _environment._variableNodes.first[name] = nodeWithSpan; + if (nodeWithSpan != null) { + _environment._variableNodes?.first[name] = nodeWithSpan; } return; } diff --git a/lib/src/exception.dart b/lib/src/exception.dart index 72d61ee82..3aaf7b3c1 100644 --- a/lib/src/exception.dart +++ b/lib/src/exception.dart @@ -7,6 +7,7 @@ import 'package:source_span/source_span.dart'; import 'package:stack_trace/stack_trace.dart'; import 'package:term_glyph/term_glyph.dart' as term_glyph; +import 'util/nullable.dart'; import 'utils.dart'; import 'value.dart'; @@ -17,14 +18,13 @@ class SassException extends SourceSpanException { /// This includes [span]. Trace get trace => Trace([frameForSpan(span, "root stylesheet")]); - FileSpan get span => super.span as FileSpan; + FileSpan /*?*/ get span => super.span as FileSpan; - SassException(String message, FileSpan span) : super(message, span); + SassException(String message, FileSpan /*?*/ span) : super(message, span); String toString({Object color}) { - var buffer = StringBuffer() - ..writeln("Error: $message") - ..write(span.highlight(color: color)); + var buffer = StringBuffer("Error: $message"); + span?.highlight(color: color).andThen(buffer.write); for (var frame in trace.toString().split("\n")) { if (frame.isEmpty) continue; @@ -84,7 +84,7 @@ class MultiSpanSassException extends SassException final Map secondarySpans; MultiSpanSassException(String message, FileSpan span, this.primaryLabel, - Map secondarySpans) + Map secondarySpans) : secondarySpans = Map.unmodifiable(secondarySpans), super(message, span); @@ -98,12 +98,14 @@ class MultiSpanSassException extends SassException useColor = true; } - var buffer = StringBuffer() - ..writeln("Error: $message") - ..write(span.highlightMultiple(primaryLabel, secondarySpans, - color: useColor, - primaryColor: primaryColor, - secondaryColor: secondaryColor)); + var buffer = StringBuffer("Error: $message"); + + span + ?.highlightMultiple(primaryLabel, secondarySpans, + color: useColor, + primaryColor: primaryColor, + secondaryColor: secondaryColor) + .andThen(buffer.write); for (var frame in trace.toString().split("\n")) { if (frame.isEmpty) continue; @@ -127,17 +129,21 @@ class MultiSpanSassRuntimeException extends MultiSpanSassException implements SassRuntimeException { final Trace trace; - MultiSpanSassRuntimeException(String message, FileSpan span, - String primaryLabel, Map secondarySpans, this.trace) + MultiSpanSassRuntimeException( + String message, + FileSpan span, + String primaryLabel, + Map secondarySpans, + this.trace) : super(message, span, primaryLabel, secondarySpans); } /// An exception thrown when Sass parsing has failed. class SassFormatException extends SassException implements SourceSpanFormatException { - String get source => span.file.getText(0); + String get source => span?.file.getText(0); - int get offset => span.start.offset; + int get offset => span?.start.offset; SassFormatException(String message, FileSpan span) : super(message, span); } @@ -165,8 +171,8 @@ class MultiSpanSassScriptException extends SassScriptException { /// See [MultiSourceSpanException.secondarySpans]. final Map secondarySpans; - MultiSpanSassScriptException( - String message, this.primaryLabel, Map secondarySpans) + MultiSpanSassScriptException(String message, this.primaryLabel, + Map secondarySpans) : secondarySpans = Map.unmodifiable(secondarySpans), super(message); } diff --git a/lib/src/executable/compile_stylesheet.dart b/lib/src/executable/compile_stylesheet.dart index fe1e37df9..2359a05d6 100644 --- a/lib/src/executable/compile_stylesheet.dart +++ b/lib/src/executable/compile_stylesheet.dart @@ -155,6 +155,8 @@ String _writeSourceMap( url = Uri.dataFromString(sourceMapText, mimeType: 'application/json', encoding: utf8); } else { + // [destination] can't be null here because --embed-source-map is + // incompatible with writing to stdout. var sourceMapPath = destination + '.map'; ensureDir(p.dirname(sourceMapPath)); writeFile(sourceMapPath, sourceMapText); diff --git a/lib/src/executable/options.dart b/lib/src/executable/options.dart index 5fb54eb4d..ca33e8fa7 100644 --- a/lib/src/executable/options.dart +++ b/lib/src/executable/options.dart @@ -10,6 +10,7 @@ import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:term_glyph/term_glyph.dart' as term_glyph; +import 'package:tuple/tuple.dart'; import '../../sass.dart'; import '../io.dart'; @@ -129,13 +130,32 @@ class ExecutableOptions { final ArgResults _options; /// Whether to print the version of Sass and exit. - bool get version => _options['version'] as bool; + bool get version => _options['version'] as bool /*!*/; + +// TODO: +// /// Whether to run an interactive shell. +// late final bool interactive = () { +// if (!(_options['interactive'] as bool)) return false; + +// var invalidOptions = [ +// 'stdin', 'indented', 'style', 'source-map', 'source-map-urls', // +// 'embed-sources', 'embed-source-map', 'update', 'watch' +// ]; +// for (var option in invalidOptions) { +// if (_options.wasParsed(option)) { +// throw UsageException("--$option isn't allowed with --interactive."); +// } +// } +// return true; +// }(); /// Whether to run an interactive shell. - bool get interactive { + /*late*/ bool /*!*/ get interactive { if (_interactive != null) return _interactive; - _interactive = _options['interactive'] as bool; - if (!_interactive) return false; + + // TODO: remove ? + var interactive = (_interactive = _options['interactive'] as bool); + if (!interactive) return false; var invalidOptions = [ 'stdin', 'indented', 'style', 'source-map', 'source-map-urls', // @@ -155,20 +175,20 @@ class ExecutableOptions { /// /// This may be `null`, indicating that this should be determined by each /// stylesheet's extension. - bool get indented => _ifParsed('indented') as bool; + bool get indented => _ifParsed('indented') as bool /*!*/; /// Whether to use ANSI terminal colors. bool get color => _options.wasParsed('color') - ? _options['color'] as bool + ? _options['color'] as bool /*!*/ : supportsAnsiEscapes; /// Whether to use non-ASCII Unicode glyphs. bool get unicode => _options.wasParsed('unicode') - ? _options['unicode'] as bool + ? _options['unicode'] as bool /*!*/ : !term_glyph.ascii; /// Whether to silence normal output. - bool get quiet => _options['quiet'] as bool; + bool get quiet => _options['quiet'] as bool /*!*/; /// The logger to use to emit messages from Sass. Logger get logger => quiet ? Logger.quiet : Logger.stderr(color: color); @@ -180,33 +200,34 @@ class ExecutableOptions { /// Whether to include a `@charset` declaration or a BOM if the stylesheet /// contains any non-ASCII characters. - bool get charset => _options['charset'] as bool; + bool get charset => _options['charset'] as bool /*!*/; /// The set of paths Sass in which should look for imported files. - List get loadPaths => _options['load-path'] as List; + List get loadPaths => _options['load-path'] as List /*!*/; /// Whether to run the evaluator in asynchronous mode, for debugging purposes. - bool get asynchronous => _options['async'] as bool; + bool get asynchronous => _options['async'] as bool /*!*/; /// Whether to print the full Dart stack trace on exceptions. - bool get trace => _options['trace'] as bool; + bool get trace => _options['trace'] as bool /*!*/; /// Whether to update only files that have changed since the last compilation. - bool get update => _options['update'] as bool; + bool get update => _options['update'] as bool /*!*/; /// Whether to continuously watch the filesystem for changes. - bool get watch => _options['watch'] as bool; + bool get watch => _options['watch'] as bool /*!*/; /// Whether to manually poll for changes when watching. - bool get poll => _options['poll'] as bool; + bool get poll => _options['poll'] as bool /*!*/; /// Whether to stop compiling additional files once one file produces an /// error. - bool get stopOnError => _options['stop-on-error'] as bool; + bool get stopOnError => _options['stop-on-error'] as bool /*!*/; /// Whether to emit error messages as CSS stylesheets bool get emitErrorCss => - _options['error-css'] as bool ?? + _options['error-css'] as bool /*!*/ ?? + // TODO: remove as sourcesToDestinations.values.any((destination) => destination != null); /// A map from source paths to the destination paths where the compiled CSS @@ -217,7 +238,7 @@ class ExecutableOptions { /// A `null` source indicates that a stylesheet should be read from standard /// input. A `null` destination indicates that a stylesheet should be written /// to standard output. - Map get sourcesToDestinations { + Map /*!*/ get sourcesToDestinations { _ensureSources(); return _sourcesToDestinations; } @@ -228,19 +249,19 @@ class ExecutableOptions { /// compiled CSS for stylesheets in the source directories should be written. /// /// Considers keys to be the same if they represent the same path on disk. - Map get sourceDirectoriesToDestinations { + Map /*!*/ get sourceDirectoriesToDestinations { _ensureSources(); return _sourceDirectoriesToDestinations; } - Map _sourceDirectoriesToDestinations; + /*late final*/ Map _sourceDirectoriesToDestinations; /// Ensure that both [sourcesToDestinations] and [sourceDirectories] have been /// computed. void _ensureSources() { if (_sourcesToDestinations != null) return; - var stdin = _options['stdin'] as bool; + var stdin = _options['stdin'] as bool /*!*/; if (_options.rest.isEmpty && !stdin) _fail("Compile Sass to CSS."); var directories = {}; @@ -302,8 +323,11 @@ class ExecutableOptions { _fail("--watch is not allowed when printing to stdout."); } } - _sourcesToDestinations = - UnmodifiableMapView(p.PathMap.of({source: destination})); + + var map = p.PathMap< + String /*?*/ >(); // p.PathMap.of() doesn't support null keys. + map[source] = destination; + _sourcesToDestinations = UnmodifiableMapView(map); } _sourceDirectoriesToDestinations = const {}; return; @@ -314,7 +338,7 @@ class ExecutableOptions { // Track [seen] separately from `sourcesToDestinations.keys` because we want // to report errors for sources as users entered them, rather than after // directories have been resolved. - var seen = {}; + var seen = {}; var sourcesToDestinations = p.PathMap(); var sourceDirectoriesToDestinations = p.PathMap(); for (var argument in _options.rest) { @@ -326,25 +350,9 @@ class ExecutableOptions { continue; } - String source; - String destination; - for (var i = 0; i < argument.length; i++) { - // A colon at position 1 may be a Windows drive letter and not a - // separator. - if (i == 1 && _isWindowsPath(argument, i - 1)) continue; - - if (argument.codeUnitAt(i) == $colon) { - if (source == null) { - source = argument.substring(0, i); - destination = argument.substring(i + 1); - } else if (i != source.length + 2 || - !_isWindowsPath(argument, i - 1)) { - // A colon 2 characters after the separator may also be a Windows - // drive letter. - _fail('"$argument" may only contain one ":".'); - } - } - } + var sourceAndDestination = _splitSourceAndDestination(argument); + var source = sourceAndDestination.item1; + var destination = sourceAndDestination.item2; if (!seen.add(source)) _fail('Duplicate source "$source".'); @@ -362,6 +370,30 @@ class ExecutableOptions { UnmodifiableMapView(sourceDirectoriesToDestinations); } + /// Splits an argument that contains a colon and returns its source and its + /// destination component. + Tuple2 _splitSourceAndDestination(String argument) { + for (var i = 0; i < argument.length; i++) { + // A colon at position 1 may be a Windows drive letter and not a + // separator. + if (i == 1 && _isWindowsPath(argument, i - 1)) continue; + + if (argument.codeUnitAt(i) == $colon) { + var nextColon = argument.indexOf(':', i + 1); + // A colon 2 characters after the separator may also be a Windows + // drive letter. + if (nextColon == i + 2 && _isWindowsPath(argument, i + 1)) { + nextColon = argument.indexOf(':', nextColon); + } + if (nextColon != -1) _fail('"$argument" may only contain one ":".'); + + return Tuple2(argument.substring(0, i), argument.substring(i + 1)); + } + } + + throw ArgumentError('Expected "$argument" to contain a colon.'); + } + /// Returns whether [string] contains an absolute Windows path at [index]. bool _isWindowsPath(String string, int index) => string.length > index + 2 && @@ -370,7 +402,8 @@ class ExecutableOptions { /// Returns the sub-map of [sourcesToDestinations] for the given [source] and /// [destination] directories. - Map _listSourceDirectory(String source, String destination) { + Map _listSourceDirectory( + String /*!*/ source, String /*!*/ destination) { return { for (var path in listDir(source, recursive: true)) if (_isEntrypoint(path) && @@ -404,7 +437,7 @@ class ExecutableOptions { _fail("--embed-source-map isn't allowed with --no-source-map."); } } - if (!_writeToStdout) return _options['source-map'] as bool; + if (!_writeToStdout) return _options['source-map'] as bool /*!*/; if (_ifParsed('source-map-urls') == 'relative') { _fail( @@ -412,7 +445,7 @@ class ExecutableOptions { } if (_options['embed-source-map'] as bool) { - return _options['source-map'] as bool; + return _options['source-map'] as bool /*!*/; } else if (_ifParsed('source-map') == true) { _fail( "When printing to stdout, --source-map requires --embed-source-map."); @@ -428,10 +461,10 @@ class ExecutableOptions { } /// Whether to embed the generated source map as a data URL in the output CSS. - bool get embedSourceMap => _options['embed-source-map'] as bool; + bool get embedSourceMap => _options['embed-source-map'] as bool /*!*/; /// Whether to embed the source files in the generated source map. - bool get embedSources => _options['embed-sources'] as bool; + bool get embedSources => _options['embed-sources'] as bool /*!*/; /// Parses options from [args]. /// @@ -461,7 +494,9 @@ class ExecutableOptions { var path = p.fromUri(url); return p.toUri(_options['source-map-urls'] == 'relative' && !_writeToStdout - ? p.relative(path, from: p.dirname(destination)) + // [destination] can't be null here because --source-map-urls=relative + // is incompatible with writing to stdout. + ? p.relative(path, from: p.dirname(destination /*!*/)) : p.absolute(path)); } diff --git a/lib/src/executable/watch.dart b/lib/src/executable/watch.dart index 3d06ae14f..fb318cf45 100644 --- a/lib/src/executable/watch.dart +++ b/lib/src/executable/watch.dart @@ -20,8 +20,8 @@ import 'options.dart'; /// Watches all the files in [graph] for changes and updates them as necessary. Future watch(ExecutableOptions options, StylesheetGraph graph) async { var directoriesToWatch = [ - ...options.sourceDirectoriesToDestinations.keys, - ...options.sourcesToDestinations.keys.map(p.dirname), + ..._sourceDirectoriesToDestinations(options).keys, + for (var dir in _sourcesToDestinations(options).keys) p.dirname(dir), ...options.loadPaths ]; @@ -39,12 +39,12 @@ Future watch(ExecutableOptions options, StylesheetGraph graph) async { // they currently exist. This ensures that changes that come in update a // known-good state. var watcher = _Watcher(options, graph); - for (var source in options.sourcesToDestinations.keys) { - var destination = options.sourcesToDestinations[source]; - graph.addCanonical( - FilesystemImporter('.'), p.toUri(canonicalize(source)), p.toUri(source), + for (var entry in _sourcesToDestinations(options).entries) { + graph.addCanonical(FilesystemImporter('.'), + p.toUri(canonicalize(entry.key)), p.toUri(entry.key), recanonicalize: false); - var success = await watcher.compile(source, destination, ifModified: true); + var success = + await watcher.compile(entry.key, entry.value, ifModified: true); if (!success && options.stopOnError) { dirWatcher.events.listen(null).cancel(); return; @@ -90,7 +90,7 @@ class _Watcher { } /// Deletes the file at [path] and prints a message about it. - void _delete(String path) { + void _delete(String /*!*/ path) { try { deleteFile(path); var buffer = StringBuffer(); @@ -150,12 +150,13 @@ class _Watcher { /// Returns whether all necessary recompilations succeeded. Future _handleModify(String path) async { var url = _canonicalize(path); - if (!_graph.nodes.containsKey(url)) return _handleAdd(path); - // Access the node ahead-of-time because it's possible that - // `_graph.reload()` notices the file has been deleted and removes it from - // the graph. + // It's important to access the node ahead-of-time because it's possible + // that `_graph.reload()` notices the file has been deleted and removes it + // from the graph. var node = _graph.nodes[url]; + if (node == null) return _handleAdd(path); + _graph.reload(url); return await _recompileDownstream([node]); } @@ -208,8 +209,10 @@ class _Watcher { } } - return typeForPath.keys - .map((path) => WatchEvent(typeForPath[path], path)); + return [ + for (var entry in typeForPath.entries) + WatchEvent(entry.value, entry.key) + ]; }); } @@ -217,7 +220,8 @@ class _Watcher { /// necessary. /// /// Returns whether all recompilations succeeded. - Future _recompileDownstream(Iterable nodes) async { + Future _recompileDownstream( + Iterable nodes) async { var seen = {}; var toRecompile = Queue.of(nodes); @@ -254,16 +258,15 @@ class _Watcher { /// /// Otherwise, returns `null`. String _destinationFor(String source) { - var destination = _options.sourcesToDestinations[source]; + var destination = _sourcesToDestinations(_options)[source]; if (destination != null) return destination; if (p.basename(source).startsWith('_')) return null; - for (var sourceDir in _options.sourceDirectoriesToDestinations.keys) { - if (!p.isWithin(sourceDir, source)) continue; + for (var entry in _sourceDirectoriesToDestinations(_options).entries) { + if (!p.isWithin(entry.key, source)) continue; - var destination = p.join( - _options.sourceDirectoriesToDestinations[sourceDir], - p.setExtension(p.relative(source, from: sourceDir), '.css')); + var destination = p.join(entry.value, + p.setExtension(p.relative(source, from: entry.key), '.css')); // Don't compile ".css" files to their own locations. if (!p.equals(destination, source)) return destination; @@ -272,3 +275,14 @@ class _Watcher { return null; } } + +/// Exposes [options.sourcesToDestinations] as a non-nullable map, since stdin +/// inputs and stdout outputs aren't allowed in `--watch` mode. +Map _sourcesToDestinations(ExecutableOptions options) => + options.sourcesToDestinations.cast(); + +/// Exposes [options.sourcesDirectoriesToDestinations] as a non-nullable map, +/// since stdin inputs and stdout outputs aren't allowed in `--watch` mode. +Map _sourceDirectoriesToDestinations( + ExecutableOptions options) => + options.sourceDirectoriesToDestinations.cast(); diff --git a/lib/src/extend/empty_extender.dart b/lib/src/extend/empty_extender.dart index 013ed0efa..db47eb31a 100644 --- a/lib/src/extend/empty_extender.dart +++ b/lib/src/extend/empty_extender.dart @@ -25,7 +25,7 @@ class EmptyExtender implements Extender { const []; ModifiableCssValue addSelector( - SelectorList selector, FileSpan span, + SelectorList selector, FileSpan /*!*/ span, [List mediaContext]) { throw UnsupportedError( "addSelector() can't be called for a const Extender."); diff --git a/lib/src/extend/extender.dart b/lib/src/extend/extender.dart index 32ca2ef23..277b9a919 100644 --- a/lib/src/extend/extender.dart +++ b/lib/src/extend/extender.dart @@ -14,6 +14,7 @@ import '../ast/selector.dart'; import '../ast/sass.dart'; import '../exception.dart'; import '../utils.dart'; +import '../util/nullable.dart'; import 'empty_extender.dart'; import 'extension.dart'; import 'merged_extension.dart'; @@ -34,7 +35,7 @@ class Extender { /// A map from all extended simple selectors to the sources of those /// extensions. - final Map> _extensions; + final Map /*!*/ > _extensions; /// A map from all simple selectors in extenders to the extensions that those /// extenders define. @@ -143,9 +144,9 @@ class Extender { /// returned. Iterable extensionsWhereTarget( bool callback(SimpleSelector target)) sync* { - for (var target in _extensions.keys) { - if (!callback(target)) continue; - for (var extension in _extensions[target].values) { + for (var entry in _extensions.entries) { + if (!callback(entry.key)) continue; + for (var extension in entry.value.values) { if (extension is MergedExtension) { yield* extension .unmerge() @@ -167,7 +168,7 @@ class Extender { /// The [mediaContext] is the media query context in which the selector was /// defined, or `null` if it was defined at the top level of the document. ModifiableCssValue addSelector( - SelectorList selector, FileSpan span, + SelectorList selector, FileSpan /*?*/ span, [List mediaContext]) { var originalSelector = selector; if (!originalSelector.isInvisible) { @@ -200,13 +201,15 @@ class Extender { SelectorList list, ModifiableCssValue selector) { for (var complex in list.components) { for (var component in complex.components) { - if (component is CompoundSelector) { - for (var simple in component.components) { - _selectors.putIfAbsent(simple, () => {}).add(selector); + if (component is! CompoundSelector) continue; - if (simple is PseudoSelector && simple.selector != null) { - _registerSelector(simple.selector, selector); - } + for (var simple in (component as CompoundSelector).components) { + _selectors.putIfAbsent(simple, () => {}).add(selector); + if (simple is! PseudoSelector) return; + + var selectorInPseudo = (simple as PseudoSelector).selector; + if (selectorInPseudo != null) { + _registerSelector(selectorInPseudo, selector); } } } @@ -295,13 +298,16 @@ class Extender { /// .z.b {@extend .c} /// /// Returns `null` if there are no extensions to add. - Map> - _extendExistingExtensions(List extensions, - Map> newExtensions) { - Map> additionalExtensions; + Map> + _extendExistingExtensions( + List extensions, + Map /*!*/ > + newExtensions) { + Map> + additionalExtensions; for (var extension in extensions.toList()) { - var sources = _extensions[extension.target]; + var sources = _extensions[extension.target] /*!*/; // [_extendExistingSelectors] would have thrown already. List selectors; @@ -371,6 +377,9 @@ class Extender { selector.value = _extendList( selector.value, newExtensions, _mediaContexts[selector]); } on SassException catch (error) { + if (selector.span == null) rethrow; + + // TODO(nweiz): Make this a MultiSpanSassException. throw SassException( "From ${selector.span.message('')}\n" "${error.message}", @@ -724,8 +733,7 @@ class Extender { } } - var result = withoutPseudo(simple); - return result == null ? null : [result]; + return withoutPseudo(simple).andThen((result) => [result]); } /// Returns a one-off [Extension] whose extender is composed solely of a diff --git a/lib/src/extend/functions.dart b/lib/src/extend/functions.dart index deb97e615..95445cf20 100644 --- a/lib/src/extend/functions.dart +++ b/lib/src/extend/functions.dart @@ -27,8 +27,8 @@ final _subselectorPseudos = {'matches', 'any', 'nth-child', 'nth-last-child'}; /// matched by both [complex1] and [complex2]. /// /// If no such list can be produced, returns `null`. -List> unifyComplex( - List> complexes) { +List> unifyComplex( + List> complexes) { assert(complexes.isNotEmpty); if (complexes.length == 1) return complexes; diff --git a/lib/src/functions/color.dart b/lib/src/functions/color.dart index a49ba6ff0..d57b3e4fe 100644 --- a/lib/src/functions/color.dart +++ b/lib/src/functions/color.dart @@ -10,6 +10,7 @@ import '../callable.dart'; import '../exception.dart'; import '../module/built_in.dart'; import '../util/number.dart'; +import '../util/nullable.dart'; import '../utils.dart'; import '../value.dart'; import '../warn.dart'; @@ -29,7 +30,10 @@ final global = UnmodifiableListView([ r"$color, $alpha": (arguments) => _rgbTwoArg("rgb", arguments), r"$channels": (arguments) { var parsed = _parseChannels( - "rgb", [r"$red", r"$green", r"$blue"], arguments.first); + // TODO: no ! + "rgb", + [r"$red", r"$green", r"$blue"], + arguments.first); return parsed is SassString ? parsed : _rgb("rgb", parsed as List); } }), @@ -40,7 +44,10 @@ final global = UnmodifiableListView([ r"$color, $alpha": (arguments) => _rgbTwoArg("rgba", arguments), r"$channels": (arguments) { var parsed = _parseChannels( - "rgba", [r"$red", r"$green", r"$blue"], arguments.first); + // TODO: no ! + "rgba", + [r"$red", r"$green", r"$blue"], + arguments.first); return parsed is SassString ? parsed : _rgb("rgba", parsed as List); @@ -547,6 +554,7 @@ SassString _functionString(String name, Iterable arguments) => /// value to [argument], with a leading minus sign if [negative] is `true`. BuiltInCallable _removedColorFunction(String name, String argument, {bool negative = false}) => + // TODO: no as _function(name, r"$color, $amount", (arguments) { throw SassScriptException( "The function $name() isn't in the sass:color module.\n" @@ -574,9 +582,8 @@ Value _rgb(String name, List arguments) { fuzzyRound(_percentageOrUnitless(red, 255, "red")), fuzzyRound(_percentageOrUnitless(green, 255, "green")), fuzzyRound(_percentageOrUnitless(blue, 255, "blue")), - alpha == null - ? null - : _percentageOrUnitless(alpha.assertNumber("alpha"), 1, "alpha")); + alpha.andThen((alpha) => + _percentageOrUnitless(alpha.assertNumber("alpha"), 1, "alpha"))); } Value _rgbTwoArg(String name, List arguments) { @@ -628,9 +635,8 @@ Value _hsl(String name, List arguments) { hue.value, saturation.value.clamp(0, 100), lightness.value.clamp(0, 100), - alpha == null - ? null - : _percentageOrUnitless(alpha.assertNumber("alpha"), 1, "alpha")); + alpha.andThen((alpha) => + _percentageOrUnitless(alpha.assertNumber("alpha"), 1, "alpha"))); } /// Prints a deprecation warning if [hue] has a unit other than `deg`. @@ -698,9 +704,8 @@ Value _hwb(List arguments) { hue.value, whiteness.valueInRange(0, 100, "whiteness"), blackness.valueInRange(0, 100, "whiteness"), - alpha == null - ? null - : _percentageOrUnitless(alpha.assertNumber("alpha"), 1, "alpha")); + alpha.andThen((alpha) => + _percentageOrUnitless(alpha.assertNumber("alpha"), 1, "alpha"))); } Object /* SassString | List */ _parseChannels( @@ -735,14 +740,10 @@ Object /* SassString | List */ _parseChannels( } var maybeSlashSeparated = list[2]; - if (maybeSlashSeparated is SassNumber && - maybeSlashSeparated.asSlash != null) { - return [ - list[0], - list[1], - maybeSlashSeparated.asSlash.item1, - maybeSlashSeparated.asSlash.item2 - ]; + if (maybeSlashSeparated is SassNumber) { + var slash = maybeSlashSeparated.asSlash; + if (slash == null) return list; + return [list[0], list[1], slash.item1, slash.item2]; } else if (maybeSlashSeparated is SassString && !maybeSlashSeparated.hasQuotes && maybeSlashSeparated.text.contains("/")) { @@ -844,4 +845,5 @@ SassColor _transparentize(List arguments) { /// `sass:color`. BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => + // TODO: no as BuiltInCallable.function(name, arguments, callback, url: "sass:color"); diff --git a/lib/src/functions/list.dart b/lib/src/functions/list.dart index eb8e1a462..4e188f630 100644 --- a/lib/src/functions/list.dart +++ b/lib/src/functions/list.dart @@ -134,4 +134,5 @@ final _isBracketed = _function("is-bracketed", r"$list", /// Like [new BuiltInCallable.function], but always sets the URL to `sass:list`. BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => + // TODO: no as BuiltInCallable.function(name, arguments, callback, url: "sass:list"); diff --git a/lib/src/functions/map.dart b/lib/src/functions/map.dart index 76a1f2289..6a5dcc57d 100644 --- a/lib/src/functions/map.dart +++ b/lib/src/functions/map.dart @@ -100,7 +100,9 @@ final _deepRemove = var keys = [arguments[1], ...arguments[2].asList]; return _modify(map, keys.take(keys.length - 1), (value) { var nestedMap = value?.tryMap(); - if (nestedMap?.contents?.containsKey(keys.last) ?? false) { + if (nestedMap != null && nestedMap.contents?.containsKey(keys.last) ?? + false) { + // TODO: no ! return SassMap(Map.of(nestedMap.contents)..remove(keys.last)); } return value; @@ -167,6 +169,7 @@ final _hasKey = _function("has-key", r"$map, $key, $keys...", (arguments) { Value _modify(SassMap map, Iterable keys, Value modify(Value old)) { var keyIterator = keys.iterator; SassMap _modifyNestedMap(SassMap map, [Value newValue]) { + // TODO: no as throughout var mutableMap = Map.of(map.contents); var key = keyIterator.current; @@ -187,7 +190,7 @@ Value _modify(SassMap map, Iterable keys, Value modify(Value old)) { return SassMap(mutableMap); } - return keyIterator.moveNext() ? _modifyNestedMap(map) : modify(map); + return keyIterator.moveNext() ? _modifyNestedMap(map) : modify(map) /*!*/; } /// Merges [map1] and [map2], with values in [map2] taking precedence. @@ -235,4 +238,5 @@ SassMap _deepMergeImpl(SassMap map1, SassMap map2) { /// Like [new BuiltInCallable.function], but always sets the URL to `sass:map`. BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => + // TODO: no as BuiltInCallable.function(name, arguments, callback, url: "sass:map"); diff --git a/lib/src/functions/math.dart b/lib/src/functions/math.dart index c87083d32..544148b3a 100644 --- a/lib/src/functions/math.dart +++ b/lib/src/functions/math.dart @@ -144,10 +144,11 @@ final _pow = _function("pow", r"$base, $exponent", (arguments) { if (fuzzyEquals(baseValue.abs(), 1) && exponentValue.isInfinite) { return SassNumber(double.nan); } else if (fuzzyEquals(baseValue, 0)) { - if (exponentValue.isFinite && - fuzzyIsInt(exponentValue) && - fuzzyAsInt(exponentValue) % 2 == 1) { - exponentValue = fuzzyRound(exponentValue); + if (exponentValue.isFinite) { + var intExponent = fuzzyAsInt(exponentValue); + if (intExponent != null && intExponent % 2 == 1) { + exponentValue = fuzzyRound(exponentValue); + } } } else if (baseValue.isFinite && fuzzyLessThan(baseValue, 0) && @@ -156,10 +157,11 @@ final _pow = _function("pow", r"$base, $exponent", (arguments) { exponentValue = fuzzyRound(exponentValue); } else if (baseValue.isInfinite && fuzzyLessThan(baseValue, 0) && - exponentValue.isFinite && - fuzzyIsInt(exponentValue) && - fuzzyAsInt(exponentValue) % 2 == 1) { - exponentValue = fuzzyRound(exponentValue); + exponentValue.isFinite) { + var intExponent = fuzzyAsInt(exponentValue); + if (intExponent != null && intExponent % 2 == 1) { + exponentValue = fuzzyRound(exponentValue); + } } return SassNumber(math.pow(baseValue, exponentValue)); }); @@ -316,4 +318,5 @@ BuiltInCallable _numberFunction(String name, num transform(num value)) { /// Like [new _function.function], but always sets the URL to `sass:math`. BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => + // TODO: no as BuiltInCallable.function(name, arguments, callback, url: "sass:math"); diff --git a/lib/src/functions/meta.dart b/lib/src/functions/meta.dart index b959e0be7..0b1b80c35 100644 --- a/lib/src/functions/meta.dart +++ b/lib/src/functions/meta.dart @@ -63,4 +63,5 @@ final global = UnmodifiableListView([ /// Like [new BuiltInCallable.function], but always sets the URL to `sass:meta`. BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => + // TODO: no as BuiltInCallable.function(name, arguments, callback, url: "sass:meta"); diff --git a/lib/src/functions/selector.dart b/lib/src/functions/selector.dart index 6af3174c1..ed01482b9 100644 --- a/lib/src/functions/selector.dart +++ b/lib/src/functions/selector.dart @@ -148,4 +148,5 @@ CompoundSelector _prependParent(CompoundSelector compound) { /// `sass:selector`. BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => + // TODO: no as BuiltInCallable.function(name, arguments, callback, url: "sass:selector"); diff --git a/lib/src/functions/string.dart b/lib/src/functions/string.dart index 076a1b662..20a1cb58a 100644 --- a/lib/src/functions/string.dart +++ b/lib/src/functions/string.dart @@ -172,4 +172,5 @@ int _codepointForIndex(int index, int lengthInCodepoints, /// `sass:string`. BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => + // TODO: no as BuiltInCallable.function(name, arguments, callback, url: "sass:string"); diff --git a/lib/src/import_cache.dart b/lib/src/import_cache.dart index 908369674..c5835c127 100644 --- a/lib/src/import_cache.dart +++ b/lib/src/import_cache.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_import_cache.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: 181b14d1635a824cb4387d7b5ce70bbf5a30c7df +// Checksum: 1196dc4a7cad43b056f32e5e76aeff4362973276 // // ignore_for_file: unused_import @@ -38,10 +38,11 @@ class ImportCache { /// /// This cache isn't used for relative imports, because they're /// context-dependent. - final Map, Tuple3> _canonicalizeCache; + final Map, Tuple3 /*?*/ > + _canonicalizeCache; /// The parsed stylesheets for each canonicalized import URL. - final Map _importCache; + final Map _importCache; /// The import results for each canonicalized import URL. final Map _resultsCache; @@ -113,13 +114,14 @@ class ImportCache { Tuple3 canonicalize(Uri url, {Importer baseImporter, Uri baseUrl, bool forImport = false}) { if (baseImporter != null) { - var resolvedUrl = baseUrl != null ? baseUrl.resolveUri(url) : url; + var resolvedUrl = baseUrl?.resolveUri(url) ?? url; var canonicalUrl = _canonicalize(baseImporter, resolvedUrl, forImport); if (canonicalUrl != null) { return Tuple3(baseImporter, canonicalUrl, resolvedUrl); } } + // TODO: no as return _canonicalizeCache.putIfAbsent(Tuple2(url, forImport), () { for (var importer in _importers) { var canonicalUrl = _canonicalize(importer, url, forImport); @@ -178,6 +180,7 @@ Relative canonical URLs are deprecated and will eventually be disallowed. /// Caches the result of the import and uses cached results if possible. Stylesheet importCanonical(Importer importer, Uri canonicalUrl, [Uri originalUrl]) { + // TODO: no as return _importCache.putIfAbsent(canonicalUrl, () { var result = importer.load(canonicalUrl); if (result == null) return null; @@ -200,9 +203,11 @@ Relative canonical URLs are deprecated and will eventually be disallowed. // Display the URL with the shortest path length. var url = minBy( _canonicalizeCache.values - .where((tuple) => tuple?.item2 == canonicalUrl) + .whereNotNull() + .where((tuple) => tuple.item2 == canonicalUrl) .map((tuple) => tuple.item3), - (url) => url.path.length); + // TODO: no Uri + (Uri url) => url.path.length); if (url == null) return canonicalUrl; // Use the canonicalized basename so that we display e.g. diff --git a/lib/src/importer.dart b/lib/src/importer.dart index 861528276..23eefb769 100644 --- a/lib/src/importer.dart +++ b/lib/src/importer.dart @@ -31,9 +31,9 @@ abstract class Importer extends AsyncImporter { /// those created from Dart code with plain strings. static final Importer noOp = NoOpImporter(); - Uri canonicalize(Uri url); + Uri /*?*/ canonicalize(Uri url); - ImporterResult load(Uri url); + ImporterResult /*?*/ load(Uri url); DateTime modificationTime(Uri url) => DateTime.now(); diff --git a/lib/src/importer/async.dart b/lib/src/importer/async.dart index 80b8c8eb8..02882bb2b 100644 --- a/lib/src/importer/async.dart +++ b/lib/src/importer/async.dart @@ -72,7 +72,7 @@ abstract class AsyncImporter { /// same result. Calling [canonicalize] with a URL returned by [canonicalize] /// must return that URL. Calling [canonicalize] with a URL relative to one /// returned by [canonicalize] must return a meaningful result. - FutureOr canonicalize(Uri url); + FutureOr /*!*/ canonicalize(Uri url); /// Loads the Sass text for the given [url], or returns `null` if /// this importer can't find the stylesheet it refers to. @@ -93,7 +93,7 @@ abstract class AsyncImporter { /// will be used as the wrapped exception's message; otherwise, the exception /// object's `toString()` will be used. This means it's safe for importers to /// throw plain strings. - FutureOr load(Uri url); + FutureOr /*!*/ load(Uri url); /// Returns the time that the Sass file at [url] was last modified. /// diff --git a/lib/src/importer/filesystem.dart b/lib/src/importer/filesystem.dart index ac02f7b96..cb249bed5 100644 --- a/lib/src/importer/filesystem.dart +++ b/lib/src/importer/filesystem.dart @@ -8,6 +8,8 @@ import 'package:path/path.dart' as p; import '../importer.dart'; import '../io.dart' as io; import '../syntax.dart'; +import '../utils.dart'; +import '../util/nullable.dart'; import 'utils.dart'; /// An importer that loads files from a load path on the filesystem. @@ -19,13 +21,13 @@ class FilesystemImporter extends Importer { /// Creates an importer that loads files relative to [loadPath]. FilesystemImporter(String loadPath) : _loadPath = p.absolute(loadPath); - Uri canonicalize(Uri url) { + Uri /*?*/ canonicalize(Uri url) { if (url.scheme != 'file' && url.scheme != '') return null; - var resolved = resolveImportPath(p.join(_loadPath, p.fromUri(url))); - return resolved == null ? null : p.toUri(io.canonicalize(resolved)); + return resolveImportPath(p.join(_loadPath, p.fromUri(url))) + .andThen((resolved) => p.toUri(io.canonicalize(resolved))); } - ImporterResult load(Uri url) { + ImporterResult /*?*/ load(Uri url) { var path = p.fromUri(url); return ImporterResult(io.readFile(path), sourceMapUrl: url, syntax: Syntax.forPath(path)); diff --git a/lib/src/importer/no_op.dart b/lib/src/importer/no_op.dart index dde55c060..f7a1e8bac 100644 --- a/lib/src/importer/no_op.dart +++ b/lib/src/importer/no_op.dart @@ -9,8 +9,8 @@ import '../importer.dart'; /// This is used for stylesheets which don't support relative imports, such as /// those created from Dart code with plain strings. class NoOpImporter extends Importer { - Uri canonicalize(Uri url) => null; - ImporterResult load(Uri url) => null; + Uri /*?*/ canonicalize(Uri url) => null; + ImporterResult /*?*/ load(Uri url) => null; bool couldCanonicalize(Uri url, Uri canonicalUrl) => false; String toString() => "(unknown)"; diff --git a/lib/src/importer/node/implementation.dart b/lib/src/importer/node/implementation.dart index ce88f7598..9d9bbbc86 100644 --- a/lib/src/importer/node/implementation.dart +++ b/lib/src/importer/node/implementation.dart @@ -12,6 +12,7 @@ import '../../io.dart'; import '../../node/function.dart'; import '../../node/importer_result.dart'; import '../../node/utils.dart'; +import '../../util/nullable.dart'; import '../utils.dart'; /// An importer that encapsulates Node Sass's import logic. @@ -57,6 +58,7 @@ class NodeImporter { /// environment variable. static Iterable _addSassPath(Iterable includePaths) sync* { yield* includePaths; + // TODO: no ! var sassPath = getEnvironmentVariable("SASS_PATH"); if (sassPath == null) return; yield* sassPath.split(isWindows ? ';' : ':'); @@ -67,7 +69,7 @@ class NodeImporter { /// The [previous] URL is the URL of the stylesheet in which the import /// appeared. Returns the contents of the stylesheet and the URL to use as /// [previous] for imports within the loaded stylesheet. - Tuple2 load(String url, Uri previous, bool forImport) { + Tuple2 load(String url, Uri /*?*/ previous, bool forImport) { var parsed = Uri.parse(url); if (parsed.scheme == '' || parsed.scheme == 'file') { var result = _resolveRelativePath(p.fromUri(parsed), previous, forImport); @@ -75,8 +77,7 @@ class NodeImporter { } // The previous URL is always an absolute file path for filesystem imports. - var previousString = - previous.scheme == 'file' ? p.fromUri(previous) : previous.toString(); + var previousString = _previousToString(previous); for (var importer in _importers) { var value = call2(importer, _context, url, previousString); if (value != null) { @@ -84,7 +85,7 @@ class NodeImporter { } } - return _resolveLoadPathFromUrl(parsed, previous, forImport); + return _resolveLoadPathFromUrl(parsed, forImport); } /// Asynchronously loads the stylesheet at [url]. @@ -93,7 +94,7 @@ class NodeImporter { /// appeared. Returns the contents of the stylesheet and the URL to use as /// [previous] for imports within the loaded stylesheet. Future> loadAsync( - String url, Uri previous, bool forImport) async { + String url, Uri /*?*/ previous, bool forImport) async { var parsed = Uri.parse(url); if (parsed.scheme == '' || parsed.scheme == 'file') { var result = _resolveRelativePath(p.fromUri(parsed), previous, forImport); @@ -101,8 +102,7 @@ class NodeImporter { } // The previous URL is always an absolute file path for filesystem imports. - var previousString = - previous.scheme == 'file' ? p.fromUri(previous) : previous.toString(); + var previousString = _previousToString(previous); for (var importer in _importers) { var value = await _callImporterAsync(importer, url, previousString); if (value != null) { @@ -110,7 +110,7 @@ class NodeImporter { } } - return _resolveLoadPathFromUrl(parsed, previous, forImport); + return _resolveLoadPathFromUrl(parsed, forImport); } /// Tries to load a stylesheet at the given [path] relative to [previous]. @@ -118,16 +118,19 @@ class NodeImporter { /// Returns the stylesheet at that path and the URL used to load it, or `null` /// if loading failed. Tuple2 _resolveRelativePath( - String path, Uri previous, bool forImport) { + String path, Uri /*?*/ previous, bool forImport) { if (p.isAbsolute(path)) return _tryPath(path, forImport); + if (previous?.scheme != 'file') return null; // 1: Filesystem imports relative to the base file. - if (previous.scheme == 'file') { - var result = - _tryPath(p.join(p.dirname(p.fromUri(previous)), path), forImport); - if (result != null) return result; - } - return null; + return _tryPath(p.join(p.dirname(p.fromUri(previous)), path), forImport); + } + + /// Converts [previous] to a string to pass to the importer function. + String _previousToString(Uri previous) { + if (previous == null) return 'stdin'; + if (previous.scheme == 'file') return p.fromUri(previous); + return previous.toString(); } /// Tries to load a stylesheet at the given [url] from a load path (including @@ -135,10 +138,9 @@ class NodeImporter { /// /// Returns the stylesheet at that path and the URL used to load it, or `null` /// if loading failed. - Tuple2 _resolveLoadPathFromUrl( - Uri url, Uri previous, bool forImport) => + Tuple2 _resolveLoadPathFromUrl(Uri url, bool forImport) => url.scheme == '' || url.scheme == 'file' - ? _resolveLoadPath(p.fromUri(url), previous, forImport) + ? _resolveLoadPath(p.fromUri(url), forImport) : null; /// Tries to load a stylesheet at the given [path] from a load path (including @@ -146,8 +148,7 @@ class NodeImporter { /// /// Returns the stylesheet at that path and the URL used to load it, or `null` /// if loading failed. - Tuple2 _resolveLoadPath( - String path, Uri previous, bool forImport) { + Tuple2 _resolveLoadPath(String path, bool forImport) { // 2: Filesystem imports relative to the working directory. var cwdResult = _tryPath(p.absolute(path), forImport); if (cwdResult != null) return cwdResult; @@ -165,30 +166,30 @@ class NodeImporter { /// /// Returns the stylesheet at that path and the URL used to load it, or `null` /// if loading failed. - Tuple2 _tryPath(String path, bool forImport) { - var resolved = forImport - ? inImportRule(() => resolveImportPath(path)) - : resolveImportPath(path); - return resolved == null - ? null - : Tuple2(readFile(resolved), p.toUri(resolved).toString()); - } + Tuple2 _tryPath(String path, bool forImport) => (forImport + ? inImportRule(() => resolveImportPath(path)) + : resolveImportPath(path)) + .andThen((resolved) => + Tuple2(readFile(resolved), p.toUri(resolved).toString())); /// Converts an importer's return [value] to a tuple that can be returned by /// [load]. Tuple2 _handleImportResult( - String url, Uri previous, Object value, bool forImport) { + String url, Uri /*?*/ previous, Object value, bool forImport) { if (isJSError(value)) throw value; if (value is! NodeImporterResult) return null; + // TODO: no var rename var result = value as NodeImporterResult; - if (result.file == null) { - return Tuple2(result.contents ?? '', url); - } else if (result.contents != null) { - return Tuple2(result.contents, result.file); + var file = result.file; + var contents = result.contents; + if (file == null) { + return Tuple2(contents ?? '', url); + } else if (contents != null) { + return Tuple2(contents, file); } else { - var resolved = _resolveRelativePath(result.file, previous, forImport) ?? - _resolveLoadPath(result.file, previous, forImport); + var resolved = _resolveRelativePath(file, previous, forImport) ?? + _resolveLoadPath(file, forImport); if (resolved != null) return resolved; throw "Can't find stylesheet to import."; } diff --git a/lib/src/importer/node/interface.dart b/lib/src/importer/node/interface.dart index 4ae9afb85..7dfebbb4b 100644 --- a/lib/src/importer/node/interface.dart +++ b/lib/src/importer/node/interface.dart @@ -5,12 +5,13 @@ import 'package:tuple/tuple.dart'; class NodeImporter { - NodeImporter(Object context, Iterable includePaths, + NodeImporter(Object /*!*/ context, Iterable includePaths, Iterable importers); - Tuple2 load(String url, Uri previous, bool forImport) => null; + Tuple2 load(String url, Uri /*?*/ previous, bool forImport) => + throw ''; - Future> loadAsync( - String url, Uri previous, bool forImport) => - null; + Future /*?*/ > /*!*/ loadAsync( + String url, Uri /*?*/ previous, bool forImport) => + throw ''; } diff --git a/lib/src/importer/package.dart b/lib/src/importer/package.dart index 770968750..c339a2f74 100644 --- a/lib/src/importer/package.dart +++ b/lib/src/importer/package.dart @@ -26,7 +26,7 @@ class PackageImporter extends Importer { /// [`PackageConfig`]: https://pub.dev/documentation/package_config/latest/package_config.package_config/PackageConfig-class.html PackageImporter(this._packageConfig); - Uri canonicalize(Uri url) { + Uri /*?*/ canonicalize(Uri url) { if (url.scheme == 'file') return _filesystemImporter.canonicalize(url); if (url.scheme != 'package') return null; @@ -40,7 +40,7 @@ class PackageImporter extends Importer { return _filesystemImporter.canonicalize(resolved); } - ImporterResult load(Uri url) => _filesystemImporter.load(url); + ImporterResult /*?*/ load(Uri url) => _filesystemImporter.load(url); DateTime modificationTime(Uri url) => _filesystemImporter.modificationTime(url); diff --git a/lib/src/importer/utils.dart b/lib/src/importer/utils.dart index 8bf19a736..8e81134b9 100644 --- a/lib/src/importer/utils.dart +++ b/lib/src/importer/utils.dart @@ -46,11 +46,13 @@ String resolveImportPath(String path) { var extension = p.extension(path); if (extension == '.sass' || extension == '.scss' || extension == '.css') { return _ifInImport(() => _exactlyOne( + // TODO: no !, as _tryPath('${p.withoutExtension(path)}.import$extension'))) ?? _exactlyOne(_tryPath(path)); } return _ifInImport( + // TODO: no !, as () => _exactlyOne(_tryPathWithExtensions('$path.import'))) ?? _exactlyOne(_tryPathWithExtensions(path)) ?? _tryPathAsDirectory(path); diff --git a/lib/src/interpolation_buffer.dart b/lib/src/interpolation_buffer.dart index 6fdb193f8..479a7b2e5 100644 --- a/lib/src/interpolation_buffer.dart +++ b/lib/src/interpolation_buffer.dart @@ -18,7 +18,7 @@ class InterpolationBuffer implements StringSink { /// The contents of the [Interpolation] so far. /// /// This contains [String]s and [Expression]s. - final _contents = []; + final _contents = []; /// Returns whether this buffer has no contents. bool get isEmpty => _contents.isEmpty && _text.isEmpty; diff --git a/lib/src/io.dart b/lib/src/io.dart index c96f7a25a..e950525da 100644 --- a/lib/src/io.dart +++ b/lib/src/io.dart @@ -24,7 +24,7 @@ final _realCaseCache = {}; bool get _couldBeCaseInsensitive => isWindows || isMacOS; /// Returns the canonical form of `path` on disk. -String canonicalize(String path) => _couldBeCaseInsensitive +String canonicalize(String /*!*/ path) => _couldBeCaseInsensitive ? _realCasePath(p.absolute(p.normalize(path))) : p.canonicalize(path); diff --git a/lib/src/io/interface.dart b/lib/src/io/interface.dart index 2835ee87f..5efa32290 100644 --- a/lib/src/io/interface.dart +++ b/lib/src/io/interface.dart @@ -21,77 +21,77 @@ class Stderr { /// An error thrown by [readFile]. class FileSystemException { - String get message => null; - String get path => null; + String get message => throw ''; + String get path => throw ''; } /// The standard error for the current process. -Stderr get stderr => null; +Stderr get stderr => throw ''; /// Whether the current process is running on Windows. -bool get isWindows => false; +bool get isWindows => throw ''; /// Whether the current process is running on Mac OS. -bool get isMacOS => false; +bool get isMacOS => throw ''; /// Returns whether or not stdout is connected to an interactive terminal. -bool get hasTerminal => false; +bool get hasTerminal => throw ''; /// Whether we're running as Node.JS. -bool get isNode => false; +bool get isNode => throw ''; /// Whether this process is connected to a terminal that supports ANSI escape /// sequences. -bool get supportsAnsiEscapes => false; +bool get supportsAnsiEscapes => throw ''; /// The current working directory. -String get currentPath => null; +String get currentPath => throw ''; /// Reads the file at [path] as a UTF-8 encoded string. /// /// Throws a [FileSystemException] if reading fails, and a [SassException] if /// the file isn't valid UTF-8. -String readFile(String path) => null; +String readFile(String path) => throw ''; /// Writes [contents] to the file at [path], encoded as UTF-8. /// /// Throws a [FileSystemException] if writing fails. -void writeFile(String path, String contents) => null; +void writeFile(String path, String contents) => throw ''; /// Deletes the file at [path]. /// /// Throws a [FileSystemException] if deletion fails. -void deleteFile(String path) => null; +void deleteFile(String path) => throw ''; /// Reads from the standard input for the current process until it closes, /// returning the contents. -Future readStdin() async => null; +Future readStdin() async => throw ''; /// Returns whether a file at [path] exists. -bool fileExists(String path) => null; +bool fileExists(String path) => throw ''; /// Returns whether a dir at [path] exists. -bool dirExists(String path) => null; +bool dirExists(String path) => throw ''; /// Ensures that a directory exists at [path], creating it and its ancestors if /// necessary. -void ensureDir(String path) => null; +void ensureDir(String path) => throw ''; /// Lists the files (not sub-directories) in the directory at [path]. /// /// If [recursive] is `true`, this lists files in directories transitively /// beneath [path] as well. -Iterable listDir(String path, {bool recursive = false}) => null; +Iterable listDir(String path, {bool recursive = false}) => throw ''; /// Returns the modification time of the file at [path]. -DateTime modificationTime(String path) => null; +DateTime modificationTime(String path) => throw ''; /// Returns the value of the environment variable with the given [name], or /// `null` if it's not set. -String getEnvironmentVariable(String name) => null; +String /*?*/ getEnvironmentVariable(String name) => throw ''; /// Gets and sets the exit code that the process will use when it exits. -int exitCode; +int /*!*/ exitCode; /// Recursively watches the directory at [path] for modifications. /// @@ -101,4 +101,5 @@ int exitCode; /// /// If [poll] is `true`, this manually checks the filesystem for changes /// periodically rather than using a native filesystem monitoring API. -Future> watchDir(String path, {bool poll = false}) => null; +Future> watchDir(String path, {bool poll = false}) => + throw ''; diff --git a/lib/src/io/node.dart b/lib/src/io/node.dart index 203ceb3e8..73e871ac0 100644 --- a/lib/src/io/node.dart +++ b/lib/src/io/node.dart @@ -58,7 +58,8 @@ String readFile(String path) { } /// Wraps `fs.readFileSync` to throw a [FileSystemException]. -Object _readFile(String path, [String encoding]) => +Object /*!*/ _readFile(String path, [String encoding]) => + // TODO: no as _systemErrorToFileSystemException(() => fs.readFileSync(path, encoding)); void writeFile(String path, String contents) => @@ -157,12 +158,12 @@ Iterable listDir(String path, {bool recursive = false}) { if (!recursive) { return fs .readdirSync(path) - .map((child) => p.join(path, child as String)) + .map((child) => p.join(path, child as String /*!*/)) .where((child) => !dirExists(child)); } else { Iterable list(String parent) => fs.readdirSync(parent).expand((child) { - var path = p.join(parent, child as String); + var path = p.join(parent, child as String /*!*/); return dirExists(path) ? list(path) : [path]; }); @@ -236,6 +237,7 @@ Future> watchDir(String path, {bool poll = false}) { controller = StreamController(onCancel: () { watcher.close(); }); + // TODO: no ! completer.complete(controller.stream); })); diff --git a/lib/src/logger.dart b/lib/src/logger.dart index bf9cbb187..91a0dc828 100644 --- a/lib/src/logger.dart +++ b/lib/src/logger.dart @@ -26,7 +26,7 @@ abstract class Logger { /// a deprecation warning. Implementations should surface all this information /// to the end user. void warn(String message, - {FileSpan span, Trace trace, bool deprecation = false}); + {FileSpan span, Trace trace, bool /*!*/ deprecation = false}); /// Emits a debugging message associated with the given [span]. void debug(String message, SourceSpan span); @@ -35,6 +35,6 @@ abstract class Logger { /// A logger that emits no messages. class _QuietLogger implements Logger { void warn(String message, - {FileSpan span, Trace trace, bool deprecation = false}) {} + {FileSpan span, Trace trace, bool /*!*/ deprecation = false}) {} void debug(String message, SourceSpan span) {} } diff --git a/lib/src/logger/stderr.dart b/lib/src/logger/stderr.dart index 7f4a21fe3..cb73f70b8 100644 --- a/lib/src/logger/stderr.dart +++ b/lib/src/logger/stderr.dart @@ -13,12 +13,12 @@ import '../utils.dart'; /// A logger that prints warnings to standard error. class StderrLogger implements Logger { /// Whether to use terminal colors in messages. - final bool color; + final bool /*!*/ color; const StderrLogger({this.color = false}); void warn(String message, - {FileSpan span, Trace trace, bool deprecation = false}) { + {FileSpan span, Trace trace, bool /*!*/ deprecation = false}) { if (color) { // Bold yellow. stderr.write('\u001b[33m\u001b[1m'); diff --git a/lib/src/logger/tracking.dart b/lib/src/logger/tracking.dart index 4591be3b0..5d5099691 100644 --- a/lib/src/logger/tracking.dart +++ b/lib/src/logger/tracking.dart @@ -22,7 +22,7 @@ class TrackingLogger implements Logger { TrackingLogger(this._logger); void warn(String message, - {FileSpan span, Trace trace, bool deprecation = false}) { + {FileSpan span, Trace trace, bool /*!*/ deprecation = false}) { _emittedWarning = true; _logger.warn(message, span: span, trace: trace, deprecation: deprecation); } diff --git a/lib/src/module.dart b/lib/src/module.dart index 189f4d732..947c5828e 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -22,7 +22,7 @@ abstract class Module { List> get upstream; /// The module's variables. - Map get variables; + Map /*!*/ get variables; /// The nodes where each variable in [_variables] was defined. /// @@ -34,19 +34,19 @@ abstract class Module { /// /// Implementations must ensure that this has the same keys as [variables] if /// it's not `null`. - Map get variableNodes; + Map get variableNodes; /// The module's functions. /// /// Implementations must ensure that each [AsyncCallable] is stored under its /// own name. - Map get functions; + Map /*!*/ get functions; /// The module's mixins. /// /// Implementations must ensure that each [AsyncCallable] is stored under its /// own name. - Map get mixins; + Map /*!*/ get mixins; /// The extensions defined in this module, which is also able to update /// [css]'s style rules in-place based on downstream extensions. @@ -71,13 +71,13 @@ abstract class Module { /// /// Throws a [SassScriptException] if this module doesn't define a variable /// named [name]. - void setVariable(String name, Value value, AstNode nodeWithSpan); + void setVariable(String name, Value /*!*/ value, AstNode /*?*/ nodeWithSpan); /// Returns an opaque object that will be equal to another /// `variableIdentity()` return value for the same name in another module if /// and only if both modules expose identical definitions of the variable in /// question, as defined by the Sass spec. - Object variableIdentity(String name); + Object variableIdentity(String /*!*/ name); /// Creates a copy of this module with new [css] and [extender]. Module cloneCss(); diff --git a/lib/src/module/forwarded_view.dart b/lib/src/module/forwarded_view.dart index 1c5dfb144..739140669 100644 --- a/lib/src/module/forwarded_view.dart +++ b/lib/src/module/forwarded_view.dart @@ -10,6 +10,7 @@ import '../exception.dart'; import '../extend/extender.dart'; import '../module.dart'; import '../util/limited_map_view.dart'; +import '../util/nullable.dart'; import '../util/prefixed_map_view.dart'; import '../value.dart'; @@ -39,11 +40,11 @@ class ForwardedModuleView implements Module { static Module ifNecessary( Module inner, ForwardRule rule) { if (rule.prefix == null && - rule.shownMixinsAndFunctions == null && - rule.shownVariables == null && - (rule.hiddenMixinsAndFunctions == null || - rule.hiddenMixinsAndFunctions.isEmpty) && - (rule.hiddenVariables == null || rule.hiddenVariables.isEmpty)) { + rule.shownMixinsAndFunctions == null && + rule.shownVariables == null && + rule?.hiddenMixinsAndFunctions?.isEmpty ?? + false && rule?.hiddenVariables?.isEmpty ?? + false) { return inner; } else { return ForwardedModuleView(inner, rule); @@ -53,10 +54,8 @@ class ForwardedModuleView implements Module { ForwardedModuleView(this._inner, this._rule) : variables = _forwardedMap(_inner.variables, _rule.prefix, _rule.shownVariables, _rule.hiddenVariables), - variableNodes = _inner.variableNodes == null - ? null - : _forwardedMap(_inner.variableNodes, _rule.prefix, - _rule.shownVariables, _rule.hiddenVariables), + variableNodes = _inner.variableNodes.andThen((inner) => _forwardedMap( + inner, _rule.prefix, _rule.shownVariables, _rule.hiddenVariables)), functions = _forwardedMap(_inner.functions, _rule.prefix, _rule.shownMixinsAndFunctions, _rule.hiddenMixinsAndFunctions), mixins = _forwardedMap(_inner.mixins, _rule.prefix, @@ -66,8 +65,8 @@ class ForwardedModuleView implements Module { /// [safelist], with the given [prefix], if given. /// /// Only one of [blocklist] or [safelist] may be non-`null`. - static Map _forwardedMap(Map map, String prefix, - Set safelist, Set blocklist) { + static Map /*!*/ _forwardedMap(Map /*!*/ map, + String prefix, Set safelist, Set blocklist) { assert(safelist == null || blocklist == null); if (prefix == null && safelist == null && @@ -89,19 +88,21 @@ class ForwardedModuleView implements Module { } void setVariable(String name, Value value, AstNode nodeWithSpan) { - if (_rule.shownVariables != null && !_rule.shownVariables.contains(name)) { + var shownVariables = _rule.shownVariables; + var hiddenVariables = _rule.hiddenVariables; + if (shownVariables != null && !shownVariables.contains(name)) { throw SassScriptException("Undefined variable."); - } else if (_rule.hiddenVariables != null && - _rule.hiddenVariables.contains(name)) { + } else if (hiddenVariables != null && hiddenVariables.contains(name)) { throw SassScriptException("Undefined variable."); } - if (_rule.prefix != null) { - if (!name.startsWith(_rule.prefix)) { + var prefix = _rule.prefix; + if (prefix != null) { + if (!name.startsWith(prefix)) { throw SassScriptException("Undefined variable."); } - name = name.substring(_rule.prefix.length); + name = name.substring(prefix.length); } return _inner.setVariable(name, value, nodeWithSpan); @@ -110,9 +111,10 @@ class ForwardedModuleView implements Module { Object variableIdentity(String name) { assert(variables.containsKey(name)); - if (_rule.prefix != null) { - assert(name.startsWith(_rule.prefix)); - name = name.substring(_rule.prefix.length); + var prefix = _rule.prefix; + if (prefix != null) { + assert(name.startsWith(prefix)); + name = name.substring(prefix.length); } return _inner.variableIdentity(name); diff --git a/lib/src/module/shadowed_view.dart b/lib/src/module/shadowed_view.dart index d27c526ab..0071c5314 100644 --- a/lib/src/module/shadowed_view.dart +++ b/lib/src/module/shadowed_view.dart @@ -44,9 +44,9 @@ class ShadowedModuleView implements Module { {Set variables, Set functions, Set mixins}) => - _needsBlacklist(inner.variables, variables) || - _needsBlacklist(inner.functions, functions) || - _needsBlacklist(inner.mixins, mixins) + _needsBlocklist(inner.variables, variables) || + _needsBlocklist(inner.functions, functions) || + _needsBlocklist(inner.mixins, mixins) ? ShadowedModuleView(inner, variables: variables, functions: functions, mixins: mixins) : null; @@ -64,14 +64,14 @@ class ShadowedModuleView implements Module { this.functions, this.mixins); /// Returns a view of [map] with all keys in [blocklist] omitted. - static Map _shadowedMap( - Map map, Set blocklist) { - if (map == null || !_needsBlacklist(map, blocklist)) return map; - return LimitedMapView.blocklist(map, blocklist); - } + static Map /*!*/ _shadowedMap /*!*/ >( + M /*!*/ map, Set blocklist) => + map == null || blocklist == null || !_needsBlocklist(map, blocklist) + ? map + : LimitedMapView.blocklist(map, blocklist); /// Returns whether any of [map]'s keys are in [blocklist]. - static bool _needsBlacklist(Map map, Set blocklist) => + static bool _needsBlocklist(Map map, Set blocklist) => blocklist != null && map.isNotEmpty && blocklist.any(map.containsKey); void setVariable(String name, Value value, AstNode nodeWithSpan) { diff --git a/lib/src/node.dart b/lib/src/node.dart index ebb63aa37..3d60dba4e 100644 --- a/lib/src/node.dart +++ b/lib/src/node.dart @@ -29,6 +29,8 @@ import 'node/value.dart'; import 'node/utils.dart'; import 'parse/scss.dart'; import 'syntax.dart'; +import 'utils.dart'; +import 'util/nullable.dart'; import 'value.dart'; import 'visitor/serialize.dart'; @@ -66,8 +68,9 @@ void main() { /// [render]: https://github.com/sass/node-sass#options void _render( RenderOptions options, void callback(Object error, RenderResult result)) { - if (options.fiber != null) { - options.fiber.call(allowInterop(() { + var fiber = options.fiber; + if (fiber != null) { + fiber.call(allowInterop(() { try { callback(null, _renderSync(options)); } catch (error) { @@ -91,10 +94,12 @@ void _render( /// Converts Sass to CSS asynchronously. Future _renderAsync(RenderOptions options) async { var start = DateTime.now(); - var file = options.file == null ? null : p.absolute(options.file); CompileResult result; - if (options.data != null) { - result = await compileStringAsync(options.data, + + var data = options.data; + var file = options.file.andThen(p.absolute); + if (data != null) { + result = await compileStringAsync(data, nodeImporter: _parseImporter(options, start), functions: _parseFunctions(options, start, asynch: true), syntax: isTruthy(options.indentedSyntax) ? Syntax.sass : null, @@ -102,9 +107,9 @@ Future _renderAsync(RenderOptions options) async { useSpaces: options.indentType != 'tab', indentWidth: _parseIndentWidth(options.indentWidth), lineFeed: _parseLineFeed(options.linefeed), - url: options.file == null ? 'stdin' : p.toUri(file).toString(), + url: file == null ? 'stdin' : p.toUri(file).toString(), sourceMap: _enableSourceMaps(options)); - } else if (options.file != null) { + } else if (file != null) { result = await compileAsync(file, nodeImporter: _parseImporter(options, start), functions: _parseFunctions(options, start, asynch: true), @@ -130,10 +135,12 @@ Future _renderAsync(RenderOptions options) async { RenderResult _renderSync(RenderOptions options) { try { var start = DateTime.now(); - var file = options.file == null ? null : p.absolute(options.file); CompileResult result; - if (options.data != null) { - result = compileString(options.data, + + var data = options.data; + var file = options.file.andThen(p.absolute); + if (data != null) { + result = compileString(data, nodeImporter: _parseImporter(options, start), functions: _parseFunctions(options, start).cast(), syntax: isTruthy(options.indentedSyntax) ? Syntax.sass : null, @@ -141,9 +148,9 @@ RenderResult _renderSync(RenderOptions options) { useSpaces: options.indentType != 'tab', indentWidth: _parseIndentWidth(options.indentWidth), lineFeed: _parseLineFeed(options.linefeed), - url: options.file == null ? 'stdin' : p.toUri(file).toString(), + url: file == null ? 'stdin' : p.toUri(file).toString(), sourceMap: _enableSourceMaps(options)); - } else if (options.file != null) { + } else if (file != null) { result = compile(file, nodeImporter: _parseImporter(options, start), functions: _parseFunctions(options, start).cast(), @@ -170,11 +177,9 @@ RenderResult _renderSync(RenderOptions options) { JsError _wrapException(Object exception) { if (exception is SassException) { return _newRenderError(exception.toString().replaceFirst("Error: ", ""), - line: exception.span.start.line + 1, - column: exception.span.start.column + 1, - file: exception.span.sourceUrl == null - ? 'stdin' - : p.fromUri(exception.span.sourceUrl), + line: exception.span?.start.line + 1, + column: exception.span?.start.column + 1, + file: exception.span?.sourceUrl.andThen(p.fromUri) ?? 'stdin', status: 1); } else { return JsError(exception.toString()); @@ -202,15 +207,16 @@ List _parseFunctions(RenderOptions options, DateTime start, var context = _contextWithOptions(options, start); - if (options.fiber != null) { + var fiber = options.fiber; + if (fiber != null) { result.add(BuiltInCallable.parsed(tuple.item1, tuple.item2, (arguments) { - var fiber = options.fiber.current; + var currentFiber = fiber.current; var jsArguments = [ ...arguments.map(wrapValue), allowInterop(([Object result]) { // Schedule a microtask so we don't try to resume the running fiber // if [importer] calls `done()` synchronously. - scheduleMicrotask(() => fiber.run(result)); + scheduleMicrotask(() => currentFiber.run(result)); }) ]; var result = (callback as JSFunction).apply(context, jsArguments); @@ -218,7 +224,7 @@ List _parseFunctions(RenderOptions options, DateTime start, // Run `fiber.yield()` in runZoned() so that Dart resets the current // zone once it's done. Otherwise, interweaving fibers can leave // `Zone.current` in an inconsistent state. - ? runZoned(() => options.fiber.yield()) + ? runZoned(() => fiber.yield()) : result); })); } else if (!asynch) { @@ -247,34 +253,35 @@ List _parseFunctions(RenderOptions options, DateTime start, /// Parses [importer] and [includePaths] from [RenderOptions] into a /// [NodeImporter]. NodeImporter _parseImporter(RenderOptions options, DateTime start) { - List importers; + List importers; if (options.importer == null) { importers = []; - } else if (options.importer is List) { - importers = (options.importer as List).cast(); + } else if (options.importer is List) { + importers = (options.importer as List).cast(); } else { - importers = [options.importer as JSFunction]; + importers = [options.importer as JSFunction /*!*/]; } RenderContext context; if (importers.isNotEmpty) context = _contextWithOptions(options, start); - if (options.fiber != null) { + var fiber = options.fiber; + if (fiber != null) { importers = importers.map((importer) { return allowInteropCaptureThis( (Object thisArg, String url, String previous, [Object _]) { - var fiber = options.fiber.current; + var currentFiber = fiber.current; var result = call3(importer, thisArg, url, previous, allowInterop((Object result) { // Schedule a microtask so we don't try to resume the running fiber if // [importer] calls `done()` synchronously. - scheduleMicrotask(() => fiber.run(result)); + scheduleMicrotask(() => currentFiber.run(result)); })); // Run `fiber.yield()` in runZoned() so that Dart resets the current // zone once it's done. Otherwise, interweaving fibers can leave // `Zone.current` in an inconsistent state. - if (isUndefined(result)) return runZoned(() => options.fiber.yield()); + if (isUndefined(result)) return runZoned(() => fiber.yield()); return result; }) as JSFunction; }).toList(); @@ -341,27 +348,30 @@ RenderResult _newRenderResult( var css = result.css; Uint8List sourceMapBytes; if (_enableSourceMaps(options)) { - var sourceMapPath = options.sourceMap is String - ? options.sourceMap as String - : options.outFile + '.map'; + var sourceMapOption = options.sourceMap; + var sourceMapPath = sourceMapOption is String + ? sourceMapOption as String + : options.outFile /*!*/ + '.map'; var sourceMapDir = p.dirname(sourceMapPath); - result.sourceMap.sourceRoot = options.sourceMapRoot; - if (options.outFile == null) { - if (options.file == null) { - result.sourceMap.targetUrl = 'stdin.css'; + var sourceMap = result.sourceMap /*!*/; + sourceMap.sourceRoot = options.sourceMapRoot; + var outFile = options.outFile; + if (outFile == null) { + var file = options.file; + if (file == null) { + sourceMap.targetUrl = 'stdin.css'; } else { - result.sourceMap.targetUrl = - p.toUri(p.setExtension(options.file, '.css')).toString(); + sourceMap.targetUrl = p.toUri(p.setExtension(file, '.css')).toString(); } } else { - result.sourceMap.targetUrl = - p.toUri(p.relative(options.outFile, from: sourceMapDir)).toString(); + sourceMap.targetUrl = + p.toUri(p.relative(outFile, from: sourceMapDir)).toString(); } var sourceMapDirUrl = p.toUri(sourceMapDir).toString(); - for (var i = 0; i < result.sourceMap.urls.length; i++) { - var source = result.sourceMap.urls[i]; + for (var i = 0; i < sourceMap.urls.length; i++) { + var source = sourceMap.urls[i]; if (source == "stdin") continue; // URLs handled by Node importers that directly return file contents are @@ -369,19 +379,19 @@ RenderResult _newRenderResult( // not be intended as `file:` URLs, but there's nothing we can do about it // either way so we keep them as-is. if (p.url.isRelative(source) || p.url.isRootRelative(source)) continue; - result.sourceMap.urls[i] = p.url.relative(source, from: sourceMapDirUrl); + sourceMap.urls[i] = p.url.relative(source, from: sourceMapDirUrl); } - var json = result.sourceMap - .toJson(includeSourceContents: isTruthy(options.sourceMapContents)); + var json = sourceMap.toJson( + includeSourceContents: isTruthy(options.sourceMapContents)); sourceMapBytes = utf8Encode(jsonEncode(json)); if (!isTruthy(options.omitSourceMapUrl)) { var url = isTruthy(options.sourceMapEmbed) ? Uri.dataFromBytes(sourceMapBytes, mimeType: "application/json") - : p.toUri(options.outFile == null + : p.toUri(outFile == null ? sourceMapPath - : p.relative(sourceMapPath, from: p.dirname(options.outFile))); + : p.relative(sourceMapPath, from: p.dirname(outFile))); css += "\n\n/*# sourceMappingURL=$url */"; } } diff --git a/lib/src/node/chokidar.dart b/lib/src/node/chokidar.dart index 8ceb972ee..68fac0754 100644 --- a/lib/src/node/chokidar.dart +++ b/lib/src/node/chokidar.dart @@ -12,8 +12,8 @@ class Chokidar { @JS() @anonymous class ChokidarOptions { - external bool get disableGlobbing; - external bool get usePolling; + external bool /*?*/ get disableGlobbing; + external bool /*?*/ get usePolling; external factory ChokidarOptions({bool disableGlobbing, bool usePolling}); } diff --git a/lib/src/node/importer_result.dart b/lib/src/node/importer_result.dart index 9144441b4..e41d096e5 100644 --- a/lib/src/node/importer_result.dart +++ b/lib/src/node/importer_result.dart @@ -7,8 +7,8 @@ import 'package:js/js.dart'; @JS() @anonymous class NodeImporterResult { - external String get file; - external String get contents; + external String /*?*/ get file; + external String /*?*/ get contents; external factory NodeImporterResult({String file, String contents}); } diff --git a/lib/src/node/render_context.dart b/lib/src/node/render_context.dart index 79103b22a..cf53ebfbb 100644 --- a/lib/src/node/render_context.dart +++ b/lib/src/node/render_context.dart @@ -3,6 +3,7 @@ // https://opensource.org/licenses/MIT. import 'package:js/js.dart'; +import 'package:meta/meta.dart'; import 'render_context_options.dart'; @@ -11,5 +12,5 @@ import 'render_context_options.dart'; class RenderContext { external RenderContextOptions get options; - external factory RenderContext({RenderContextOptions options}); + external factory RenderContext({@required RenderContextOptions options}); } diff --git a/lib/src/node/render_context_options.dart b/lib/src/node/render_context_options.dart index 953e1eca3..39c519d28 100644 --- a/lib/src/node/render_context_options.dart +++ b/lib/src/node/render_context_options.dart @@ -10,17 +10,17 @@ import 'render_result.dart'; @JS() @anonymous class RenderContextOptions { - external String get file; - external String get data; - external String get includePaths; - external int get precision; - external int get style; - external int get indentType; - external int get indentWidth; - external String get linefeed; - external RenderContext get context; - external set context(RenderContext value); - external RenderResult get result; + external String /*?*/ get file; + external String /*?*/ get data; + external String /*?*/ get includePaths; + external int /*?*/ get precision; + external int /*?*/ get style; + external int /*?*/ get indentType; + external int /*?*/ get indentWidth; + external String /*?*/ get linefeed; + external RenderContext /*?*/ get context; + external set context(RenderContext /*?*/ value); + external RenderResult /*?*/ get result; external factory RenderContextOptions( {String file, diff --git a/lib/src/node/render_options.dart b/lib/src/node/render_options.dart index 0f98f4a59..ca172fd25 100644 --- a/lib/src/node/render_options.dart +++ b/lib/src/node/render_options.dart @@ -9,23 +9,23 @@ import 'fiber.dart'; @JS() @anonymous class RenderOptions { - external String get file; - external String get data; - external dynamic get importer; - external dynamic get functions; - external List get includePaths; - external bool get indentedSyntax; - external bool get omitSourceMapUrl; - external String get outFile; - external String get outputStyle; - external String get indentType; - external dynamic get indentWidth; - external String get linefeed; - external FiberClass get fiber; - external Object get sourceMap; - external bool get sourceMapContents; - external bool get sourceMapEmbed; - external String get sourceMapRoot; + external String /*?*/ /*?*/ get file; + external String /*?*/ get data; + external Object /*?*/ get importer; + external Object /*?*/ get functions; + external List /*?*/ get includePaths; + external bool /*?*/ get indentedSyntax; + external bool /*?*/ get omitSourceMapUrl; + external String /*?*/ get outFile; + external String /*?*/ get outputStyle; + external String /*?*/ get indentType; + external Object /*?*/ get indentWidth; + external String /*?*/ get linefeed; + external FiberClass /*?*/ get fiber; + external Object /*?*/ get sourceMap; + external bool /*?*/ get sourceMapContents; + external bool /*?*/ get sourceMapEmbed; + external String /*?*/ get sourceMapRoot; external factory RenderOptions( {String file, diff --git a/lib/src/node/render_result.dart b/lib/src/node/render_result.dart index 2804eb998..4d0814e21 100644 --- a/lib/src/node/render_result.dart +++ b/lib/src/node/render_result.dart @@ -9,9 +9,9 @@ import 'package:js/js.dart'; @JS() @anonymous class RenderResult { - external Uint8List get css; - external Uint8List get map; - external RenderResultStats get stats; + external Uint8List /*?*/ get css; + external Uint8List /*?*/ get map; + external RenderResultStats /*?*/ get stats; external factory RenderResult( {Uint8List css, Uint8List map, RenderResultStats stats}); @@ -20,11 +20,11 @@ class RenderResult { @JS() @anonymous class RenderResultStats { - external String get entry; - external int get start; - external int get end; - external int get duration; - external List get includedFiles; + external String /*?*/ get entry; + external int /*?*/ get start; + external int /*?*/ get end; + external int /*?*/ get duration; + external List /*?*/ get includedFiles; external factory RenderResultStats( {String entry, diff --git a/lib/src/node/utils.dart b/lib/src/node/utils.dart index 00a23b466..760815bf0 100644 --- a/lib/src/node/utils.dart +++ b/lib/src/node/utils.dart @@ -29,7 +29,7 @@ void jsThrow(Object error) => _jsThrow.call(error); final _jsThrow = JSFunction("error", "throw error;"); /// Returns whether or not [value] is the JS `undefined` value. -bool isUndefined(Object value) => _isUndefined.call(value) as bool; +bool isUndefined(Object /*?*/ value) => _isUndefined.call(value) as bool; final _isUndefined = JSFunction("value", "return value === undefined;"); @@ -50,12 +50,13 @@ external Function get jsErrorConstructor; bool isJSError(Object value) => jsInstanceOf(value, jsErrorConstructor); /// Invokes [function] with [thisArg] as `this`. -Object call2(JSFunction function, Object thisArg, Object arg1, Object arg2) => +Object /*?*/ call2( + JSFunction function, Object thisArg, Object arg1, Object arg2) => function.apply(thisArg, [arg1, arg2]); /// Invokes [function] with [thisArg] as `this`. -Object call3(JSFunction function, Object thisArg, Object arg1, Object arg2, - Object arg3) => +Object /*?*/ call3(JSFunction function, Object thisArg, Object arg1, + Object arg2, Object arg3) => function.apply(thisArg, [arg1, arg2, arg3]); @JS("Object.keys") @@ -84,14 +85,14 @@ Function createClass( } @JS("Object.getPrototypeOf") -external Object _getPrototypeOf(Object object); +external Object /*?*/ _getPrototypeOf(Object object); @JS("Object.setPrototypeOf") -external void _setPrototypeOf(Object object, Object prototype); +external void _setPrototypeOf(Object /*!*/ object, Object prototype); @JS("Object.defineProperty") external void _defineProperty( - Object object, String name, _PropertyDescriptor prototype); + Object /*!*/ object, String name, _PropertyDescriptor prototype); @JS() @anonymous @@ -102,7 +103,7 @@ class _PropertyDescriptor { } @JS("Object.create") -external Object _create(Object prototype); +external Object _create(Object /*!*/ prototype); /// Sets the name of `object`'s class to `name`. void setClassName(Object object, String name) { diff --git a/lib/src/node/value/color.dart b/lib/src/node/value/color.dart index 8bd650166..afc099f99 100644 --- a/lib/src/node/value/color.dart +++ b/lib/src/node/value/color.dart @@ -16,7 +16,7 @@ class _NodeSassColor { } /// Creates a new `sass.types.Color` object wrapping [value]. -Object newNodeSassColor(SassColor value) => +Object /*!*/ newNodeSassColor(SassColor value) => callConstructor(colorConstructor, [null, null, null, null, value]); /// The JS constructor for the `sass.types.Color` class. @@ -35,7 +35,7 @@ final Function colorConstructor = createClass('SassColor', // // The latter takes an integer that's interpreted as the hex value 0xAARRGGBB. num red; - if (green == null) { + if (green == null || blue == null) { var argb = redOrArgb as int; alpha = (argb >> 24) / 0xff; red = (argb >> 16) % 0x100; diff --git a/lib/src/node/value/list.dart b/lib/src/node/value/list.dart index 36edd4cf6..6a743cbe6 100644 --- a/lib/src/node/value/list.dart +++ b/lib/src/node/value/list.dart @@ -17,7 +17,7 @@ class _NodeSassList { } /// Creates a new `sass.types.List` object wrapping [value]. -Object newNodeSassList(SassList value) => +Object /*!*/ newNodeSassList(SassList value) => callConstructor(listConstructor, [null, null, value]); /// The JS constructor for the `sass.types.List` class. diff --git a/lib/src/node/value/map.dart b/lib/src/node/value/map.dart index 8cc78a0ed..5ae600b99 100644 --- a/lib/src/node/value/map.dart +++ b/lib/src/node/value/map.dart @@ -17,7 +17,7 @@ class _NodeSassMap { } /// Creates a new `sass.types.Map` object wrapping [value]. -Object newNodeSassMap(SassMap value) => +Object /*!*/ newNodeSassMap(SassMap value) => callConstructor(mapConstructor, [null, value]); /// The JS constructor for the `sass.types.Map` class. @@ -37,7 +37,8 @@ final Function mapConstructor = createClass('SassMap', RangeError.checkValidIndex(index, oldMap, "index"); var newKey = unwrapValue(key); - var newMap = {}; + // TODO: no var + var newMap = {}; var i = 0; for (var oldKey in thisArg.dartValue.contents.keys) { if (i == index) { @@ -51,6 +52,7 @@ final Function mapConstructor = createClass('SassMap', i++; } + // TODO: no as thisArg.dartValue = SassMap(newMap); }, 'setValue': (_NodeSassMap thisArg, int index, Object value) { diff --git a/lib/src/node/value/number.dart b/lib/src/node/value/number.dart index cf2bd549f..a299bdfd2 100644 --- a/lib/src/node/value/number.dart +++ b/lib/src/node/value/number.dart @@ -16,7 +16,7 @@ class _NodeSassNumber { } /// Creates a new `sass.types.Number` object wrapping [value]. -Object newNodeSassNumber(SassNumber value) => +Object /*!*/ newNodeSassNumber(SassNumber value) => callConstructor(numberConstructor, [null, null, value]); /// The JS constructor for the `sass.types.Number` class. diff --git a/lib/src/node/value/string.dart b/lib/src/node/value/string.dart index 72bdf2710..b188deb12 100644 --- a/lib/src/node/value/string.dart +++ b/lib/src/node/value/string.dart @@ -16,7 +16,7 @@ class _NodeSassString { } /// Creates a new `sass.types.String` object wrapping [value]. -Object newNodeSassString(SassString value) => +Object /*!*/ newNodeSassString(SassString value) => callConstructor(stringConstructor, [null, value]); /// The JS constructor for the `sass.types.String` class. diff --git a/lib/src/parse/css.dart b/lib/src/parse/css.dart index d2bd10ffd..4976d73d4 100644 --- a/lib/src/parse/css.dart +++ b/lib/src/parse/css.dart @@ -37,7 +37,7 @@ class CssParser extends ScssParser { scanner.spanFrom(start)); } - Statement atRule(Statement child(), {bool root = false}) { + Statement atRule(Statement /*!*/ child(), {bool root = false}) { // NOTE: this logic is largely duplicated in CssParser.atRule. Most changes // here should be mirrored there. @@ -66,12 +66,6 @@ class CssParser extends ScssParser { scanner.spanFrom(start)); break; - case "charset": - string(); - if (!root) { - error("This at-rule is not allowed here.", scanner.spanFrom(start)); - } - return null; case "import": return _cssImportRule(start); case "media": diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index 17c0a2441..8796e5db2 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -224,8 +224,7 @@ class Parser { var quote = scanner.readChar(); if (quote != $single_quote && quote != $double_quote) { - scanner.error("Expected string.", - position: quote == null ? scanner.position : scanner.position - 1); + scanner.error("Expected string.", position: scanner.position - 1); } var buffer = StringBuffer(); @@ -284,6 +283,7 @@ class Parser { var wroteNewline = false; loop: while (true) { + // TODO: no next! var next = scanner.peekChar(); switch (next) { case $backslash: @@ -696,7 +696,7 @@ class Parser { try { return callback(); } on SourceSpanFormatException catch (error) { - var span = error.span as FileSpan; + var span = error.span as FileSpan /*!*/; if (startsWithIgnoreCase(error.message, "expected") && span.length == 0) { var startPosition = _firstNewlineBefore(span.start.offset); if (startPosition != span.start.offset) { diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index adf5aa249..cc5a1100b 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -14,8 +14,9 @@ import 'stylesheet.dart'; /// A parser for the indented syntax. class SassParser extends StylesheetParser { - int get currentIndentation => _currentIndentation; - var _currentIndentation = 0; + int /*!*/ get currentIndentation => _currentIndentation; + // TODO: var + int /*!*/ _currentIndentation = 0; /// The indentation level of the next source line after the scanner's /// position, or `null` if that hasn't been computed yet. @@ -24,7 +25,7 @@ class SassParser extends StylesheetParser { int _nextIndentation; /// The beginning of the next source line after the scanner's position, or - /// `null` if that hasn't been computed yet. + /// `null` if the next indentation hasn't been computed yet. /// /// A source line is any line that's not entirely whitespace. LineScannerState _nextIndentationEnd; @@ -116,7 +117,7 @@ class SassParser extends StylesheetParser { } } - bool scanElse(int ifIndentation) { + bool scanElse(int /*!*/ ifIndentation) { if (_peekIndentation() != ifIndentation) return false; var start = scanner.state; var startIndentation = currentIndentation; @@ -136,7 +137,8 @@ class SassParser extends StylesheetParser { List children(Statement child()) { var children = []; _whileIndentedLower(() { - children.add(_child(child)); + var parsedChild = _child(child); + if (parsedChild != null) children.add(parsedChild); }); return children; } @@ -236,9 +238,8 @@ class SassParser extends StylesheetParser { } } while (scanner.scan("//")); - lastSilentComment = + return lastSilentComment = SilentComment(buffer.toString(), scanner.spanFrom(start)); - return lastSilentComment; } /// Consumes an indented-style loud context. @@ -390,9 +391,11 @@ class SassParser extends StylesheetParser { /// Consumes indentation whitespace and returns the indentation level of the /// next line. - int _readIndentation() { - if (_nextIndentation == null) _peekIndentation(); - _currentIndentation = _nextIndentation; + int /*!*/ _readIndentation() { + // TODO: This "!" is totally bogus + var nextIndentation = _nextIndentation; + if (nextIndentation == null) _peekIndentation(); + var currentIndentation = _currentIndentation = nextIndentation; scanner.state = _nextIndentationEnd; _nextIndentation = null; _nextIndentationEnd = null; @@ -400,8 +403,9 @@ class SassParser extends StylesheetParser { } /// Returns the indentation level of the next line. - int _peekIndentation() { - if (_nextIndentation != null) return _nextIndentation; + int /*!*/ _peekIndentation() { + var nextIndentation = _nextIndentation; + if (nextIndentation != null) return nextIndentation; if (scanner.isDone) { _nextIndentation = 0; @@ -419,7 +423,7 @@ class SassParser extends StylesheetParser { do { containsTab = false; containsSpace = false; - _nextIndentation = 0; + nextIndentation = 0; while (true) { var next = scanner.peekChar(); @@ -430,7 +434,7 @@ class SassParser extends StylesheetParser { } else { break; } - _nextIndentation++; + nextIndentation++; scanner.readChar(); } @@ -444,10 +448,12 @@ class SassParser extends StylesheetParser { _checkIndentationConsistency(containsTab, containsSpace); - if (_nextIndentation > 0) _spaces ??= containsSpace; + _nextIndentation = nextIndentation; + // TODO: no ! + if (nextIndentation > 0) _spaces ??= containsSpace; _nextIndentationEnd = scanner.state; scanner.state = start; - return _nextIndentation; + return nextIndentation; } /// Ensures that the document uses consistent characters for indentation. diff --git a/lib/src/parse/scss.dart b/lib/src/parse/scss.dart index fa9facb2c..dc50c9ee8 100644 --- a/lib/src/parse/scss.dart +++ b/lib/src/parse/scss.dart @@ -13,7 +13,7 @@ import 'stylesheet.dart'; /// A parser for the CSS-compatible syntax. class ScssParser extends StylesheetParser { bool get indented => false; - int get currentIndentation => null; + int get currentIndentation => 0; ScssParser(String contents, {Object url, Logger logger}) : super(contents, url: url, logger: logger); @@ -101,7 +101,7 @@ class ScssParser extends StylesheetParser { } } - List statements(Statement statement()) { + List statements(Statement /*?*/ statement()) { var statements = []; whitespaceWithoutComments(); while (!scanner.isDone) { @@ -157,9 +157,8 @@ class ScssParser extends StylesheetParser { scanner.spanFrom(start)); } - lastSilentComment = SilentComment( + return lastSilentComment = SilentComment( scanner.substring(start.position), scanner.spanFrom(start)); - return lastSilentComment; } /// Consumes a statement-level loud comment block. diff --git a/lib/src/parse/selector.dart b/lib/src/parse/selector.dart index 3ef290083..a7011a878 100644 --- a/lib/src/parse/selector.dart +++ b/lib/src/parse/selector.dart @@ -208,7 +208,8 @@ class SelectorParser extends Parser { : identifier(); whitespace(); - var modifier = isAlphabetic(scanner.peekChar()) + next = scanner.peekChar(); + var modifier = next != null && isAlphabetic(next) ? String.fromCharCode(scanner.readChar()) : null; diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index f279f4570..86c42465d 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -18,6 +18,7 @@ import '../interpolation_buffer.dart'; import '../logger.dart'; import '../util/character.dart'; import '../utils.dart'; +import '../util/nullable.dart'; import '../value.dart'; import 'parser.dart'; @@ -85,7 +86,17 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; // Allow a byte-order mark at the beginning of the document. scanner.scanChar(0xFEFF); - var statements = this.statements(() => _statement(root: true)); + var statements = this.statements(() { + // Handle this specially so that [atRule] always returns a non-nullable + // Statement. + if (scanner.scan('@charset')) { + whitespace(); + string(); + return null; + } + + _statement(root: true); + }); scanner.expectDone(); /// Ensure that all gloal variable assignments produce a variable in this @@ -111,7 +122,7 @@ abstract class StylesheetParser extends Parser { return arguments; }); - Expression parseExpression() => _parseSingleProduction(expression); + Expression /*!*/ parseExpression() => _parseSingleProduction(expression); VariableDeclaration parseVariableDeclaration() => _parseSingleProduction(() => lookingAtIdentifier() @@ -178,7 +189,7 @@ abstract class StylesheetParser extends Parser { case $rbrace: scanner.error('unmatched "}".', length: 1); - return null; + break; default: return _inStyleRule || _inUnknownAtRule || _inMixin || _inContentBlock @@ -207,6 +218,7 @@ abstract class StylesheetParser extends Parser { start ??= scanner.state; var name = variableName(); + // TODO: no start! if (namespace != null) _assertPublic(name, () => scanner.spanFrom(start)); if (plainCss) { @@ -492,6 +504,7 @@ abstract class StylesheetParser extends Parser { _inStyleRule = wasInStyleRule; + // TODO: no start! return StyleRule(interpolation, children, scanner.spanFrom(start)); }); } @@ -585,7 +598,7 @@ abstract class StylesheetParser extends Parser { /// If [root] is `true`, this parses at-rules that are allowed only at the /// root of the stylesheet. @protected - Statement atRule(Statement child(), {bool root = false}) { + Statement /*!*/ atRule(Statement /*!*/ child(), {bool root = false}) { // NOTE: this logic is largely duplicated in CssParser.atRule. Most changes // here should be mirrored there. @@ -604,11 +617,6 @@ abstract class StylesheetParser extends Parser { switch (name.asPlain) { case "at-root": return _atRootRule(start); - case "charset": - _isUseAllowed = wasUseAllowed; - if (!root) _disallowedAtRule(start); - string(); - return null; case "content": return _contentRule(start); case "debug": @@ -947,6 +955,7 @@ abstract class StylesheetParser extends Parser { return _withChildren(child, start, (children, span) { _inControlDirective = wasInControlDirective; + // TODO: no exclusive! return ForRule(variable, from, to, children, span, exclusive: exclusive); }); } @@ -967,9 +976,9 @@ abstract class StylesheetParser extends Parser { } Set shownMixinsAndFunctions; - Set shownVariables; + Set /*?*/ shownVariables; Set hiddenMixinsAndFunctions; - Set hiddenVariables; + Set /*?*/ hiddenVariables; if (scanIdentifier("show")) { var members = _memberList(); shownMixinsAndFunctions = members.item1; @@ -1200,6 +1209,7 @@ abstract class StylesheetParser extends Parser { var wasInContentBlock = _inContentBlock; _inContentBlock = true; content = _withChildren(_statement, start, (children, span) { + // TODO: no ! return ContentBlock(contentArguments, children, span); }); _inContentBlock = wasInContentBlock; @@ -1384,6 +1394,8 @@ relase. For details, see http://bit.ly/moz-document. /// Parses the namespace of a `@use` rule from an `as` clause, or returns the /// default namespace from its URL. + /// + /// Returns `null` to indicate a `@use` rule without a URL. String _useNamespace(Uri url, LineScannerState start) { if (scanIdentifier("as")) { whitespace(); @@ -1620,8 +1632,9 @@ relase. For details, see http://bit.ly/moz-document. /// still be a valid expression. When it returns `true`, this returns the /// expression. @protected - Expression expression( + Expression /*!*/ expression( {bool bracketList = false, bool singleEquals = false, bool until()}) { + // TODO: no ! throughout if (until != null && until()) scanner.error("Expected expression."); LineScannerState beforeBracket; @@ -1639,9 +1652,9 @@ relase. For details, see http://bit.ly/moz-document. var start = scanner.state; var wasInParentheses = _inParentheses; - List commaExpressions; + List commaExpressions; - List spaceExpressions; + List spaceExpressions; // Operators whose right-hand operands are not fully parsed yet, in order of // appearance in the document. Because a low-precedence operator will cause @@ -1651,14 +1664,20 @@ relase. For details, see http://bit.ly/moz-document. // The left-hand sides of [operators]. `operands[n]` is the left-hand side // of `operators[n]`. - List operands; + List operands; /// Whether the single expression parsed so far may be interpreted as /// slash-separated numbers. var allowSlash = lookingAtNumber(); - /// The leftmost expression that's been fully-parsed. Never `null`. - var singleExpression = _singleExpression(); + /// The leftmost expression that's been fully-parsed. This can be null in + /// special cases where the expression begins with a sub-expression but has + /// a later character that indicates that the outer expression isn't done, + /// as here: + /// + /// foo, bar + /// ^ + Expression singleExpression = _singleExpression(); // Resets the scanner state to the state it was at at the beginning of the // expression, except for [_inParentheses]. @@ -1673,6 +1692,10 @@ relase. For details, see http://bit.ly/moz-document. } void resolveOneOperation() { + if (operators == null) { + throw StateError("operators must be set for resolveOneOperation()."); + } + var operator = operators.removeLast(); if (operator != BinaryOperator.dividedBy) allowSlash = false; if (allowSlash && !_inParentheses) { @@ -2145,7 +2168,7 @@ relase. For details, see http://bit.ly/moz-document. default: if (first != null && first >= 0x80) return identifierLike(); scanner.error("Expected expression."); - return null; + break; } } @@ -2332,6 +2355,7 @@ relase. For details, see http://bit.ly/moz-document. /// Consumes a unary operation expression. UnaryOperationExpression _unaryOperation() { var start = scanner.state; + // TODO: no ! var operator = _unaryOperatorFor(scanner.readChar()); if (operator == null) { scanner.error("Expected unary operator.", position: scanner.position - 1); @@ -2675,8 +2699,8 @@ relase. For details, see http://bit.ly/moz-document. break; case "url": - var contents = _tryUrlContents(start); - return contents == null ? null : StringExpression(contents); + return _tryUrlContents(start) + .andThen((contents) => StringExpression(contents)); case "clamp": // Vendor-prefixed clamp() functions aren't parsed specially, because @@ -3023,6 +3047,7 @@ relase. For details, see http://bit.ly/moz-document. var wroteNewline = false; loop: while (true) { + // TODO: no next!s var next = scanner.peekChar(); switch (next) { case $backslash: @@ -3293,17 +3318,19 @@ relase. For details, see http://bit.ly/moz-document. buffer.add(expression()); } else { var next = scanner.peekChar(); - var isAngle = next == $langle || next == $rangle; - if (isAngle || next == $equal) { + if (next == $langle || next == $rangle || next == $equal) { buffer.writeCharCode($space); buffer.writeCharCode(scanner.readChar()); - if (isAngle && scanner.scanChar($equal)) buffer.writeCharCode($equal); + if ((next == $langle || next == $rangle) && scanner.scanChar($equal)) { + buffer.writeCharCode($equal); + } buffer.writeCharCode($space); whitespace(); buffer.add(_expressionUntilComparison()); - if (isAngle && scanner.scanChar(next)) { + // TODO: no ! + if ((next == $langle || next == $rangle) && scanner.scanChar(next)) { buffer.writeCharCode($space); buffer.writeCharCode(next); if (scanner.scanChar($equal)) buffer.writeCharCode($equal); @@ -3559,8 +3586,8 @@ relase. For details, see http://bit.ly/moz-document. /// Consumes a block of [child] statements and passes them, as well as the /// span from [start] to the end of the child block, to [create]. - T _withChildren(Statement child(), LineScannerState start, - T create(List children, FileSpan span)) { + T _withChildren(Statement /*!*/ child(), LineScannerState start, + T create(List children, FileSpan span)) { var result = create(children(child), scanner.spanFrom(start)); whitespaceWithoutComments(); return result; @@ -3648,7 +3675,7 @@ relase. For details, see http://bit.ly/moz-document. /// whitespace. This is necessary to ensure that the source span for the /// parent rule doesn't cover whitespace after the rule. @protected - List children(Statement child()); + List children(Statement /*!*/ child()); /// Consumes top-level statements. /// diff --git a/lib/src/stylesheet_graph.dart b/lib/src/stylesheet_graph.dart index 02e8f7481..491470264 100644 --- a/lib/src/stylesheet_graph.dart +++ b/lib/src/stylesheet_graph.dart @@ -10,6 +10,7 @@ import 'package:tuple/tuple.dart'; import 'ast/sass.dart'; import 'import_cache.dart'; import 'importer.dart'; +import 'util/nullable.dart'; import 'visitor/find_dependencies.dart'; /// A graph of the import relationships between stylesheets, available via @@ -66,6 +67,7 @@ class StylesheetGraph { /// /// Returns `null` if the import cache can't find a stylesheet at [url]. StylesheetNode _add(Uri url, [Importer baseImporter, Uri baseUrl]) { + // TODO: no as var tuple = _ignoreErrors(() => importCache.canonicalize(url, baseImporter: baseImporter, baseUrl: baseUrl)); if (tuple == null) return null; @@ -95,6 +97,7 @@ class StylesheetGraph { var node = _nodes[canonicalUrl]; if (node != null) return const {}; + // TODO: no as var stylesheet = _ignoreErrors( () => importCache.importCanonical(importer, canonicalUrl, originalUrl)); if (stylesheet == null) return const {}; @@ -145,6 +148,7 @@ class StylesheetGraph { _transitiveModificationTimes.clear(); importCache.clearImport(canonicalUrl); + // TODO: no as var stylesheet = _ignoreErrors( () => importCache.importCanonical(node.importer, canonicalUrl)); if (stylesheet == null) return false; @@ -260,6 +264,7 @@ class StylesheetGraph { StylesheetNode _nodeFor( Uri url, Importer baseImporter, Uri baseUrl, Set active, {bool forImport = false}) { + // TODO: no as var tuple = _ignoreErrors(() => importCache.canonicalize(url, baseImporter: baseImporter, baseUrl: baseUrl, forImport: forImport)); @@ -278,6 +283,7 @@ class StylesheetGraph { /// error will be produced during compilation. if (active.contains(canonicalUrl)) return null; + // TODO: no as var stylesheet = _ignoreErrors( () => importCache.importCanonical(importer, canonicalUrl, resolvedUrl)); if (stylesheet == null) return null; @@ -354,10 +360,10 @@ class StylesheetNode { /// accordingly. void _replaceUpstream(Map newUpstream, Map newUpstreamImports) { - var oldUpstream = {...upstream.values, ...upstreamImports.values} - ..remove(null); - var newUpstreamSet = {...newUpstream.values, ...newUpstreamImports.values} - ..remove(null); + var oldUpstream = + {...upstream.values, ...upstreamImports.values}.removeNull(); + var newUpstreamSet = + {...newUpstream.values, ...newUpstreamImports.values}.removeNull(); for (var removed in oldUpstream.difference(newUpstreamSet)) { var wasRemoved = removed._downstream.remove(this); @@ -398,5 +404,6 @@ class StylesheetNode { } } - String toString() => p.prettyUri(stylesheet.span.sourceUrl); + String toString() => + stylesheet.span?.sourceUrl.andThen(p.prettyUri) ?? ''; } diff --git a/lib/src/util/character.dart b/lib/src/util/character.dart index 13d2e937d..efa2eb950 100644 --- a/lib/src/util/character.dart +++ b/lib/src/util/character.dart @@ -118,7 +118,8 @@ int opposite(int character) { case $lbracket: return $rbracket; default: - return null; + throw ArgumentError( + '"${String.fromCharCode(character)}" isn\'t a brace-like character.'); } } diff --git a/lib/src/util/fixed_length_list_builder.dart b/lib/src/util/fixed_length_list_builder.dart deleted file mode 100644 index 880f8ac24..000000000 --- a/lib/src/util/fixed_length_list_builder.dart +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2019 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. - -/// A class for efficiently adding elements to a list whose final length is -/// known in advance. -class FixedLengthListBuilder { - /// The list to which elements are being added. - final List _list; - - /// The index at which to add the next element to the list. - /// - /// This is set to -1 once the list has been returned. - var _index = 0; - - /// Creates a new builder that creates a list of length [length]. - FixedLengthListBuilder(int length) : _list = List(length); - - /// Adds [element] to the next available space in the list. - /// - /// This may only be called if [build] has not yet been called, and if the - /// list is not yet full. - void add(T element) { - _checkUnbuilt(); - _list[_index] = element; - _index++; - } - - /// Adds all elements in [elements] to the next available spaces in the list. - /// - /// This may only be called if [build] has not yet been called, and if the - /// list has room for all of [elements]. - void addAll(Iterable elements) { - _checkUnbuilt(); - _list.setAll(_index, elements); - _index += elements.length; - } - - /// Adds the elements from [start] (inclusive) to [end] (exclusive) of - /// [elements] to the next available spaces in the list. - /// - /// The [end] defaults to `elements.length`. - /// - /// This may only be called if [build] has not yet been called, and if the - /// list has room for all the elements to add. - void addRange(Iterable elements, int start, [int end]) { - _checkUnbuilt(); - var length = (end ?? elements.length) - start; - _list.setRange(_index, _index + length, elements, start); - _index += length; - } - - /// Returns the mutable, fixed-length built list. - /// - /// Any spaces in the list that haven't had elements added explicitly will be - /// `null`. This may only be called once. - List build() { - _checkUnbuilt(); - _index = -1; - return _list; - } - - /// Throws a [StateError] if [build] has been called already. - void _checkUnbuilt() { - if (_index == -1) throw StateError("build() has already been called."); - } -} diff --git a/lib/src/util/limited_map_view.dart b/lib/src/util/limited_map_view.dart index 308a65862..72759c406 100644 --- a/lib/src/util/limited_map_view.dart +++ b/lib/src/util/limited_map_view.dart @@ -38,12 +38,13 @@ class LimitedMapView extends UnmodifiableMapBase { /// Returns a [LimitedMapView] that doesn't allow keys in [blocklist]. /// /// The [blocklist] must have the same notion of equality as the [map]. - LimitedMapView.blocklist(this._map, Set blocklist) + LimitedMapView.blocklist(this._map, Set /*!*/ blocklist) : _keys = { for (var key in _map.keys) if (!blocklist.contains(key)) key }; + // TODO: no as V operator [](Object key) => _keys.contains(key) ? _map[key] : null; bool containsKey(Object key) => _keys.contains(key); diff --git a/lib/src/util/merged_map_view.dart b/lib/src/util/merged_map_view.dart index 9dbb2d262..3b5ed4830 100644 --- a/lib/src/util/merged_map_view.dart +++ b/lib/src/util/merged_map_view.dart @@ -5,6 +5,8 @@ import 'dart:collection'; import '../utils.dart'; +import '../util/nullable.dart'; +import 'nullable.dart'; /// An unmodifiable view of multiple maps merged together as though they were a /// single map. @@ -31,7 +33,7 @@ class MergedMapView extends MapBase { /// Each map must have the default notion of equality. The underlying maps' /// values may change independently of this view, but their set of keys may /// not. - MergedMapView(Iterable> maps) { + MergedMapView(Iterable /*!*/ > maps) { for (var map in maps) { if (map is MergedMapView) { // Flatten nested merged views to avoid O(depth) overhead. @@ -44,10 +46,7 @@ class MergedMapView extends MapBase { } } - V operator [](Object key) { - var child = _mapsByKey[key]; - return child == null ? null : child[key]; - } + V operator [](Object key) => _mapsByKey[key].andGet(key); operator []=(K key, V value) { var child = _mapsByKey[key]; @@ -58,7 +57,7 @@ class MergedMapView extends MapBase { child[key] = value; } - V remove(Object key) { + V /*?*/ remove(Object key) { throw UnsupportedError("Entries may not be removed from MergedMapView."); } diff --git a/lib/src/util/multi_dir_watcher.dart b/lib/src/util/multi_dir_watcher.dart index 19b157a01..af24008fd 100644 --- a/lib/src/util/multi_dir_watcher.dart +++ b/lib/src/util/multi_dir_watcher.dart @@ -16,7 +16,7 @@ class MultiDirWatcher { /// A map from paths to the event streams for those paths. /// /// No key in this map is a parent directories of any other key in this map. - final _watchers = >{}; + final _watchers = p.PathMap>(); /// The stream of events from all directories that are being watched. Stream get events => _group.stream; @@ -37,7 +37,10 @@ class MultiDirWatcher { /// from [directory]. Future watch(String directory) { var isParentOfExistingDir = false; - for (var existingDir in _watchers.keys.toList()) { + for (var entry in _watchers.entries.toList()) { + var existingDir = entry.key /*!*/; // dart-lang/path#100 + var existingWatcher = entry.value; + if (!isParentOfExistingDir && (p.equals(existingDir, directory) || p.isWithin(existingDir, directory))) { @@ -45,7 +48,8 @@ class MultiDirWatcher { } if (p.isWithin(directory, existingDir)) { - _group.remove(_watchers.remove(existingDir)); + _watchers.remove(existingDir); + _group.remove(existingWatcher); isParentOfExistingDir = true; } } diff --git a/lib/src/util/nullable.dart b/lib/src/util/nullable.dart new file mode 100644 index 000000000..cd59306d2 --- /dev/null +++ b/lib/src/util/nullable.dart @@ -0,0 +1,44 @@ +// Copyright 2020 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. + +extension NullableExtension on T /*?*/ { + /// If [this] is `null`, returns `null`. Otherwise, runs [fn] and returns its + /// result. + /// + /// Based on Rust's `Option.and_then`. + V /*?*/ andThen(V Function(T value) fn) { + var self = this; // dart-lang/language#1520 + return self == null ? null : fn(self); + } +} + +extension NullableListExtension on List /*?*/ { + /// If [this] is `null`, returns `null`. Otherwise, returns `this[index]`. + /// + /// This is the equivalent of `list?.[key]`, if such a thing existed. + T /*?*/ andGet(int index) { + var self = this; + return self == null ? null : self[index]; + } +} + +extension NullableMapExtension on Map /*?*/ { + /// If [this] is `null`, returns `null`. Otherwise, returns `this[key]`. + /// + /// This is the equivalent of `map?.[key]`, if such a thing existed. + V /*?*/ andGet(Object key) { + var self = this; + // TODO: no as + return self == null ? null : self[key]; + } +} + +extension SetExtension on Set { + /// Destructively removes the `null` element from this set, if it exists, and + /// returns a view of it casted to a non-nullable type. + Set removeNull() { + this.remove(null); + return this.cast(); + } +} diff --git a/lib/src/util/public_member_map_view.dart b/lib/src/util/public_member_map_view.dart index 4ab9c190d..641a7ebd3 100644 --- a/lib/src/util/public_member_map_view.dart +++ b/lib/src/util/public_member_map_view.dart @@ -10,7 +10,7 @@ import '../utils.dart'; /// begin with `_` or `-`. /// /// Note that [PublicMemberMap.length] is *not* `O(1)`. -class PublicMemberMapView extends UnmodifiableMapBase { +class PublicMemberMapView extends UnmodifiableMapBase { /// The wrapped map. final Map _inner; diff --git a/lib/src/util/source_map_buffer.dart b/lib/src/util/source_map_buffer.dart index 7907051d6..65877e169 100644 --- a/lib/src/util/source_map_buffer.dart +++ b/lib/src/util/source_map_buffer.dart @@ -49,7 +49,11 @@ class SourceMapBuffer implements StringBuffer { /// Specifically, this associates the point at the beginning of the written /// text with [span.start] and the point at the end of the written text with /// [span.end]. + /// + /// Just calls `callback()` if [span] is `null`. T forSpan(FileSpan span, T callback()) { + if (span == null) return callback(); + var wasInSpan = _inSpan; _inSpan = true; _addEntry(span.start, _targetLocation); diff --git a/lib/src/util/unprefixed_map_view.dart b/lib/src/util/unprefixed_map_view.dart index e11de4532..d7200eb16 100644 --- a/lib/src/util/unprefixed_map_view.dart +++ b/lib/src/util/unprefixed_map_view.dart @@ -15,10 +15,10 @@ import 'dart:collection'; /// `@used with` to mark configured variables as used. class UnprefixedMapView extends UnmodifiableMapBase { /// The wrapped map. - final Map _map; + final Map _map; /// The prefix to remove from the map keys. - final String _prefix; + final String /*!*/ _prefix; Iterable get keys => _UnprefixedKeys(this); diff --git a/lib/src/utils.dart b/lib/src/utils.dart index cb3a34479..58ebd71be 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -12,6 +12,7 @@ import 'package:term_glyph/term_glyph.dart' as glyph; import 'ast/node.dart'; import 'util/character.dart'; +import 'util/nullable.dart'; /// The URL used in stack traces when no source URL is available. final _noSourceUrl = Uri.parse("-"); @@ -147,6 +148,7 @@ List flattenVertically(Iterable> iterable) { } /// Returns the first element of [iterable], or `null` if the iterable is empty. +// TODO(nweiz): Use package:collection T firstOrNull(Iterable iterable) { var iterator = iterable.iterator; return iterator.moveNext() ? iterator.current : null; @@ -204,10 +206,10 @@ int mapHash(Map map) => /// /// By default, the frame's URL is set to `span.sourceUrl`. However, if [url] is /// passed, it's used instead. -Frame frameForSpan(SourceSpan span, String member, {Uri url}) => Frame( - url ?? span.sourceUrl ?? _noSourceUrl, - span.start.line + 1, - span.start.column + 1, +Frame frameForSpan(SourceSpan /*?*/ span, String member, {Uri url}) => Frame( + url ?? span?.sourceUrl ?? _noSourceUrl, + span.andThen((span) => span.start.line + 1) ?? 1, + span.andThen((span) => span.start.column + 1) ?? 1, member); /// Returns a source span that covers the spans of both the first and last nodes @@ -218,11 +220,10 @@ Frame frameForSpan(SourceSpan span, String member, {Uri url}) => Frame( FileSpan spanForList(List nodes) { if (nodes.isEmpty) return null; - // Spans may be null for dynamically-constructed ASTs. - var left = nodes.first?.span; + var left = nodes.first.span; if (left == null) return null; - var right = nodes.last?.span; + var right = nodes.last.span; if (right == null) return null; return left.expand(right); @@ -304,7 +305,7 @@ List longestCommonSubsequence(List list1, List list2, growable: false); var selections = List>.generate( - list1.length, (_) => List(list2.length), + list1.length, (_) => List.filled(list2.length, null), growable: false); for (var i = 0; i < list1.length; i++) { @@ -334,7 +335,8 @@ List longestCommonSubsequence(List list1, List list2, /// /// By default, throws a [StateError] if no value matches. If [orElse] is /// passed, its return value is used instead. -T removeFirstWhere(List list, bool test(T value), {T orElse()}) { +T /*!*/ removeFirstWhere(List list, bool test(T value), + {T /*!*/ orElse()}) { T toRemove; for (var element in list) { if (!test(element)) continue; @@ -357,8 +359,9 @@ T removeFirstWhere(List list, bool test(T value), {T orElse()}) { void mapAddAll2( Map> destination, Map> source) { source.forEach((key, inner) { - if (destination.containsKey(key)) { - destination[key].addAll(inner); + var innerDestination = destination[key]; + if (innerDestination != null) { + innerDestination.addAll(inner); } else { destination[key] = inner; } @@ -394,7 +397,7 @@ Future> mapAsync( /// same key. Future putIfAbsentAsync( Map map, K key, Future ifAbsent()) async { - if (map.containsKey(key)) return map[key]; + if (map.containsKey(key)) return map[key] /*!*/; var value = await ifAbsent(); map[key] = value; return value; diff --git a/lib/src/value/argument_list.dart b/lib/src/value/argument_list.dart index 4e506ec00..f26b6cbb6 100644 --- a/lib/src/value/argument_list.dart +++ b/lib/src/value/argument_list.dart @@ -23,8 +23,8 @@ class SassArgumentList extends SassList implements ext.SassArgumentList { bool get wereKeywordsAccessed => _wereKeywordsAccessed; var _wereKeywordsAccessed = false; - SassArgumentList(Iterable contents, Map keywords, - ListSeparator separator) + SassArgumentList(Iterable contents, + Map keywords, ListSeparator separator) : _keywords = Map.unmodifiable(keywords), super(contents, separator); } diff --git a/lib/src/value/color.dart b/lib/src/value/color.dart index c815954f8..171655744 100644 --- a/lib/src/value/color.dart +++ b/lib/src/value/color.dart @@ -13,6 +13,8 @@ import '../visitor/interface/value.dart'; import 'external/value.dart' as ext; class SassColor extends Value implements ext.SassColor { + // TODO: lates all around + int get red { if (_red == null) _hslToRgb(); return _red; @@ -111,6 +113,7 @@ class SassColor extends Value implements ext.SassColor { var factor = 1 - scaledWhiteness - scaledBlackness; int toRgb(num hue) { + // TODO: var var channel = _hueToRgb(0, 1, hue) * factor + scaledWhiteness; return fuzzyRound(channel * 255); } @@ -146,7 +149,7 @@ class SassColor extends Value implements ext.SassColor { SassColor changeAlpha(num alpha) => SassColor._(_red, _green, _blue, _hue, _saturation, _lightness, fuzzyAssertRange(alpha, 0, 1, "alpha")); - Value plus(Value other) { + Value plus(Value /*!*/ other) { if (other is! SassNumber && other is! SassColor) return super.plus(other); throw SassScriptException('Undefined operation "$this + $other".'); } @@ -163,7 +166,7 @@ class SassColor extends Value implements ext.SassColor { throw SassScriptException('Undefined operation "$this / $other".'); } - Value modulo(Value other) => + Value modulo(Value /*!*/ other) => throw SassScriptException('Undefined operation "$this % $other".'); bool operator ==(Object other) => @@ -198,11 +201,11 @@ class SassColor extends Value implements ext.SassColor { _hue = (240 + 60 * (scaledRed - scaledGreen) / delta) % 360; } - _lightness = 50 * (max + min); + var lightness = _lightness = 50 * (max + min); if (max == min) { _saturation = 0; - } else if (_lightness < 50) { + } else if (lightness < 50) { _saturation = 100 * delta / (max + min); } else { _saturation = 100 * delta / (2 - max - min); diff --git a/lib/src/value/external/color.dart b/lib/src/value/external/color.dart index 3bdc4efdd..13e7fc467 100644 --- a/lib/src/value/external/color.dart +++ b/lib/src/value/external/color.dart @@ -11,22 +11,22 @@ import 'value.dart'; @sealed abstract class SassColor extends Value { /// This color's red channel, between `0` and `255`. - int get red; + int /*!*/ get red; /// This color's green channel, between `0` and `255`. - int get green; + int /*!*/ get green; /// This color's blue channel, between `0` and `255`. - int get blue; + int /*!*/ get blue; /// This color's hue, between `0` and `360`. - num get hue; + num /*!*/ get hue; /// This color's saturation, a percentage between `0` and `100`. - num get saturation; + num /*!*/ get saturation; /// This color's lightness, a percentage between `0` and `100`. - num get lightness; + num /*!*/ get lightness; /// This color's whiteness, a percentage between `0` and `100`. num get whiteness; diff --git a/lib/src/value/external/list.dart b/lib/src/value/external/list.dart index 87dd6c751..daa447d8e 100644 --- a/lib/src/value/external/list.dart +++ b/lib/src/value/external/list.dart @@ -13,11 +13,12 @@ import 'value.dart'; abstract class SassList extends Value { ListSeparator get separator; - bool get hasBrackets; + bool /*!*/ get hasBrackets; /// Returns an empty list with the given [separator] and [brackets]. /// /// The [separator] defaults to [ListSeparator.undecided], and [brackets] defaults to `false`. + // TODO: No ? for brackets const factory SassList.empty({ListSeparator separator, bool brackets}) = internal.SassList.empty; diff --git a/lib/src/value/external/string.dart b/lib/src/value/external/string.dart index f352e0591..1f4d5d123 100644 --- a/lib/src/value/external/string.dart +++ b/lib/src/value/external/string.dart @@ -25,10 +25,10 @@ abstract class SassString extends Value { /// contain characters that aren't valid in identifiers, such as /// `url(http://example.com)`. Unfortunately, it also means that we don't /// consider `foo` and `f\6F\6F` the same string. - String get text; + String /*!*/ get text; /// Whether this string has quotes. - bool get hasQuotes; + bool /*!*/ get hasQuotes; /// Sass's notion of the length of this string. /// @@ -41,16 +41,18 @@ abstract class SassString extends Value { /// /// This returns the same value as `text.runes.length`, but it's more /// efficient. - int get sassLength; + int /*!*/ get sassLength; /// Creates an empty string. /// /// The [quotes] argument defaults to `false`. + // TODO: no required factory SassString.empty({bool quotes}) = internal.SassString.empty; /// Creates a string with the given [text]. /// /// The [quotes] argument defaults to `false`. + // TODO: no ? factory SassString(String text, {bool quotes}) = internal.SassString; /// Converts [sassIndex] into a Dart-style index into [text]. diff --git a/lib/src/value/external/value.dart b/lib/src/value/external/value.dart index 2089726c5..2d64bec1a 100644 --- a/lib/src/value/external/value.dart +++ b/lib/src/value/external/value.dart @@ -53,7 +53,7 @@ abstract class Value { /// /// All SassScript values can be used as lists. Maps count as lists of pairs, /// and all other values count as single-value lists. - bool get hasBrackets; + bool /*!*/ get hasBrackets; /// This value as a list. /// diff --git a/lib/src/value/list.dart b/lib/src/value/list.dart index 1960e0c5e..fca8b8e69 100644 --- a/lib/src/value/list.dart +++ b/lib/src/value/list.dart @@ -32,7 +32,8 @@ class SassList extends Value implements ext.SassList { separator = separator ?? ListSeparator.undecided, hasBrackets = brackets; - SassList(Iterable contents, this.separator, {bool brackets = false}) + SassList(Iterable contents, this.separator, + {bool brackets = false}) : _contents = List.unmodifiable(contents), hasBrackets = brackets { if (separator == ListSeparator.undecided && asList.length > 1) { @@ -79,7 +80,7 @@ class ListSeparator { /// /// If the separator of a list has not been decided, this value will be /// `null`. - final String separator; + final String /*?*/ separator; const ListSeparator._(this._name, this.separator); diff --git a/lib/src/value/map.dart b/lib/src/value/map.dart index 971b858c9..5883c4964 100644 --- a/lib/src/value/map.dart +++ b/lib/src/value/map.dart @@ -26,7 +26,8 @@ class SassMap extends Value implements ext.SassMap { /// Returns an empty map. const SassMap.empty() : contents = const {}; - SassMap(Map contents) : contents = Map.unmodifiable(contents); + SassMap(Map contents) + : contents = Map.unmodifiable(contents); T accept(ValueVisitor visitor) => visitor.visitMap(this); diff --git a/lib/src/value/number.dart b/lib/src/value/number.dart index dfb9acf9b..a0c8038bb 100644 --- a/lib/src/value/number.dart +++ b/lib/src/value/number.dart @@ -183,17 +183,22 @@ abstract class SassNumber extends Value implements ext.SassNumber { factory SassNumber.withUnits(num value, {List numeratorUnits, List denominatorUnits}) { - var emptyNumerator = numeratorUnits == null || numeratorUnits.isEmpty; - var emptyDenominator = denominatorUnits == null || denominatorUnits.isEmpty; - if (emptyNumerator && emptyDenominator) return UnitlessSassNumber(value); - - if (emptyDenominator && numeratorUnits.length == 1) { - return SingleUnitSassNumber(value, numeratorUnits[0]); + if (denominatorUnits == null || denominatorUnits.isEmpty) { + if (numeratorUnits == null || numeratorUnits.isEmpty) { + return UnitlessSassNumber(value); + } else if (numeratorUnits.length == 1) { + return SingleUnitSassNumber(value, numeratorUnits[0]); + } else { + return ComplexSassNumber( + value, List.unmodifiable(numeratorUnits), const []); + } } else { return ComplexSassNumber( value, - emptyNumerator ? const [] : List.unmodifiable(numeratorUnits), - emptyDenominator ? const [] : List.unmodifiable(denominatorUnits)); + numeratorUnits == null || numeratorUnits.isEmpty + ? const [] + : List.unmodifiable(numeratorUnits), + List.unmodifiable(denominatorUnits)); } } @@ -355,6 +360,7 @@ abstract class SassNumber extends Value implements ext.SassNumber { if (factor == null) return false; value *= factor; return true; + // TODO: no as }, orElse: () => throw _compatibilityException()); } @@ -365,6 +371,7 @@ abstract class SassNumber extends Value implements ext.SassNumber { if (factor == null) return false; value /= factor; return true; + // TODO: no as }, orElse: () => throw _compatibilityException()); } @@ -523,7 +530,7 @@ abstract class SassNumber extends Value implements ext.SassNumber { var newNumerators = []; var mutableOtherDenominators = otherDenominators.toList(); for (var numerator in numeratorUnits) { - removeFirstWhere(mutableOtherDenominators, (denominator) { + removeFirstWhere(mutableOtherDenominators, (denominator) { var factor = conversionFactor(numerator, denominator); if (factor == null) return false; value /= factor; @@ -536,7 +543,7 @@ abstract class SassNumber extends Value implements ext.SassNumber { var mutableDenominatorUnits = denominatorUnits.toList(); for (var numerator in otherNumerators) { - removeFirstWhere(mutableDenominatorUnits, (denominator) { + removeFirstWhere(mutableDenominatorUnits, (denominator) { var factor = conversionFactor(numerator, denominator); if (factor == null) return false; value /= factor; @@ -557,20 +564,17 @@ abstract class SassNumber extends Value implements ext.SassNumber { /// unit in [units2]. bool _areAnyConvertible(List units1, List units2) { return units1.any((unit1) { - if (!_isConvertable(unit1)) return units2.contains(unit1); var innerMap = _conversions[unit1]; + if (innerMap == null) return units2.contains(unit1); return units2.any(innerMap.containsKey); }); } - /// Returns whether [unit] can be converted to or from any other units. - bool _isConvertable(String unit) => _conversions.containsKey(unit); - /// Returns the number of [unit1]s per [unit2]. /// /// Equivalently, `1unit2 * conversionFactor(unit1, unit2) = 1unit1`. @protected - num conversionFactor(String unit1, String unit2) { + num conversionFactor(String /*!*/ unit1, String /*!*/ unit2) { if (unit1 == unit2) return 1; var innerMap = _conversions[unit1]; if (innerMap == null) return null; diff --git a/lib/src/value/number/single_unit.dart b/lib/src/value/number/single_unit.dart index fa29d5d63..182a24fb7 100644 --- a/lib/src/value/number/single_unit.dart +++ b/lib/src/value/number/single_unit.dart @@ -9,6 +9,7 @@ import 'package:tuple/tuple.dart'; import '../../util/number.dart'; import '../../utils.dart'; +import '../../util/nullable.dart'; import '../../value.dart'; import '../external/value.dart' as ext; import '../number.dart'; @@ -85,16 +86,13 @@ class SingleUnitSassNumber extends SassNumber { /// returns `null` if coercion fails. SassNumber _coerceToUnit(String unit) { if (_unit == unit) return this; - - var factor = conversionFactor(unit, _unit); - return factor == null ? null : SingleUnitSassNumber(value * factor, unit); + return conversionFactor(unit, _unit) + .andThen((factor) => SingleUnitSassNumber(value * factor, unit)); } /// Like [coerceValueToUnit], except that it returns `null` if coercion fails. - num _coerceValueToUnit(String unit) { - var factor = conversionFactor(unit, _unit); - return factor == null ? null : value * factor; - } + num _coerceValueToUnit(String unit) => + conversionFactor(unit, _unit).andThen((factor) => value * factor); SassNumber multiplyUnits( num value, List otherNumerators, List otherDenominators) { diff --git a/lib/src/value/string.dart b/lib/src/value/string.dart index 152b22180..3a96dabd7 100644 --- a/lib/src/value/string.dart +++ b/lib/src/value/string.dart @@ -22,6 +22,8 @@ class SassString extends Value implements ext.SassString { final bool hasQuotes; + // TODO: late + int get sassLength { _sassLength ??= text.runes.length; return _sassLength; @@ -109,7 +111,7 @@ class SassString extends Value implements ext.SassString { SassString assertString([String name]) => this; - Value plus(Value other) { + Value plus(Value /*!*/ other) { if (other is SassString) { return SassString(text + other.text, quotes: hasQuotes); } else { diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index c797075fc..0510b0194 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -37,8 +37,8 @@ import '../module.dart'; import '../module/built_in.dart'; import '../parse/keyframe_selector.dart'; import '../syntax.dart'; -import '../util/fixed_length_list_builder.dart'; import '../utils.dart'; +import '../util/nullable.dart'; import '../value.dart'; import '../warn.dart'; import 'interface/css.dart'; @@ -92,7 +92,7 @@ class AsyncEvaluator { /// The visitor that evaluates each expression and statement. final _EvaluateVisitor _visitor; - /// The importer to use to resolve `@use` rules in [_importer]. + /// The importer to use to resolve `@use` rules in [_visitor]. final AsyncImporter _importer; /// Creates an evaluator. @@ -109,7 +109,7 @@ class AsyncEvaluator { Future use(UseRule use) => _visitor.runStatement(_importer, use); - Future evaluate(Expression expression) => + Future evaluate(Expression expression) => _visitor.runExpression(_importer, expression); Future setVariable(VariableDeclaration declaration) => @@ -119,8 +119,8 @@ class AsyncEvaluator { /// A visitor that executes Sass code to produce a CSS tree. class _EvaluateVisitor implements - StatementVisitor>, - ExpressionVisitor>, + StatementVisitor /*!*/ >, + ExpressionVisitor>, CssVisitor> { /// The import cache used to import other stylesheets. final AsyncImportCache _importCache; @@ -137,14 +137,14 @@ class _EvaluateVisitor final _builtInModules = {}; /// All modules that have been loaded and evaluated so far. - final _modules = {}; + final _modules = {}; /// A map from canonical module URLs to the nodes whose spans indicate where /// those modules were originally loaded. /// /// This is not guaranteed to have a node for every module in [_modules]. For /// example, the entrypoint module was not loaded by a node. - final _moduleNodes = {}; + final _moduleNodes = {}; /// The logger to use to print warnings. final Logger _logger; @@ -165,7 +165,7 @@ class _EvaluateVisitor List _mediaQueries; /// The current parent node in the output CSS tree. - ModifiableCssParentNode _parent; + /*late*/ ModifiableCssParentNode _parent; /// The name of the current declaration parent. String _declarationName; @@ -229,6 +229,7 @@ class _EvaluateVisitor /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. + // TODO: no cast final _stack = >[]; /// Whether we're running in Node Sass-compatibility mode. @@ -243,14 +244,14 @@ class _EvaluateVisitor AsyncImporter _importer; /// The stylesheet that's currently being evaluated. - Stylesheet _stylesheet; + /*late*/ Stylesheet _stylesheet; /// The root stylesheet node. - ModifiableCssStylesheet _root; + /*late*/ ModifiableCssStylesheet _root; /// The first index in [_root.children] after the initial block of CSS /// imports. - int _endOfImports; + /*late*/ int _endOfImports; /// Plain-CSS imports that didn't appear in the initial block of CSS imports. /// @@ -259,11 +260,11 @@ class _EvaluateVisitor /// /// This is `null` unless there are any out-of-order imports in the current /// stylesheet. - List _outOfOrderImports; + /*late*/ List _outOfOrderImports; /// The extender that tracks extensions and style rules for the current /// module. - Extender _extender; + /*late*/ Extender _extender; /// The configuration for the current module. /// @@ -372,7 +373,7 @@ class _EvaluateVisitor var callable = css ? PlainCssCallable(name.text) : _addExceptionSpan( - _callableNode, + _callableNode /*!*/, () => _getFunction(name.text.replaceAll("_", "-"), namespace: module?.text)); if (callable != null) return SassFunction(callable); @@ -385,8 +386,9 @@ class _EvaluateVisitor var function = arguments[0]; var args = arguments[1] as SassArgumentList; - var invocation = ArgumentInvocation([], {}, _callableNode.span, - rest: ValueExpression(args, _callableNode.span), + var callableNode = _callableNode /*!*/; + var invocation = ArgumentInvocation([], {}, callableNode.span, + rest: ValueExpression(args, callableNode.span), keywordRest: args.keywords.isEmpty ? null : ValueExpression( @@ -394,7 +396,7 @@ class _EvaluateVisitor for (var entry in args.keywords.entries) SassString(entry.key, quotes: false): entry.value }), - _callableNode.span)); + callableNode.span)); if (function is SassString) { warn( @@ -402,17 +404,18 @@ class _EvaluateVisitor "in Dart Sass 2.0.0. Use call(get-function($function)) instead.", deprecation: true); + var callableNode = _callableNode /*!*/; var expression = FunctionExpression( - Interpolation([function.text], _callableNode.span), + Interpolation([function.text], callableNode.span), invocation, - _callableNode.span); + callableNode.span); return await expression.accept(this); } var callable = function.assertFunction("function").callable; if (callable is AsyncCallable) { return await _runFunctionCallable( - invocation, callable, _callableNode); + invocation, callable, _callableNode /*!*/); } else { throw SassScriptException( "The function ${callable.name} is asynchronous.\n" @@ -427,10 +430,11 @@ class _EvaluateVisitor var url = Uri.parse(arguments[0].assertString("url").text); var withMap = arguments[1].realNull?.assertMap("with")?.contents; + var callableNode = _callableNode /*!*/; var configuration = const Configuration.empty(); if (withMap != null) { var values = {}; - var span = _callableNode.span; + var span = callableNode.span; withMap.forEach((variable, value) { var name = variable.assertString("with key").text.replaceAll("_", "-"); @@ -438,14 +442,14 @@ class _EvaluateVisitor throw "The variable \$$name was configured twice."; } - values[name] = ConfiguredValue(value, span); + values[name] = ConfiguredValue(value, span, callableNode); }); - configuration = Configuration(values, _callableNode); + configuration = Configuration(values, callableNode); } - await _loadModule(url, "load-css()", _callableNode, + await _loadModule(url, "load-css()", callableNode, (module) => _combineCss(module, clone: true).accept(this), - baseUrl: _callableNode.span?.sourceUrl, + baseUrl: callableNode.span?.sourceUrl, configuration: configuration, namesInErrors: true); _assertConfigurationIsEmpty(configuration, nameInError: true); @@ -467,7 +471,8 @@ class _EvaluateVisitor } } - Future run(AsyncImporter importer, Stylesheet node) async { + Future run( + AsyncImporter importer, Stylesheet /*!*/ node) async { return _withWarnCallback(() async { var url = node.span?.sourceUrl; if (url != null) { @@ -499,7 +504,7 @@ class _EvaluateVisitor T _withWarnCallback(T callback()) { return withWarnCallback( (message, deprecation) => _warn( - message, _importSpan ?? _callableNode.span, + message, _importSpan ?? _callableNode?.span, deprecation: deprecation), callback); } @@ -507,7 +512,7 @@ class _EvaluateVisitor /// Runs [callback] with [importer] as [_importer] and a fake [_stylesheet] /// with [nodeWithSpan]'s source span. Future _withFakeStylesheet(AsyncImporter importer, AstNode nodeWithSpan, - FutureOr callback()) async { + FutureOr /*!*/ callback()) async { var oldImporter = _importer; _importer = importer; var oldStylesheet = _stylesheet; @@ -537,19 +542,19 @@ class _EvaluateVisitor /// /// The [stackFrame] and [nodeWithSpan] are used for the name and location of /// the stack frame for the duration of the [callback]. - Future _loadModule(Uri url, String stackFrame, AstNode nodeWithSpan, - void callback(Module module), + Future _loadModule(Uri url, String stackFrame, + AstNode /*!*/ nodeWithSpan, void callback(Module module), {Uri baseUrl, Configuration configuration, bool namesInErrors = false}) async { var builtInModule = _builtInModules[url]; if (builtInModule != null) { - if (configuration != null && !configuration.isImplicit) { + if (configuration is ExplicitConfiguration) { throw _exception( namesInErrors ? "Built-in module $url can't be configured." : "Built-in modules can't be configured.", - nodeWithSpan.span); + configuration.nodeWithSpan.span); } _addExceptionSpan(nodeWithSpan, () => callback(builtInModule)); @@ -562,19 +567,21 @@ class _EvaluateVisitor var importer = result.item1; var stylesheet = result.item2; - var canonicalUrl = stylesheet.span.sourceUrl; - if (_activeModules.containsKey(canonicalUrl)) { + var canonicalUrl = stylesheet.span?.sourceUrl; + if (canonicalUrl != null && _activeModules.containsKey(canonicalUrl)) { var message = namesInErrors ? "Module loop: ${p.prettyUri(canonicalUrl)} is already being " "loaded." : "Module loop: this module is already being loaded."; - var previousLoad = _activeModules[canonicalUrl]; - throw previousLoad == null - ? _exception(message) - : _multiSpanException( - message, "new load", {previousLoad.span: "original load"}); + + throw _activeModules[canonicalUrl].andThen((previousLoad) { + var span = previousLoad.span; + return _multiSpanException(message, "new load", + {if (span != null) span: "original load"}); + }) ?? + _exception(message); } - _activeModules[canonicalUrl] = nodeWithSpan; + if (canonicalUrl != null) _activeModules[canonicalUrl] = nodeWithSpan; Module module; try { @@ -616,22 +623,26 @@ class _EvaluateVisitor {Configuration configuration, AstNode nodeWithSpan, bool namesInErrors = false}) async { - var url = stylesheet.span.sourceUrl; + var url = stylesheet.span?.sourceUrl; + // TODO: no ! var alreadyLoaded = _modules[url]; if (alreadyLoaded != null) { - if (!(configuration ?? _configuration).isImplicit) { + var currentConfiguration = configuration ?? _configuration; + if (currentConfiguration is ExplicitConfiguration) { var message = namesInErrors ? "${p.prettyUri(url)} was already loaded, so it can't be " "configured using \"with\"." : "This module was already loaded, so it can't be configured using " "\"with\"."; - var existingNode = _moduleNodes[url]; + var existingSpan = _moduleNodes[url]?.span; + var configurationSpan = configuration == null + ? currentConfiguration.nodeWithSpan.span + : null; var secondarySpans = { - if (existingNode != null) existingNode.span: "original load", - if (configuration == null) - _configuration.nodeWithSpan.span: "configuration" + if (existingSpan != null) existingSpan: "original load", + if (configurationSpan != null) configurationSpan: "configuration" }; throw secondarySpans.isEmpty @@ -662,8 +673,8 @@ class _EvaluateVisitor var oldConfiguration = _configuration; _importer = importer; _stylesheet = stylesheet; - _root = ModifiableCssStylesheet(stylesheet.span); - _parent = _root; + var root = _root = ModifiableCssStylesheet(stylesheet.span); + _parent = root; _endOfImports = 0; _outOfOrderImports = null; _extender = extender; @@ -677,7 +688,7 @@ class _EvaluateVisitor await visitStylesheet(stylesheet); css = _outOfOrderImports == null - ? _root + ? root : CssStylesheet(_addOutOfOrderImports(), stylesheet.span); _importer = oldImporter; @@ -697,8 +708,11 @@ class _EvaluateVisitor }); var module = environment.toModule(css, extender); - _modules[url] = module; - _moduleNodes[url] = nodeWithSpan; + if (url != null) { + _modules[url] = module; + if (nodeWithSpan != null) _moduleNodes[url] = nodeWithSpan; + } + return module; } @@ -707,12 +721,12 @@ class _EvaluateVisitor List _addOutOfOrderImports() { if (_outOfOrderImports == null) return _root.children; - var statements = FixedLengthListBuilder( - _root.children.length + _outOfOrderImports.length) - ..addRange(_root.children, 0, _endOfImports) - ..addAll(_outOfOrderImports) - ..addRange(_root.children, _endOfImports); - return statements.build(); + return [ + ..._root.children.take(_endOfImports), + // TODO: no ! + ..._outOfOrderImports, + ..._root.children.skip(_endOfImports) + ]; } /// Returns a new stylesheet containing [root]'s CSS as well as the CSS of all @@ -764,7 +778,8 @@ class _EvaluateVisitor // canonical URL). It's important that we create this in topological order, // so that by the time we're processing a module we've already filled in all // its downstream extenders and we can use them to extend that module. - var downstreamExtenders = >{}; + // TODO: var + var downstreamExtenders = >{}; /// Extensions that haven't yet been satisfied by some upstream module. This /// adds extensions when they're defined but not satisfied, and removes them @@ -787,6 +802,7 @@ class _EvaluateVisitor if (module.extender.isEmpty) continue; for (var upstream in module.upstream) { + if (upstream.url == null) continue; downstreamExtenders .putIfAbsent(upstream.url, () => []) .add(module.extender); @@ -866,18 +882,19 @@ class _EvaluateVisitor Future visitAtRootRule(AtRootRule node) async { var query = AtRootQuery.defaultQuery; - if (node.query != null) { + var unparsedQuery = node.query; + if (unparsedQuery != null) { var resolved = - await _performInterpolation(node.query, warnForColor: true); + await _performInterpolation(unparsedQuery, warnForColor: true); query = _adjustParseError( - node.query, () => AtRootQuery.parse(resolved, logger: _logger)); + unparsedQuery, () => AtRootQuery.parse(resolved, logger: _logger)); } var parent = _parent; var included = []; while (parent is! CssStylesheet) { if (!query.excludes(parent)) included.add(parent); - parent = parent.parent; + parent = parent.parent /*!*/; } var root = _trimIncluded(included); @@ -892,17 +909,20 @@ class _EvaluateVisitor return null; } - var innerCopy = - included.isEmpty ? null : included.first.copyWithoutChildren(); - var outerCopy = innerCopy; - for (var node in included.skip(1)) { - var copy = node.copyWithoutChildren(); - copy.addChild(outerCopy); - outerCopy = copy; + var innerCopy = root; + if (included.isNotEmpty) { + innerCopy = included.first.copyWithoutChildren(); + var outerCopy = innerCopy; + for (var node in included.skip(1)) { + var copy = node.copyWithoutChildren(); + copy.addChild(outerCopy); + outerCopy = copy; + } + + root.addChild(outerCopy); } - if (outerCopy != null) root.addChild(outerCopy); - await _scopeForAtRoot(node, innerCopy ?? root, query, included)(() async { + await _scopeForAtRoot(node, innerCopy, query, included)(() async { for (var child in node.children) { await child.accept(this); } @@ -911,8 +931,8 @@ class _EvaluateVisitor return null; } - /// Destructively trims a trailing sublist that matches the current list of - /// parents from [nodes]. + /// Destructively trims a trailing sublist from [nodes] that matches the + /// current list of parents. /// /// [nodes] should be a list of parents included by an `@at-root` rule, from /// innermost to outermost. If it contains a trailing sublist that's @@ -930,13 +950,14 @@ class _EvaluateVisitor for (; i < nodes.length; i++) { while (parent != nodes[i]) { innermostContiguous = null; - parent = parent.parent; + parent = parent.parent /*!*/; } innermostContiguous ??= i; - parent = parent.parent; + parent = parent.parent /*!*/; } if (parent != _root) return _root; + // TODO: no ! var root = nodes[innermostContiguous]; nodes.removeRange(innermostContiguous, nodes.length); return root; @@ -1033,9 +1054,8 @@ class _EvaluateVisitor if (_declarationName != null) { name = CssValue("$_declarationName-${name.value}", name.span); } - var cssValue = node.value == null - ? null - : CssValue(await node.value.accept(this), node.value.span); + var cssValue = await node.value.andThen( + (value) async => CssValue(await value.accept(this), value.span)); // 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. @@ -1043,10 +1063,11 @@ class _EvaluateVisitor (!cssValue.value.isBlank || _isEmptyList(cssValue.value))) { _parent.addChild(ModifiableCssDeclaration(name, cssValue, node.span, parsedAsCustomProperty: node.isCustomProperty, - valueSpanForMap: _expressionNode(node.value)?.span)); - } else if (name.value.startsWith('--') && node.children == null) { + valueSpanForMap: + _sourceMap ? null : _expressionNode(node.value).span)); + } else if (name.value.startsWith('--') && cssValue != null) { throw _exception( - "Custom property values may not be empty.", node.value.span); + "Custom property values may not be empty.", cssValue.span); } if (node.children != null) { @@ -1086,7 +1107,7 @@ class _EvaluateVisitor /// Destructures [value] and assigns it to [variables], as in an `@each` /// statement. void _setMultipleVariables( - List variables, Value value, AstNode nodeWithSpan) { + List variables, Value value, AstNode /*!*/ nodeWithSpan) { var list = value.asList; var minLength = math.min(variables.length, list.length); for (var i = 0; i < minLength; i++) { @@ -1156,10 +1177,8 @@ class _EvaluateVisitor var name = await _interpolationToValue(node.name); - var value = node.value == null - ? null - : await _interpolationToValue(node.value, - trim: true, warnForColor: true); + var value = await node.value.andThen((value) => + _interpolationToValue(value, trim: true, warnForColor: true)); if (node.children == null) { _parent.addChild( @@ -1342,7 +1361,9 @@ class _EvaluateVisitor return await _environment.scope( () => _handleReturn( - clause.children, (child) => child.accept(this)), + // TODO: no ! + clause.children, + (child) => child.accept(this)), semiGlobal: true, when: clause.hasDeclarations); } @@ -1366,15 +1387,18 @@ class _EvaluateVisitor var importer = result.item1; var stylesheet = result.item2; - var url = stylesheet.span.sourceUrl; - if (_activeModules.containsKey(url)) { - var previousLoad = _activeModules[url]; - throw previousLoad == null - ? _exception("This file is already being loaded.") - : _multiSpanException("This file is already being loaded.", - "new load", {previousLoad.span: "original load"}); + var url = stylesheet.span?.sourceUrl; + if (url != null) { + if (_activeModules.containsKey(url)) { + throw _activeModules[url].andThen((previousLoad) { + var span = previousLoad.span; + return _multiSpanException("This file is already being loaded.", + "new load", {if (span != null) span: "original load"}); + }) ?? + _exception("This file is already being loaded."); + } + _activeModules[url] = import; } - _activeModules[url] = import; // If the imported stylesheet doesn't use any modules, we can inject its // CSS directly into the current stylesheet. If it does use modules, we @@ -1463,15 +1487,16 @@ class _EvaluateVisitor assert(_importSpan == null); _importSpan = span; - if (_nodeImporter != null) { - var stylesheet = await _importLikeNode(url, forImport); - if (stylesheet != null) return Tuple2(null, stylesheet); - } else { - var tuple = await _importCache.import(Uri.parse(url), + var importCache = _importCache; + if (importCache != null) { + var tuple = await importCache.import(Uri.parse(url), baseImporter: _importer, baseUrl: baseUrl ?? _stylesheet?.span?.sourceUrl, forImport: forImport); if (tuple != null) return tuple; + } else { + var stylesheet = await _importLikeNode(url, forImport); + if (stylesheet != null) return Tuple2(null, stylesheet); } if (url.startsWith('package:') && isNode) { @@ -1486,6 +1511,7 @@ class _EvaluateVisitor } catch (error) { String message; try { + // TODO: as String! message = error.message as String; } catch (_) { message = error.toString(); @@ -1520,19 +1546,15 @@ class _EvaluateVisitor // here should be mirrored there. var url = await _interpolationToValue(import.url); - var supports = import.supports; - var resolvedSupports = supports is SupportsDeclaration - ? "${await _evaluateToCss(supports.name)}: " - "${await _evaluateToCss(supports.value)}" - : (supports == null ? null : await _visitSupportsCondition(supports)); - var mediaQuery = - import.media == null ? null : await _visitMediaQueries(import.media); + var supports = await import.supports.andThen((supports) async => CssValue( + "supports(${supports is SupportsDeclaration ? "${await _evaluateToCss(supports.name)}: " + "${await _evaluateToCss(supports.value)}" : await supports.andThen(_visitSupportsCondition)})", + supports.span)); + var rawMedia = import.media; + var mediaQuery = await rawMedia.andThen(_visitMediaQueries); var node = ModifiableCssImport(url, import.span, - supports: resolvedSupports == null - ? null - : CssValue("supports($resolvedSupports)", import.supports.span), - media: mediaQuery); + supports: supports, media: mediaQuery); if (_parent != _root) { _parent.addChild(node); @@ -1540,8 +1562,7 @@ class _EvaluateVisitor _root.addChild(node); _endOfImports++; } else { - _outOfOrderImports ??= []; - _outOfOrderImports.add(node); + (_outOfOrderImports ??= []).add(node); } return null; } @@ -1563,18 +1584,17 @@ class _EvaluateVisitor } else if (mixin is UserDefinedCallable) { if (node.content != null && !(mixin.declaration as MixinRule).hasContent) { + var declarationSpan = mixin.declaration.arguments.spanWithName; throw MultiSpanSassRuntimeException( "Mixin doesn't accept a content block.", node.spanWithoutContent, "invocation", - {mixin.declaration.arguments.spanWithName: "declaration"}, + {if (declarationSpan != null) declarationSpan: "declaration"}, _stackTrace(node.spanWithoutContent)); } - var contentCallable = node.content == null - ? null - : UserDefinedCallable(node.content, _environment.closure()); - + var contentCallable = node.content.andThen( + (content) => UserDefinedCallable(content, _environment.closure())); await _runUserDefinedCallable(node.arguments, mixin, nodeWithSpan, () async { await _environment.withContent(contentCallable, () async { @@ -1625,9 +1645,8 @@ class _EvaluateVisitor } var queries = await _visitMediaQueries(node.query); - var mergedQueries = _mediaQueries == null - ? null - : _mergeMediaQueries(_mediaQueries, queries); + var mergedQueries = _mediaQueries + .andThen((mediaQueries) => _mergeMediaQueries(mediaQueries, queries)); if (mergedQueries != null && mergedQueries.isEmpty) return null; await _withParent( @@ -1823,7 +1842,8 @@ class _EvaluateVisitor } else if (condition is SupportsAnything) { return "(${await _performInterpolation(condition.contents)})"; } else { - return null; + throw ArgumentError( + "Unknown supports condition type ${condition.runtimeType}."); } } @@ -2036,8 +2056,9 @@ class _EvaluateVisitor // ignore: prefer_is_empty var condition = positional.length > 0 ? positional[0] : named["condition"]; - var ifTrue = positional.length > 1 ? positional[1] : named["if-true"]; - var ifFalse = positional.length > 2 ? positional[2] : named["if-false"]; + var ifTrue = positional.length > 1 ? positional[1] : named["if-true"] /*!*/; + var ifFalse = + positional.length > 2 ? positional[2] : named["if-false"] /*!*/; return await ((await condition.accept(this)).isTruthy ? ifTrue : ifFalse) .accept(this); @@ -2066,12 +2087,15 @@ class _EvaluateVisitor for (var pair in node.pairs) { var keyValue = await pair.item1.accept(this); var valueValue = await pair.item2.accept(this); - if (map.containsKey(keyValue)) { + + var oldValue = map[keyValue]; + if (oldValue != null) { + var oldValueSpan = keyNodes[keyValue]?.span; throw MultiSpanSassRuntimeException( 'Duplicate key.', pair.item1.span, 'second key', - {keyNodes[keyValue].span: 'first key'}, + {if (oldValueSpan != null) oldValueSpan: 'first key'}, _stackTrace(pair.item1.span)); } map[keyValue] = valueValue; @@ -2121,14 +2145,16 @@ class _EvaluateVisitor /// Evaluates the arguments in [arguments] as applied to [callable], and /// invokes [run] in a scope with those arguments defined. - Future _runUserDefinedCallable( + Future _runUserDefinedCallable( ArgumentInvocation arguments, UserDefinedCallable callable, AstNode nodeWithSpan, - Future run()) async { + Future run()) async { var evaluated = await _evaluateArguments(arguments); - var name = callable.name == null ? "@content" : callable.name + "()"; + var name = callable.name; + if (name != "@content") name += "()"; + return await _withStackFrame(name, nodeWithSpan, () { // Add an extra closure() call so that modifications to the environment // don't affect the underlying environment closure. @@ -2144,7 +2170,7 @@ class _EvaluateVisitor _environment.setLocalVariable( declaredArguments[i].name, evaluated.positional[i].withoutSlash(), - _sourceMap ? evaluated.positionalNodes[i] : null); + evaluated.positionalNodes.andGet(i)); } for (var i = evaluated.positional.length; @@ -2156,14 +2182,13 @@ class _EvaluateVisitor _environment.setLocalVariable( argument.name, value.withoutSlash(), - _sourceMap - ? evaluated.namedNodes[argument.name] ?? - _expressionNode(argument.defaultValue) - : null); + evaluated.namedNodes.andGet(argument.name) ?? + _expressionNode(argument.defaultValue)); } SassArgumentList argumentList; - if (callable.declaration.arguments.restArgument != null) { + var restArgument = callable.declaration.arguments.restArgument; + if (restArgument != null) { var rest = evaluated.positional.length > declaredArguments.length ? evaluated.positional.sublist(declaredArguments.length) : const []; @@ -2174,9 +2199,7 @@ class _EvaluateVisitor ? ListSeparator.comma : evaluated.separator); _environment.setLocalVariable( - callable.declaration.arguments.restArgument, - argumentList, - nodeWithSpan); + restArgument, argumentList, nodeWithSpan); } var result = await run(); @@ -2188,11 +2211,12 @@ class _EvaluateVisitor var argumentWord = pluralize('argument', evaluated.named.keys.length); var argumentNames = toSentence(evaluated.named.keys.map((name) => "\$$name"), 'or'); + var declarationSpan = callable.declaration.arguments.spanWithName; throw MultiSpanSassRuntimeException( "No $argumentWord named $argumentNames.", nodeWithSpan.span, "invocation", - {callable.declaration.arguments.spanWithName: "declaration"}, + {if (declarationSpan != null) declarationSpan: "declaration"}, _stackTrace(nodeWithSpan.span)); }); }); @@ -2203,13 +2227,8 @@ class _EvaluateVisitor Future _runFunctionCallable(ArgumentInvocation arguments, AsyncCallable callable, AstNode nodeWithSpan) async { if (callable is AsyncBuiltInCallable) { - var result = await _runBuiltInCallable(arguments, callable, nodeWithSpan); - if (result == null) { - throw _exception( - "Custom functions may not return Dart's null.", nodeWithSpan.span); - } - - return result.withoutSlash(); + return (await _runBuiltInCallable(arguments, callable, nodeWithSpan)) + .withoutSlash(); } else if (callable is UserDefinedCallable) { return (await _runUserDefinedCallable(arguments, callable, nodeWithSpan, () async { @@ -2240,6 +2259,7 @@ class _EvaluateVisitor buffer.write(await _evaluateToCss(argument)); } + // TODO: no ! var rest = await arguments.rest?.accept(this); if (rest != null) { if (!first) buffer.write(", "); @@ -2249,7 +2269,7 @@ class _EvaluateVisitor return SassString(buffer.toString(), quotes: false); } else { - return null; + throw ArgumentError('Unknown callable type ${callable.runtimeType}.'); } } @@ -2314,6 +2334,7 @@ class _EvaluateVisitor } catch (error) { String message; try { + // TODO: as String! message = error.message as String; } catch (_) { message = error.toString(); @@ -2325,12 +2346,14 @@ class _EvaluateVisitor if (argumentList == null) return result; if (evaluated.named.isEmpty) return result; if (argumentList.wereKeywordsAccessed) return result; + + var declarationSpan = overload.spanWithName; throw MultiSpanSassRuntimeException( "No ${pluralize('argument', evaluated.named.keys.length)} named " "${toSentence(evaluated.named.keys.map((name) => "\$$name"), 'or')}.", nodeWithSpan.span, "invocation", - {overload.spanWithName: "declaration"}, + {if (declarationSpan != null) declarationSpan: "declaration"}, _stackTrace(nodeWithSpan.span)); } @@ -2363,16 +2386,17 @@ class _EvaluateVisitor } : null; - if (arguments.rest == null) { + var restArgs = arguments.rest; + if (restArgs == null) { return _ArgumentResults(positional, named, ListSeparator.undecided, positionalNodes: positionalNodes, namedNodes: namedNodes); } - var rest = await arguments.rest.accept(this); - var restNodeForSpan = trackSpans ? _expressionNode(arguments.rest) : null; + var rest = await restArgs.accept(this); + var restNodeForSpan = trackSpans ? _expressionNode(restArgs) : null; var separator = ListSeparator.undecided; if (rest is SassMap) { - _addRestMap(named, rest, arguments.rest); + _addRestMap(named, rest, restArgs, (value) => value); namedNodes?.addAll({ for (var key in rest.contents.keys) (key as SassString).text: restNodeForSpan @@ -2393,16 +2417,17 @@ class _EvaluateVisitor positionalNodes?.add(restNodeForSpan); } - if (arguments.keywordRest == null) { + var keywordRestArgs = arguments.keywordRest; + if (keywordRestArgs == null) { return _ArgumentResults(positional, named, separator, positionalNodes: positionalNodes, namedNodes: namedNodes); } - var keywordRest = await arguments.keywordRest.accept(this); + var keywordRest = await keywordRestArgs.accept(this); var keywordRestNodeForSpan = - trackSpans ? _expressionNode(arguments.keywordRest) : null; + trackSpans ? _expressionNode(keywordRestArgs) : null; if (keywordRest is SassMap) { - _addRestMap(named, keywordRest, arguments.keywordRest); + _addRestMap(named, keywordRest, keywordRestArgs, (value) => value); namedNodes?.addAll({ for (var key in keywordRest.contents.keys) (key as SassString).text: keywordRestNodeForSpan @@ -2412,7 +2437,7 @@ class _EvaluateVisitor } else { throw _exception( "Variable keyword arguments must be a map (was $keywordRest).", - arguments.keywordRest.span); + keywordRestArgs.span); } } @@ -2423,14 +2448,15 @@ class _EvaluateVisitor /// for macros such as `if()`. Future, Map>> _evaluateMacroArguments(CallableInvocation invocation) async { - if (invocation.arguments.rest == null) { + var restArgs = invocation.arguments.rest; + if (restArgs == null) { return Tuple2( invocation.arguments.positional, invocation.arguments.named); } var positional = invocation.arguments.positional.toList(); var named = Map.of(invocation.arguments.named); - var rest = await invocation.arguments.rest.accept(this); + var rest = await restArgs.accept(this); if (rest is SassMap) { _addRestMap(named, rest, invocation, (value) => ValueExpression(value)); } else if (rest is SassList) { @@ -2444,11 +2470,10 @@ class _EvaluateVisitor positional.add(ValueExpression(rest)); } - if (invocation.arguments.keywordRest == null) { - return Tuple2(positional, named); - } + var keywordRestArgs = invocation.arguments.keywordRest; + if (keywordRestArgs == null) return Tuple2(positional, named); - var keywordRest = await invocation.arguments.keywordRest.accept(this); + var keywordRest = await keywordRestArgs.accept(this); if (keywordRest is SassMap) { _addRestMap( named, keywordRest, invocation, (value) => ValueExpression(value)); @@ -2456,7 +2481,7 @@ class _EvaluateVisitor } else { throw _exception( "Variable keyword arguments must be a map (was $keywordRest).", - invocation.span); + keywordRestArgs.span); } } @@ -2472,8 +2497,7 @@ class _EvaluateVisitor /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. void _addRestMap(Map values, SassMap map, AstNode nodeWithSpan, - [T convert(Value value)]) { - convert ??= (value) => value as T; + T convert(Value value)) { map.contents.forEach((key, value) { if (key is SassString) { values[key.text] = convert(value); @@ -2493,16 +2517,15 @@ class _EvaluateVisitor _addExceptionSpan( nodeWithSpan, () => arguments.verify(positional, MapKeySet(named))); - Future visitSelectorExpression(SelectorExpression node) async { - if (_styleRuleIgnoringAtRoot == null) return sassNull; - return _styleRuleIgnoringAtRoot.originalSelector.asSassList; - } + Future visitSelectorExpression(SelectorExpression node) async => + _styleRuleIgnoringAtRoot?.originalSelector.asSassList ?? sassNull; Future visitStringExpression(StringExpression node) async { // Don't use [performInterpolation] here because we need to get the raw text // from strings, rather than the semantic value. return SassString( - (await mapAsync(node.text.contents, (value) async { + // TODO: no Object + (await mapAsync(node.text.contents, (Object value) async { if (value is String) return value; var expression = value as Expression; var result = await expression.accept(this); @@ -2592,8 +2615,7 @@ class _EvaluateVisitor _root.addChild(modifiableNode); _endOfImports++; } else { - _outOfOrderImports ??= []; - _outOfOrderImports.add(modifiableNode); + (_outOfOrderImports ??= []).add(modifiableNode); } } @@ -2618,9 +2640,9 @@ class _EvaluateVisitor "Media rules may not be used within nested declarations.", node.span); } - var mergedQueries = _mediaQueries == null - ? null - : _mergeMediaQueries(_mediaQueries, node.queries); + // TODO: no !, no as + var mergedQueries = _mediaQueries.andThen( + (mediaQueries) => _mergeMediaQueries(mediaQueries, node.queries)); if (mergedQueries != null && mergedQueries.isEmpty) return null; await _withParent( @@ -2768,7 +2790,8 @@ class _EvaluateVisitor /// values passed into the interpolation. Future _performInterpolation(Interpolation interpolation, {bool warnForColor = false}) async { - return (await mapAsync(interpolation.contents, (value) async { + // TODO: no Object + return (await mapAsync(interpolation.contents, (Object value) async { if (value is String) return value; var expression = value as Expression; var result = await expression.accept(this); @@ -2817,13 +2840,15 @@ class _EvaluateVisitor /// where that variable was originally declared. Otherwise, this will just /// return [expression]. /// - /// Returns `null` if [_sourceMap] is `false`. - /// /// This returns an [AstNode] rather than a [FileSpan] so we can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - AstNode _expressionNode(Expression expression) { - if (!_sourceMap) return null; + AstNode _expressionNode(Expression /*!*/ expression) { + // If we aren't making a source map this doesn't matter, but we still return + // the expression so we don't have to make the type (and everything + // downstream of it) nullable. + if (!_sourceMap) return expression; + if (expression is VariableExpression) { return _environment.getVariableNode(expression.name, namespace: expression.namespace) ?? @@ -2844,9 +2869,11 @@ class _EvaluateVisitor Future _withParent( S node, Future callback(), {bool through(CssNode node), bool scopeWhen = true}) async { + // TODO: no as _addChild(node, through: through); var oldParent = _parent; + // TODO: no as _parent = node; var result = await _environment.scope(callback, when: scopeWhen); _parent = oldParent; @@ -2864,7 +2891,7 @@ class _EvaluateVisitor var parent = _parent; if (through != null) { while (through(parent)) { - parent = parent.parent; + parent = parent.parent /*!*/; } // If the parent has a (visible) following sibling, we shouldn't add to @@ -2877,6 +2904,7 @@ class _EvaluateVisitor } } + // TODO: no as parent.addChild(node); } @@ -2921,11 +2949,10 @@ class _EvaluateVisitor /// Creates a new stack frame with location information from [member] and /// [span]. - Frame _stackFrame(String member, FileSpan span) { - var url = span.sourceUrl; - if (url != null && _importCache != null) url = _importCache.humanize(url); - return frameForSpan(span, member, url: url); - } + Frame _stackFrame(String member, FileSpan /*?*/ span) => frameForSpan( + span, member, + url: + span?.sourceUrl.andThen((url) => _importCache?.humanize(url) ?? url)); /// Returns a stack trace at the current point. /// @@ -2955,7 +2982,7 @@ class _EvaluateVisitor /// /// The primary span is taken from the current stack trace span. SassRuntimeException _multiSpanException(String message, String primaryLabel, - Map secondaryLabels) => + Map secondaryLabels) => MultiSpanSassRuntimeException(message, _stack.last.item2.span, primaryLabel, secondaryLabels, _stackTrace()); @@ -2970,19 +2997,24 @@ class _EvaluateVisitor /// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - T _adjustParseError(AstNode nodeWithSpan, T callback()) { + T _adjustParseError(AstNode /*!*/ nodeWithSpan, T callback()) { try { return callback(); } on SassFormatException catch (error) { - var errorText = error.span.file.getText(0); - var span = nodeWithSpan.span; - var syntheticFile = span.file + var errorSpan = error.span; + if (errorSpan == null) rethrow; + + var nodeSpan = nodeWithSpan.span; + if (nodeSpan == null) rethrow; + + var errorText = errorSpan.file.getText(0); + var syntheticFile = nodeSpan.file .getText(0) - .replaceRange(span.start.offset, span.end.offset, errorText); + .replaceRange(nodeSpan.start.offset, nodeSpan.end.offset, errorText); var syntheticSpan = - SourceFile.fromString(syntheticFile, url: span.file.url).span( - span.start.offset + error.span.start.offset, - span.start.offset + error.span.end.offset); + SourceFile.fromString(syntheticFile, url: nodeSpan.file.url).span( + nodeSpan.start.offset + errorSpan.start.offset, + nodeSpan.start.offset + errorSpan.end.offset); throw _exception(error.message, syntheticSpan); } } @@ -2993,7 +3025,7 @@ class _EvaluateVisitor /// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - T _addExceptionSpan(AstNode nodeWithSpan, T callback()) { + T _addExceptionSpan(AstNode /*!*/ nodeWithSpan, T callback()) { try { return callback(); } on MultiSpanSassScriptException catch (error) { @@ -3010,7 +3042,7 @@ class _EvaluateVisitor /// Like [_addExceptionSpan], but for an asynchronous [callback]. Future _addExceptionSpanAsync( - AstNode nodeWithSpan, Future callback()) async { + AstNode /*!*/ nodeWithSpan, Future callback()) async { try { return await callback(); } on MultiSpanSassScriptException catch (error) { @@ -3032,7 +3064,9 @@ class _EvaluateVisitor try { return await callback(); } on SassRuntimeException catch (error) { - if (!error.span.text.startsWith("@error")) rethrow; + var span = error.span; + if (span == null) rethrow; + if (span.text.startsWith("@error")) rethrow; throw SassRuntimeException( error.message, nodeWithSpan.span, _stackTrace()); } @@ -3074,8 +3108,7 @@ class _ImportedCssVisitor implements ModifiableCssVisitor { _visitor._addChild(node); _visitor._endOfImports++; } else { - _visitor._outOfOrderImports ??= []; - _visitor._outOfOrderImports.add(node); + (_visitor._outOfOrderImports ??= []).add(node); } } @@ -3087,9 +3120,9 @@ class _ImportedCssVisitor implements ModifiableCssVisitor { // Whether [node.query] has been merged with [_visitor._mediaQueries]. If it // has been merged, merging again is a no-op; if it hasn't been merged, // merging again will fail. - var hasBeenMerged = _visitor._mediaQueries == null || - _visitor._mergeMediaQueries(_visitor._mediaQueries, node.queries) != - null; + var mediaQueries = _visitor._mediaQueries; + var hasBeenMerged = mediaQueries == null || + _visitor._mergeMediaQueries(mediaQueries, node.queries) != null; _visitor._addChild(node, through: (node) => @@ -3128,7 +3161,7 @@ class EvaluateResult { /// The result of evaluating arguments to a function or mixin. class _ArgumentResults { /// Arguments passed by position. - final List positional; + final List positional; /// The [AstNode]s that hold the spans for each [positional] argument, or /// `null` if source span tracking is disabled. @@ -3136,7 +3169,7 @@ class _ArgumentResults { /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - final List positionalNodes; + final List positionalNodes; /// Arguments passed by name. final Map named; @@ -3147,7 +3180,7 @@ class _ArgumentResults { /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - final Map namedNodes; + final Map namedNodes; /// The separator used for the rest argument list, if any. final ListSeparator separator; diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 30e28aea4..31285c28c 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: e129cd22922df1f120a08d5a4ef022f6648ba0dd +// Checksum: 370eeeb96dca6b66ab00f7cfb71c3a18c46e22e6 // // ignore_for_file: unused_import @@ -46,8 +46,8 @@ import '../module.dart'; import '../module/built_in.dart'; import '../parse/keyframe_selector.dart'; import '../syntax.dart'; -import '../util/fixed_length_list_builder.dart'; import '../utils.dart'; +import '../util/nullable.dart'; import '../value.dart'; import '../warn.dart'; import 'interface/css.dart'; @@ -100,7 +100,7 @@ class Evaluator { /// The visitor that evaluates each expression and statement. final _EvaluateVisitor _visitor; - /// The importer to use to resolve `@use` rules in [_importer]. + /// The importer to use to resolve `@use` rules in [_visitor]. final Importer _importer; /// Creates an evaluator. @@ -117,7 +117,7 @@ class Evaluator { void use(UseRule use) => _visitor.runStatement(_importer, use); - Value evaluate(Expression expression) => + Value /*!*/ evaluate(Expression expression) => _visitor.runExpression(_importer, expression); void setVariable(VariableDeclaration declaration) => @@ -127,8 +127,8 @@ class Evaluator { /// A visitor that executes Sass code to produce a CSS tree. class _EvaluateVisitor implements - StatementVisitor, - ExpressionVisitor, + StatementVisitor, + ExpressionVisitor, CssVisitor { /// The import cache used to import other stylesheets. final ImportCache _importCache; @@ -145,14 +145,14 @@ class _EvaluateVisitor final _builtInModules = >{}; /// All modules that have been loaded and evaluated so far. - final _modules = >{}; + final _modules = >{}; /// A map from canonical module URLs to the nodes whose spans indicate where /// those modules were originally loaded. /// /// This is not guaranteed to have a node for every module in [_modules]. For /// example, the entrypoint module was not loaded by a node. - final _moduleNodes = {}; + final _moduleNodes = {}; /// The logger to use to print warnings. final Logger _logger; @@ -173,7 +173,7 @@ class _EvaluateVisitor List _mediaQueries; /// The current parent node in the output CSS tree. - ModifiableCssParentNode _parent; + /*late*/ ModifiableCssParentNode _parent; /// The name of the current declaration parent. String _declarationName; @@ -237,6 +237,7 @@ class _EvaluateVisitor /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. + // TODO: no cast final _stack = >[]; /// Whether we're running in Node Sass-compatibility mode. @@ -251,14 +252,14 @@ class _EvaluateVisitor Importer _importer; /// The stylesheet that's currently being evaluated. - Stylesheet _stylesheet; + /*late*/ Stylesheet _stylesheet; /// The root stylesheet node. - ModifiableCssStylesheet _root; + /*late*/ ModifiableCssStylesheet _root; /// The first index in [_root.children] after the initial block of CSS /// imports. - int _endOfImports; + /*late*/ int _endOfImports; /// Plain-CSS imports that didn't appear in the initial block of CSS imports. /// @@ -267,11 +268,11 @@ class _EvaluateVisitor /// /// This is `null` unless there are any out-of-order imports in the current /// stylesheet. - List _outOfOrderImports; + /*late*/ List _outOfOrderImports; /// The extender that tracks extensions and style rules for the current /// module. - Extender _extender; + /*late*/ Extender _extender; /// The configuration for the current module. /// @@ -380,7 +381,7 @@ class _EvaluateVisitor var callable = css ? PlainCssCallable(name.text) : _addExceptionSpan( - _callableNode, + _callableNode /*!*/, () => _getFunction(name.text.replaceAll("_", "-"), namespace: module?.text)); if (callable != null) return SassFunction(callable); @@ -392,8 +393,9 @@ class _EvaluateVisitor var function = arguments[0]; var args = arguments[1] as SassArgumentList; - var invocation = ArgumentInvocation([], {}, _callableNode.span, - rest: ValueExpression(args, _callableNode.span), + var callableNode = _callableNode /*!*/; + var invocation = ArgumentInvocation([], {}, callableNode.span, + rest: ValueExpression(args, callableNode.span), keywordRest: args.keywords.isEmpty ? null : ValueExpression( @@ -401,7 +403,7 @@ class _EvaluateVisitor for (var entry in args.keywords.entries) SassString(entry.key, quotes: false): entry.value }), - _callableNode.span)); + callableNode.span)); if (function is SassString) { warn( @@ -409,16 +411,18 @@ class _EvaluateVisitor "in Dart Sass 2.0.0. Use call(get-function($function)) instead.", deprecation: true); + var callableNode = _callableNode /*!*/; var expression = FunctionExpression( - Interpolation([function.text], _callableNode.span), + Interpolation([function.text], callableNode.span), invocation, - _callableNode.span); + callableNode.span); return expression.accept(this); } var callable = function.assertFunction("function").callable; if (callable is Callable) { - return _runFunctionCallable(invocation, callable, _callableNode); + return _runFunctionCallable( + invocation, callable, _callableNode /*!*/); } else { throw SassScriptException( "The function ${callable.name} is asynchronous.\n" @@ -432,10 +436,11 @@ class _EvaluateVisitor var url = Uri.parse(arguments[0].assertString("url").text); var withMap = arguments[1].realNull?.assertMap("with")?.contents; + var callableNode = _callableNode /*!*/; var configuration = const Configuration.empty(); if (withMap != null) { var values = {}; - var span = _callableNode.span; + var span = callableNode.span; withMap.forEach((variable, value) { var name = variable.assertString("with key").text.replaceAll("_", "-"); @@ -443,14 +448,14 @@ class _EvaluateVisitor throw "The variable \$$name was configured twice."; } - values[name] = ConfiguredValue(value, span); + values[name] = ConfiguredValue(value, span, callableNode); }); - configuration = Configuration(values, _callableNode); + configuration = Configuration(values, callableNode); } - _loadModule(url, "load-css()", _callableNode, + _loadModule(url, "load-css()", callableNode, (module) => _combineCss(module, clone: true).accept(this), - baseUrl: _callableNode.span?.sourceUrl, + baseUrl: callableNode.span?.sourceUrl, configuration: configuration, namesInErrors: true); _assertConfigurationIsEmpty(configuration, nameInError: true); @@ -472,7 +477,7 @@ class _EvaluateVisitor } } - EvaluateResult run(Importer importer, Stylesheet node) { + EvaluateResult run(Importer importer, Stylesheet /*!*/ node) { return _withWarnCallback(() { var url = node.span?.sourceUrl; if (url != null) { @@ -504,7 +509,7 @@ class _EvaluateVisitor T _withWarnCallback(T callback()) { return withWarnCallback( (message, deprecation) => _warn( - message, _importSpan ?? _callableNode.span, + message, _importSpan ?? _callableNode?.span, deprecation: deprecation), callback); } @@ -512,7 +517,7 @@ class _EvaluateVisitor /// Runs [callback] with [importer] as [_importer] and a fake [_stylesheet] /// with [nodeWithSpan]'s source span. T _withFakeStylesheet( - Importer importer, AstNode nodeWithSpan, T callback()) { + Importer importer, AstNode nodeWithSpan, T /*!*/ callback()) { var oldImporter = _importer; _importer = importer; var oldStylesheet = _stylesheet; @@ -542,17 +547,17 @@ class _EvaluateVisitor /// /// The [stackFrame] and [nodeWithSpan] are used for the name and location of /// the stack frame for the duration of the [callback]. - void _loadModule(Uri url, String stackFrame, AstNode nodeWithSpan, + void _loadModule(Uri url, String stackFrame, AstNode /*!*/ nodeWithSpan, void callback(Module module), {Uri baseUrl, Configuration configuration, bool namesInErrors = false}) { var builtInModule = _builtInModules[url]; if (builtInModule != null) { - if (configuration != null && !configuration.isImplicit) { + if (configuration is ExplicitConfiguration) { throw _exception( namesInErrors ? "Built-in module $url can't be configured." : "Built-in modules can't be configured.", - nodeWithSpan.span); + configuration.nodeWithSpan.span); } _addExceptionSpan(nodeWithSpan, () => callback(builtInModule)); @@ -565,19 +570,21 @@ class _EvaluateVisitor var importer = result.item1; var stylesheet = result.item2; - var canonicalUrl = stylesheet.span.sourceUrl; - if (_activeModules.containsKey(canonicalUrl)) { + var canonicalUrl = stylesheet.span?.sourceUrl; + if (canonicalUrl != null && _activeModules.containsKey(canonicalUrl)) { var message = namesInErrors ? "Module loop: ${p.prettyUri(canonicalUrl)} is already being " "loaded." : "Module loop: this module is already being loaded."; - var previousLoad = _activeModules[canonicalUrl]; - throw previousLoad == null - ? _exception(message) - : _multiSpanException( - message, "new load", {previousLoad.span: "original load"}); + + throw _activeModules[canonicalUrl].andThen((previousLoad) { + var span = previousLoad.span; + return _multiSpanException(message, "new load", + {if (span != null) span: "original load"}); + }) ?? + _exception(message); } - _activeModules[canonicalUrl] = nodeWithSpan; + if (canonicalUrl != null) _activeModules[canonicalUrl] = nodeWithSpan; Module module; try { @@ -619,22 +626,26 @@ class _EvaluateVisitor {Configuration configuration, AstNode nodeWithSpan, bool namesInErrors = false}) { - var url = stylesheet.span.sourceUrl; + var url = stylesheet.span?.sourceUrl; + // TODO: no ! var alreadyLoaded = _modules[url]; if (alreadyLoaded != null) { - if (!(configuration ?? _configuration).isImplicit) { + var currentConfiguration = configuration ?? _configuration; + if (currentConfiguration is ExplicitConfiguration) { var message = namesInErrors ? "${p.prettyUri(url)} was already loaded, so it can't be " "configured using \"with\"." : "This module was already loaded, so it can't be configured using " "\"with\"."; - var existingNode = _moduleNodes[url]; + var existingSpan = _moduleNodes[url]?.span; + var configurationSpan = configuration == null + ? currentConfiguration.nodeWithSpan.span + : null; var secondarySpans = { - if (existingNode != null) existingNode.span: "original load", - if (configuration == null) - _configuration.nodeWithSpan.span: "configuration" + if (existingSpan != null) existingSpan: "original load", + if (configurationSpan != null) configurationSpan: "configuration" }; throw secondarySpans.isEmpty @@ -665,8 +676,8 @@ class _EvaluateVisitor var oldConfiguration = _configuration; _importer = importer; _stylesheet = stylesheet; - _root = ModifiableCssStylesheet(stylesheet.span); - _parent = _root; + var root = _root = ModifiableCssStylesheet(stylesheet.span); + _parent = root; _endOfImports = 0; _outOfOrderImports = null; _extender = extender; @@ -680,7 +691,7 @@ class _EvaluateVisitor visitStylesheet(stylesheet); css = _outOfOrderImports == null - ? _root + ? root : CssStylesheet(_addOutOfOrderImports(), stylesheet.span); _importer = oldImporter; @@ -700,8 +711,11 @@ class _EvaluateVisitor }); var module = environment.toModule(css, extender); - _modules[url] = module; - _moduleNodes[url] = nodeWithSpan; + if (url != null) { + _modules[url] = module; + if (nodeWithSpan != null) _moduleNodes[url] = nodeWithSpan; + } + return module; } @@ -710,12 +724,12 @@ class _EvaluateVisitor List _addOutOfOrderImports() { if (_outOfOrderImports == null) return _root.children; - var statements = FixedLengthListBuilder( - _root.children.length + _outOfOrderImports.length) - ..addRange(_root.children, 0, _endOfImports) - ..addAll(_outOfOrderImports) - ..addRange(_root.children, _endOfImports); - return statements.build(); + return [ + ..._root.children.take(_endOfImports), + // TODO: no ! + ..._outOfOrderImports, + ..._root.children.skip(_endOfImports) + ]; } /// Returns a new stylesheet containing [root]'s CSS as well as the CSS of all @@ -767,7 +781,8 @@ class _EvaluateVisitor // canonical URL). It's important that we create this in topological order, // so that by the time we're processing a module we've already filled in all // its downstream extenders and we can use them to extend that module. - var downstreamExtenders = >{}; + // TODO: var + var downstreamExtenders = >{}; /// Extensions that haven't yet been satisfied by some upstream module. This /// adds extensions when they're defined but not satisfied, and removes them @@ -790,6 +805,7 @@ class _EvaluateVisitor if (module.extender.isEmpty) continue; for (var upstream in module.upstream) { + if (upstream.url == null) continue; downstreamExtenders .putIfAbsent(upstream.url, () => []) .add(module.extender); @@ -869,17 +885,18 @@ class _EvaluateVisitor Value visitAtRootRule(AtRootRule node) { var query = AtRootQuery.defaultQuery; - if (node.query != null) { - var resolved = _performInterpolation(node.query, warnForColor: true); + var unparsedQuery = node.query; + if (unparsedQuery != null) { + var resolved = _performInterpolation(unparsedQuery, warnForColor: true); query = _adjustParseError( - node.query, () => AtRootQuery.parse(resolved, logger: _logger)); + unparsedQuery, () => AtRootQuery.parse(resolved, logger: _logger)); } var parent = _parent; var included = []; while (parent is! CssStylesheet) { if (!query.excludes(parent)) included.add(parent); - parent = parent.parent; + parent = parent.parent /*!*/; } var root = _trimIncluded(included); @@ -894,17 +911,20 @@ class _EvaluateVisitor return null; } - var innerCopy = - included.isEmpty ? null : included.first.copyWithoutChildren(); - var outerCopy = innerCopy; - for (var node in included.skip(1)) { - var copy = node.copyWithoutChildren(); - copy.addChild(outerCopy); - outerCopy = copy; + var innerCopy = root; + if (included.isNotEmpty) { + innerCopy = included.first.copyWithoutChildren(); + var outerCopy = innerCopy; + for (var node in included.skip(1)) { + var copy = node.copyWithoutChildren(); + copy.addChild(outerCopy); + outerCopy = copy; + } + + root.addChild(outerCopy); } - if (outerCopy != null) root.addChild(outerCopy); - _scopeForAtRoot(node, innerCopy ?? root, query, included)(() { + _scopeForAtRoot(node, innerCopy, query, included)(() { for (var child in node.children) { child.accept(this); } @@ -913,8 +933,8 @@ class _EvaluateVisitor return null; } - /// Destructively trims a trailing sublist that matches the current list of - /// parents from [nodes]. + /// Destructively trims a trailing sublist from [nodes] that matches the + /// current list of parents. /// /// [nodes] should be a list of parents included by an `@at-root` rule, from /// innermost to outermost. If it contains a trailing sublist that's @@ -932,13 +952,14 @@ class _EvaluateVisitor for (; i < nodes.length; i++) { while (parent != nodes[i]) { innermostContiguous = null; - parent = parent.parent; + parent = parent.parent /*!*/; } innermostContiguous ??= i; - parent = parent.parent; + parent = parent.parent /*!*/; } if (parent != _root) return _root; + // TODO: no ! var root = nodes[innermostContiguous]; nodes.removeRange(innermostContiguous, nodes.length); return root; @@ -1035,9 +1056,8 @@ class _EvaluateVisitor if (_declarationName != null) { name = CssValue("$_declarationName-${name.value}", name.span); } - var cssValue = node.value == null - ? null - : CssValue(node.value.accept(this), node.value.span); + var cssValue = + node.value.andThen((value) => CssValue(value.accept(this), value.span)); // 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. @@ -1045,10 +1065,11 @@ class _EvaluateVisitor (!cssValue.value.isBlank || _isEmptyList(cssValue.value))) { _parent.addChild(ModifiableCssDeclaration(name, cssValue, node.span, parsedAsCustomProperty: node.isCustomProperty, - valueSpanForMap: _expressionNode(node.value)?.span)); - } else if (name.value.startsWith('--') && node.children == null) { + valueSpanForMap: + _sourceMap ? null : _expressionNode(node.value).span)); + } else if (name.value.startsWith('--') && cssValue != null) { throw _exception( - "Custom property values may not be empty.", node.value.span); + "Custom property values may not be empty.", cssValue.span); } if (node.children != null) { @@ -1088,7 +1109,7 @@ class _EvaluateVisitor /// Destructures [value] and assigns it to [variables], as in an `@each` /// statement. void _setMultipleVariables( - List variables, Value value, AstNode nodeWithSpan) { + List variables, Value value, AstNode /*!*/ nodeWithSpan) { var list = value.asList; var minLength = math.min(variables.length, list.length); for (var i = 0; i < minLength; i++) { @@ -1156,9 +1177,8 @@ class _EvaluateVisitor var name = _interpolationToValue(node.name); - var value = node.value == null - ? null - : _interpolationToValue(node.value, trim: true, warnForColor: true); + var value = node.value.andThen((value) => + _interpolationToValue(value, trim: true, warnForColor: true)); if (node.children == null) { _parent.addChild( @@ -1340,7 +1360,9 @@ class _EvaluateVisitor return _environment.scope( () => _handleReturn( - clause.children, (child) => child.accept(this)), + // TODO: no ! + clause.children, + (child) => child.accept(this)), semiGlobal: true, when: clause.hasDeclarations); } @@ -1363,15 +1385,18 @@ class _EvaluateVisitor var importer = result.item1; var stylesheet = result.item2; - var url = stylesheet.span.sourceUrl; - if (_activeModules.containsKey(url)) { - var previousLoad = _activeModules[url]; - throw previousLoad == null - ? _exception("This file is already being loaded.") - : _multiSpanException("This file is already being loaded.", - "new load", {previousLoad.span: "original load"}); + var url = stylesheet.span?.sourceUrl; + if (url != null) { + if (_activeModules.containsKey(url)) { + throw _activeModules[url].andThen((previousLoad) { + var span = previousLoad.span; + return _multiSpanException("This file is already being loaded.", + "new load", {if (span != null) span: "original load"}); + }) ?? + _exception("This file is already being loaded."); + } + _activeModules[url] = import; } - _activeModules[url] = import; // If the imported stylesheet doesn't use any modules, we can inject its // CSS directly into the current stylesheet. If it does use modules, we @@ -1459,15 +1484,16 @@ class _EvaluateVisitor assert(_importSpan == null); _importSpan = span; - if (_nodeImporter != null) { - var stylesheet = _importLikeNode(url, forImport); - if (stylesheet != null) return Tuple2(null, stylesheet); - } else { - var tuple = _importCache.import(Uri.parse(url), + var importCache = _importCache; + if (importCache != null) { + var tuple = importCache.import(Uri.parse(url), baseImporter: _importer, baseUrl: baseUrl ?? _stylesheet?.span?.sourceUrl, forImport: forImport); if (tuple != null) return tuple; + } else { + var stylesheet = _importLikeNode(url, forImport); + if (stylesheet != null) return Tuple2(null, stylesheet); } if (url.startsWith('package:') && isNode) { @@ -1482,6 +1508,7 @@ class _EvaluateVisitor } catch (error) { String message; try { + // TODO: as String! message = error.message as String; } catch (_) { message = error.toString(); @@ -1516,19 +1543,15 @@ class _EvaluateVisitor // here should be mirrored there. var url = _interpolationToValue(import.url); - var supports = import.supports; - var resolvedSupports = supports is SupportsDeclaration - ? "${_evaluateToCss(supports.name)}: " - "${_evaluateToCss(supports.value)}" - : (supports == null ? null : _visitSupportsCondition(supports)); - var mediaQuery = - import.media == null ? null : _visitMediaQueries(import.media); + var supports = import.supports.andThen((supports) => CssValue( + "supports(${supports is SupportsDeclaration ? "${_evaluateToCss(supports.name)}: " + "${_evaluateToCss(supports.value)}" : supports.andThen(_visitSupportsCondition)})", + supports.span)); + var rawMedia = import.media; + var mediaQuery = rawMedia.andThen(_visitMediaQueries); var node = ModifiableCssImport(url, import.span, - supports: resolvedSupports == null - ? null - : CssValue("supports($resolvedSupports)", import.supports.span), - media: mediaQuery); + supports: supports, media: mediaQuery); if (_parent != _root) { _parent.addChild(node); @@ -1536,8 +1559,7 @@ class _EvaluateVisitor _root.addChild(node); _endOfImports++; } else { - _outOfOrderImports ??= []; - _outOfOrderImports.add(node); + (_outOfOrderImports ??= []).add(node); } return null; } @@ -1559,18 +1581,17 @@ class _EvaluateVisitor } else if (mixin is UserDefinedCallable) { if (node.content != null && !(mixin.declaration as MixinRule).hasContent) { + var declarationSpan = mixin.declaration.arguments.spanWithName; throw MultiSpanSassRuntimeException( "Mixin doesn't accept a content block.", node.spanWithoutContent, "invocation", - {mixin.declaration.arguments.spanWithName: "declaration"}, + {if (declarationSpan != null) declarationSpan: "declaration"}, _stackTrace(node.spanWithoutContent)); } - var contentCallable = node.content == null - ? null - : UserDefinedCallable(node.content, _environment.closure()); - + var contentCallable = node.content.andThen( + (content) => UserDefinedCallable(content, _environment.closure())); _runUserDefinedCallable(node.arguments, mixin, nodeWithSpan, () { _environment.withContent(contentCallable, () { _environment.asMixin(() { @@ -1620,9 +1641,8 @@ class _EvaluateVisitor } var queries = _visitMediaQueries(node.query); - var mergedQueries = _mediaQueries == null - ? null - : _mergeMediaQueries(_mediaQueries, queries); + var mergedQueries = _mediaQueries + .andThen((mediaQueries) => _mergeMediaQueries(mediaQueries, queries)); if (mergedQueries != null && mergedQueries.isEmpty) return null; _withParent(ModifiableCssMediaRule(mergedQueries ?? queries, node.span), @@ -1814,7 +1834,8 @@ class _EvaluateVisitor } else if (condition is SupportsAnything) { return "(${_performInterpolation(condition.contents)})"; } else { - return null; + throw ArgumentError( + "Unknown supports condition type ${condition.runtimeType}."); } } @@ -2024,8 +2045,9 @@ class _EvaluateVisitor // ignore: prefer_is_empty var condition = positional.length > 0 ? positional[0] : named["condition"]; - var ifTrue = positional.length > 1 ? positional[1] : named["if-true"]; - var ifFalse = positional.length > 2 ? positional[2] : named["if-false"]; + var ifTrue = positional.length > 1 ? positional[1] : named["if-true"] /*!*/; + var ifFalse = + positional.length > 2 ? positional[2] : named["if-false"] /*!*/; return (condition.accept(this).isTruthy ? ifTrue : ifFalse).accept(this); } @@ -2051,12 +2073,15 @@ class _EvaluateVisitor for (var pair in node.pairs) { var keyValue = pair.item1.accept(this); var valueValue = pair.item2.accept(this); - if (map.containsKey(keyValue)) { + + var oldValue = map[keyValue]; + if (oldValue != null) { + var oldValueSpan = keyNodes[keyValue]?.span; throw MultiSpanSassRuntimeException( 'Duplicate key.', pair.item1.span, 'second key', - {keyNodes[keyValue].span: 'first key'}, + {if (oldValueSpan != null) oldValueSpan: 'first key'}, _stackTrace(pair.item1.span)); } map[keyValue] = valueValue; @@ -2106,14 +2131,16 @@ class _EvaluateVisitor /// Evaluates the arguments in [arguments] as applied to [callable], and /// invokes [run] in a scope with those arguments defined. - Value _runUserDefinedCallable( + V _runUserDefinedCallable( ArgumentInvocation arguments, UserDefinedCallable callable, AstNode nodeWithSpan, - Value run()) { + V run()) { var evaluated = _evaluateArguments(arguments); - var name = callable.name == null ? "@content" : callable.name + "()"; + var name = callable.name; + if (name != "@content") name += "()"; + return _withStackFrame(name, nodeWithSpan, () { // Add an extra closure() call so that modifications to the environment // don't affect the underlying environment closure. @@ -2129,7 +2156,7 @@ class _EvaluateVisitor _environment.setLocalVariable( declaredArguments[i].name, evaluated.positional[i].withoutSlash(), - _sourceMap ? evaluated.positionalNodes[i] : null); + evaluated.positionalNodes.andGet(i)); } for (var i = evaluated.positional.length; @@ -2141,14 +2168,13 @@ class _EvaluateVisitor _environment.setLocalVariable( argument.name, value.withoutSlash(), - _sourceMap - ? evaluated.namedNodes[argument.name] ?? - _expressionNode(argument.defaultValue) - : null); + evaluated.namedNodes.andGet(argument.name) ?? + _expressionNode(argument.defaultValue)); } SassArgumentList argumentList; - if (callable.declaration.arguments.restArgument != null) { + var restArgument = callable.declaration.arguments.restArgument; + if (restArgument != null) { var rest = evaluated.positional.length > declaredArguments.length ? evaluated.positional.sublist(declaredArguments.length) : const []; @@ -2159,9 +2185,7 @@ class _EvaluateVisitor ? ListSeparator.comma : evaluated.separator); _environment.setLocalVariable( - callable.declaration.arguments.restArgument, - argumentList, - nodeWithSpan); + restArgument, argumentList, nodeWithSpan); } var result = run(); @@ -2173,11 +2197,12 @@ class _EvaluateVisitor var argumentWord = pluralize('argument', evaluated.named.keys.length); var argumentNames = toSentence(evaluated.named.keys.map((name) => "\$$name"), 'or'); + var declarationSpan = callable.declaration.arguments.spanWithName; throw MultiSpanSassRuntimeException( "No $argumentWord named $argumentNames.", nodeWithSpan.span, "invocation", - {callable.declaration.arguments.spanWithName: "declaration"}, + {if (declarationSpan != null) declarationSpan: "declaration"}, _stackTrace(nodeWithSpan.span)); }); }); @@ -2188,13 +2213,8 @@ class _EvaluateVisitor Value _runFunctionCallable( ArgumentInvocation arguments, Callable callable, AstNode nodeWithSpan) { if (callable is BuiltInCallable) { - var result = _runBuiltInCallable(arguments, callable, nodeWithSpan); - if (result == null) { - throw _exception( - "Custom functions may not return Dart's null.", nodeWithSpan.span); - } - - return result.withoutSlash(); + return _runBuiltInCallable(arguments, callable, nodeWithSpan) + .withoutSlash(); } else if (callable is UserDefinedCallable) { return _runUserDefinedCallable(arguments, callable, nodeWithSpan, () { for (var statement in callable.declaration.children) { @@ -2223,6 +2243,7 @@ class _EvaluateVisitor buffer.write(_evaluateToCss(argument)); } + // TODO: no ! var rest = arguments.rest?.accept(this); if (rest != null) { if (!first) buffer.write(", "); @@ -2232,7 +2253,7 @@ class _EvaluateVisitor return SassString(buffer.toString(), quotes: false); } else { - return null; + throw ArgumentError('Unknown callable type ${callable.runtimeType}.'); } } @@ -2297,6 +2318,7 @@ class _EvaluateVisitor } catch (error) { String message; try { + // TODO: as String! message = error.message as String; } catch (_) { message = error.toString(); @@ -2308,12 +2330,14 @@ class _EvaluateVisitor if (argumentList == null) return result; if (evaluated.named.isEmpty) return result; if (argumentList.wereKeywordsAccessed) return result; + + var declarationSpan = overload.spanWithName; throw MultiSpanSassRuntimeException( "No ${pluralize('argument', evaluated.named.keys.length)} named " "${toSentence(evaluated.named.keys.map((name) => "\$$name"), 'or')}.", nodeWithSpan.span, "invocation", - {overload.spanWithName: "declaration"}, + {if (declarationSpan != null) declarationSpan: "declaration"}, _stackTrace(nodeWithSpan.span)); } @@ -2346,16 +2370,17 @@ class _EvaluateVisitor } : null; - if (arguments.rest == null) { + var restArgs = arguments.rest; + if (restArgs == null) { return _ArgumentResults(positional, named, ListSeparator.undecided, positionalNodes: positionalNodes, namedNodes: namedNodes); } - var rest = arguments.rest.accept(this); - var restNodeForSpan = trackSpans ? _expressionNode(arguments.rest) : null; + var rest = restArgs.accept(this); + var restNodeForSpan = trackSpans ? _expressionNode(restArgs) : null; var separator = ListSeparator.undecided; if (rest is SassMap) { - _addRestMap(named, rest, arguments.rest); + _addRestMap(named, rest, restArgs, (value) => value); namedNodes?.addAll({ for (var key in rest.contents.keys) (key as SassString).text: restNodeForSpan @@ -2376,16 +2401,17 @@ class _EvaluateVisitor positionalNodes?.add(restNodeForSpan); } - if (arguments.keywordRest == null) { + var keywordRestArgs = arguments.keywordRest; + if (keywordRestArgs == null) { return _ArgumentResults(positional, named, separator, positionalNodes: positionalNodes, namedNodes: namedNodes); } - var keywordRest = arguments.keywordRest.accept(this); + var keywordRest = keywordRestArgs.accept(this); var keywordRestNodeForSpan = - trackSpans ? _expressionNode(arguments.keywordRest) : null; + trackSpans ? _expressionNode(keywordRestArgs) : null; if (keywordRest is SassMap) { - _addRestMap(named, keywordRest, arguments.keywordRest); + _addRestMap(named, keywordRest, keywordRestArgs, (value) => value); namedNodes?.addAll({ for (var key in keywordRest.contents.keys) (key as SassString).text: keywordRestNodeForSpan @@ -2395,7 +2421,7 @@ class _EvaluateVisitor } else { throw _exception( "Variable keyword arguments must be a map (was $keywordRest).", - arguments.keywordRest.span); + keywordRestArgs.span); } } @@ -2406,14 +2432,15 @@ class _EvaluateVisitor /// for macros such as `if()`. Tuple2, Map> _evaluateMacroArguments( CallableInvocation invocation) { - if (invocation.arguments.rest == null) { + var restArgs = invocation.arguments.rest; + if (restArgs == null) { return Tuple2( invocation.arguments.positional, invocation.arguments.named); } var positional = invocation.arguments.positional.toList(); var named = Map.of(invocation.arguments.named); - var rest = invocation.arguments.rest.accept(this); + var rest = restArgs.accept(this); if (rest is SassMap) { _addRestMap(named, rest, invocation, (value) => ValueExpression(value)); } else if (rest is SassList) { @@ -2427,11 +2454,10 @@ class _EvaluateVisitor positional.add(ValueExpression(rest)); } - if (invocation.arguments.keywordRest == null) { - return Tuple2(positional, named); - } + var keywordRestArgs = invocation.arguments.keywordRest; + if (keywordRestArgs == null) return Tuple2(positional, named); - var keywordRest = invocation.arguments.keywordRest.accept(this); + var keywordRest = keywordRestArgs.accept(this); if (keywordRest is SassMap) { _addRestMap( named, keywordRest, invocation, (value) => ValueExpression(value)); @@ -2439,7 +2465,7 @@ class _EvaluateVisitor } else { throw _exception( "Variable keyword arguments must be a map (was $keywordRest).", - invocation.span); + keywordRestArgs.span); } } @@ -2455,8 +2481,7 @@ class _EvaluateVisitor /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. void _addRestMap(Map values, SassMap map, AstNode nodeWithSpan, - [T convert(Value value)]) { - convert ??= (value) => value as T; + T convert(Value value)) { map.contents.forEach((key, value) { if (key is SassString) { values[key.text] = convert(value); @@ -2476,16 +2501,15 @@ class _EvaluateVisitor _addExceptionSpan( nodeWithSpan, () => arguments.verify(positional, MapKeySet(named))); - Value visitSelectorExpression(SelectorExpression node) { - if (_styleRuleIgnoringAtRoot == null) return sassNull; - return _styleRuleIgnoringAtRoot.originalSelector.asSassList; - } + Value visitSelectorExpression(SelectorExpression node) => + _styleRuleIgnoringAtRoot?.originalSelector.asSassList ?? sassNull; SassString visitStringExpression(StringExpression node) { // Don't use [performInterpolation] here because we need to get the raw text // from strings, rather than the semantic value. return SassString( - node.text.contents.map((value) { + // TODO: no Object + node.text.contents.map((Object value) { if (value is String) return value; var expression = value as Expression; var result = expression.accept(this); @@ -2574,8 +2598,7 @@ class _EvaluateVisitor _root.addChild(modifiableNode); _endOfImports++; } else { - _outOfOrderImports ??= []; - _outOfOrderImports.add(modifiableNode); + (_outOfOrderImports ??= []).add(modifiableNode); } } @@ -2600,9 +2623,9 @@ class _EvaluateVisitor "Media rules may not be used within nested declarations.", node.span); } - var mergedQueries = _mediaQueries == null - ? null - : _mergeMediaQueries(_mediaQueries, node.queries); + // TODO: no !, no as + var mergedQueries = _mediaQueries.andThen( + (mediaQueries) => _mergeMediaQueries(mediaQueries, node.queries)); if (mergedQueries != null && mergedQueries.isEmpty) return null; _withParent( @@ -2746,7 +2769,8 @@ class _EvaluateVisitor /// values passed into the interpolation. String _performInterpolation(Interpolation interpolation, {bool warnForColor = false}) { - return interpolation.contents.map((value) { + // TODO: no Object + return interpolation.contents.map((Object value) { if (value is String) return value; var expression = value as Expression; var result = expression.accept(this); @@ -2793,13 +2817,15 @@ class _EvaluateVisitor /// where that variable was originally declared. Otherwise, this will just /// return [expression]. /// - /// Returns `null` if [_sourceMap] is `false`. - /// /// This returns an [AstNode] rather than a [FileSpan] so we can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - AstNode _expressionNode(Expression expression) { - if (!_sourceMap) return null; + AstNode _expressionNode(Expression /*!*/ expression) { + // If we aren't making a source map this doesn't matter, but we still return + // the expression so we don't have to make the type (and everything + // downstream of it) nullable. + if (!_sourceMap) return expression; + if (expression is VariableExpression) { return _environment.getVariableNode(expression.name, namespace: expression.namespace) ?? @@ -2819,9 +2845,11 @@ class _EvaluateVisitor /// Runs [callback] in a new environment scope unless [scopeWhen] is false. T _withParent(S node, T callback(), {bool through(CssNode node), bool scopeWhen = true}) { + // TODO: no as _addChild(node, through: through); var oldParent = _parent; + // TODO: no as _parent = node; var result = _environment.scope(callback, when: scopeWhen); _parent = oldParent; @@ -2839,7 +2867,7 @@ class _EvaluateVisitor var parent = _parent; if (through != null) { while (through(parent)) { - parent = parent.parent; + parent = parent.parent /*!*/; } // If the parent has a (visible) following sibling, we shouldn't add to @@ -2852,6 +2880,7 @@ class _EvaluateVisitor } } + // TODO: no as parent.addChild(node); } @@ -2893,11 +2922,10 @@ class _EvaluateVisitor /// Creates a new stack frame with location information from [member] and /// [span]. - Frame _stackFrame(String member, FileSpan span) { - var url = span.sourceUrl; - if (url != null && _importCache != null) url = _importCache.humanize(url); - return frameForSpan(span, member, url: url); - } + Frame _stackFrame(String member, FileSpan /*?*/ span) => frameForSpan( + span, member, + url: + span?.sourceUrl.andThen((url) => _importCache?.humanize(url) ?? url)); /// Returns a stack trace at the current point. /// @@ -2927,7 +2955,7 @@ class _EvaluateVisitor /// /// The primary span is taken from the current stack trace span. SassRuntimeException _multiSpanException(String message, String primaryLabel, - Map secondaryLabels) => + Map secondaryLabels) => MultiSpanSassRuntimeException(message, _stack.last.item2.span, primaryLabel, secondaryLabels, _stackTrace()); @@ -2942,19 +2970,24 @@ class _EvaluateVisitor /// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - T _adjustParseError(AstNode nodeWithSpan, T callback()) { + T _adjustParseError(AstNode /*!*/ nodeWithSpan, T callback()) { try { return callback(); } on SassFormatException catch (error) { - var errorText = error.span.file.getText(0); - var span = nodeWithSpan.span; - var syntheticFile = span.file + var errorSpan = error.span; + if (errorSpan == null) rethrow; + + var nodeSpan = nodeWithSpan.span; + if (nodeSpan == null) rethrow; + + var errorText = errorSpan.file.getText(0); + var syntheticFile = nodeSpan.file .getText(0) - .replaceRange(span.start.offset, span.end.offset, errorText); + .replaceRange(nodeSpan.start.offset, nodeSpan.end.offset, errorText); var syntheticSpan = - SourceFile.fromString(syntheticFile, url: span.file.url).span( - span.start.offset + error.span.start.offset, - span.start.offset + error.span.end.offset); + SourceFile.fromString(syntheticFile, url: nodeSpan.file.url).span( + nodeSpan.start.offset + errorSpan.start.offset, + nodeSpan.start.offset + errorSpan.end.offset); throw _exception(error.message, syntheticSpan); } } @@ -2965,7 +2998,7 @@ class _EvaluateVisitor /// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - T _addExceptionSpan(AstNode nodeWithSpan, T callback()) { + T _addExceptionSpan(AstNode /*!*/ nodeWithSpan, T callback()) { try { return callback(); } on MultiSpanSassScriptException catch (error) { @@ -2987,7 +3020,9 @@ class _EvaluateVisitor try { return callback(); } on SassRuntimeException catch (error) { - if (!error.span.text.startsWith("@error")) rethrow; + var span = error.span; + if (span == null) rethrow; + if (span.text.startsWith("@error")) rethrow; throw SassRuntimeException( error.message, nodeWithSpan.span, _stackTrace()); } @@ -3029,8 +3064,7 @@ class _ImportedCssVisitor implements ModifiableCssVisitor { _visitor._addChild(node); _visitor._endOfImports++; } else { - _visitor._outOfOrderImports ??= []; - _visitor._outOfOrderImports.add(node); + (_visitor._outOfOrderImports ??= []).add(node); } } @@ -3042,9 +3076,9 @@ class _ImportedCssVisitor implements ModifiableCssVisitor { // Whether [node.query] has been merged with [_visitor._mediaQueries]. If it // has been merged, merging again is a no-op; if it hasn't been merged, // merging again will fail. - var hasBeenMerged = _visitor._mediaQueries == null || - _visitor._mergeMediaQueries(_visitor._mediaQueries, node.queries) != - null; + var mediaQueries = _visitor._mediaQueries; + var hasBeenMerged = mediaQueries == null || + _visitor._mergeMediaQueries(mediaQueries, node.queries) != null; _visitor._addChild(node, through: (node) => @@ -3067,7 +3101,7 @@ class _ImportedCssVisitor implements ModifiableCssVisitor { /// The result of evaluating arguments to a function or mixin. class _ArgumentResults { /// Arguments passed by position. - final List positional; + final List positional; /// The [AstNode]s that hold the spans for each [positional] argument, or /// `null` if source span tracking is disabled. @@ -3075,7 +3109,7 @@ class _ArgumentResults { /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - final List positionalNodes; + final List positionalNodes; /// Arguments passed by name. final Map named; @@ -3086,7 +3120,7 @@ class _ArgumentResults { /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - final Map namedNodes; + final Map namedNodes; /// The separator used for the rest argument list, if any. final ListSeparator separator; diff --git a/lib/src/visitor/recursive_statement.dart b/lib/src/visitor/recursive_statement.dart index caddfad91..2c64f82bd 100644 --- a/lib/src/visitor/recursive_statement.dart +++ b/lib/src/visitor/recursive_statement.dart @@ -5,6 +5,7 @@ import 'package:meta/meta.dart'; import '../ast/sass.dart'; +import '../util/nullable.dart'; import 'interface/statement.dart'; /// A visitor that recursively traverses each statement in a Sass AST. @@ -21,14 +22,14 @@ import 'interface/statement.dart'; /// * [visitExpression] abstract class RecursiveStatementVisitor implements StatementVisitor { void visitAtRootRule(AtRootRule node) { - if (node.query != null) visitInterpolation(node.query); - visitChildren(node); + node.query.andThen(visitInterpolation); + visitChildren(node.children); } void visitAtRule(AtRule node) { visitInterpolation(node.name); - if (node.value != null) visitInterpolation(node.value); - if (node.children != null) visitChildren(node); + node.value.andThen(visitInterpolation); + node.children.andThen(visitChildren); } void visitContentBlock(ContentBlock node) => visitCallableDeclaration(node); @@ -43,13 +44,13 @@ abstract class RecursiveStatementVisitor implements StatementVisitor { void visitDeclaration(Declaration node) { visitInterpolation(node.name); - if (node.value != null) visitExpression(node.value); - if (node.children != null) visitChildren(node); + node.value.andThen(visitExpression); + node.children.andThen(visitChildren); } void visitEachRule(EachRule node) { visitExpression(node.list); - visitChildren(node); + visitChildren(node.children); } void visitErrorRule(ErrorRule node) { @@ -63,7 +64,7 @@ abstract class RecursiveStatementVisitor implements StatementVisitor { void visitForRule(ForRule node) { visitExpression(node.from); visitExpression(node.to); - visitChildren(node); + visitChildren(node.children); } void visitForwardRule(ForwardRule node) {} @@ -78,26 +79,26 @@ abstract class RecursiveStatementVisitor implements StatementVisitor { } } - if (node.lastClause != null) { - for (var child in node.lastClause.children) { + node.lastClause.andThen((lastClause) { + for (var child in lastClause.children) { child.accept(this); } - } + }); } void visitImportRule(ImportRule node) { for (var import in node.imports) { if (import is StaticImport) { visitInterpolation(import.url); - if (import.supports != null) visitSupportsCondition(import.supports); - if (import.media != null) visitInterpolation(import.media); + import.supports.andThen(visitSupportsCondition); + import.media.andThen(visitInterpolation); } } } void visitIncludeRule(IncludeRule node) { visitArgumentInvocation(node.arguments); - if (node.content != null) visitContentBlock(node.content); + node.content.andThen(visitContentBlock); } void visitLoudComment(LoudComment node) { @@ -106,7 +107,7 @@ abstract class RecursiveStatementVisitor implements StatementVisitor { void visitMediaRule(MediaRule node) { visitInterpolation(node.query); - visitChildren(node); + visitChildren(node.children); } void visitMixinRule(MixinRule node) => visitCallableDeclaration(node); @@ -119,14 +120,14 @@ abstract class RecursiveStatementVisitor implements StatementVisitor { void visitStyleRule(StyleRule node) { visitInterpolation(node.selector); - visitChildren(node); + visitChildren(node.children); } - void visitStylesheet(Stylesheet node) => visitChildren(node); + void visitStylesheet(Stylesheet node) => visitChildren(node.children); void visitSupportsRule(SupportsRule node) { visitSupportsCondition(node.condition); - visitChildren(node); + visitChildren(node.children); } void visitUseRule(UseRule node) {} @@ -141,7 +142,7 @@ abstract class RecursiveStatementVisitor implements StatementVisitor { void visitWhileRule(WhileRule node) { visitExpression(node.condition); - visitChildren(node); + visitChildren(node.children); } /// Visits each of [node]'s expressions and children. @@ -151,9 +152,9 @@ abstract class RecursiveStatementVisitor implements StatementVisitor { @protected void visitCallableDeclaration(CallableDeclaration node) { for (var argument in node.arguments.arguments) { - if (argument.defaultValue != null) visitExpression(argument.defaultValue); + argument.defaultValue.andThen(visitExpression); } - visitChildren(node); + visitChildren(node.children); } /// Visits each expression in an [invocation]. @@ -168,12 +169,8 @@ abstract class RecursiveStatementVisitor implements StatementVisitor { for (var expression in invocation.named.values) { visitExpression(expression); } - if (invocation.rest != null) { - visitExpression(invocation.rest); - } - if (invocation.keywordRest != null) { - visitExpression(invocation.keywordRest); - } + invocation.rest.andThen(visitExpression); + invocation.keywordRest.andThen(visitExpression); } /// Visits each expression in [condition]. @@ -195,13 +192,13 @@ abstract class RecursiveStatementVisitor implements StatementVisitor { } } - /// Visits each of [node]'s children. + /// Visits each child in [children]. /// /// The default implementation of the visit methods for all [ParentStatement]s /// call this. @protected - void visitChildren(ParentStatement node) { - for (var child in node.children) { + void visitChildren(List children) { + for (var child in children) { child.accept(this); } } diff --git a/lib/src/visitor/serialize.dart b/lib/src/visitor/serialize.dart index e5654fef3..0af80302f 100644 --- a/lib/src/visitor/serialize.dart +++ b/lib/src/visitor/serialize.dart @@ -188,9 +188,9 @@ class _SerializeVisitor return; } - if (node.span != null) { - minimumIndentation = - math.min(minimumIndentation, node.span.start.column); + var span = node.span; + if (span != null) { + minimumIndentation = math.min(minimumIndentation, span.start.column); } _writeIndentation(); @@ -205,9 +205,10 @@ class _SerializeVisitor _buffer.writeCharCode($at); _write(node.name); - if (node.value != null) { + var value = node.value; + if (value != null) { _buffer.writeCharCode($space); - _write(node.value); + _write(value); } }); @@ -242,14 +243,16 @@ class _SerializeVisitor _writeOptionalSpace(); _for(node.url, () => _writeImportUrl(node.url.value)); - if (node.supports != null) { + var supports = node.supports; + if (supports != null) { _writeOptionalSpace(); - _write(node.supports); + _write(supports); } - if (node.media != null) { + var media = node.media; + if (media != null) { _writeOptionalSpace(); - _writeBetween(node.media, _commaSeparator, _visitMediaQuery); + _writeBetween(media, _commaSeparator, _visitMediaQuery); } }); } @@ -389,9 +392,9 @@ class _SerializeVisitor return; } - if (node.value.span != null) { - minimumIndentation = - math.min(minimumIndentation, node.name.span.start.column); + var span = node.value.span; + if (span != null) { + minimumIndentation = math.min(minimumIndentation, span.start.column); } _writeWithIndent(value, minimumIndentation); @@ -426,7 +429,7 @@ class _SerializeVisitor /// [_indentation] for each non-empty line after the first. /// /// Compresses trailing empty lines of [text] into a single trailing space. - void _writeWithIndent(String text, int minimumIndentation) { + void _writeWithIndent(String text, int /*!*/ minimumIndentation) { var scanner = LineScanner(text); // Write the first line as-is. @@ -607,10 +610,10 @@ class _SerializeVisitor throw SassScriptException("$map isn't a valid CSS value."); } _buffer.writeCharCode($lparen); - _writeBetween(map.contents.keys, ", ", (key) { - _writeMapElement(key); + _writeBetween>(map.contents.entries, ", ", (entry) { + _writeMapElement(entry.key); _buffer.write(": "); - _writeMapElement(map.contents[key]); + _writeMapElement(entry.value); }); _buffer.writeCharCode($rparen); } @@ -630,10 +633,11 @@ class _SerializeVisitor } void visitNumber(SassNumber value) { - if (value.asSlash != null) { - visitNumber(value.asSlash.item1); + var asSlash = value.asSlash; + if (asSlash != null) { + visitNumber(asSlash.item1); _buffer.writeCharCode($slash); - visitNumber(value.asSlash.item2); + visitNumber(asSlash.item2); return; } @@ -932,17 +936,19 @@ class _SerializeVisitor void visitAttributeSelector(AttributeSelector attribute) { _buffer.writeCharCode($lbracket); _buffer.write(attribute.name); - if (attribute.op != null) { + + var value = attribute.value; + if (value != null) { _buffer.write(attribute.op); - if (Parser.isIdentifier(attribute.value) && + if (Parser.isIdentifier(value) && // Emit identifiers that start with `--` with quotes, because IE11 // doesn't consider them to be valid identifiers. - !attribute.value.startsWith('--')) { - _buffer.write(attribute.value); + !value.startsWith('--')) { + _buffer.write(value); if (attribute.modifier != null) _buffer.writeCharCode($space); } else { - _visitQuotedString(attribute.value); + _visitQuotedString(value); if (attribute.modifier != null) _writeOptionalSpace(); } if (attribute.modifier != null) _buffer.write(attribute.modifier); @@ -994,7 +1000,7 @@ class _SerializeVisitor _buffer.write(id.name); } - void visitSelectorList(SelectorList list) { + void visitSelectorList(SelectorList /*!*/ list) { var complexes = _inspect ? list.components : list.components.where((complex) => !complex.isInvisible); @@ -1026,10 +1032,11 @@ class _SerializeVisitor } void visitPseudoSelector(PseudoSelector pseudo) { + var innerSelector = pseudo.selector; // `:not(%a)` is semantically identical to `*`. - if (pseudo.selector != null && + if (innerSelector != null && pseudo.name == 'not' && - pseudo.selector.isInvisible) { + innerSelector.isInvisible) { return; } @@ -1043,7 +1050,7 @@ class _SerializeVisitor _buffer.write(pseudo.argument); if (pseudo.selector != null) _buffer.writeCharCode($space); } - if (pseudo.selector != null) visitSelectorList(pseudo.selector); + if (innerSelector != null) visitSelectorList(innerSelector); _buffer.writeCharCode($rparen); } @@ -1087,6 +1094,7 @@ class _SerializeVisitor if (previous != null) { if (_requiresSemicolon(previous)) _buffer.writeCharCode($semicolon); _writeLineFeed(); + // TODO: no ! if (previous.isGroupEnd) _writeLineFeed(); } previous = child; diff --git a/test/cli/shared/source_maps.dart b/test/cli/shared/source_maps.dart index 2df6c58ad..35130a94a 100644 --- a/test/cli/shared/source_maps.dart +++ b/test/cli/shared/source_maps.dart @@ -35,7 +35,7 @@ void sharedTests(Future runSass(Iterable arguments)) { }); test("contains mappings", () { - SingleMapping sourceMap; + /*late*/ SingleMapping sourceMap; sass.compileString("a {b: 1 + 2}", sourceMap: (map) => sourceMap = map); expect(map, containsPair("mappings", sourceMap.toJson()["mappings"])); }); @@ -360,4 +360,4 @@ void sharedTests(Future runSass(Iterable arguments)) { /// Reads the file at [path] within [d.sandbox] and JSON-decodes it. Map _readJson(String path) => - jsonDecode(readFile(d.path(path))) as Map; + jsonDecode(readFile(d.path(path))) as Map /*!*/; diff --git a/test/dart_api/function_test.dart b/test/dart_api/function_test.dart index fcda027ee..eb29bf7ec 100644 --- a/test/dart_api/function_test.dart +++ b/test/dart_api/function_test.dart @@ -77,17 +77,11 @@ void main() { test("gracefully handles a custom function throwing", () { expect(() { compileString('a {b: foo()}', + // TODO: no as functions: [Callable("foo", "", (arguments) => throw "heck")]); }, throwsA(const TypeMatcher())); }); - test("gracefully handles a custom function returning null", () { - expect(() { - compileString('a {b: foo()}', - functions: [Callable("foo", "", (arguments) => null)]); - }, throwsA(const TypeMatcher())); - }); - test("supports default argument values", () { var css = compileString('a {b: foo()}', functions: [ Callable("foo", r"$arg: 1", expectAsync1((arguments) { @@ -125,6 +119,7 @@ void main() { expect(list.keywords, contains("bar")); expect(list.keywords["bar"].assertNumber().value, equals(1)); return list.keywords["bar"]; + // TODO: no as })) ]); diff --git a/test/dart_api/importer_test.dart b/test/dart_api/importer_test.dart index c48aa95dc..a1c9b57d8 100644 --- a/test/dart_api/importer_test.dart +++ b/test/dart_api/importer_test.dart @@ -92,7 +92,7 @@ void main() { }); test("uses an importer's source map URL", () { - SingleMapping map; + /*late*/ SingleMapping map; compileString('@import "orange";', importers: [ TestImporter((url) => Uri.parse("u:$url"), (url) { @@ -107,7 +107,7 @@ void main() { }); test("uses a data: source map URL if the importer doesn't provide one", () { - SingleMapping map; + /*late*/ SingleMapping map; compileString('@import "orange";', importers: [ TestImporter((url) => Uri.parse("u:$url"), (url) { @@ -128,9 +128,10 @@ void main() { compileString('@import "orange";', importers: [ TestImporter((url) { throw "this import is bad actually"; - }, expectAsync1((_) => null, count: 0)) + }, expectAsync1((_) => null, count: 0)) // TODO: no as ]); }, throwsA(predicate((error) { + // TODO: no dynamic expect(error, const TypeMatcher()); expect( error.toString(), startsWith("Error: this import is bad actually")); @@ -146,6 +147,7 @@ void main() { }) ]); }, throwsA(predicate((error) { + // TODO: no dynamic expect(error, const TypeMatcher()); expect( error.toString(), startsWith("Error: this import is bad actually")); @@ -161,6 +163,7 @@ void main() { }) ]); }, throwsA(predicate((error) { + // TODO: no dynamic expect(error, const TypeMatcher()); // FormatException.toString() starts with "FormatException:", but // the error message should not. @@ -175,6 +178,7 @@ void main() { TestImporter((url) => Uri.parse("u:$url"), (url) => null) ]); }, throwsA(predicate((error) { + // TODO: no dynamic expect(error, const TypeMatcher()); expect(error.toString(), startsWith("Error: Can't find stylesheet to import")); diff --git a/test/dart_api/logger_test.dart b/test/dart_api/logger_test.dart index a981c1b54..9b0bd61d9 100644 --- a/test/dart_api/logger_test.dart +++ b/test/dart_api/logger_test.dart @@ -12,6 +12,8 @@ import 'package:sass/sass.dart'; import 'test_importer.dart'; +// TODO: no required + void main() { group("with @warn", () { test("passes the message and stack trace to the logger", () { @@ -229,8 +231,8 @@ void main() { /// A [Logger] whose [warn] and [debug] methods are provided by callbacks. class _TestLogger implements Logger { - final void Function(String, {FileSpan span, Trace trace, bool deprecation}) - _warn; + final void Function(String, + {FileSpan span, Trace trace, bool /*!*/ deprecation}) _warn; final void Function(String, SourceSpan) _debug; _TestLogger.withWarn(this._warn) : _debug = const Logger.stderr().debug; diff --git a/test/dart_api/test_importer.dart b/test/dart_api/test_importer.dart index 5dac8e0d7..f84a2bb79 100644 --- a/test/dart_api/test_importer.dart +++ b/test/dart_api/test_importer.dart @@ -8,7 +8,7 @@ import 'package:sass/sass.dart'; /// closures. class TestImporter extends Importer { final Uri Function(Uri url) _canonicalize; - final ImporterResult Function(Uri url) _load; + final ImporterResult /*!*/ Function(Uri url) _load; TestImporter(this._canonicalize, this._load); diff --git a/test/dart_api/value/boolean_test.dart b/test/dart_api/value/boolean_test.dart index 875431485..18acc71f1 100644 --- a/test/dart_api/value/boolean_test.dart +++ b/test/dart_api/value/boolean_test.dart @@ -12,7 +12,7 @@ import 'utils.dart'; void main() { group("true", () { - Value value; + /*late*/ Value /*!*/ value; setUp(() => value = parseValue("true")); test("is truthy", () { @@ -38,7 +38,7 @@ void main() { }); group("false", () { - Value value; + /*late*/ Value /*!*/ value; setUp(() => value = parseValue("false")); test("is falsey", () { diff --git a/test/dart_api/value/utils.dart b/test/dart_api/value/utils.dart index 0bec45ea3..1635e034c 100644 --- a/test/dart_api/value/utils.dart +++ b/test/dart_api/value/utils.dart @@ -9,7 +9,7 @@ import 'package:sass/src/exception.dart'; /// Parses [source] by way of a function call. Value parseValue(String source) { - Value value; + /*late*/ Value value; compileString("a {b: foo(($source))}", functions: [ Callable("foo", r"$arg", expectAsync1((arguments) { expect(arguments, hasLength(1)); @@ -26,6 +26,7 @@ final throwsSassScriptException = /// Like [equals], but asserts that the hash codes of the values are the same as /// well. +// TODO: no dynamic Matcher equalsWithHash(Object expected) => predicate((actual) { expect(actual, equals(expected)); expect(actual.hashCode, equals(expected.hashCode), diff --git a/test/double_check_test.dart b/test/double_check_test.dart index 23983a27f..fd26193d8 100644 --- a/test/double_check_test.dart +++ b/test/double_check_test.dart @@ -21,14 +21,16 @@ void main() { synchronize.sources.forEach((sourcePath, targetPath) { test(targetPath, () { + var message = "$targetPath is out-of-date.\n" + "Run pub run grinder to update it."; + var target = File(targetPath).readAsStringSync(); - var actualHash = checksumPattern.firstMatch(target)[1]; + var match = checksumPattern.firstMatch(target); // TODO: no ! + if (match == null) fail(message); var source = File(sourcePath).readAsBytesSync(); var expectedHash = sha1.convert(source).toString(); - expect(actualHash, equals(expectedHash), - reason: "$targetPath is out-of-date.\n" - "Run pub run grinder to update it."); + expect(match[1], equals(expectedHash), reason: message); }); }); }, @@ -46,7 +48,7 @@ void main() { var pubspec = loadYaml(File("pubspec.yaml").readAsStringSync(), sourceUrl: Uri(path: "pubspec.yaml")) as Map; expect(pubspec, containsPair("version", isA())); - var pubspecVersion = pubspec["version"] as String; + var pubspecVersion = pubspec["version"] as String /*!*/; expect(pubspecVersion, anyOf(equals(changelogVersion), equals("$changelogVersion-dev"))); diff --git a/test/hybrid.dart b/test/hybrid.dart index 2629dd552..a40f12d95 100644 --- a/test/hybrid.dart +++ b/test/hybrid.dart @@ -8,7 +8,8 @@ import 'package:test/test.dart'; /// Creates a directory in the system temp directory and returns its path. Future createTempDir() async => (await runHybridExpression( - '(await Directory.systemTemp.createTemp("dart_sass_")).path')) as String; + '(await Directory.systemTemp.createTemp("dart_sass_")).path')) + as String /*!*/; /// Writes [text] to [path]. Future writeTextFile(String path, String text) => runHybridExpression( diff --git a/test/node_api/function_test.dart b/test/node_api/function_test.dart index e56584025..8eee066ff 100644 --- a/test/node_api/function_test.dart +++ b/test/node_api/function_test.dart @@ -21,6 +21,8 @@ import '../hybrid.dart'; import 'api.dart'; import 'utils.dart'; +// TODO: no dynamic + void main() { setUpAll(ensureNpmPackage); useSandbox(); diff --git a/test/node_api/importer_test.dart b/test/node_api/importer_test.dart index f475cc93f..2d4f9f18c 100644 --- a/test/node_api/importer_test.dart +++ b/test/node_api/importer_test.dart @@ -21,12 +21,14 @@ import '../hybrid.dart'; import 'api.dart'; import 'utils.dart'; -String sassPath; +// TODO: no dynamic void main() { setUpAll(ensureNpmPackage); useSandbox(); + /*late*/ String sassPath; + setUp(() async { sassPath = p.join(sandbox, 'test.scss'); await writeTextFile(sassPath, 'a {b: c}'); diff --git a/test/node_api/source_map_test.dart b/test/node_api/source_map_test.dart index ff87e1512..fcd506843 100644 --- a/test/node_api/source_map_test.dart +++ b/test/node_api/source_map_test.dart @@ -29,7 +29,7 @@ void main() { group("a basic invocation", () { String css; - Map map; + /*late*/ Map /*!*/ map; setUp(() { var result = sass.renderSync( RenderOptions(data: "a {b: c}", sourceMap: true, outFile: "out.css")); @@ -38,7 +38,7 @@ void main() { }); test("includes correct mappings", () { - SingleMapping expectedMap; + /*late*/ SingleMapping /*!*/ expectedMap; dart_sass.compileString("a {b: c}", sourceMap: (map) => expectedMap = map); expectedMap.targetUrl = "out.css"; @@ -158,7 +158,7 @@ void main() { test("emits a source map", () { var result = sass.renderSync( RenderOptions(data: "a {b: c}", sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map) as Map; + var map = _jsonUtf8.decode(result.map) as Map /*!*/; expect(map, containsPair("sources", ["stdin"])); }); @@ -168,7 +168,7 @@ void main() { var result = sass.renderSync(RenderOptions( file: p.join(sandbox, "test.scss"), sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map) as Map; + var map = _jsonUtf8.decode(result.map) as Map /*!*/; expect( map, containsPair( @@ -182,7 +182,7 @@ void main() { var result = sass.renderSync(RenderOptions( file: p.join(sandbox, "test"), sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map) as Map; + var map = _jsonUtf8.decode(result.map) as Map /*!*/; expect( map, containsPair( @@ -192,7 +192,7 @@ void main() { test("derives the target URL from stdin", () { var result = sass.renderSync( RenderOptions(data: "a {b: c}", sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map) as Map; + var map = _jsonUtf8.decode(result.map) as Map /*!*/; expect(map, containsPair("file", "stdin.css")); }); @@ -332,4 +332,4 @@ void main() { /// Renders [options] and returns the decoded source map. Map _renderSourceMap(RenderOptions options) => - _jsonUtf8.decode(sass.renderSync(options).map) as Map; + _jsonUtf8.decode(sass.renderSync(options).map) as Map /*!*/; diff --git a/test/node_api/utils.dart b/test/node_api/utils.dart index 3e5aff785..5a830fa70 100644 --- a/test/node_api/utils.dart +++ b/test/node_api/utils.dart @@ -12,6 +12,7 @@ import 'package:node_interop/node_interop.dart'; import 'package:sass/src/io.dart'; import 'package:sass/src/node/function.dart'; +import 'package:sass/src/util/nullable.dart'; import '../hybrid.dart'; import 'api.dart'; @@ -19,15 +20,22 @@ import 'api.dart'; @JS('process.env') external Object get _environment; -String sandbox; +String get sandbox { + var sandbox = _sandbox; + if (sandbox != null) return sandbox; + fail("useSandbox() must be called in any test file that uses the sandbox " + "field."); +} + +String _sandbox; void useSandbox() { setUp(() async { - sandbox = await createTempDir(); + _sandbox = await createTempDir(); }); tearDown(() async { - if (sandbox != null) await deleteDirectory(sandbox); + await _sandbox.andThen(deleteDirectory); }); } diff --git a/test/node_api/value/list_test.dart b/test/node_api/value/list_test.dart index c23310330..8865fdc77 100644 --- a/test/node_api/value/list_test.dart +++ b/test/node_api/value/list_test.dart @@ -16,7 +16,7 @@ import 'utils.dart'; void main() { group("an argument list", () { - NodeSassList args; + /*late*/ NodeSassList args; setUp(() { renderSync(RenderOptions( data: "a {b: foo(1, 'a', blue)}", diff --git a/test/node_api/value/utils.dart b/test/node_api/value/utils.dart index f69c83da7..2c900db37 100644 --- a/test/node_api/value/utils.dart +++ b/test/node_api/value/utils.dart @@ -14,7 +14,7 @@ import '../utils.dart'; /// Parses [source] by way of a function call. T parseValue(String source) { - T value; + /*late*/ T value; renderSync(RenderOptions( data: "a {b: foo(($source))}", functions: jsify({ @@ -28,4 +28,6 @@ T parseValue(String source) { /// A matcher that matches values that are JS `instanceof` [type]. Matcher isJSInstanceOf(Object type) => predicate( - (value) => jsInstanceOf(value, type), "to be an instance of $type"); + // TODO: no dynamic + (value) => jsInstanceOf(value, type), + "to be an instance of $type"); diff --git a/test/node_api_test.dart b/test/node_api_test.dart index f7511a38f..ede14ca27 100644 --- a/test/node_api_test.dart +++ b/test/node_api_test.dart @@ -20,12 +20,12 @@ import 'node_api/intercept_stdout.dart'; import 'node_api/utils.dart'; import 'utils.dart'; -String sassPath; - void main() { setUpAll(ensureNpmPackage); useSandbox(); + /*late*/ String sassPath; + setUp(() async { sassPath = p.join(sandbox, 'test.scss'); await writeTextFile(sassPath, 'a {b: c}'); @@ -311,12 +311,11 @@ a { }); test("includes timing information", () { - var result = sass.renderSync(RenderOptions(file: sassPath)); - expect(result.stats.start, const TypeMatcher()); - expect(result.stats.end, const TypeMatcher()); - expect(result.stats.start, lessThanOrEqualTo(result.stats.end)); - expect(result.stats.duration, - equals(result.stats.end - result.stats.start)); + var stats = sass.renderSync(RenderOptions(file: sassPath)).stats /*!*/; + expect(stats.start, const TypeMatcher()); + expect(stats.end, const TypeMatcher()); + expect(stats.start, lessThanOrEqualTo(stats.end)); + expect(stats.duration, equals(stats.end - stats.start)); }); group("has includedFiles which", () { @@ -351,7 +350,7 @@ a { }); group("the error object", () { - RenderError error; + /*late*/ RenderError error; group("for a parse error in a file", () { setUp(() async { await writeTextFile(sassPath, "a {b: }"); diff --git a/test/source_map_test.dart b/test/source_map_test.dart index e6e5d22e3..ca557c48b 100644 --- a/test/source_map_test.dart +++ b/test/source_map_test.dart @@ -682,6 +682,7 @@ void main() { x {y: $map} """, sourceMap: (_) {}); }, throwsA(predicate((untypedError) { + // TODO: no dynamic var error = untypedError as SourceSpanException; expect(error.span.text, equals(r"$map")); return true; diff --git a/test/utils.dart b/test/utils.dart index fdbe402c3..a032bcba2 100644 --- a/test/utils.dart +++ b/test/utils.dart @@ -29,7 +29,7 @@ Map embeddedSourceMap(String css) { var match = _sourceMapCommentRegExp.firstMatch(css); var data = Uri.parse(match[1]).data; expect(data.mimeType, equals("application/json")); - return jsonDecode(data.contentAsString()) as Map; + return jsonDecode(data.contentAsString()) as Map /*!*/; } // Like `p.prettyUri()`, but for a non-URL path. diff --git a/tool/grind.dart b/tool/grind.dart index 547db8941..04d6ee196 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -33,8 +33,8 @@ void main(List args) { as Map; pkg.npmReadme.fn = () => _readAndResolveMarkdown("package/README.npm.md"); pkg.standaloneName.value = "dart-sass"; - pkg.githubUser.value = Platform.environment["GH_USER"]; - pkg.githubPassword.value = Platform.environment["GH_TOKEN"]; + pkg.githubUser.fn = () => Platform.environment["GH_USER"] /*!*/; + pkg.githubPassword.fn = () => Platform.environment["GH_TOKEN"] /*!*/; pkg.githubReleaseNotes.fn = () => "To install Sass ${pkg.version}, download one of the packages below " diff --git a/tool/grind/benchmark.dart b/tool/grind/benchmark.dart index 3617fddac..6ad412b67 100644 --- a/tool/grind/benchmark.dart +++ b/tool/grind/benchmark.dart @@ -186,9 +186,11 @@ I ran five instances of each configuration and recorded the fastest time. Duration sasscTime; if (!libsassIncompatible.contains(info[1])) { sasscTime = await _benchmark(p.join(sassc, 'bin', 'sassc'), [path]); - buffer.writeln("* sassc: ${_formatTime(sasscTime)}"); + buffer.writeln("* sassc: ${_formatTime(sasscTime)}"); // TODO: no ! } + // TODO: no as + var scriptSnapshotTime = await _benchmark(Platform.executable, ['--no-enable-asserts', p.join('build', 'sass.snapshot'), path]); buffer.writeln("* Dart Sass from a script snapshot: " @@ -244,7 +246,7 @@ Future _benchmark(String executable, List arguments) async { // chance to warm up at the OS level. await _benchmarkOnce(executable, arguments); - Duration lowest; + /*late*/ Duration lowest; for (var i = 0; i < 5; i++) { var duration = await _benchmarkOnce(executable, arguments); if (lowest == null || duration < lowest) lowest = duration; @@ -261,8 +263,12 @@ Future _benchmarkOnce( fail("Process failed with exit code ${result.exitCode}\n${result.stderr}"); } - var match = - RegExp(r"(\d+)m(\d+)\.(\d+)s").firstMatch(result.stderr as String); + var match = RegExp(r"(\d+)m(\d+)\.(\d+)s") + .firstMatch(result.stderr as String); // TODO: no ! + if (match == null) { + fail("Process didn't print the expected format:\n${result.stderr}"); + } + return Duration( minutes: int.parse(match[1]), seconds: int.parse(match[2]), diff --git a/tool/grind/synchronize.dart b/tool/grind/synchronize.dart index 8b171e830..acea62dab 100644 --- a/tool/grind/synchronize.dart +++ b/tool/grind/synchronize.dart @@ -15,6 +15,8 @@ import 'package:dart_style/dart_style.dart'; import 'package:grinder/grinder.dart'; import 'package:path/path.dart' as p; +import 'package:sass/src/util/nullable.dart'; + /// The files to compile to synchronous versions. final sources = const { 'lib/src/visitor/async_evaluate.dart': 'lib/src/visitor/evaluate.dart', @@ -166,7 +168,7 @@ class _Visitor extends RecursiveAstVisitor { _write(arguments.first); _buffer.write(".${_synchronizeName(node.methodName.name)}"); - if (node.typeArguments != null) _write(node.typeArguments); + node.typeArguments.andThen(_write); _buffer.write("("); _position = arguments[1].beginToken.offset; @@ -186,10 +188,11 @@ class _Visitor extends RecursiveAstVisitor { void visitTypeName(TypeName node) { if (["Future", "FutureOr"].contains(node.name.name)) { _skip(node.name.beginToken); - if (node.typeArguments != null) { - _skip(node.typeArguments.leftBracket); - node.typeArguments.arguments.first.accept(this); - _skip(node.typeArguments.rightBracket); + var typeArguments = node.typeArguments; + if (typeArguments != null) { + _skip(typeArguments.leftBracket); + typeArguments.arguments.first.accept(this); + _skip(typeArguments.rightBracket); } else { _buffer.write("void"); } @@ -212,7 +215,6 @@ class _Visitor extends RecursiveAstVisitor { /// Writes [_source] to [_buffer] up to the beginning of [node], then puts /// [_position] after [node] so it doesn't get written. void _skipNode(AstNode node) { - if (node == null) return; _writeTo(node); _position = node.endToken.end; } diff --git a/tool/grind/utils.dart b/tool/grind/utils.dart index cd33f0cff..3d43378e9 100644 --- a/tool/grind/utils.dart +++ b/tool/grind/utils.dart @@ -24,8 +24,8 @@ void ensureBuild() { /// Returns the environment variable named [name], or throws an exception if it /// can't be found. -String environment(String name) { - var value = Platform.environment[name]; +String /*!*/ environment(String name) { + var value = Platform.environment[name]; // TODO: no ! if (value == null) fail("Required environment variable $name not found."); return value; } From 81d952dc7dfb1790b4c49f5f76a5ae00443e2c97 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 16 Mar 2021 19:25:39 -0700 Subject: [PATCH 09/19] Automated null-safety migration --- bin/sass.dart | 4 +- lib/sass.dart | 68 ++-- lib/src/ast/css/at_rule.dart | 2 +- lib/src/ast/css/declaration.dart | 4 +- lib/src/ast/css/import.dart | 4 +- lib/src/ast/css/media_query.dart | 12 +- lib/src/ast/css/modifiable/at_rule.dart | 4 +- lib/src/ast/css/modifiable/comment.dart | 2 +- lib/src/ast/css/modifiable/declaration.dart | 6 +- lib/src/ast/css/modifiable/import.dart | 8 +- .../ast/css/modifiable/keyframe_block.dart | 2 +- lib/src/ast/css/modifiable/media_rule.dart | 2 +- lib/src/ast/css/modifiable/node.dart | 14 +- lib/src/ast/css/modifiable/style_rule.dart | 4 +- lib/src/ast/css/modifiable/stylesheet.dart | 2 +- lib/src/ast/css/modifiable/supports_rule.dart | 4 +- lib/src/ast/css/modifiable/value.dart | 2 +- lib/src/ast/css/node.dart | 2 +- lib/src/ast/css/stylesheet.dart | 8 +- lib/src/ast/css/value.dart | 6 +- lib/src/ast/node.dart | 2 +- lib/src/ast/sass/argument.dart | 4 +- lib/src/ast/sass/argument_declaration.dart | 22 +- lib/src/ast/sass/argument_invocation.dart | 10 +- lib/src/ast/sass/at_root_query.dart | 4 +- lib/src/ast/sass/configured_variable.dart | 2 +- lib/src/ast/sass/expression.dart | 4 +- .../ast/sass/expression/binary_operation.dart | 12 +- lib/src/ast/sass/expression/boolean.dart | 2 +- lib/src/ast/sass/expression/color.dart | 4 +- lib/src/ast/sass/expression/function.dart | 4 +- lib/src/ast/sass/expression/if.dart | 2 +- lib/src/ast/sass/expression/list.dart | 8 +- lib/src/ast/sass/expression/map.dart | 3 +- lib/src/ast/sass/expression/null.dart | 2 +- lib/src/ast/sass/expression/number.dart | 2 +- .../ast/sass/expression/parenthesized.dart | 2 +- lib/src/ast/sass/expression/string.dart | 10 +- .../ast/sass/expression/unary_operation.dart | 6 +- lib/src/ast/sass/expression/value.dart | 4 +- lib/src/ast/sass/expression/variable.dart | 2 +- lib/src/ast/sass/import/static.dart | 4 +- lib/src/ast/sass/interpolation.dart | 7 +- lib/src/ast/sass/statement.dart | 2 +- lib/src/ast/sass/statement/at_root_rule.dart | 8 +- lib/src/ast/sass/statement/at_rule.dart | 7 +- .../sass/statement/callable_declaration.dart | 11 +- lib/src/ast/sass/statement/content_block.dart | 4 +- lib/src/ast/sass/statement/content_rule.dart | 2 +- lib/src/ast/sass/statement/debug_rule.dart | 4 +- lib/src/ast/sass/statement/declaration.dart | 8 +- lib/src/ast/sass/statement/each_rule.dart | 10 +- lib/src/ast/sass/statement/error_rule.dart | 2 +- lib/src/ast/sass/statement/extend_rule.dart | 2 +- lib/src/ast/sass/statement/for_rule.dart | 14 +- lib/src/ast/sass/statement/forward_rule.dart | 22 +- lib/src/ast/sass/statement/function_rule.dart | 6 +- lib/src/ast/sass/statement/if_rule.dart | 13 +- lib/src/ast/sass/statement/import_rule.dart | 2 +- lib/src/ast/sass/statement/include_rule.dart | 8 +- lib/src/ast/sass/statement/loud_comment.dart | 4 +- lib/src/ast/sass/statement/media_rule.dart | 6 +- lib/src/ast/sass/statement/mixin_rule.dart | 8 +- lib/src/ast/sass/statement/parent.dart | 2 +- lib/src/ast/sass/statement/return_rule.dart | 2 +- .../ast/sass/statement/silent_comment.dart | 4 +- lib/src/ast/sass/statement/style_rule.dart | 6 +- lib/src/ast/sass/statement/stylesheet.dart | 21 +- lib/src/ast/sass/statement/supports_rule.dart | 6 +- lib/src/ast/sass/statement/use_rule.dart | 8 +- .../sass/statement/variable_declaration.dart | 12 +- lib/src/ast/sass/statement/warn_rule.dart | 4 +- lib/src/ast/sass/statement/while_rule.dart | 8 +- .../sass/supports_condition/declaration.dart | 4 +- .../supports_condition/interpolation.dart | 2 +- lib/src/ast/selector/attribute.dart | 6 +- lib/src/ast/selector/complex.dart | 12 +- lib/src/ast/selector/compound.dart | 14 +- lib/src/ast/selector/id.dart | 2 +- lib/src/ast/selector/list.dart | 20 +- lib/src/ast/selector/parent.dart | 2 +- lib/src/ast/selector/pseudo.dart | 22 +- lib/src/ast/selector/qualified_name.dart | 2 +- lib/src/ast/selector/simple.dart | 8 +- lib/src/ast/selector/type.dart | 2 +- lib/src/ast/selector/universal.dart | 4 +- lib/src/async_compile.dart | 66 ++-- lib/src/async_environment.dart | 111 +++---- lib/src/async_import_cache.dart | 36 +- lib/src/callable.dart | 8 +- lib/src/callable/async.dart | 6 +- lib/src/callable/async_built_in.dart | 9 +- lib/src/callable/built_in.dart | 27 +- lib/src/callable/plain_css.dart | 2 +- lib/src/callable/user_defined.dart | 4 +- lib/src/compile.dart | 64 ++-- lib/src/configuration.dart | 4 +- lib/src/configured_value.dart | 6 +- lib/src/environment.dart | 114 +++---- lib/src/exception.dart | 30 +- lib/src/executable/compile_stylesheet.dart | 8 +- lib/src/executable/options.dart | 74 ++--- lib/src/executable/repl.dart | 10 +- lib/src/executable/watch.dart | 13 +- lib/src/extend/empty_extender.dart | 6 +- lib/src/extend/extender.dart | 135 ++++---- lib/src/extend/extension.dart | 14 +- lib/src/extend/functions.dart | 155 ++++----- lib/src/extend/merged_extension.dart | 2 +- lib/src/functions/color.dart | 32 +- lib/src/functions/list.dart | 3 +- lib/src/functions/map.dart | 17 +- lib/src/functions/math.dart | 7 +- lib/src/functions/meta.dart | 3 +- lib/src/functions/selector.dart | 5 +- lib/src/functions/string.dart | 3 +- lib/src/import_cache.dart | 35 +- lib/src/importer.dart | 4 +- lib/src/importer/async.dart | 4 +- lib/src/importer/filesystem.dart | 4 +- lib/src/importer/no_op.dart | 4 +- lib/src/importer/node/implementation.dart | 28 +- lib/src/importer/node/interface.dart | 8 +- lib/src/importer/package.dart | 4 +- lib/src/importer/result.dart | 8 +- lib/src/importer/utils.dart | 23 +- lib/src/interpolation_buffer.dart | 8 +- lib/src/io.dart | 2 +- lib/src/io/interface.dart | 6 +- lib/src/io/node.dart | 21 +- lib/src/io/vm.dart | 2 +- lib/src/logger.dart | 6 +- lib/src/logger/stderr.dart | 4 +- lib/src/logger/tracking.dart | 2 +- lib/src/module.dart | 14 +- lib/src/module/built_in.dart | 8 +- lib/src/module/forwarded_view.dart | 10 +- lib/src/module/shadowed_view.dart | 21 +- lib/src/node.dart | 32 +- lib/src/node/chokidar.dart | 6 +- lib/src/node/fiber.dart | 6 +- lib/src/node/function.dart | 6 +- lib/src/node/importer_result.dart | 6 +- lib/src/node/render_context.dart | 2 +- lib/src/node/render_context_options.dart | 40 +-- lib/src/node/render_options.dart | 68 ++-- lib/src/node/render_result.dart | 28 +- lib/src/node/types.dart | 16 +- lib/src/node/utils.dart | 29 +- lib/src/node/value/color.dart | 4 +- lib/src/node/value/list.dart | 4 +- lib/src/node/value/map.dart | 8 +- lib/src/node/value/number.dart | 7 +- lib/src/node/value/string.dart | 4 +- lib/src/parse/at_root_query.dart | 2 +- lib/src/parse/css.dart | 6 +- lib/src/parse/keyframe_selector.dart | 2 +- lib/src/parse/media_query.dart | 6 +- lib/src/parse/parser.dart | 28 +- lib/src/parse/sass.dart | 34 +- lib/src/parse/scss.dart | 6 +- lib/src/parse/selector.dart | 10 +- lib/src/parse/stylesheet.dart | 168 +++++----- lib/src/stylesheet_graph.dart | 58 ++-- lib/src/util/character.dart | 12 +- lib/src/util/limited_map_view.dart | 8 +- lib/src/util/merged_map_view.dart | 8 +- lib/src/util/multi_dir_watcher.dart | 2 +- lib/src/util/no_source_map_buffer.dart | 10 +- lib/src/util/nullable.dart | 18 +- lib/src/util/number.dart | 6 +- lib/src/util/prefixed_map_view.dart | 8 +- lib/src/util/public_member_map_view.dart | 6 +- lib/src/util/source_map_buffer.dart | 12 +- lib/src/util/unprefixed_map_view.dart | 14 +- lib/src/utils.dart | 35 +- lib/src/value.dart | 33 +- lib/src/value/argument_list.dart | 4 +- lib/src/value/boolean.dart | 2 +- lib/src/value/color.dart | 49 +-- lib/src/value/external/color.dart | 24 +- lib/src/value/external/list.dart | 7 +- lib/src/value/external/number.dart | 33 +- lib/src/value/external/string.dart | 14 +- lib/src/value/external/value.dart | 20 +- lib/src/value/function.dart | 2 +- lib/src/value/list.dart | 11 +- lib/src/value/map.dart | 5 +- lib/src/value/null.dart | 2 +- lib/src/value/number.dart | 78 ++--- lib/src/value/number/complex.dart | 2 +- lib/src/value/number/single_unit.dart | 24 +- lib/src/value/number/unitless.dart | 16 +- lib/src/value/string.dart | 14 +- lib/src/visitor/async_evaluate.dart | 311 +++++++++--------- lib/src/visitor/evaluate.dart | 303 ++++++++--------- lib/src/visitor/interface/expression.dart | 2 +- lib/src/visitor/interface/statement.dart | 48 +-- lib/src/visitor/serialize.dart | 38 +-- pubspec.yaml | 2 +- test/cli/dart_test.dart | 2 +- test/cli/node_test.dart | 2 +- test/cli/shared.dart | 4 +- test/cli/shared/source_maps.dart | 10 +- test/dart_api/function_test.dart | 26 +- test/dart_api/importer_test.dart | 20 +- test/dart_api/logger_test.dart | 26 +- test/dart_api/test_importer.dart | 2 +- test/dart_api/value/boolean_test.dart | 4 +- test/dart_api/value/color_test.dart | 6 +- test/dart_api/value/function_test.dart | 2 +- test/dart_api/value/list_test.dart | 10 +- test/dart_api/value/map_test.dart | 4 +- test/dart_api/value/null_test.dart | 2 +- test/dart_api/value/number_test.dart | 10 +- test/dart_api/value/string_test.dart | 6 +- test/dart_api/value/utils.dart | 4 +- test/doc_comments_test.dart | 30 +- test/double_check_test.dart | 4 +- test/hybrid.dart | 6 +- test/node_api/api.dart | 4 +- test/node_api/function_test.dart | 33 +- test/node_api/importer_test.dart | 54 +-- test/node_api/intercept_stdout.dart | 6 +- test/node_api/source_map_test.dart | 38 +-- test/node_api/utils.dart | 12 +- test/node_api/value/color_test.dart | 2 +- test/node_api/value/list_test.dart | 4 +- test/node_api/value/map_test.dart | 2 +- test/node_api/value/null_test.dart | 2 +- test/node_api/value/number_test.dart | 2 +- test/node_api/value/string_test.dart | 2 +- test/node_api/value/utils.dart | 4 +- test/node_api_test.dart | 20 +- test/source_map_test.dart | 16 +- test/utils.dart | 6 +- tool/grind.dart | 10 +- tool/grind/benchmark.dart | 14 +- tool/grind/synchronize.dart | 4 +- tool/grind/utils.dart | 4 +- 240 files changed, 2016 insertions(+), 1970 deletions(-) diff --git a/bin/sass.dart b/bin/sass.dart index 9610363f5..cc6bc4c4c 100644 --- a/bin/sass.dart +++ b/bin/sass.dart @@ -24,7 +24,7 @@ Future main(List args) async { // has been printed to stderr. // // If [trace] is passed, its terse representation is printed after the error. - void printError(String error, StackTrace stackTrace) { + void printError(String error, StackTrace? stackTrace) { if (printedError) stderr.writeln(); printedError = true; stderr.writeln(error); @@ -35,7 +35,7 @@ Future main(List args) async { } } - /*late*/ ExecutableOptions options; + late ExecutableOptions options; try { options = ExecutableOptions.parse(args); term_glyph.ascii = !options.unicode; diff --git a/lib/sass.dart b/lib/sass.dart index 60a0a60d4..4c79dd622 100644 --- a/lib/sass.dart +++ b/lib/sass.dart @@ -88,13 +88,13 @@ export 'src/warn.dart' show warn; /// Throws a [SassException] if conversion fails. String compile(String path, {bool color = false, - Logger logger, - Iterable importers, - Iterable loadPaths, - PackageConfig packageConfig, - Iterable functions, - OutputStyle style, - void sourceMap(SingleMapping /*!*/ map), + Logger? logger, + Iterable? importers, + Iterable? loadPaths, + PackageConfig? packageConfig, + Iterable? functions, + OutputStyle? style, + void sourceMap(SingleMapping map)?, bool charset = true}) { logger ??= Logger.stderr(color: color); var result = c.compile(path, @@ -176,17 +176,17 @@ String compile(String path, /// /// Throws a [SassException] if conversion fails. String compileString(String source, - {Syntax syntax, + {Syntax? syntax, bool color = false, - Logger logger, - Iterable importers, - PackageConfig packageConfig, - Iterable loadPaths, - Iterable functions, - OutputStyle style, - Importer importer, - Object url, - void sourceMap(SingleMapping /*!*/ map), + Logger? logger, + Iterable? importers, + PackageConfig? packageConfig, + Iterable? loadPaths, + Iterable? functions, + OutputStyle? style, + Importer? importer, + Object? url, + void sourceMap(SingleMapping map)?, bool charset = true, @Deprecated("Use syntax instead.") bool indented = false}) { logger ??= Logger.stderr(color: color); @@ -215,13 +215,13 @@ String compileString(String source, /// slower, so [compile] should be preferred if possible. Future compileAsync(String path, {bool color = false, - Logger logger, - Iterable importers, - PackageConfig packageConfig, - Iterable loadPaths, - Iterable functions, - OutputStyle style, - void sourceMap(SingleMapping map)}) async { + Logger? logger, + Iterable? importers, + PackageConfig? packageConfig, + Iterable? loadPaths, + Iterable? functions, + OutputStyle? style, + void sourceMap(SingleMapping map)?}) async { logger ??= Logger.stderr(color: color); var result = await c.compileAsync(path, logger: logger, @@ -243,17 +243,17 @@ Future compileAsync(String path, /// synchronous [Importer]s. However, running asynchronously is also somewhat /// slower, so [compileString] should be preferred if possible. Future compileStringAsync(String source, - {Syntax syntax, + {Syntax? syntax, bool color = false, - Logger logger, - Iterable importers, - PackageConfig packageConfig, - Iterable loadPaths, - Iterable functions, - OutputStyle style, - AsyncImporter importer, - Object url, - void sourceMap(SingleMapping map), + Logger? logger, + Iterable? importers, + PackageConfig? packageConfig, + Iterable? loadPaths, + Iterable? functions, + OutputStyle? style, + AsyncImporter? importer, + Object? url, + void sourceMap(SingleMapping map)?, bool charset = true, @Deprecated("Use syntax instead.") bool indented = false}) async { logger ??= Logger.stderr(color: color); diff --git a/lib/src/ast/css/at_rule.dart b/lib/src/ast/css/at_rule.dart index 864d8751b..8aff6529e 100644 --- a/lib/src/ast/css/at_rule.dart +++ b/lib/src/ast/css/at_rule.dart @@ -12,7 +12,7 @@ abstract class CssAtRule extends CssParentNode { CssValue get name; /// The value of this rule. - CssValue get value; + CssValue? get value; /// Whether the rule has no children. /// diff --git a/lib/src/ast/css/declaration.dart b/lib/src/ast/css/declaration.dart index 4f6be41ee..1e03237c8 100644 --- a/lib/src/ast/css/declaration.dart +++ b/lib/src/ast/css/declaration.dart @@ -15,14 +15,14 @@ abstract class CssDeclaration extends CssNode { CssValue get name; /// The value of this declaration. - CssValue get value; + CssValue get value; /// The span for [value] that should be emitted to the source map. /// /// When the declaration's expression is just a variable, this is the span /// where that variable was declared whereas [value.span] is the span where /// the variable was used. Otherwise, this is identical to [value.span]. - FileSpan get valueSpanForMap; + FileSpan? get valueSpanForMap; /// Returns whether this is a CSS Custom Property declaration. bool get isCustomProperty; diff --git a/lib/src/ast/css/import.dart b/lib/src/ast/css/import.dart index 065ef076e..33db7c9b9 100644 --- a/lib/src/ast/css/import.dart +++ b/lib/src/ast/css/import.dart @@ -15,10 +15,10 @@ abstract class CssImport extends CssNode { CssValue get url; /// The supports condition attached to this import. - CssValue get supports; + CssValue? get supports; /// The media query attached to this import. - List get media; + List? get media; T accept(CssVisitor visitor) => visitor.visitCssImport(this); } diff --git a/lib/src/ast/css/media_query.dart b/lib/src/ast/css/media_query.dart index 1f2bc1962..54d5d81c2 100644 --- a/lib/src/ast/css/media_query.dart +++ b/lib/src/ast/css/media_query.dart @@ -11,12 +11,12 @@ class CssMediaQuery { /// The modifier, probably either "not" or "only". /// /// This may be `null` if no modifier is in use. - final String modifier; + final String? modifier; /// The media type, for example "screen" or "print". /// /// This may be `null`. If so, [features] will not be empty. - final String type; + final String? type; /// Feature queries, including parentheses. final List features; @@ -33,11 +33,11 @@ class CssMediaQuery { /// /// Throws a [SassFormatException] if parsing fails. static List parseList(String contents, - {Object url, Logger logger}) => + {Object? url, Logger? logger}) => MediaQueryParser(contents, url: url, logger: logger).parse(); /// Creates a media query specifies a type and, optionally, features. - CssMediaQuery(this.type, {this.modifier, Iterable features}) + CssMediaQuery(this.type, {this.modifier, Iterable? features}) : features = features == null ? const [] : List.unmodifiable(features); /// Creates a media query that only specifies features. @@ -59,8 +59,8 @@ class CssMediaQuery { CssMediaQuery.condition([...this.features, ...other.features])); } - String modifier; - String type; + String? modifier; + String? type; List features; if ((ourModifier == 'not') != (theirModifier == 'not')) { if (ourType == theirType) { diff --git a/lib/src/ast/css/modifiable/at_rule.dart b/lib/src/ast/css/modifiable/at_rule.dart index 4a4f3ce6e..ae70ff7a6 100644 --- a/lib/src/ast/css/modifiable/at_rule.dart +++ b/lib/src/ast/css/modifiable/at_rule.dart @@ -12,9 +12,9 @@ import 'node.dart'; /// A modifiable version of [CssAtRule] for use in the evaluation step. class ModifiableCssAtRule extends ModifiableCssParentNode implements CssAtRule { final CssValue name; - final CssValue value; + final CssValue? value; final bool isChildless; - final FileSpan span; + final FileSpan? span; ModifiableCssAtRule(this.name, this.span, {bool childless = false, this.value}) diff --git a/lib/src/ast/css/modifiable/comment.dart b/lib/src/ast/css/modifiable/comment.dart index 40e7838c7..80aff487d 100644 --- a/lib/src/ast/css/modifiable/comment.dart +++ b/lib/src/ast/css/modifiable/comment.dart @@ -12,7 +12,7 @@ import 'node.dart'; /// A modifiable version of [CssComment] for use in the evaluation step. class ModifiableCssComment extends ModifiableCssNode implements CssComment { final String text; - final FileSpan span; + final FileSpan? span; bool get isPreserved => text.codeUnitAt(2) == $exclamation; diff --git a/lib/src/ast/css/modifiable/declaration.dart b/lib/src/ast/css/modifiable/declaration.dart index e4bb7fc44..680594dd3 100644 --- a/lib/src/ast/css/modifiable/declaration.dart +++ b/lib/src/ast/css/modifiable/declaration.dart @@ -17,14 +17,14 @@ class ModifiableCssDeclaration extends ModifiableCssNode final CssValue name; final CssValue value; final bool parsedAsCustomProperty; - final FileSpan valueSpanForMap; - final FileSpan span; + final FileSpan? valueSpanForMap; + final FileSpan? span; bool get isCustomProperty => name.value.startsWith('--'); /// Returns a new CSS declaration with the given properties. ModifiableCssDeclaration(this.name, this.value, this.span, - {@required bool parsedAsCustomProperty, FileSpan valueSpanForMap}) + {required bool parsedAsCustomProperty, FileSpan? valueSpanForMap}) : parsedAsCustomProperty = parsedAsCustomProperty, valueSpanForMap = valueSpanForMap ?? span { if (parsedAsCustomProperty) { diff --git a/lib/src/ast/css/modifiable/import.dart b/lib/src/ast/css/modifiable/import.dart index cf8127ff8..c52da8d2d 100644 --- a/lib/src/ast/css/modifiable/import.dart +++ b/lib/src/ast/css/modifiable/import.dart @@ -18,15 +18,15 @@ class ModifiableCssImport extends ModifiableCssNode implements CssImport { final CssValue url; /// The supports condition attached to this import. - final CssValue supports; + final CssValue? supports; /// The media query attached to this import. - final List media; + final List? media; - final FileSpan span; + final FileSpan? span; ModifiableCssImport(this.url, this.span, - {this.supports, Iterable media}) + {this.supports, Iterable? media}) : media = media == null ? null : List.unmodifiable(media); T accept(ModifiableCssVisitor visitor) => visitor.visitCssImport(this); diff --git a/lib/src/ast/css/modifiable/keyframe_block.dart b/lib/src/ast/css/modifiable/keyframe_block.dart index 69233f355..089a25014 100644 --- a/lib/src/ast/css/modifiable/keyframe_block.dart +++ b/lib/src/ast/css/modifiable/keyframe_block.dart @@ -13,7 +13,7 @@ import 'node.dart'; class ModifiableCssKeyframeBlock extends ModifiableCssParentNode implements CssKeyframeBlock { final CssValue> selector; - final FileSpan span; + final FileSpan? span; ModifiableCssKeyframeBlock(this.selector, this.span); diff --git a/lib/src/ast/css/modifiable/media_rule.dart b/lib/src/ast/css/modifiable/media_rule.dart index f1dcf25e4..710ee9fd2 100644 --- a/lib/src/ast/css/modifiable/media_rule.dart +++ b/lib/src/ast/css/modifiable/media_rule.dart @@ -13,7 +13,7 @@ import 'node.dart'; class ModifiableCssMediaRule extends ModifiableCssParentNode implements CssMediaRule { final List queries; - final FileSpan span; + final FileSpan? span; ModifiableCssMediaRule(Iterable queries, this.span) : queries = List.unmodifiable(queries) { diff --git a/lib/src/ast/css/modifiable/node.dart b/lib/src/ast/css/modifiable/node.dart index f4d0e1b9c..0d5faa583 100644 --- a/lib/src/ast/css/modifiable/node.dart +++ b/lib/src/ast/css/modifiable/node.dart @@ -16,13 +16,13 @@ import '../style_rule.dart'; /// unmodifiable types are used elsewhere to enfore that constraint. abstract class ModifiableCssNode extends CssNode { /// The node that contains this, or `null` for the root [CssStylesheet] node. - ModifiableCssParentNode get parent => _parent; - ModifiableCssParentNode _parent; + ModifiableCssParentNode? get parent => _parent; + ModifiableCssParentNode? _parent; /// The index of [this] in `parent.children`. /// /// This makes [remove] more efficient. - int _indexInParent; + int? _indexInParent; var isGroupEnd = false; @@ -31,7 +31,7 @@ abstract class ModifiableCssNode extends CssNode { var parent = _parent; if (parent == null) return false; var siblings = parent.children; - for (var i = _indexInParent + 1; i < siblings.length; i++) { + for (var i = _indexInParent! + 1; i < siblings.length; i++) { var sibling = siblings[i]; if (!_isInvisible(sibling)) return true; } @@ -44,7 +44,7 @@ abstract class ModifiableCssNode extends CssNode { /// This can return a false negative for a comment node in compressed mode, /// since the AST doesn't know the output style, but that's an extremely /// narrow edge case so we don't worry about it. - bool _isInvisible(CssNode /*!*/ node) { + bool _isInvisible(CssNode node) { if (node is CssParentNode) { // An unknown at-rule is never invisible. Because we don't know the // semantics of unknown rules, we can't guarantee that (for example) @@ -69,8 +69,8 @@ abstract class ModifiableCssNode extends CssNode { throw StateError("Can't remove a node without a parent."); } - parent._children.removeAt(_indexInParent); - for (var i = _indexInParent; i < parent._children.length; i++) { + parent._children.removeAt(_indexInParent!); + for (var i = _indexInParent!; i < parent._children.length; i++) { parent._children[i]._indexInParent--; } _parent = null; diff --git a/lib/src/ast/css/modifiable/style_rule.dart b/lib/src/ast/css/modifiable/style_rule.dart index 69875813a..38c5b66e3 100644 --- a/lib/src/ast/css/modifiable/style_rule.dart +++ b/lib/src/ast/css/modifiable/style_rule.dart @@ -15,13 +15,13 @@ class ModifiableCssStyleRule extends ModifiableCssParentNode implements CssStyleRule { final ModifiableCssValue selector; final SelectorList originalSelector; - final FileSpan span; + final FileSpan? span; /// Creates a new [ModifiableCssStyleRule]. /// /// If [originalSelector] isn't passed, it defaults to [selector.value]. ModifiableCssStyleRule(ModifiableCssValue selector, this.span, - {SelectorList originalSelector}) + {SelectorList? originalSelector}) : selector = selector, originalSelector = originalSelector ?? selector.value; diff --git a/lib/src/ast/css/modifiable/stylesheet.dart b/lib/src/ast/css/modifiable/stylesheet.dart index 46610a948..87a332ace 100644 --- a/lib/src/ast/css/modifiable/stylesheet.dart +++ b/lib/src/ast/css/modifiable/stylesheet.dart @@ -11,7 +11,7 @@ import 'node.dart'; /// A modifiable version of [CssStylesheet] for use in the evaluation step. class ModifiableCssStylesheet extends ModifiableCssParentNode implements CssStylesheet { - final FileSpan span; + final FileSpan? span; ModifiableCssStylesheet(this.span); diff --git a/lib/src/ast/css/modifiable/supports_rule.dart b/lib/src/ast/css/modifiable/supports_rule.dart index 4fd8048f5..51611679c 100644 --- a/lib/src/ast/css/modifiable/supports_rule.dart +++ b/lib/src/ast/css/modifiable/supports_rule.dart @@ -12,8 +12,8 @@ import 'node.dart'; /// A modifiable version of [CssSupportsRule] for use in the evaluation step. class ModifiableCssSupportsRule extends ModifiableCssParentNode implements CssSupportsRule { - final CssValue condition; - final FileSpan span; + final CssValue condition; + final FileSpan? span; ModifiableCssSupportsRule(this.condition, this.span); diff --git a/lib/src/ast/css/modifiable/value.dart b/lib/src/ast/css/modifiable/value.dart index fa042efde..b63ca3c2b 100644 --- a/lib/src/ast/css/modifiable/value.dart +++ b/lib/src/ast/css/modifiable/value.dart @@ -9,7 +9,7 @@ import '../value.dart'; /// A modifiable version of [CssValue] for use in the evaluation step. class ModifiableCssValue implements CssValue { T value; - final FileSpan span; + final FileSpan? span; ModifiableCssValue(this.value, this.span); diff --git a/lib/src/ast/css/node.dart b/lib/src/ast/css/node.dart index 3a3eef416..0366961ca 100644 --- a/lib/src/ast/css/node.dart +++ b/lib/src/ast/css/node.dart @@ -23,7 +23,7 @@ abstract class CssNode extends AstNode { /// A [CssNode] that can have child statements. abstract class CssParentNode extends CssNode { /// The child statements of this node. - List get children; + List get children; /// Whether the rule has no children and should be emitted without curly /// braces. diff --git a/lib/src/ast/css/stylesheet.dart b/lib/src/ast/css/stylesheet.dart index c4b061b8f..8c43bc6d7 100644 --- a/lib/src/ast/css/stylesheet.dart +++ b/lib/src/ast/css/stylesheet.dart @@ -13,20 +13,20 @@ import 'node.dart'; /// /// This is the root plain CSS node. It contains top-level statements. class CssStylesheet extends CssParentNode { - final List children; - final FileSpan span; + final List children; + final FileSpan? span; bool get isGroupEnd => false; bool get isChildless => false; /// Creates an unmodifiable stylesheet containing [children]. - CssStylesheet(Iterable children, this.span) + CssStylesheet(Iterable children, this.span) // Use [UnmodifiableListView] rather than [List.unmodifiable] because // the underlying nodes are mutable anyway, so it's better to have the // whole thing consistently represent mutation of the underlying data. : children = UnmodifiableListView(children); /// Creates an empty stylesheet with the given source URL. - CssStylesheet.empty({Object url}) + CssStylesheet.empty({Object? url}) : children = const [], span = SourceFile.decoded(const [], url: url).span(0, 0); diff --git a/lib/src/ast/css/value.dart b/lib/src/ast/css/value.dart index 6775cae37..2898db9b2 100644 --- a/lib/src/ast/css/value.dart +++ b/lib/src/ast/css/value.dart @@ -10,12 +10,12 @@ import '../node.dart'; /// /// This is used to associate a span with a value that doesn't otherwise track /// its span. -class CssValue implements AstNode { +class CssValue implements AstNode { /// The value. - final T /*!*/ value; + final T value; /// The span associated with the value. - final FileSpan span; + final FileSpan? span; CssValue(this.value, this.span); diff --git a/lib/src/ast/node.dart b/lib/src/ast/node.dart index 1d9a9fd3d..1ebab4db4 100644 --- a/lib/src/ast/node.dart +++ b/lib/src/ast/node.dart @@ -10,7 +10,7 @@ abstract class AstNode { /// /// This indicates where in the source Sass or SCSS stylesheet the node was /// defined. - FileSpan get span; + FileSpan? get span; /// Returns an [AstNode] that doesn't have any data and whose span is /// generated by [callback]. diff --git a/lib/src/ast/sass/argument.dart b/lib/src/ast/sass/argument.dart index 43ade4277..e8391e3e4 100644 --- a/lib/src/ast/sass/argument.dart +++ b/lib/src/ast/sass/argument.dart @@ -14,9 +14,9 @@ class Argument implements SassNode { final String name; /// The default value of this argument, or `null` if none was declared. - final Expression defaultValue; + final Expression? defaultValue; - final FileSpan span; + final FileSpan? span; /// The variable name as written in the document, without underscores /// converted to hyphens and including the leading `$`. diff --git a/lib/src/ast/sass/argument_declaration.dart b/lib/src/ast/sass/argument_declaration.dart index 694b9f5b4..1af14a069 100644 --- a/lib/src/ast/sass/argument_declaration.dart +++ b/lib/src/ast/sass/argument_declaration.dart @@ -19,13 +19,13 @@ class ArgumentDeclaration implements SassNode { /// The name of the rest argument (as in `$args...`), or `null` if none was /// declared. - final String restArgument; + final String? restArgument; - final FileSpan span; + final FileSpan? span; /// Returns [span] expanded to include an identifier immediately before the /// declaration, if possible. - FileSpan get spanWithName { + FileSpan? get spanWithName { var span = this.span; if (span == null) return null; @@ -57,7 +57,7 @@ class ArgumentDeclaration implements SassNode { /// /// This isn't particularly efficient, and should only be used for error /// messages. - String get originalRestArgument { + String? get originalRestArgument { if (restArgument == null) return null; var span = this.span; @@ -87,12 +87,12 @@ class ArgumentDeclaration implements SassNode { /// /// Throws a [SassFormatException] if parsing fails. factory ArgumentDeclaration.parse(String contents, - {Object url, Logger logger}) => + {Object? url, Logger? logger}) => ScssParser(contents, url: url, logger: logger).parseArgumentDeclaration(); /// Throws a [SassScriptException] if [positional] and [names] aren't valid /// for this argument declaration. - void verify(int positional, Set names) { + void verify(int positional, Set names) { var namedUsed = 0; for (var i = 0; i < arguments.length; i++) { var argument = arguments[i]; @@ -108,7 +108,7 @@ class ArgumentDeclaration implements SassNode { throw MultiSpanSassScriptException( "Missing argument ${_originalArgumentName(argument.name)}.", "invocation", - {spanWithName: "declaration"}); + {spanWithName!: "declaration"}); } } @@ -122,7 +122,7 @@ class ArgumentDeclaration implements SassNode { "$positional ${pluralize('was', positional, plural: 'were')} " "passed.", "invocation", - {spanWithName: "declaration"}); + {spanWithName!: "declaration"}); } if (namedUsed < names.length) { @@ -132,14 +132,14 @@ class ArgumentDeclaration implements SassNode { "No ${pluralize('argument', unknownNames.length)} named " "${toSentence(unknownNames.map((name) => "\$$name"), 'or')}.", "invocation", - {spanWithName: "declaration"}); + {spanWithName!: "declaration"}); } } /// Returns the argument named [name] with a leading `$` and its original /// underscores (which are otherwise converted to hyphens). String _originalArgumentName(String name) { - if (name == restArgument) return originalRestArgument /*!*/; + if (name == restArgument) return originalRestArgument!; for (var argument in arguments) { if (argument.name == name) return argument.originalName; @@ -150,7 +150,7 @@ class ArgumentDeclaration implements SassNode { /// Returns whether [positional] and [names] are valid for this argument /// declaration. - bool matches(int positional, Set names) { + bool matches(int positional, Set names) { var namedUsed = 0; for (var i = 0; i < arguments.length; i++) { var argument = arguments[i]; diff --git a/lib/src/ast/sass/argument_invocation.dart b/lib/src/ast/sass/argument_invocation.dart index 2866177ec..144547c7d 100644 --- a/lib/src/ast/sass/argument_invocation.dart +++ b/lib/src/ast/sass/argument_invocation.dart @@ -16,18 +16,18 @@ class ArgumentInvocation implements SassNode { final Map named; /// The first rest argument (as in `$args...`). - final Expression rest; + final Expression? rest; /// The second rest argument, which is expected to only contain a keyword map. - final Expression keywordRest; + final Expression? keywordRest; - final FileSpan /*?*/ span; + final FileSpan? span; /// Returns whether this invocation passes no arguments. bool get isEmpty => positional.isEmpty && named.isEmpty && rest == null; - ArgumentInvocation(Iterable positional, - Map named, this.span, + ArgumentInvocation( + Iterable positional, Map named, this.span, {this.rest, this.keywordRest}) : positional = List.unmodifiable(positional), named = Map.unmodifiable(named) { diff --git a/lib/src/ast/sass/at_root_query.dart b/lib/src/ast/sass/at_root_query.dart index 4b6d8f222..6fb57b3ba 100644 --- a/lib/src/ast/sass/at_root_query.dart +++ b/lib/src/ast/sass/at_root_query.dart @@ -50,11 +50,11 @@ class AtRootQuery { /// If passed, [url] is the name of the file from which [contents] comes. /// /// Throws a [SassFormatException] if parsing fails. - factory AtRootQuery.parse(String contents, {Object url, Logger logger}) => + factory AtRootQuery.parse(String contents, {Object? url, Logger? logger}) => AtRootQueryParser(contents, url: url, logger: logger).parse(); /// Returns whether [this] excludes [node]. - bool excludes(CssParentNode /*!*/ node) { + bool excludes(CssParentNode node) { if (_all) return !include; if (node is CssStyleRule) return excludesStyleRules; if (node is CssMediaRule) return excludesName("media"); diff --git a/lib/src/ast/sass/configured_variable.dart b/lib/src/ast/sass/configured_variable.dart index 5f11ce305..302c4dde2 100644 --- a/lib/src/ast/sass/configured_variable.dart +++ b/lib/src/ast/sass/configured_variable.dart @@ -13,7 +13,7 @@ class ConfiguredVariable implements SassNode { final String name; /// The variable's value. - final Expression /*!*/ expression; + final Expression expression; /// Whether the variable can be further configured by outer modules. /// diff --git a/lib/src/ast/sass/expression.dart b/lib/src/ast/sass/expression.dart index e637ec316..3e642f41c 100644 --- a/lib/src/ast/sass/expression.dart +++ b/lib/src/ast/sass/expression.dart @@ -10,13 +10,13 @@ import 'node.dart'; /// A SassScript expression in a Sass syntax tree. abstract class Expression implements SassNode { /// Calls the appropriate visit method on [visitor]. - T /*!*/ accept(ExpressionVisitor visitor); + T accept(ExpressionVisitor visitor); /// Parses an expression from [contents]. /// /// If passed, [url] is the name of the file from which [contents] comes. /// /// Throws a [SassFormatException] if parsing fails. - factory Expression.parse(String contents, {Object url, Logger logger}) => + factory Expression.parse(String contents, {Object? url, Logger? logger}) => ScssParser(contents, url: url, logger: logger).parseExpression(); } diff --git a/lib/src/ast/sass/expression/binary_operation.dart b/lib/src/ast/sass/expression/binary_operation.dart index 3ebb587ff..282d6007e 100644 --- a/lib/src/ast/sass/expression/binary_operation.dart +++ b/lib/src/ast/sass/expression/binary_operation.dart @@ -15,26 +15,26 @@ class BinaryOperationExpression implements Expression { final BinaryOperator operator; /// The left-hand operand. - final Expression /*!*/ left; + final Expression left; /// The right-hand operand. - final Expression /*!*/ right; + final Expression right; /// Whether this is a [BinaryOperator.dividedBy] operation that may be /// interpreted as slash-separated numbers. final bool allowsSlash; - FileSpan get span { + FileSpan? get span { // Avoid creating a bunch of intermediate spans for multiple binary // expressions in a row by moving to the left- and right-most expressions. var left = this.left; while (left is BinaryOperationExpression) { - left = (left as BinaryOperationExpression).left; + left = left.left; } var right = this.right; while (right is BinaryOperationExpression) { - right = (right as BinaryOperationExpression).right; + right = right.right; } return spanForList([left, right]); } @@ -49,7 +49,7 @@ class BinaryOperationExpression implements Expression { allowsSlash = true; T accept(ExpressionVisitor visitor) => - visitor.visitBinaryOperationExpression(this); + visitor.visitBinaryOperationExpression(this)!; String toString() { var buffer = StringBuffer(); diff --git a/lib/src/ast/sass/expression/boolean.dart b/lib/src/ast/sass/expression/boolean.dart index 7a21c45ed..f822eea40 100644 --- a/lib/src/ast/sass/expression/boolean.dart +++ b/lib/src/ast/sass/expression/boolean.dart @@ -12,7 +12,7 @@ class BooleanExpression implements Expression { /// The value of this expression. final bool value; - final FileSpan span; + final FileSpan? span; BooleanExpression(this.value, this.span); diff --git a/lib/src/ast/sass/expression/color.dart b/lib/src/ast/sass/expression/color.dart index f8e0fde82..ee46fceda 100644 --- a/lib/src/ast/sass/expression/color.dart +++ b/lib/src/ast/sass/expression/color.dart @@ -11,9 +11,9 @@ import '../expression.dart'; /// A color literal. class ColorExpression implements Expression { /// The value of this color. - final SassColor /*!*/ value; + final SassColor value; - FileSpan get span => value.originalSpan; + FileSpan? get span => value.originalSpan; ColorExpression(this.value); diff --git a/lib/src/ast/sass/expression/function.dart b/lib/src/ast/sass/expression/function.dart index 682e5a76a..8afcc48dc 100644 --- a/lib/src/ast/sass/expression/function.dart +++ b/lib/src/ast/sass/expression/function.dart @@ -16,7 +16,7 @@ import '../interpolation.dart'; class FunctionExpression implements Expression, CallableInvocation { /// The namespace of the function being invoked, or `null` if it's invoked /// without a namespace. - final String namespace; + final String? namespace; /// The name of the function being invoked. /// @@ -30,7 +30,7 @@ class FunctionExpression implements Expression, CallableInvocation { /// The arguments to pass to the function. final ArgumentInvocation arguments; - final FileSpan span; + final FileSpan? span; FunctionExpression(this.name, this.arguments, this.span, {this.namespace}); diff --git a/lib/src/ast/sass/expression/if.dart b/lib/src/ast/sass/expression/if.dart index 99f1898c1..74ed82333 100644 --- a/lib/src/ast/sass/expression/if.dart +++ b/lib/src/ast/sass/expression/if.dart @@ -20,7 +20,7 @@ class IfExpression implements Expression, CallableInvocation { /// The arguments passed to `if()`. final ArgumentInvocation arguments; - final FileSpan span; + final FileSpan? span; IfExpression(this.arguments, this.span); diff --git a/lib/src/ast/sass/expression/list.dart b/lib/src/ast/sass/expression/list.dart index e2db171f7..26fa5a544 100644 --- a/lib/src/ast/sass/expression/list.dart +++ b/lib/src/ast/sass/expression/list.dart @@ -22,14 +22,14 @@ class ListExpression implements Expression { /// Whether the list has square brackets or not. final bool hasBrackets; - final FileSpan span; + final FileSpan? span; - ListExpression(Iterable contents, ListSeparator separator, - {bool brackets = false, FileSpan span}) + ListExpression(Iterable contents, ListSeparator separator, + {bool brackets = false, FileSpan? span}) : this._(List.unmodifiable(contents), separator, brackets, span); ListExpression._(List contents, this.separator, this.hasBrackets, - FileSpan span) + FileSpan? span) : contents = contents, span = span ?? spanForList(contents); diff --git a/lib/src/ast/sass/expression/map.dart b/lib/src/ast/sass/expression/map.dart index 3bb7a9db6..6dcdc06df 100644 --- a/lib/src/ast/sass/expression/map.dart +++ b/lib/src/ast/sass/expression/map.dart @@ -18,8 +18,7 @@ class MapExpression implements Expression { final FileSpan span; - MapExpression( - Iterable> pairs, this.span) + MapExpression(Iterable> pairs, this.span) : pairs = List.unmodifiable(pairs); T accept(ExpressionVisitor visitor) => visitor.visitMapExpression(this); diff --git a/lib/src/ast/sass/expression/null.dart b/lib/src/ast/sass/expression/null.dart index db2140be9..d12d96264 100644 --- a/lib/src/ast/sass/expression/null.dart +++ b/lib/src/ast/sass/expression/null.dart @@ -9,7 +9,7 @@ import '../expression.dart'; /// A null literal. class NullExpression implements Expression { - final FileSpan span; + final FileSpan? span; NullExpression(this.span); diff --git a/lib/src/ast/sass/expression/number.dart b/lib/src/ast/sass/expression/number.dart index 909655b8d..ccee50137 100644 --- a/lib/src/ast/sass/expression/number.dart +++ b/lib/src/ast/sass/expression/number.dart @@ -13,7 +13,7 @@ class NumberExpression implements Expression { final num value; /// The number's unit, or `null`. - final String unit; + final String? unit; final FileSpan span; diff --git a/lib/src/ast/sass/expression/parenthesized.dart b/lib/src/ast/sass/expression/parenthesized.dart index 451e87ae2..72a91708d 100644 --- a/lib/src/ast/sass/expression/parenthesized.dart +++ b/lib/src/ast/sass/expression/parenthesized.dart @@ -10,7 +10,7 @@ import '../expression.dart'; /// An expression wrapped in parentheses. class ParenthesizedExpression implements Expression { /// The internal expression. - final Expression /*!*/ expression; + final Expression expression; final FileSpan span; diff --git a/lib/src/ast/sass/expression/string.dart b/lib/src/ast/sass/expression/string.dart index b401c9ad1..6f38d198f 100644 --- a/lib/src/ast/sass/expression/string.dart +++ b/lib/src/ast/sass/expression/string.dart @@ -22,18 +22,18 @@ class StringExpression implements Expression { /// Whether [this] has quotes. final bool hasQuotes; - FileSpan get span => text.span; + FileSpan? get span => text.span; /// Returns Sass source for a quoted string that, when evaluated, will have /// [text] as its contents. - static String /*!*/ quoteText(String text) => + static String quoteText(String text) => StringExpression.plain(text, null, quotes: true) .asInterpolation(static: true) - .asPlain; + .asPlain!; StringExpression(this.text, {bool quotes = false}) : hasQuotes = quotes; - StringExpression.plain(String text, FileSpan span, {bool quotes = false}) + StringExpression.plain(String text, FileSpan? span, {bool quotes = false}) : text = Interpolation([text], span), hasQuotes = quotes; @@ -48,7 +48,7 @@ class StringExpression implements Expression { /// If [static] is true, this escapes any `#{` sequences in the string. If /// [quote] is passed, it uses that character as the quote mark; otherwise, it /// determines the best quote to add by looking at the string. - Interpolation asInterpolation({bool static = false, int quote}) { + Interpolation asInterpolation({bool static = false, int? quote}) { if (!hasQuotes) return text; quote ??= _bestQuote(); diff --git a/lib/src/ast/sass/expression/unary_operation.dart b/lib/src/ast/sass/expression/unary_operation.dart index 088c9333a..f9070e54c 100644 --- a/lib/src/ast/sass/expression/unary_operation.dart +++ b/lib/src/ast/sass/expression/unary_operation.dart @@ -11,12 +11,12 @@ import '../expression.dart'; /// A unary operator, as in `+$var` or `not fn()`. class UnaryOperationExpression implements Expression { /// The operator being invoked. - final UnaryOperator /*!*/ operator; + final UnaryOperator operator; /// The operand. - final Expression /*!*/ operand; + final Expression operand; - final FileSpan span; + final FileSpan? span; UnaryOperationExpression(this.operator, this.operand, this.span); diff --git a/lib/src/ast/sass/expression/value.dart b/lib/src/ast/sass/expression/value.dart index c4cebc3ab..8aac8454b 100644 --- a/lib/src/ast/sass/expression/value.dart +++ b/lib/src/ast/sass/expression/value.dart @@ -14,9 +14,9 @@ import '../expression.dart'; /// constructed dynamically, as for the `call()` function. class ValueExpression implements Expression { /// The embedded value. - final Value /*!*/ value; + final Value value; - final FileSpan span; + final FileSpan? span; ValueExpression(this.value, [this.span]); diff --git a/lib/src/ast/sass/expression/variable.dart b/lib/src/ast/sass/expression/variable.dart index ab01f9440..7cb5357e9 100644 --- a/lib/src/ast/sass/expression/variable.dart +++ b/lib/src/ast/sass/expression/variable.dart @@ -11,7 +11,7 @@ import '../expression.dart'; class VariableExpression implements Expression { /// The namespace of the variable being referenced, or `null` if it's /// referenced without a namespace. - final String namespace; + final String? namespace; /// The name of this variable, with underscores converted to hyphens. final String name; diff --git a/lib/src/ast/sass/import/static.dart b/lib/src/ast/sass/import/static.dart index c3bf69de3..a9bc57aa0 100644 --- a/lib/src/ast/sass/import/static.dart +++ b/lib/src/ast/sass/import/static.dart @@ -18,11 +18,11 @@ class StaticImport implements Import { /// The supports condition attached to this import, or `null` if no condition /// is attached. - final SupportsCondition supports; + final SupportsCondition? supports; /// The media query attached to this import, or `null` if no condition is /// attached. - final Interpolation media; + final Interpolation? media; final FileSpan span; diff --git a/lib/src/ast/sass/interpolation.dart b/lib/src/ast/sass/interpolation.dart index 2a7462645..83f568886 100644 --- a/lib/src/ast/sass/interpolation.dart +++ b/lib/src/ast/sass/interpolation.dart @@ -15,12 +15,12 @@ class Interpolation implements SassNode { /// [String]s. final List contents; - final FileSpan /*?*/ span; + final FileSpan? span; /// If this contains no interpolated expressions, returns its text contents. /// /// Otherwise, returns `null`. - String get asPlain { + String? get asPlain { if (contents.isEmpty) return ''; if (contents.length > 1) return null; var first = contents.first; @@ -33,8 +33,7 @@ class Interpolation implements SassNode { return first is String ? first : ''; } - Interpolation( - Iterable contents, this.span) + Interpolation(Iterable contents, this.span) : contents = List.unmodifiable(contents) { for (var i = 0; i < this.contents.length; i++) { if (this.contents[i] is! String && this.contents[i] is! Expression) { diff --git a/lib/src/ast/sass/statement.dart b/lib/src/ast/sass/statement.dart index 934b7a946..cfdffd320 100644 --- a/lib/src/ast/sass/statement.dart +++ b/lib/src/ast/sass/statement.dart @@ -8,5 +8,5 @@ import 'node.dart'; /// A statement in a Sass syntax tree. abstract class Statement implements SassNode { /// Calls the appropriate visit method on [visitor]. - T /*!*/ accept(StatementVisitor visitor); + T accept(StatementVisitor visitor); } diff --git a/lib/src/ast/sass/statement/at_root_rule.dart b/lib/src/ast/sass/statement/at_root_rule.dart index dba1309e9..b3216f23c 100644 --- a/lib/src/ast/sass/statement/at_root_rule.dart +++ b/lib/src/ast/sass/statement/at_root_rule.dart @@ -12,17 +12,17 @@ import 'parent.dart'; /// An `@at-root` rule. /// /// This moves it contents "up" the tree through parent nodes. -class AtRootRule extends ParentStatement /*!*/ > { +class AtRootRule extends ParentStatement> { /// The query specifying which statements this should move its contents /// through. - final Interpolation query; + final Interpolation? query; final FileSpan span; - AtRootRule(Iterable children, this.span, {this.query}) + AtRootRule(Iterable children, this.span, {this.query}) : super(List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitAtRootRule(this); + T accept(StatementVisitor visitor) => visitor.visitAtRootRule(this)!; String toString() { var buffer = StringBuffer("@at-root "); diff --git a/lib/src/ast/sass/statement/at_rule.dart b/lib/src/ast/sass/statement/at_rule.dart index 899762fa7..5e6bce3ba 100644 --- a/lib/src/ast/sass/statement/at_rule.dart +++ b/lib/src/ast/sass/statement/at_rule.dart @@ -15,15 +15,14 @@ class AtRule extends ParentStatement { final Interpolation name; /// The value of this rule. - final Interpolation value; + final Interpolation? value; final FileSpan span; - AtRule(this.name, this.span, - {this.value, Iterable children}) + AtRule(this.name, this.span, {this.value, Iterable? children}) : super(children == null ? null : List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitAtRule(this); + T accept(StatementVisitor visitor) => visitor.visitAtRule(this)!; String toString() { var buffer = StringBuffer("@$name"); diff --git a/lib/src/ast/sass/statement/callable_declaration.dart b/lib/src/ast/sass/statement/callable_declaration.dart index be7afd648..4c67b3670 100644 --- a/lib/src/ast/sass/statement/callable_declaration.dart +++ b/lib/src/ast/sass/statement/callable_declaration.dart @@ -11,22 +11,21 @@ import 'silent_comment.dart'; /// An abstract class for callables (functions or mixins) that are declared in /// user code. -abstract class CallableDeclaration - extends ParentStatement /*!*/ > { +abstract class CallableDeclaration extends ParentStatement> { /// The name of this callable, with underscores converted to hyphens. final String name; /// The comment immediately preceding this declaration. - final SilentComment comment; + final SilentComment? comment; /// The declared arguments this callable accepts. - final ArgumentDeclaration /*!*/ arguments; + final ArgumentDeclaration arguments; final FileSpan span; CallableDeclaration( - this.name, this.arguments, Iterable children, this.span, - {SilentComment comment}) + this.name, this.arguments, Iterable children, this.span, + {SilentComment? comment}) : comment = comment, super(List.unmodifiable(children)); } diff --git a/lib/src/ast/sass/statement/content_block.dart b/lib/src/ast/sass/statement/content_block.dart index 2d97e1917..0b1c85e08 100644 --- a/lib/src/ast/sass/statement/content_block.dart +++ b/lib/src/ast/sass/statement/content_block.dart @@ -11,8 +11,8 @@ import 'callable_declaration.dart'; /// An anonymous block of code that's invoked for a [ContentRule]. class ContentBlock extends CallableDeclaration { - ContentBlock(ArgumentDeclaration arguments, - Iterable children, FileSpan span) + ContentBlock(ArgumentDeclaration arguments, Iterable children, + FileSpan span) : super("@content", arguments, children, span); T accept(StatementVisitor visitor) => visitor.visitContentBlock(this); diff --git a/lib/src/ast/sass/statement/content_rule.dart b/lib/src/ast/sass/statement/content_rule.dart index ab9bbd738..d9348f83c 100644 --- a/lib/src/ast/sass/statement/content_rule.dart +++ b/lib/src/ast/sass/statement/content_rule.dart @@ -19,7 +19,7 @@ class ContentRule implements Statement { ContentRule(this.arguments, this.span); - T accept(StatementVisitor visitor) => visitor.visitContentRule(this); + T accept(StatementVisitor visitor) => visitor.visitContentRule(this)!; String toString() => arguments.isEmpty ? "@content;" : "@content($arguments);"; diff --git a/lib/src/ast/sass/statement/debug_rule.dart b/lib/src/ast/sass/statement/debug_rule.dart index e829ec2f5..75f24f8b4 100644 --- a/lib/src/ast/sass/statement/debug_rule.dart +++ b/lib/src/ast/sass/statement/debug_rule.dart @@ -13,13 +13,13 @@ import '../statement.dart'; /// This prints a Sass value for debugging purposes. class DebugRule implements Statement { /// The expression to print. - final Expression /*!*/ expression; + final Expression expression; final FileSpan span; DebugRule(this.expression, this.span); - T accept(StatementVisitor visitor) => visitor.visitDebugRule(this); + T accept(StatementVisitor visitor) => visitor.visitDebugRule(this)!; String toString() => "@debug $expression;"; } diff --git a/lib/src/ast/sass/statement/declaration.dart b/lib/src/ast/sass/statement/declaration.dart index 172218b5b..04398536c 100644 --- a/lib/src/ast/sass/statement/declaration.dart +++ b/lib/src/ast/sass/statement/declaration.dart @@ -20,7 +20,7 @@ class Declaration extends ParentStatement { /// /// If [children] is `null`, this is never `null`. Otherwise, it may or may /// not be `null`. - final Expression value; + final Expression? value; final FileSpan span; @@ -33,7 +33,7 @@ class Declaration extends ParentStatement { /// If this is `true`, then `value` will be a [StringExpression]. bool get isCustomProperty => name.initialPlain.startsWith('--'); - Declaration(this.name, Expression /*!*/ value, this.span) + Declaration(this.name, Expression value, this.span) : value = value, super(null) { if (isCustomProperty && value is! StringExpression) { @@ -46,7 +46,7 @@ class Declaration extends ParentStatement { /// Creates a declaration with children. /// /// For these declaraions, a value is optional. - Declaration.nested(this.name, Iterable children, this.span, + Declaration.nested(this.name, Iterable children, this.span, {this.value}) : super(List.unmodifiable(children)) { if (isCustomProperty && value is! StringExpression) { @@ -55,5 +55,5 @@ class Declaration extends ParentStatement { } } - T accept(StatementVisitor visitor) => visitor.visitDeclaration(this); + T accept(StatementVisitor visitor) => visitor.visitDeclaration(this)!; } diff --git a/lib/src/ast/sass/statement/each_rule.dart b/lib/src/ast/sass/statement/each_rule.dart index c80bf322b..16d34a1ba 100644 --- a/lib/src/ast/sass/statement/each_rule.dart +++ b/lib/src/ast/sass/statement/each_rule.dart @@ -12,21 +12,21 @@ import 'parent.dart'; /// An `@each` rule. /// /// This iterates over values in a list or map. -class EachRule extends ParentStatement /*!*/ > { +class EachRule extends ParentStatement> { /// The variables assigned for each iteration. final List variables; /// The expression whose value this iterates through. - final Expression /*!*/ list; + final Expression list; final FileSpan span; - EachRule(Iterable variables, this.list, - Iterable children, this.span) + EachRule(Iterable variables, this.list, Iterable children, + this.span) : variables = List.unmodifiable(variables), super(List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitEachRule(this); + T accept(StatementVisitor visitor) => visitor.visitEachRule(this)!; String toString() => "@each ${variables.map((variable) => '\$' + variable).join(', ')} in " diff --git a/lib/src/ast/sass/statement/error_rule.dart b/lib/src/ast/sass/statement/error_rule.dart index 29633991b..1f51f2226 100644 --- a/lib/src/ast/sass/statement/error_rule.dart +++ b/lib/src/ast/sass/statement/error_rule.dart @@ -13,7 +13,7 @@ import '../statement.dart'; /// This emits an error and stops execution. class ErrorRule implements Statement { /// The expression to evaluate for the error message. - final Expression /*!*/ expression; + final Expression expression; final FileSpan span; diff --git a/lib/src/ast/sass/statement/extend_rule.dart b/lib/src/ast/sass/statement/extend_rule.dart index 75cc5c1f2..4f035f2ce 100644 --- a/lib/src/ast/sass/statement/extend_rule.dart +++ b/lib/src/ast/sass/statement/extend_rule.dart @@ -26,7 +26,7 @@ class ExtendRule implements Statement { ExtendRule(this.selector, this.span, {bool optional = false}) : isOptional = optional; - T accept(StatementVisitor visitor) => visitor.visitExtendRule(this); + T accept(StatementVisitor visitor) => visitor.visitExtendRule(this)!; String toString() => "@extend $selector"; } diff --git a/lib/src/ast/sass/statement/for_rule.dart b/lib/src/ast/sass/statement/for_rule.dart index 89378646f..0ae61b4ae 100644 --- a/lib/src/ast/sass/statement/for_rule.dart +++ b/lib/src/ast/sass/statement/for_rule.dart @@ -12,28 +12,28 @@ import 'parent.dart'; /// A `@for` rule. /// /// This iterates a set number of times. -class ForRule extends ParentStatement /*!*/ > { +class ForRule extends ParentStatement> { /// The name of the variable that will contain the index value. final String variable; /// The expression for the start index. - final Expression /*!*/ from; + final Expression from; /// The expression for the end index. - final Expression /*!*/ to; + final Expression to; /// Whether [to] is exclusive. final bool isExclusive; final FileSpan span; - ForRule(this.variable, this.from, this.to, - Iterable children, this.span, - {bool /*!*/ exclusive = true}) + ForRule(this.variable, this.from, this.to, Iterable children, + this.span, + {bool exclusive = true}) : isExclusive = exclusive, super(List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitForRule(this); + T accept(StatementVisitor visitor) => visitor.visitForRule(this)!; String toString() => "@for \$$variable from $from ${isExclusive ? 'to' : 'through'} $to " diff --git a/lib/src/ast/sass/statement/forward_rule.dart b/lib/src/ast/sass/statement/forward_rule.dart index 80a206371..04c44eaa1 100644 --- a/lib/src/ast/sass/statement/forward_rule.dart +++ b/lib/src/ast/sass/statement/forward_rule.dart @@ -26,7 +26,7 @@ class ForwardRule implements Statement { /// If this is non-`null`, [hiddenMixinsAndFunctions] and [hiddenVariables] /// are guaranteed to both be `null` and [shownVariables] is guaranteed to be /// non-`null`. - final Set shownMixinsAndFunctions; + final Set? shownMixinsAndFunctions; /// The set of variable names (without `$`) that may be accessed from the /// forwarded module. @@ -37,7 +37,7 @@ class ForwardRule implements Statement { /// If this is non-`null`, [hiddenMixinsAndFunctions] and [hiddenVariables] /// are guaranteed to both be `null` and [shownMixinsAndFunctions] is /// guaranteed to be non-`null`. - final Set shownVariables; + final Set? shownVariables; /// The set of mixin and function names that may not be accessed from the /// forwarded module. @@ -48,7 +48,7 @@ class ForwardRule implements Statement { /// If this is non-`null`, [shownMixinsAndFunctions] and [shownVariables] are /// guaranteed to both be `null` and [hiddenVariables] is guaranteed to be /// non-`null`. - final Set hiddenMixinsAndFunctions; + final Set? hiddenMixinsAndFunctions; /// The set of variable names (without `$`) that may be accessed from the /// forwarded module. @@ -59,11 +59,11 @@ class ForwardRule implements Statement { /// If this is non-`null`, [shownMixinsAndFunctions] and [shownVariables] are /// guaranteed to both be `null` and [hiddenMixinsAndFunctions] is guaranteed /// to be non-`null`. - final Set hiddenVariables; + final Set? hiddenVariables; /// The prefix to add to the beginning of the names of members of the used /// module, or `null` if member names are used as-is. - final String prefix; + final String? prefix; /// A list of variable assignments used to configure the loaded modules. final List configuration; @@ -72,7 +72,7 @@ class ForwardRule implements Statement { /// Creates a `@forward` rule that allows all members to be accessed. ForwardRule(this.url, this.span, - {this.prefix, Iterable configuration}) + {this.prefix, Iterable? configuration}) : shownMixinsAndFunctions = null, shownVariables = null, hiddenMixinsAndFunctions = null, @@ -84,7 +84,7 @@ class ForwardRule implements Statement { /// [shownMixinsAndFunctions] and [shownVariables] to be accessed. ForwardRule.show(this.url, Iterable shownMixinsAndFunctions, Iterable shownVariables, this.span, - {this.prefix, Iterable configuration}) + {this.prefix, Iterable? configuration}) : shownMixinsAndFunctions = UnmodifiableSetView(Set.of(shownMixinsAndFunctions)), shownVariables = UnmodifiableSetView(Set.of(shownVariables)), @@ -97,7 +97,7 @@ class ForwardRule implements Statement { /// [hiddenMixinsAndFunctions] and [hiddenVariables] to be accessed. ForwardRule.hide(this.url, Iterable hiddenMixinsAndFunctions, Iterable hiddenVariables, this.span, - {this.prefix, Iterable configuration}) + {this.prefix, Iterable? configuration}) : shownMixinsAndFunctions = null, shownVariables = null, hiddenMixinsAndFunctions = @@ -106,7 +106,7 @@ class ForwardRule implements Statement { configuration = configuration == null ? const [] : List.unmodifiable(configuration); - T accept(StatementVisitor visitor) => visitor.visitForwardRule(this); + T accept(StatementVisitor visitor) => visitor.visitForwardRule(this)!; String toString() { var buffer = @@ -117,12 +117,12 @@ class ForwardRule implements Statement { if (shownMixinsAndFunctions != null) { buffer ..write(" show ") - ..write(_memberList(shownMixinsAndFunctions, shownVariables /*!*/)); + ..write(_memberList(shownMixinsAndFunctions, shownVariables!)); } else if (hiddenMixinsAndFunctions != null && hiddenMixinsAndFunctions.isNotEmpty) { buffer ..write(" hide ") - ..write(_memberList(hiddenMixinsAndFunctions, hiddenVariables /*!*/)); + ..write(_memberList(hiddenMixinsAndFunctions, hiddenVariables!)); } var prefix = this.prefix; diff --git a/lib/src/ast/sass/statement/function_rule.dart b/lib/src/ast/sass/statement/function_rule.dart index c69809b36..1fd7f2b53 100644 --- a/lib/src/ast/sass/statement/function_rule.dart +++ b/lib/src/ast/sass/statement/function_rule.dart @@ -15,11 +15,11 @@ import 'silent_comment.dart'; /// This declares a function that's invoked using normal CSS function syntax. class FunctionRule extends CallableDeclaration { FunctionRule(String name, ArgumentDeclaration arguments, - Iterable children, FileSpan span, - {SilentComment comment}) + Iterable children, FileSpan span, + {SilentComment? comment}) : super(name, arguments, children, span, comment: comment); - T accept(StatementVisitor visitor) => visitor.visitFunctionRule(this); + T accept(StatementVisitor visitor) => visitor.visitFunctionRule(this)!; String toString() => "@function $name($arguments) {${children.join(' ')}}"; } diff --git a/lib/src/ast/sass/statement/if_rule.dart b/lib/src/ast/sass/statement/if_rule.dart index 8622e73f2..c8570ebaa 100644 --- a/lib/src/ast/sass/statement/if_rule.dart +++ b/lib/src/ast/sass/statement/if_rule.dart @@ -27,14 +27,14 @@ class IfRule implements Statement { /// The final, unconditional `@else` clause. /// /// This is `null` if there is no unconditional `@else`. - final ElseClause lastClause; + final ElseClause? lastClause; final FileSpan span; IfRule(Iterable clauses, this.span, {this.lastClause}) : clauses = List.unmodifiable(clauses); - T accept(StatementVisitor visitor) => visitor.visitIfRule(this); + T accept(StatementVisitor visitor) => visitor.visitIfRule(this)!; String toString() { var first = true; @@ -57,7 +57,7 @@ abstract class IfRuleClause { /// Whether any of [children] is a variable, function, or mixin declaration. final bool hasDeclarations; - IfRuleClause(Iterable children) + IfRuleClause(Iterable children) : this._(List.unmodifiable(children)); IfRuleClause._(this.children) @@ -72,17 +72,16 @@ abstract class IfRuleClause { /// An `@if` or `@else if` clause in an `@if` rule. class IfClause extends IfRuleClause { /// The expression to evaluate to determine whether to run this rule. - final Expression /*!*/ expression; + final Expression expression; - IfClause(this.expression, Iterable children) - : super(children); + IfClause(this.expression, Iterable children) : super(children); String toString() => "@if $expression {${children.join(' ')}}"; } /// An `@else` clause in an `@if` rule. class ElseClause extends IfRuleClause { - ElseClause(Iterable children) : super(children); + ElseClause(Iterable children) : super(children); String toString() => "@else {${children.join(' ')}}"; } diff --git a/lib/src/ast/sass/statement/import_rule.dart b/lib/src/ast/sass/statement/import_rule.dart index fdf1dbaa9..b51b4f54c 100644 --- a/lib/src/ast/sass/statement/import_rule.dart +++ b/lib/src/ast/sass/statement/import_rule.dart @@ -18,7 +18,7 @@ class ImportRule implements Statement { ImportRule(Iterable imports, this.span) : imports = List.unmodifiable(imports); - T accept(StatementVisitor visitor) => visitor.visitImportRule(this); + T accept(StatementVisitor visitor) => visitor.visitImportRule(this)!; String toString() => "@import ${imports.join(', ')};"; } diff --git a/lib/src/ast/sass/statement/include_rule.dart b/lib/src/ast/sass/statement/include_rule.dart index b7c6840b9..a0b8fde7b 100644 --- a/lib/src/ast/sass/statement/include_rule.dart +++ b/lib/src/ast/sass/statement/include_rule.dart @@ -15,7 +15,7 @@ import 'content_block.dart'; class IncludeRule implements Statement, CallableInvocation { /// The namespace of the mixin being invoked, or `null` if it's invoked /// without a namespace. - final String namespace; + final String? namespace; /// The name of the mixin being invoked, with underscores converted to /// hyphens. @@ -26,19 +26,19 @@ class IncludeRule implements Statement, CallableInvocation { /// The block that will be invoked for [ContentRule]s in the mixin being /// invoked, or `null` if this doesn't pass a content block. - final ContentBlock content; + final ContentBlock? content; final FileSpan span; /// Returns this include's span, without its content block (if it has one). FileSpan get spanWithoutContent => content == null ? span - : span.file.span(span.start.offset, arguments.span.end.offset).trim(); + : span.file.span(span.start.offset, arguments.span!.end.offset).trim(); IncludeRule(this.name, this.arguments, this.span, {this.namespace, this.content}); - T accept(StatementVisitor visitor) => visitor.visitIncludeRule(this); + T accept(StatementVisitor visitor) => visitor.visitIncludeRule(this)!; String toString() { var buffer = StringBuffer("@include "); diff --git a/lib/src/ast/sass/statement/loud_comment.dart b/lib/src/ast/sass/statement/loud_comment.dart index 41e824645..4ee009857 100644 --- a/lib/src/ast/sass/statement/loud_comment.dart +++ b/lib/src/ast/sass/statement/loud_comment.dart @@ -13,11 +13,11 @@ class LoudComment implements Statement { /// The interpolated text of this comment, including comment characters. final Interpolation text; - FileSpan get span => text.span; + FileSpan? get span => text.span; LoudComment(this.text); - T accept(StatementVisitor visitor) => visitor.visitLoudComment(this); + T accept(StatementVisitor visitor) => visitor.visitLoudComment(this)!; String toString() => text.toString(); } diff --git a/lib/src/ast/sass/statement/media_rule.dart b/lib/src/ast/sass/statement/media_rule.dart index 52a8c1252..70211ff83 100644 --- a/lib/src/ast/sass/statement/media_rule.dart +++ b/lib/src/ast/sass/statement/media_rule.dart @@ -10,7 +10,7 @@ import '../statement.dart'; import 'parent.dart'; /// A `@media` rule. -class MediaRule extends ParentStatement /*!*/ > { +class MediaRule extends ParentStatement> { /// The query that determines on which platforms the styles will be in effect. /// /// This is only parsed after the interpolation has been resolved. @@ -18,10 +18,10 @@ class MediaRule extends ParentStatement /*!*/ > { final FileSpan span; - MediaRule(this.query, Iterable children, this.span) + MediaRule(this.query, Iterable children, this.span) : super(List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitMediaRule(this); + T accept(StatementVisitor visitor) => visitor.visitMediaRule(this)!; String toString() => "@media $query {${children.join(" ")}}"; } diff --git a/lib/src/ast/sass/statement/mixin_rule.dart b/lib/src/ast/sass/statement/mixin_rule.dart index 1d86ce6df..cb6fa1e8f 100644 --- a/lib/src/ast/sass/statement/mixin_rule.dart +++ b/lib/src/ast/sass/statement/mixin_rule.dart @@ -15,7 +15,7 @@ import 'silent_comment.dart'; /// This declares a mixin that's invoked using `@include`. class MixinRule extends CallableDeclaration { /// Whether the mixin contains a `@content` rule. - final bool /*!*/ hasContent; + final bool hasContent; /// Creates a [MixinRule]. /// @@ -23,11 +23,11 @@ class MixinRule extends CallableDeclaration { /// recursively contains a `@content` rule. Otherwise, invoking this mixin /// won't work correctly. MixinRule(String name, ArgumentDeclaration arguments, - Iterable children, FileSpan span, - {this.hasContent = false, SilentComment comment}) + Iterable children, FileSpan span, + {this.hasContent = false, SilentComment? comment}) : super(name, arguments, children, span, comment: comment); - T accept(StatementVisitor visitor) => visitor.visitMixinRule(this); + T accept(StatementVisitor visitor) => visitor.visitMixinRule(this)!; String toString() { var buffer = StringBuffer("@mixin $name"); diff --git a/lib/src/ast/sass/statement/parent.dart b/lib/src/ast/sass/statement/parent.dart index 94b0cd3f5..4fca22fec 100644 --- a/lib/src/ast/sass/statement/parent.dart +++ b/lib/src/ast/sass/statement/parent.dart @@ -15,7 +15,7 @@ import 'variable_declaration.dart'; /// /// This has a generic parameter so that its subclasses can choose whether or /// not their children lists are nullable. -abstract class ParentStatement /*?*/ > +abstract class ParentStatement?> implements Statement { /// The child statements of this statement. final T children; diff --git a/lib/src/ast/sass/statement/return_rule.dart b/lib/src/ast/sass/statement/return_rule.dart index 3a8403249..f2c29c96c 100644 --- a/lib/src/ast/sass/statement/return_rule.dart +++ b/lib/src/ast/sass/statement/return_rule.dart @@ -13,7 +13,7 @@ import '../statement.dart'; /// This exits from the current function body with a return value. class ReturnRule implements Statement { /// The value to return from this function. - final Expression /*!*/ expression; + final Expression expression; final FileSpan span; diff --git a/lib/src/ast/sass/statement/silent_comment.dart b/lib/src/ast/sass/statement/silent_comment.dart index 4a001619c..cf1034906 100644 --- a/lib/src/ast/sass/statement/silent_comment.dart +++ b/lib/src/ast/sass/statement/silent_comment.dart @@ -18,7 +18,7 @@ class SilentComment implements Statement { /// /// The leading slashes and space on each line is removed. Returns `null` when /// there is no documentation comment. - String get docComment { + String? get docComment { var buffer = StringBuffer(); for (var line in text.split('\n')) { var scanner = StringScanner(line.trim()); @@ -35,7 +35,7 @@ class SilentComment implements Statement { SilentComment(this.text, this.span); - T accept(StatementVisitor visitor) => visitor.visitSilentComment(this); + T accept(StatementVisitor visitor) => visitor.visitSilentComment(this)!; String toString() => text; } diff --git a/lib/src/ast/sass/statement/style_rule.dart b/lib/src/ast/sass/statement/style_rule.dart index 022545250..8e55ce62a 100644 --- a/lib/src/ast/sass/statement/style_rule.dart +++ b/lib/src/ast/sass/statement/style_rule.dart @@ -12,7 +12,7 @@ import 'parent.dart'; /// A style rule. /// /// This applies style declarations to elements that match a given selector. -class StyleRule extends ParentStatement /*!*/ > { +class StyleRule extends ParentStatement> { /// The selector to which the declaration will be applied. /// /// This is only parsed after the interpolation has been resolved. @@ -20,10 +20,10 @@ class StyleRule extends ParentStatement /*!*/ > { final FileSpan span; - StyleRule(this.selector, Iterable children, this.span) + StyleRule(this.selector, Iterable children, this.span) : super(List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitStyleRule(this); + T accept(StatementVisitor visitor) => visitor.visitStyleRule(this)!; String toString() => "$selector {${children.join(" ")}}"; } diff --git a/lib/src/ast/sass/statement/stylesheet.dart b/lib/src/ast/sass/statement/stylesheet.dart index 6543b69b1..22a8c7c80 100644 --- a/lib/src/ast/sass/statement/stylesheet.dart +++ b/lib/src/ast/sass/statement/stylesheet.dart @@ -23,8 +23,8 @@ import 'variable_declaration.dart'; /// A Sass stylesheet. /// /// This is the root Sass node. It contains top-level statements. -class Stylesheet extends ParentStatement /*!*/ > { - final FileSpan /*?*/ span; +class Stylesheet extends ParentStatement> { + final FileSpan? span; /// Whether this was parsed from a plain CSS stylesheet. final bool plainCss; @@ -37,8 +37,7 @@ class Stylesheet extends ParentStatement /*!*/ > { List get forwards => UnmodifiableListView(_forwards); final _forwards = []; - Stylesheet(Iterable children, this.span, - {this.plainCss = false}) + Stylesheet(Iterable children, this.span, {this.plainCss = false}) : super(List.unmodifiable(children)) { for (var child in this.children) { if (child is UseRule) { @@ -58,8 +57,8 @@ class Stylesheet extends ParentStatement /*!*/ > { /// If passed, [url] is the name of the file from which [contents] comes. /// /// Throws a [SassFormatException] if parsing fails. - factory Stylesheet.parse(String /*!*/ contents, Syntax syntax, - {Object url, Logger logger}) { + factory Stylesheet.parse(String contents, Syntax syntax, + {Object? url, Logger? logger}) { switch (syntax) { case Syntax.sass: return Stylesheet.parseSass(contents, url: url, logger: logger); @@ -77,7 +76,8 @@ class Stylesheet extends ParentStatement /*!*/ > { /// If passed, [url] is the name of the file from which [contents] comes. /// /// Throws a [SassFormatException] if parsing fails. - factory Stylesheet.parseSass(String contents, {Object url, Logger logger}) => + factory Stylesheet.parseSass(String contents, + {Object? url, Logger? logger}) => SassParser(contents, url: url, logger: logger).parse(); /// Parses an SCSS stylesheet from [contents]. @@ -85,7 +85,8 @@ class Stylesheet extends ParentStatement /*!*/ > { /// If passed, [url] is the name of the file from which [contents] comes. /// /// Throws a [SassFormatException] if parsing fails. - factory Stylesheet.parseScss(String contents, {Object url, Logger logger}) => + factory Stylesheet.parseScss(String contents, + {Object? url, Logger? logger}) => ScssParser(contents, url: url, logger: logger).parse(); /// Parses a plain CSS stylesheet from [contents]. @@ -93,10 +94,10 @@ class Stylesheet extends ParentStatement /*!*/ > { /// If passed, [url] is the name of the file from which [contents] comes. /// /// Throws a [SassFormatException] if parsing fails. - factory Stylesheet.parseCss(String contents, {Object url, Logger logger}) => + factory Stylesheet.parseCss(String contents, {Object? url, Logger? logger}) => CssParser(contents, url: url, logger: logger).parse(); - T accept(StatementVisitor visitor) => visitor.visitStylesheet(this); + T accept(StatementVisitor visitor) => visitor.visitStylesheet(this)!; String toString() => children.join(" "); } diff --git a/lib/src/ast/sass/statement/supports_rule.dart b/lib/src/ast/sass/statement/supports_rule.dart index 8f70ead8c..cabc4cf59 100644 --- a/lib/src/ast/sass/statement/supports_rule.dart +++ b/lib/src/ast/sass/statement/supports_rule.dart @@ -10,16 +10,16 @@ import '../supports_condition.dart'; import 'parent.dart'; /// A `@supports` rule. -class SupportsRule extends ParentStatement /*!*/ > { +class SupportsRule extends ParentStatement> { /// The condition that selects what browsers this rule targets. final SupportsCondition condition; final FileSpan span; - SupportsRule(this.condition, Iterable children, this.span) + SupportsRule(this.condition, Iterable children, this.span) : super(List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitSupportsRule(this); + T accept(StatementVisitor visitor) => visitor.visitSupportsRule(this)!; String toString() => "@supports $condition {${children.join(' ')}}"; } diff --git a/lib/src/ast/sass/statement/use_rule.dart b/lib/src/ast/sass/statement/use_rule.dart index 33932e418..d37bbcc46 100644 --- a/lib/src/ast/sass/statement/use_rule.dart +++ b/lib/src/ast/sass/statement/use_rule.dart @@ -20,7 +20,7 @@ class UseRule implements Statement { /// The namespace for members of the used module, or `null` if the members /// can be accessed without a namespace. - final String namespace; + final String? namespace; /// A list of variable assignments used to configure the loaded modules. final List configuration; @@ -28,7 +28,7 @@ class UseRule implements Statement { final FileSpan span; UseRule(this.url, this.namespace, this.span, - {Iterable configuration}) + {Iterable? configuration}) : configuration = configuration == null ? const [] : List.unmodifiable(configuration) { @@ -45,10 +45,10 @@ class UseRule implements Statement { /// If passed, [url] is the name of the file from which [contents] comes. /// /// Throws a [SassFormatException] if parsing fails. - factory UseRule.parse(String contents, {Object url, Logger logger}) => + factory UseRule.parse(String contents, {Object? url, Logger? logger}) => ScssParser(contents, url: url, logger: logger).parseUseRule(); - T accept(StatementVisitor visitor) => visitor.visitUseRule(this); + T accept(StatementVisitor visitor) => visitor.visitUseRule(this)!; String toString() { var buffer = diff --git a/lib/src/ast/sass/statement/variable_declaration.dart b/lib/src/ast/sass/statement/variable_declaration.dart index 3515b1256..8aa9f494b 100644 --- a/lib/src/ast/sass/statement/variable_declaration.dart +++ b/lib/src/ast/sass/statement/variable_declaration.dart @@ -18,16 +18,16 @@ import 'silent_comment.dart'; class VariableDeclaration implements Statement { /// The namespace of the variable being set, or `null` if it's defined or set /// without a namespace. - final String namespace; + final String? namespace; /// The name of the variable. final String name; /// The comment immediately preceding this declaration. - SilentComment comment; + SilentComment? comment; /// The value the variable is being assigned to. - final Expression /*!*/ expression; + final Expression expression; /// Whether this is a guarded assignment. /// @@ -52,7 +52,7 @@ class VariableDeclaration implements Statement { {this.namespace, bool guarded = false, bool global = false, - SilentComment comment}) + SilentComment? comment}) : isGuarded = guarded, isGlobal = global, comment = comment { @@ -68,11 +68,11 @@ class VariableDeclaration implements Statement { /// /// Throws a [SassFormatException] if parsing fails. factory VariableDeclaration.parse(String contents, - {Object url, Logger logger}) => + {Object? url, Logger? logger}) => ScssParser(contents, url: url, logger: logger).parseVariableDeclaration(); T accept(StatementVisitor visitor) => - visitor.visitVariableDeclaration(this); + visitor.visitVariableDeclaration(this)!; String toString() { var buffer = StringBuffer("\$"); diff --git a/lib/src/ast/sass/statement/warn_rule.dart b/lib/src/ast/sass/statement/warn_rule.dart index ff47ca15e..06f389f5b 100644 --- a/lib/src/ast/sass/statement/warn_rule.dart +++ b/lib/src/ast/sass/statement/warn_rule.dart @@ -13,13 +13,13 @@ import '../statement.dart'; /// This prints a Sass value—usually a string—to warn the user of something. class WarnRule implements Statement { /// The expression to print. - final Expression /*!*/ expression; + final Expression expression; final FileSpan span; WarnRule(this.expression, this.span); - T accept(StatementVisitor visitor) => visitor.visitWarnRule(this); + T accept(StatementVisitor visitor) => visitor.visitWarnRule(this)!; String toString() => "@warn $expression;"; } diff --git a/lib/src/ast/sass/statement/while_rule.dart b/lib/src/ast/sass/statement/while_rule.dart index 121ca5bb2..5aea12ddb 100644 --- a/lib/src/ast/sass/statement/while_rule.dart +++ b/lib/src/ast/sass/statement/while_rule.dart @@ -13,16 +13,16 @@ import 'parent.dart'; /// /// This repeatedly executes a block of code as long as a statement evaluates to /// `true`. -class WhileRule extends ParentStatement /*!*/ > { +class WhileRule extends ParentStatement> { /// The condition that determines whether the block will be executed. - final Expression /*!*/ condition; + final Expression condition; final FileSpan span; - WhileRule(this.condition, Iterable children, this.span) + WhileRule(this.condition, Iterable children, this.span) : super(List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitWhileRule(this); + T accept(StatementVisitor visitor) => visitor.visitWhileRule(this)!; String toString() => "@while $condition {${children.join(" ")}}"; } diff --git a/lib/src/ast/sass/supports_condition/declaration.dart b/lib/src/ast/sass/supports_condition/declaration.dart index fa85a0d5a..21db700ae 100644 --- a/lib/src/ast/sass/supports_condition/declaration.dart +++ b/lib/src/ast/sass/supports_condition/declaration.dart @@ -11,10 +11,10 @@ import '../supports_condition.dart'; /// supported. class SupportsDeclaration implements SupportsCondition { /// The name of the declaration being tested. - final Expression /*!*/ name; + final Expression name; /// The value of the declaration being tested. - final Expression /*!*/ value; + final Expression value; final FileSpan span; diff --git a/lib/src/ast/sass/supports_condition/interpolation.dart b/lib/src/ast/sass/supports_condition/interpolation.dart index 3deccfa6a..4abf86e9e 100644 --- a/lib/src/ast/sass/supports_condition/interpolation.dart +++ b/lib/src/ast/sass/supports_condition/interpolation.dart @@ -12,7 +12,7 @@ class SupportsInterpolation implements SupportsCondition { /// The expression in the interpolation. final Expression expression; - final FileSpan span; + final FileSpan? span; SupportsInterpolation(this.expression, this.span); diff --git a/lib/src/ast/selector/attribute.dart b/lib/src/ast/selector/attribute.dart index 9cedbefc2..a3fbdcb18 100644 --- a/lib/src/ast/selector/attribute.dart +++ b/lib/src/ast/selector/attribute.dart @@ -17,7 +17,7 @@ class AttributeSelector extends SimpleSelector { /// /// If this is `null`, this matches any element with the given property, /// regardless of this value. It's `null` if and only if [value] is `null`. - final AttributeOperator op; + final AttributeOperator? op; /// An assertion about the value of [name]. /// @@ -25,7 +25,7 @@ class AttributeSelector extends SimpleSelector { /// /// If this is `null`, this matches any element with the given property, /// regardless of this value. It's `null` if and only if [op] is `null`. - final String value; + final String? value; /// The modifier which indicates how the attribute selector should be /// processed. @@ -35,7 +35,7 @@ class AttributeSelector extends SimpleSelector { /// [case-sensitivity]: https://www.w3.org/TR/selectors-4/#attribute-case /// /// If [op] is `null`, this is always `null` as well. - final String modifier; + final String? modifier; /// Creates an attribute selector that matches any element with a property of /// the given name. diff --git a/lib/src/ast/selector/complex.dart b/lib/src/ast/selector/complex.dart index 0213e8fa4..2bf8ce314 100644 --- a/lib/src/ast/selector/complex.dart +++ b/lib/src/ast/selector/complex.dart @@ -31,23 +31,23 @@ class ComplexSelector extends Selector { /// /// Pseudo selectors that contain selectors, like `:not()` and `:matches()`, /// can have a range of possible specificities. - int /*!*/ get minSpecificity { + int get minSpecificity { if (_minSpecificity == null) _computeSpecificity(); return _minSpecificity; } - /*late final*/ int _minSpecificity; + late final int _minSpecificity; /// The maximum possible specificity that this selector can have. /// /// Pseudo selectors that contain selectors, like `:not()` and `:matches()`, /// can have a range of possible specificities. - int /*!*/ get maxSpecificity { + int get maxSpecificity { if (_maxSpecificity == null) _computeSpecificity(); return _maxSpecificity; } - /*late final*/ int _maxSpecificity; + late final int _maxSpecificity; // TODO: make late bool get isInvisible { @@ -55,9 +55,9 @@ class ComplexSelector extends Selector { (component) => component is CompoundSelector && component.isInvisible); } - bool _isInvisible; + bool? _isInvisible; - ComplexSelector(Iterable components, + ComplexSelector(Iterable components, {this.lineBreak = false}) : components = List.unmodifiable(components) { if (this.components.isEmpty) { diff --git a/lib/src/ast/selector/compound.dart b/lib/src/ast/selector/compound.dart index d2769acbf..26556bad8 100644 --- a/lib/src/ast/selector/compound.dart +++ b/lib/src/ast/selector/compound.dart @@ -25,23 +25,23 @@ class CompoundSelector extends Selector implements ComplexSelectorComponent { /// /// Pseudo selectors that contain selectors, like `:not()` and `:matches()`, /// can have a range of possible specificities. - int /*!*/ get minSpecificity { + int get minSpecificity { if (_minSpecificity == null) _computeSpecificity(); return _minSpecificity; } - /*late final*/ int _minSpecificity; + late final int _minSpecificity; /// The maximum possible specificity that this selector can have. /// /// Pseudo selectors that contain selectors, like `:not()` and `:matches()`, /// can have a range of possible specificities. - int /*!*/ get maxSpecificity { + int get maxSpecificity { if (_maxSpecificity == null) _computeSpecificity(); - return _maxSpecificity; + return _maxSpecificity!; } - /*late final*/ int /*?*/ _maxSpecificity; + late final int? _maxSpecificity; bool get isInvisible => components.any((component) => component.isInvisible); @@ -60,7 +60,7 @@ class CompoundSelector extends Selector implements ComplexSelectorComponent { /// /// Throws a [SassFormatException] if parsing fails. factory CompoundSelector.parse(String contents, - {Object url, Logger logger, bool allowParent = true}) => + {Object? url, Logger? logger, bool allowParent = true}) => SelectorParser(contents, url: url, logger: logger, allowParent: allowParent) .parseCompoundSelector(); @@ -72,7 +72,7 @@ class CompoundSelector extends Selector implements ComplexSelectorComponent { /// /// That is, whether this matches every element that [other] matches, as well /// as possibly additional elements. - bool isSuperselector(CompoundSelector /*!*/ other) => + bool isSuperselector(CompoundSelector other) => compoundIsSuperselector(this, other); /// Computes [_minSpecificity] and [_maxSpecificity]. diff --git a/lib/src/ast/selector/id.dart b/lib/src/ast/selector/id.dart index 7985d747b..21c3eb1b3 100644 --- a/lib/src/ast/selector/id.dart +++ b/lib/src/ast/selector/id.dart @@ -22,7 +22,7 @@ class IDSelector extends SimpleSelector { IDSelector addSuffix(String suffix) => IDSelector(name + suffix); - List unify(List compound) { + List? unify(List compound) { // A given compound selector may only contain one ID. if (compound.any((simple) => simple is IDSelector && simple != this)) { return null; diff --git a/lib/src/ast/selector/list.dart b/lib/src/ast/selector/list.dart index 5901ab6c1..5bb42f9a4 100644 --- a/lib/src/ast/selector/list.dart +++ b/lib/src/ast/selector/list.dart @@ -54,8 +54,8 @@ class SelectorList extends Selector { /// /// Throws a [SassFormatException] if parsing fails. factory SelectorList.parse(String contents, - {Object url, - Logger logger, + {Object? url, + Logger? logger, bool allowParent = true, bool allowPlaceholder = true}) => SelectorParser(contents, @@ -71,7 +71,7 @@ class SelectorList extends Selector { /// both this and [other]. /// /// If no such list can be produced, returns `null`. - SelectorList unify(SelectorList other) { + SelectorList? unify(SelectorList other) { var contents = components.expand((complex1) { return other.components.expand((complex2) { var unified = unifyComplex([complex1.components, complex2.components]); @@ -91,7 +91,7 @@ class SelectorList extends Selector { /// The given [parent] may be `null`, indicating that this has no parents. If /// so, this list is returned as-is if it doesn't contain any explicit /// [ParentSelector]s. If it does, this throws a [SassScriptException]. - SelectorList resolveParentSelectors(SelectorList parent, + SelectorList resolveParentSelectors(SelectorList? parent, {bool implicitParent = true}) { if (parent == null) { if (!_containsParentSelector) return this; @@ -151,7 +151,7 @@ class SelectorList extends Selector { component.components.any((simple) { if (simple is ParentSelector) return true; if (simple is! PseudoSelector) return true; - var selector = (simple as PseudoSelector).selector; + var selector = simple.selector; return selector != null && selector._containsParentSelector; })); @@ -159,11 +159,11 @@ class SelectorList extends Selector { /// [ParentSelector]s replaced with [parent]. /// /// Returns `null` if [compound] doesn't contain any [ParentSelector]s. - Iterable _resolveParentSelectorsCompound( + Iterable? _resolveParentSelectorsCompound( CompoundSelector compound, SelectorList parent) { var containsSelectorPseudo = compound.components.any((simple) { if (simple is! PseudoSelector) return false; - var selector = (simple as PseudoSelector).selector; + var selector = simple.selector; return selector != null && selector._containsParentSelector; }); if (!containsSelectorPseudo && @@ -174,10 +174,10 @@ class SelectorList extends Selector { var resolvedMembers = containsSelectorPseudo ? compound.components.map((simple) { if (simple is! PseudoSelector) return simple; - var selector = (simple as PseudoSelector).selector; + var selector = simple.selector; if (selector == null) return simple; if (!selector._containsParentSelector) return simple; - return (simple as PseudoSelector).withSelector( + return simple.withSelector( selector.resolveParentSelectors(parent, implicitParent: false)); }) : compound.components; @@ -200,7 +200,7 @@ class SelectorList extends Selector { 'Parent "$complex" is incompatible with this selector.'); } - var last = lastComponent as CompoundSelector; + var last = lastComponent; var suffix = (compound.components.first as ParentSelector).suffix; if (suffix != null) { last = CompoundSelector([ diff --git a/lib/src/ast/selector/parent.dart b/lib/src/ast/selector/parent.dart index ec046144e..ed2b77068 100644 --- a/lib/src/ast/selector/parent.dart +++ b/lib/src/ast/selector/parent.dart @@ -15,7 +15,7 @@ class ParentSelector extends SimpleSelector { /// /// This is assumed to be a valid identifier suffix. It may be `null`, /// indicating that the parent selector will not be modified. - final String suffix; + final String? suffix; ParentSelector({this.suffix}); diff --git a/lib/src/ast/selector/pseudo.dart b/lib/src/ast/selector/pseudo.dart index c80634eea..3bf2f9a23 100644 --- a/lib/src/ast/selector/pseudo.dart +++ b/lib/src/ast/selector/pseudo.dart @@ -51,29 +51,29 @@ class PseudoSelector extends SimpleSelector { /// /// This is `null` if there's no argument. If [argument] and [selector] are /// both non-`null`, the selector follows the argument. - final String argument; + final String? argument; /// The selector argument passed to this selector. /// /// This is `null` if there's no selector. If [argument] and [selector] are /// both non-`null`, the selector follows the argument. - final SelectorList selector; + final SelectorList? selector; // TODO: late int get minSpecificity { if (_minSpecificity == null) _computeSpecificity(); - return _minSpecificity; + return _minSpecificity!; } - int _minSpecificity; + int? _minSpecificity; int get maxSpecificity { if (_maxSpecificity == null) _computeSpecificity(); - return _maxSpecificity; + return _maxSpecificity!; } - int _maxSpecificity; + int? _maxSpecificity; bool get isInvisible { var selector = this.selector; @@ -126,7 +126,7 @@ class PseudoSelector extends SimpleSelector { return PseudoSelector(name + suffix, element: isElement); } - List unify(List compound) { + List? unify(List compound) { if (compound.length == 1 && compound.first is UniversalSelector) { return compound.first.unify([this]); } @@ -172,16 +172,16 @@ class PseudoSelector extends SimpleSelector { var minSpecificity = 0; var maxSpecificity = 0; for (var complex in selector.components) { - minSpecificity = math.max(_minSpecificity, complex.minSpecificity); - maxSpecificity = math.max(_maxSpecificity, complex.maxSpecificity); + minSpecificity = math.max(_minSpecificity!, complex.minSpecificity); + maxSpecificity = math.max(_maxSpecificity!, complex.maxSpecificity); } } else { // This is higher than any selector's specificity can actually be. var minSpecificity = math.pow(super.minSpecificity, 3) as int; var maxSpecificity = 0; for (var complex in selector.components) { - minSpecificity = math.min(_minSpecificity, complex.minSpecificity); - maxSpecificity = math.max(_maxSpecificity, complex.maxSpecificity); + minSpecificity = math.min(_minSpecificity!, complex.minSpecificity); + maxSpecificity = math.max(_maxSpecificity!, complex.maxSpecificity); } } _minSpecificity = minSpecificity; diff --git a/lib/src/ast/selector/qualified_name.dart b/lib/src/ast/selector/qualified_name.dart index 24b7a461a..9abcfe0f7 100644 --- a/lib/src/ast/selector/qualified_name.dart +++ b/lib/src/ast/selector/qualified_name.dart @@ -14,7 +14,7 @@ class QualifiedName { /// If this is `null`, [name] belongs to the default namespace. If it's the /// empty string, [name] belongs to no namespace. If it's `*`, [name] belongs /// to any namespace. Otherwise, [name] belongs to the given namespace. - final String namespace; + final String? namespace; QualifiedName(this.name, {this.namespace}); diff --git a/lib/src/ast/selector/simple.dart b/lib/src/ast/selector/simple.dart index 7b3e82261..a32f9082f 100644 --- a/lib/src/ast/selector/simple.dart +++ b/lib/src/ast/selector/simple.dart @@ -17,13 +17,13 @@ abstract class SimpleSelector extends Selector { /// Specifity is represented in base 1000. The spec says this should be /// "sufficiently high"; it's extremely unlikely that any single selector /// sequence will contain 1000 simple selectors. - int /*!*/ get minSpecificity => 1000; + int get minSpecificity => 1000; /// The maximum possible specificity that this selector can have. /// /// Pseudo selectors that contain selectors, like `:not()` and `:matches()`, /// can have a range of possible specificities. - int /*!*/ get maxSpecificity => minSpecificity; + int get maxSpecificity => minSpecificity; SimpleSelector(); @@ -35,7 +35,7 @@ abstract class SimpleSelector extends Selector { /// /// Throws a [SassFormatException] if parsing fails. factory SimpleSelector.parse(String contents, - {Object url, Logger logger, bool allowParent = true}) => + {Object? url, Logger? logger, bool allowParent = true}) => SelectorParser(contents, url: url, logger: logger, allowParent: allowParent) .parseSimpleSelector(); @@ -57,7 +57,7 @@ abstract class SimpleSelector extends Selector { /// /// Returns `null` if unification is impossible—for example, if there are /// multiple ID selectors. - List unify(List compound) { + List? unify(List compound) { if (compound.length == 1 && compound.first is UniversalSelector) { return compound.first.unify([this]); } diff --git a/lib/src/ast/selector/type.dart b/lib/src/ast/selector/type.dart index b6116a85b..28d1ba8f4 100644 --- a/lib/src/ast/selector/type.dart +++ b/lib/src/ast/selector/type.dart @@ -21,7 +21,7 @@ class TypeSelector extends SimpleSelector { TypeSelector addSuffix(String suffix) => TypeSelector( QualifiedName(name.name + suffix, namespace: name.namespace)); - List unify(List compound) { + List? unify(List compound) { if (compound.first is UniversalSelector || compound.first is TypeSelector) { var unified = unifyUniversalAndElement(this, compound.first); if (unified == null) return null; diff --git a/lib/src/ast/selector/universal.dart b/lib/src/ast/selector/universal.dart index 1e07783f9..fcba37594 100644 --- a/lib/src/ast/selector/universal.dart +++ b/lib/src/ast/selector/universal.dart @@ -14,7 +14,7 @@ class UniversalSelector extends SimpleSelector { /// it's the empty string, this matches all elements that aren't in any /// namespace. If it's `*`, this matches all elements in any namespace. /// Otherwise, it matches all elements in the given namespace. - final String namespace; + final String? namespace; int get minSpecificity => 0; @@ -23,7 +23,7 @@ class UniversalSelector extends SimpleSelector { T accept(SelectorVisitor visitor) => visitor.visitUniversalSelector(this); - List unify(List compound) { + List? unify(List compound) { if (compound.first is UniversalSelector || compound.first is TypeSelector) { var unified = unifyUniversalAndElement(this, compound.first); if (unified == null) return null; diff --git a/lib/src/async_compile.dart b/lib/src/async_compile.dart index dc6306e11..a812ff9c5 100644 --- a/lib/src/async_compile.dart +++ b/lib/src/async_compile.dart @@ -24,21 +24,21 @@ import 'visitor/serialize.dart'; /// the node-sass compatible API and the executable. /// /// At most one of `importCache` and `nodeImporter` may be provided at once. -Future compileAsync(String /*!*/ path, - {Syntax syntax, - Logger logger, - AsyncImportCache importCache, - NodeImporter nodeImporter, - Iterable functions, - OutputStyle style, +Future compileAsync(String path, + {Syntax? syntax, + Logger? logger, + AsyncImportCache? importCache, + NodeImporter? nodeImporter, + Iterable? functions, + OutputStyle? style, bool useSpaces = true, - int indentWidth, - LineFeed lineFeed, + int? indentWidth, + LineFeed? lineFeed, bool sourceMap = false, bool charset = true}) async { // If the syntax is different than the importer would default to, we have to // parse the file manually and we can't store it in the cache. - Stylesheet stylesheet; + Stylesheet? stylesheet; if (nodeImporter == null && (syntax == null || syntax == Syntax.forPath(path))) { importCache ??= AsyncImportCache.none(logger: logger); @@ -52,7 +52,7 @@ Future compileAsync(String /*!*/ path, return await _compileStylesheet( // TODO: no ! - stylesheet, + stylesheet!, logger, importCache, nodeImporter, @@ -71,19 +71,19 @@ Future compileAsync(String /*!*/ path, /// /// At most one of `importCache` and `nodeImporter` may be provided at once. Future compileStringAsync(String source, - {Syntax syntax, - Logger logger, - AsyncImportCache importCache, - NodeImporter nodeImporter, - Iterable importers, - Iterable loadPaths, - AsyncImporter importer, - Iterable functions, - OutputStyle style, + {Syntax? syntax, + Logger? logger, + AsyncImportCache? importCache, + NodeImporter? nodeImporter, + Iterable? importers, + Iterable? loadPaths, + AsyncImporter? importer, + Iterable? functions, + OutputStyle? style, bool useSpaces = true, - int indentWidth, - LineFeed lineFeed, - Object url, + int? indentWidth, + LineFeed? lineFeed, + Object? url, bool sourceMap = false, bool charset = true}) async { var stylesheet = @@ -109,15 +109,15 @@ Future compileStringAsync(String source, /// Arguments are handled as for [compileStringAsync]. Future _compileStylesheet( Stylesheet stylesheet, - Logger logger, - AsyncImportCache importCache, - NodeImporter nodeImporter, + Logger? logger, + AsyncImportCache? importCache, + NodeImporter? nodeImporter, AsyncImporter importer, - Iterable functions, - OutputStyle style, + Iterable? functions, + OutputStyle? style, bool useSpaces, - int indentWidth, - LineFeed lineFeed, + int? indentWidth, + LineFeed? lineFeed, bool sourceMap, bool charset) async { var evaluateResult = await evaluateAsync(stylesheet, @@ -143,7 +143,7 @@ Future _compileStylesheet( mapInPlace( resultSourceMap.urls, (url) => url == '' - ? Uri.dataFromString(stylesheet.span.file.getText(0), + ? Uri.dataFromString(stylesheet.span!.file.getText(0), encoding: utf8) .toString() : importCache.sourceMapUrl(Uri.parse(url)).toString()); @@ -167,13 +167,13 @@ class CompileResult { /// The source map indicating how the source files map to [css]. /// /// This is `null` if source mapping was disabled for this compilation. - SingleMapping get sourceMap => _serialize.sourceMap; + SingleMapping? get sourceMap => _serialize.sourceMap; /// A map from source file URLs to the corresponding [SourceFile]s. /// /// This can be passed to [sourceMap]'s [Mapping.spanFor] method. It's `null` /// if source mapping was disabled for this compilation. - Map get sourceFiles => _serialize.sourceFiles; + Map? get sourceFiles => _serialize.sourceFiles; /// The set that will eventually populate the JS API's /// `result.stats.includedFiles` field. diff --git a/lib/src/async_environment.dart b/lib/src/async_environment.dart index f5d9bec17..cfa0917f2 100644 --- a/lib/src/async_environment.dart +++ b/lib/src/async_environment.dart @@ -44,25 +44,25 @@ class AsyncEnvironment { /// A map from modules in [_globalModules] to the nodes whose spans /// indicate where those modules were originally loaded. - final Map _globalModuleNodes; + final Map _globalModuleNodes; /// The modules forwarded by this module. /// /// This is `null` if there are no forwarded modules. - Set _forwardedModules; + Set? _forwardedModules; /// A map from modules in [_forwardedModules] to the nodes whose spans /// indicate where those modules were originally forwarded. /// /// This is `null` if there are no forwarded modules. - Map _forwardedModuleNodes; + Map? _forwardedModuleNodes; /// Modules forwarded by nested imports at each lexical scope level *beneath /// the global scope*. /// /// This is `null` until it's needed, since most environments won't ever use /// this. - List> _nestedForwardedModules; + List>? _nestedForwardedModules; /// Modules from [_modules], [_globalModules], and [_forwardedModules], in the /// order in which they were `@use`d. @@ -74,7 +74,7 @@ class AsyncEnvironment { /// /// The first element is the global scope, and each successive element is /// deeper in the tree. - final List> _variables; + final List> _variables; /// The nodes where each variable in [_variables] was defined. /// @@ -83,12 +83,12 @@ class AsyncEnvironment { /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - final List> _variableNodes; + final List>? _variableNodes; /// A map of variable names to their indices in [_variables]. /// /// This map is filled in as-needed, and may not be complete. - final Map _variableIndices; + final Map _variableIndices; /// A list of functions defined at each lexical scope level. /// @@ -118,8 +118,8 @@ class AsyncEnvironment { /// The content block passed to the lexically-enclosing mixin, or `null` if /// this is not in a mixin, or if no content block was passed. - UserDefinedCallable get content => _content; - UserDefinedCallable _content; + UserDefinedCallable? get content => _content; + UserDefinedCallable? _content; /// Whether the environment is lexically at the root of the document. bool get atRoot => _variables.length == 1; @@ -138,10 +138,10 @@ class AsyncEnvironment { /// /// This is cached to speed up repeated references to the same variable, as /// well as references to the last variable's [FileSpan]. - String _lastVariableName; + String? _lastVariableName; /// The index in [_variables] of the last variable that was accessed. - int _lastVariableIndex; + int? _lastVariableIndex; /// Creates an [AsyncEnvironment]. /// @@ -234,7 +234,7 @@ class AsyncEnvironment { /// Throws a [SassScriptException] if there's already a module with the given /// [namespace], or if [namespace] is `null` and [module] defines a variable /// with the same name as a variable defined in this environment. - void addModule(Module module, AstNode nodeWithSpan, {String namespace}) { + void addModule(Module module, AstNode nodeWithSpan, {String? namespace}) { if (namespace == null) { _globalModules.add(module); _globalModuleNodes[module] = nodeWithSpan; @@ -346,7 +346,8 @@ class AsyncEnvironment { } // TODO: var - var forwardedModuleNodes = (_forwardedModuleNodes ??= {}); + Map, AstNode?> forwardedModuleNodes = + (_forwardedModuleNodes ??= {}); var forwardedVariableNames = forwarded.expand((module) => module.variables.keys).toSet(); @@ -368,13 +369,13 @@ class AsyncEnvironment { if (!shadowed.isEmpty) { _globalModules.add(shadowed); - _globalModuleNodes[shadowed] = _globalModuleNodes.remove(module); + _globalModuleNodes[shadowed] = _globalModuleNodes.remove(module)!; } } } // TODO: no ! - for (var module in forwardedModules.toList()) { + for (var module in forwardedModules!.toList()) { var shadowed = ShadowedModuleView.ifNecessary(module, variables: forwardedVariableNames, mixins: forwardedMixinNames, @@ -426,11 +427,11 @@ class AsyncEnvironment { /// /// Throws a [SassScriptException] if there is no module named [namespace], or /// if multiple global modules expose variables named [name]. - Value getVariable(String name, {String namespace}) { + Value? getVariable(String name, {String? namespace}) { if (namespace != null) return _getModule(namespace).variables[name]; if (_lastVariableName == name) { - return _variables[_lastVariableIndex][name] ?? + return _variables[_lastVariableIndex!][name] ?? _getVariableFromGlobalModule(name); } @@ -458,7 +459,7 @@ class AsyncEnvironment { /// Returns the value of the variable named [name] from a namespaceless /// module, or `null` if no such variable is declared in any namespaceless /// module. - Value _getVariableFromGlobalModule(String name) => + Value? _getVariableFromGlobalModule(String name) => _fromOneModule(name, "variable", (module) => module.variables[name]); /// Returns the node for the variable named [name], or `null` if no such @@ -469,7 +470,7 @@ class AsyncEnvironment { /// [FileSpan] so we can avoid calling [AstNode.span] if the span isn't /// required, since some nodes need to do real work to manufacture a source /// span. - AstNode getVariableNode(String name, {String namespace}) { + AstNode? getVariableNode(String name, {String? namespace}) { var variableNodes = _variableNodes; if (variableNodes == null) { throw StateError( @@ -477,10 +478,10 @@ class AsyncEnvironment { "passed in."); } - if (namespace != null) return _getModule(namespace).variableNodes[name]; + if (namespace != null) return _getModule(namespace).variableNodes![name]; if (_lastVariableName == name) { - return variableNodes[_lastVariableIndex][name] ?? + return variableNodes[_lastVariableIndex!][name] ?? _getVariableNodeFromGlobalModule(name); } @@ -509,11 +510,11 @@ class AsyncEnvironment { /// [FileSpan] so we can avoid calling [AstNode.span] if the span isn't /// required, since some nodes need to do real work to manufacture a source /// span. - AstNode _getVariableNodeFromGlobalModule(String name) { + AstNode? _getVariableNodeFromGlobalModule(String name) { // We don't need to worry about multiple modules defining the same variable, // because that's already been checked by [getVariable]. for (var module in _globalModules) { - var value = module.variableNodes[name]; + var value = module.variableNodes![name]; if (value != null) return value; } return null; @@ -526,7 +527,7 @@ class AsyncEnvironment { /// /// Throws a [SassScriptException] if there is no module named [namespace], or /// if multiple global modules expose functions named [name]. - bool globalVariableExists(String name, {String namespace}) { + bool globalVariableExists(String name, {String? namespace}) { if (namespace != null) { return _getModule(namespace).variables.containsKey(name); } @@ -536,7 +537,7 @@ class AsyncEnvironment { /// Returns the index of the last map in [_variables] that has a [name] key, /// or `null` if none exists. - int _variableIndex(String name) { + int? _variableIndex(String name) { for (var i = _variables.length - 1; i >= 0; i--) { if (_variables[i].containsKey(name)) return i; } @@ -561,8 +562,8 @@ class AsyncEnvironment { /// defined with the given namespace, if no variable with the given [name] is /// defined in module with the given namespace, or if no [namespace] is passed /// and multiple global modules define variables named [name]. - void setVariable(String name, Value /*!*/ value, AstNode /*?*/ nodeWithSpan, - {String namespace, bool global = false}) { + void setVariable(String name, Value value, AstNode? nodeWithSpan, + {String? namespace, bool global = false}) { if (namespace != null) { _getModule(namespace).setVariable(name, value, nodeWithSpan); return; @@ -608,7 +609,7 @@ class AsyncEnvironment { } var index = _lastVariableName == name - ? _lastVariableIndex /*!*/ + ? _lastVariableIndex! : _variableIndices.putIfAbsent( name, () => _variableIndex(name) ?? _variables.length - 1); if (!_inSemiGlobalScope && index == 0) { @@ -619,7 +620,7 @@ class AsyncEnvironment { _lastVariableName = name; _lastVariableIndex = index; _variables[index][name] = value; - _variableNodes?.andGet(index)[name] = nodeWithSpan; + _variableNodes?.andGet(index)![name] = nodeWithSpan!; } /// Sets the variable named [name] to [value], associated with @@ -631,14 +632,14 @@ class AsyncEnvironment { /// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - void setLocalVariable(String name, Value value, AstNode /*?*/ nodeWithSpan) { + void setLocalVariable(String name, Value value, AstNode? nodeWithSpan) { var index = _variables.length - 1; _lastVariableName = name; _lastVariableIndex = index; _variableIndices[name] = index; _variables[index][name] = value; if (nodeWithSpan != null) { - _variableNodes?.andGet(index)[name] = nodeWithSpan; + _variableNodes?.andGet(index)![name] = nodeWithSpan; } } @@ -647,7 +648,7 @@ class AsyncEnvironment { /// /// Throws a [SassScriptException] if there is no module named [namespace], or /// if multiple global modules expose functions named [name]. - AsyncCallable getFunction(String name, {String namespace}) { + AsyncCallable? getFunction(String name, {String? namespace}) { if (namespace != null) return _getModule(namespace).functions[name]; var index = _functionIndices[name]; @@ -665,12 +666,12 @@ class AsyncEnvironment { /// Returns the value of the function named [name] from a namespaceless /// module, or `null` if no such function is declared in any namespaceless /// module. - AsyncCallable _getFunctionFromGlobalModule(String name) => + AsyncCallable? _getFunctionFromGlobalModule(String name) => _fromOneModule(name, "function", (module) => module.functions[name]); /// Returns the index of the last map in [_functions] that has a [name] key, /// or `null` if none exists. - int _functionIndex(String name) { + int? _functionIndex(String name) { for (var i = _functions.length - 1; i >= 0; i--) { if (_functions[i].containsKey(name)) return i; } @@ -681,7 +682,7 @@ class AsyncEnvironment { /// /// Throws a [SassScriptException] if there is no module named [namespace], or /// if multiple global modules expose functions named [name]. - bool functionExists(String name, {String namespace}) => + bool functionExists(String name, {String? namespace}) => getFunction(name, namespace: namespace) != null; /// Sets the variable named [name] to [value] in the current scope. @@ -696,7 +697,7 @@ class AsyncEnvironment { /// /// Throws a [SassScriptException] if there is no module named [namespace], or /// if multiple global modules expose mixins named [name]. - AsyncCallable getMixin(String name, {String namespace}) { + AsyncCallable? getMixin(String name, {String? namespace}) { if (namespace != null) return _getModule(namespace).mixins[name]; var index = _mixinIndices[name]; @@ -714,12 +715,12 @@ class AsyncEnvironment { /// Returns the value of the mixin named [name] from a namespaceless /// module, or `null` if no such mixin is declared in any namespaceless /// module. - AsyncCallable _getMixinFromGlobalModule(String name) => + AsyncCallable? _getMixinFromGlobalModule(String name) => _fromOneModule(name, "mixin", (module) => module.mixins[name]); /// Returns the index of the last map in [_mixins] that has a [name] key, or /// `null` if none exists. - int _mixinIndex(String name) { + int? _mixinIndex(String name) { for (var i = _mixins.length - 1; i >= 0; i--) { if (_mixins[i].containsKey(name)) return i; } @@ -730,7 +731,7 @@ class AsyncEnvironment { /// /// Throws a [SassScriptException] if there is no module named [namespace], or /// if multiple global modules expose functions named [name]. - bool mixinExists(String name, {String namespace}) => + bool mixinExists(String name, {String? namespace}) => getMixin(name, namespace: namespace) != null; /// Sets the variable named [name] to [value] in the current scope. @@ -741,7 +742,7 @@ class AsyncEnvironment { } /// Sets [content] as [this.content] for the duration of [callback]. - Future withContent(UserDefinedCallable content, + Future withContent(UserDefinedCallable? content, Future callback()) async { var oldContent = _content; _content = content; @@ -821,7 +822,7 @@ class AsyncEnvironment { for (var i = 0; i < _variables.length; i++) { var values = _variables[i]; var nodes = - _variableNodes == null ? {} : _variableNodes[i]; + _variableNodes == null ? {} : _variableNodes![i]; // TODO: var nodes = _variableNodes.andGet(i) ?? {}; for (var entry in values.entries) { // Implicit configurations are never invalid, making [configurationSpan] @@ -878,8 +879,7 @@ class AsyncEnvironment { /// /// The [type] should be the singular name of the value type being returned. /// It's used to format an appropriate error message. - T _fromOneModule( - String name, String type, T /*?*/ callback(Module module)) { + T? _fromOneModule(String name, String type, T? callback(Module module)) { var nestedForwardedModules = _nestedForwardedModules; if (nestedForwardedModules != null) { for (var modules in nestedForwardedModules.reversed) { @@ -890,21 +890,22 @@ class AsyncEnvironment { } } - T value; - Object identity; + T? value; + Object? identity; for (var module in _globalModules) { var valueInModule = callback(module); if (valueInModule == null) continue; - var identityFromModule = valueInModule is AsyncCallable + Object? identityFromModule = valueInModule is AsyncCallable ? valueInModule : module.variableIdentity(name); if (identityFromModule == identity) continue; if (value != null) { // TODO no !, as - var spans = _globalModuleNodes.entries.map( - (entry) => callback(entry.key).andThen((_) => entry.value.span)); + var spans = _globalModuleNodes.entries.map((entry) => + callback(entry.key) + .andThen(((_) => entry.value.span!) as FileSpan Function(T)?)); throw MultiSpanSassScriptException( 'This $type is available from multiple global modules.', @@ -923,11 +924,11 @@ class AsyncEnvironment { /// A module that represents the top-level members defined in an [Environment]. class _EnvironmentModule implements Module { - Uri get url => css?.span.sourceUrl; + Uri? get url => css?.span!.sourceUrl; final List upstream; final Map variables; - final Map variableNodes; + final Map? variableNodes; final Map functions; final Map mixins; final Extender extender; @@ -948,7 +949,7 @@ class _EnvironmentModule implements Module { factory _EnvironmentModule( AsyncEnvironment environment, CssStylesheet css, Extender extender, - {Set forwarded}) { + {Set? forwarded}) { forwarded ??= const {}; return _EnvironmentModule._( environment, @@ -960,7 +961,7 @@ class _EnvironmentModule implements Module { environment._variableNodes.andThen((nodes) => _memberMap( // TODO: no ! nodes.first, - forwarded.map((module) => module.variableNodes /*!*/))), + forwarded!.map((module) => module.variableNodes!))), _memberMap(environment._functions.first, forwarded.map((module) => module.functions)), _memberMap(environment._mixins.first, @@ -1019,11 +1020,11 @@ class _EnvironmentModule implements Module { this.variableNodes, this.functions, this.mixins, - {@required this.transitivelyContainsCss, - @required this.transitivelyContainsExtensions}) + {required this.transitivelyContainsCss, + required this.transitivelyContainsExtensions}) : upstream = _environment._allModules; - void setVariable(String name, Value value, AstNode nodeWithSpan) { + void setVariable(String name, Value value, AstNode? nodeWithSpan) { var module = _modulesByVariable[name]; if (module != null) { module.setVariable(name, value, nodeWithSpan); diff --git a/lib/src/async_import_cache.dart b/lib/src/async_import_cache.dart index 76a1c5568..4263c88dd 100644 --- a/lib/src/async_import_cache.dart +++ b/lib/src/async_import_cache.dart @@ -31,11 +31,11 @@ class AsyncImportCache { /// /// This cache isn't used for relative imports, because they're /// context-dependent. - final Map, Tuple3 /*?*/ > + final Map, Tuple3?> _canonicalizeCache; /// The parsed stylesheets for each canonicalized import URL. - final Map _importCache; + final Map _importCache; /// The import results for each canonicalized import URL. final Map _resultsCache; @@ -58,10 +58,10 @@ class AsyncImportCache { /// /// [`PackageConfig`]: https://pub.dev/documentation/package_config/latest/package_config.package_config/PackageConfig-class.html AsyncImportCache( - {Iterable importers, - Iterable loadPaths, - PackageConfig packageConfig, - Logger logger}) + {Iterable? importers, + Iterable? loadPaths, + PackageConfig? packageConfig, + Logger? logger}) : _importers = _toImporters(importers, loadPaths, packageConfig), _logger = logger ?? const Logger.stderr(), _canonicalizeCache = {}, @@ -69,7 +69,7 @@ class AsyncImportCache { _resultsCache = {}; /// Creates an import cache without any globally-available importers. - AsyncImportCache.none({Logger logger}) + AsyncImportCache.none({Logger? logger}) : _importers = const [], _logger = logger ?? const Logger.stderr(), _canonicalizeCache = {}, @@ -78,8 +78,8 @@ class AsyncImportCache { /// Converts the user's [importers], [loadPaths], and [packageConfig] /// options into a single list of importers. - static List _toImporters(Iterable importers, - Iterable loadPaths, PackageConfig packageConfig) { + static List _toImporters(Iterable? importers, + Iterable? loadPaths, PackageConfig? packageConfig) { var sassPath = getEnvironmentVariable('SASS_PATH'); return [ ...?importers, @@ -104,8 +104,10 @@ class AsyncImportCache { /// If any importers understand [url], returns that importer as well as the /// canonicalized URL and the original URL resolved relative to [baseUrl] if /// applicable. Otherwise, returns `null`. - Future> canonicalize(Uri url, - {AsyncImporter baseImporter, Uri baseUrl, bool forImport = false}) async { + Future?> canonicalize(Uri url, + {AsyncImporter? baseImporter, + Uri? baseUrl, + bool forImport = false}) async { if (baseImporter != null) { var resolvedUrl = baseUrl?.resolveUri(url) ?? url; var canonicalUrl = @@ -131,7 +133,7 @@ class AsyncImportCache { /// Calls [importer.canonicalize] and prints a deprecation warning if it /// returns a relative URL. - Future _canonicalize( + Future _canonicalize( AsyncImporter importer, Uri url, bool forImport) async { var result = await (forImport ? inImportRule(() => importer.canonicalize(url)) @@ -154,8 +156,10 @@ Relative canonical URLs are deprecated and will eventually be disallowed. /// parsed stylesheet. Otherwise, returns `null`. /// /// Caches the result of the import and uses cached results if possible. - Future> import(Uri url, - {AsyncImporter baseImporter, Uri baseUrl, bool forImport = false}) async { + Future?> import(Uri url, + {AsyncImporter? baseImporter, + Uri? baseUrl, + bool forImport = false}) async { var tuple = await canonicalize(url, baseImporter: baseImporter, baseUrl: baseUrl, forImport: forImport); if (tuple == null) return null; @@ -175,8 +179,8 @@ Relative canonical URLs are deprecated and will eventually be disallowed. /// importers may return for legacy reasons. /// /// Caches the result of the import and uses cached results if possible. - Future importCanonical(AsyncImporter importer, Uri canonicalUrl, - [Uri originalUrl]) async { + Future importCanonical(AsyncImporter importer, Uri canonicalUrl, + [Uri? originalUrl]) async { // TODO: no as return await putIfAbsentAsync(_importCache, canonicalUrl, () async { var result = await importer.load(canonicalUrl); diff --git a/lib/src/callable.dart b/lib/src/callable.dart index d25353ff1..c984e94f1 100644 --- a/lib/src/callable.dart +++ b/lib/src/callable.dart @@ -67,9 +67,9 @@ export 'callable/user_defined.dart'; abstract class Callable extends AsyncCallable { @Deprecated('Use `Callable.function` instead.') factory Callable(String name, String arguments, - ext.Value /*!*/ callback(List arguments)) => + ext.Value callback(List arguments)) => // TODO: no as - Callable.function(name, arguments, callback); + Callable.function(name, arguments as String, callback); /// Creates a function with the given [name] and [arguments] that runs /// [callback] when called. @@ -114,10 +114,10 @@ abstract class Callable extends AsyncCallable { /// which provides access to keyword arguments using /// [SassArgumentList.keywords]. factory Callable.function(String name, String arguments, - ext.Value /*!*/ callback(List arguments)) => + ext.Value callback(List arguments)) => BuiltInCallable.function( // TODO: no as name, - arguments, + arguments as String, (arguments) => callback(arguments) as Value); } diff --git a/lib/src/callable/async.dart b/lib/src/callable/async.dart index 8315eaada..be5cde0a5 100644 --- a/lib/src/callable/async.dart +++ b/lib/src/callable/async.dart @@ -27,7 +27,7 @@ abstract class AsyncCallable { factory AsyncCallable(String name, String arguments, FutureOr callback(List arguments)) => // TODO: no as - AsyncCallable.function(name, arguments, callback); + AsyncCallable.function(name, arguments as String, callback); /// Creates a callable with the given [name] and [arguments] that runs /// [callback] when called. @@ -39,9 +39,9 @@ abstract class AsyncCallable { factory AsyncCallable.function(String name, String arguments, FutureOr callback(List arguments)) => // TODO: no as - AsyncBuiltInCallable.function(name, arguments, (arguments) { + AsyncBuiltInCallable.function(name, arguments as String, (arguments) { var result = callback(arguments); if (result is ext.Value) return result as Value; - return (result as Future).then((value) => value as Value); + return result.then((value) => value as Value); }); } diff --git a/lib/src/callable/async_built_in.dart b/lib/src/callable/async_built_in.dart index 93d9e9ed3..26017ca4d 100644 --- a/lib/src/callable/async_built_in.dart +++ b/lib/src/callable/async_built_in.dart @@ -11,8 +11,7 @@ import '../value.dart'; import 'async.dart'; /// An [AsyncBuiltInCallable]'s callback. -typedef _Callback = FutureOr /*!*/ Function( - List arguments); +typedef _Callback = FutureOr Function(List arguments); /// A callable defined in Dart code. /// @@ -38,7 +37,7 @@ class AsyncBuiltInCallable implements AsyncCallable { /// If passed, [url] is the URL of the module in which the function is /// defined. AsyncBuiltInCallable.function(String name, String arguments, - FutureOr callback(List arguments), {Object url}) + FutureOr callback(List arguments), {Object? url}) : this.parsed( name, ArgumentDeclaration.parse('@function $name($arguments) {', @@ -55,7 +54,7 @@ class AsyncBuiltInCallable implements AsyncCallable { /// defined. AsyncBuiltInCallable.mixin(String name, String arguments, FutureOr callback(List arguments), - {Object url}) + {Object? url}) : this.parsed(name, ArgumentDeclaration.parse('@mixin $name($arguments) {', url: url), (arguments) async { @@ -74,6 +73,6 @@ class AsyncBuiltInCallable implements AsyncCallable { /// doesn't guarantee that [positional] and [names] are valid for the returned /// [ArgumentDeclaration]. Tuple2 callbackFor( - int positional, Set names) => + int positional, Set names) => Tuple2(_arguments, _callback); } diff --git a/lib/src/callable/built_in.dart b/lib/src/callable/built_in.dart index e328236f3..d0a79f930 100644 --- a/lib/src/callable/built_in.dart +++ b/lib/src/callable/built_in.dart @@ -8,7 +8,7 @@ import '../ast/sass.dart'; import '../callable.dart'; import '../value.dart'; -typedef _Callback = Value /*!*/ Function(List arguments); +typedef _Callback = Value Function(List arguments); /// A callable defined in Dart code. /// @@ -30,8 +30,9 @@ class BuiltInCallable implements Callable, AsyncBuiltInCallable { /// /// If passed, [url] is the URL of the module in which the function is /// defined. - BuiltInCallable.function(String name, String arguments, - Value /*!*/ callback(List arguments), {Object url}) + BuiltInCallable.function( + String name, String arguments, Value callback(List arguments), + {Object? url}) : this.parsed( name, ArgumentDeclaration.parse('@function $name($arguments) {', @@ -46,9 +47,9 @@ class BuiltInCallable implements Callable, AsyncBuiltInCallable { /// /// If passed, [url] is the URL of the module in which the mixin is /// defined. - BuiltInCallable.mixin(String name, String arguments, - void callback(List arguments), - {Object url}) + BuiltInCallable.mixin( + String name, String arguments, void callback(List arguments), + {Object? url}) : this.parsed(name, ArgumentDeclaration.parse('@mixin $name($arguments) {', url: url), (arguments) { @@ -59,9 +60,9 @@ class BuiltInCallable implements Callable, AsyncBuiltInCallable { /// Creates a callable with a single [arguments] declaration and a single /// [callback]. BuiltInCallable.parsed(this.name, ArgumentDeclaration arguments, - Value /*!*/ callback(List arguments)) + Value callback(List arguments)) // TODO: no as - : _overloads = [Tuple2(arguments, callback)]; + : _overloads = [Tuple2(arguments as ArgumentDeclaration, callback)]; /// Creates a function with multiple implementations. /// @@ -75,8 +76,8 @@ class BuiltInCallable implements Callable, AsyncBuiltInCallable { BuiltInCallable.overloadedFunction( // TODO: use _Callback this.name, - Map arguments)> overloads, - {Object url}) + Map arguments)> overloads, + {Object? url}) : _overloads = [ for (var entry in overloads.entries) Tuple2( @@ -94,9 +95,9 @@ class BuiltInCallable implements Callable, AsyncBuiltInCallable { /// doesn't guarantee that [positional] and [names] are valid for the returned /// [ArgumentDeclaration]. Tuple2 callbackFor( - int positional, Set names) { - Tuple2 /*!*/ fuzzyMatch; - int minMismatchDistance; + int positional, Set names) { + Tuple2 fuzzyMatch; + int? minMismatchDistance; for (var overload in _overloads) { // Ideally, find an exact match. diff --git a/lib/src/callable/plain_css.dart b/lib/src/callable/plain_css.dart index 8e3284520..9a74ed604 100644 --- a/lib/src/callable/plain_css.dart +++ b/lib/src/callable/plain_css.dart @@ -8,7 +8,7 @@ import '../callable.dart'; /// /// This can't be used for mixins. class PlainCssCallable implements Callable { - final String /*!*/ name; + final String name; PlainCssCallable(this.name); diff --git a/lib/src/callable/user_defined.dart b/lib/src/callable/user_defined.dart index 72b9633bb..13a19a30a 100644 --- a/lib/src/callable/user_defined.dart +++ b/lib/src/callable/user_defined.dart @@ -10,12 +10,12 @@ import '../callable.dart'; /// The type parameter [E] should either be `Environment` or `AsyncEnvironment`. class UserDefinedCallable implements Callable { /// The declaration. - final CallableDeclaration /*!*/ declaration; + final CallableDeclaration declaration; /// The environment in which this callable was declared. final E environment; - String /*!*/ get name => declaration.name; + String get name => declaration.name; UserDefinedCallable(this.declaration, this.environment); } diff --git a/lib/src/compile.dart b/lib/src/compile.dart index cdbed09c9..a883d2749 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: a3fcd1e07a08124e0a60ff933944368e49ac0999 +// Checksum: 8053f105aefbe04ac104613c7eab7251d7798655 // // ignore_for_file: unused_import @@ -34,21 +34,21 @@ import 'visitor/serialize.dart'; /// the node-sass compatible API and the executable. /// /// At most one of `importCache` and `nodeImporter` may be provided at once. -CompileResult compile(String /*!*/ path, - {Syntax syntax, - Logger logger, - ImportCache importCache, - NodeImporter nodeImporter, - Iterable functions, - OutputStyle style, +CompileResult compile(String path, + {Syntax? syntax, + Logger? logger, + ImportCache? importCache, + NodeImporter? nodeImporter, + Iterable? functions, + OutputStyle? style, bool useSpaces = true, - int indentWidth, - LineFeed lineFeed, + int? indentWidth, + LineFeed? lineFeed, bool sourceMap = false, bool charset = true}) { // If the syntax is different than the importer would default to, we have to // parse the file manually and we can't store it in the cache. - Stylesheet stylesheet; + Stylesheet? stylesheet; if (nodeImporter == null && (syntax == null || syntax == Syntax.forPath(path))) { importCache ??= ImportCache.none(logger: logger); @@ -62,7 +62,7 @@ CompileResult compile(String /*!*/ path, return _compileStylesheet( // TODO: no ! - stylesheet, + stylesheet!, logger, importCache, nodeImporter, @@ -81,19 +81,19 @@ CompileResult compile(String /*!*/ path, /// /// At most one of `importCache` and `nodeImporter` may be provided at once. CompileResult compileString(String source, - {Syntax syntax, - Logger logger, - ImportCache importCache, - NodeImporter nodeImporter, - Iterable importers, - Iterable loadPaths, - Importer importer, - Iterable functions, - OutputStyle style, + {Syntax? syntax, + Logger? logger, + ImportCache? importCache, + NodeImporter? nodeImporter, + Iterable? importers, + Iterable? loadPaths, + Importer? importer, + Iterable? functions, + OutputStyle? style, bool useSpaces = true, - int indentWidth, - LineFeed lineFeed, - Object url, + int? indentWidth, + LineFeed? lineFeed, + Object? url, bool sourceMap = false, bool charset = true}) { var stylesheet = @@ -119,15 +119,15 @@ CompileResult compileString(String source, /// Arguments are handled as for [compileString]. CompileResult _compileStylesheet( Stylesheet stylesheet, - Logger logger, - ImportCache importCache, - NodeImporter nodeImporter, + Logger? logger, + ImportCache? importCache, + NodeImporter? nodeImporter, Importer importer, - Iterable functions, - OutputStyle style, + Iterable? functions, + OutputStyle? style, bool useSpaces, - int indentWidth, - LineFeed lineFeed, + int? indentWidth, + LineFeed? lineFeed, bool sourceMap, bool charset) { var evaluateResult = evaluate(stylesheet, @@ -153,7 +153,7 @@ CompileResult _compileStylesheet( mapInPlace( resultSourceMap.urls, (url) => url == '' - ? Uri.dataFromString(stylesheet.span.file.getText(0), + ? Uri.dataFromString(stylesheet.span!.file.getText(0), encoding: utf8) .toString() : importCache.sourceMapUrl(Uri.parse(url)).toString()); diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index fae9c7ae0..3b8e96700 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -46,7 +46,7 @@ class Configuration { /// Removes a variable with [name] from this configuration, returning it. /// /// If no such variable exists in this configuration, returns null. - ConfiguredValue remove(String name) => isEmpty ? null : _values.remove(name); + ConfiguredValue? remove(String name) => isEmpty ? null : _values.remove(name); /// Creates a new configuration from this one based on a `@forward` rule. Configuration throughForward(ForwardRule forward) { @@ -86,7 +86,7 @@ class Configuration { /// silently ignored in this case. class ExplicitConfiguration extends Configuration { /// The node whose span indicates where the configuration was declared. - final AstNode /*!*/ nodeWithSpan; + final AstNode nodeWithSpan; ExplicitConfiguration._( Map values, this.nodeWithSpan) diff --git a/lib/src/configured_value.dart b/lib/src/configured_value.dart index bb732a9e2..a1e35f5bc 100644 --- a/lib/src/configured_value.dart +++ b/lib/src/configured_value.dart @@ -10,15 +10,15 @@ import 'value.dart'; /// A variable value that's been configured for a [Configuration]. class ConfiguredValue { /// The value of the variable. - final Value /*!*/ value; + final Value value; /// The span where the variable's configuration was written. - final FileSpan configurationSpan; + final FileSpan? configurationSpan; /// The [AstNode] where the variable's value originated. /// /// This is used to generate source maps. - final AstNode /*?*/ assignmentNode; + final AstNode? assignmentNode; ConfiguredValue(this.value, this.configurationSpan, this.assignmentNode); } diff --git a/lib/src/environment.dart b/lib/src/environment.dart index bd3b682ab..ae567c88b 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: cd1babbf1959dde9f4a31d720a7a48e613ad6aa9 +// Checksum: d53a246eab6683937dcd59a335320de94b316f34 // // ignore_for_file: unused_import @@ -51,25 +51,25 @@ class Environment { /// A map from modules in [_globalModules] to the nodes whose spans /// indicate where those modules were originally loaded. - final Map, AstNode /*!*/ > _globalModuleNodes; + final Map, AstNode> _globalModuleNodes; /// The modules forwarded by this module. /// /// This is `null` if there are no forwarded modules. - Set> _forwardedModules; + Set>? _forwardedModules; /// A map from modules in [_forwardedModules] to the nodes whose spans /// indicate where those modules were originally forwarded. /// /// This is `null` if there are no forwarded modules. - Map, AstNode /*!*/ > _forwardedModuleNodes; + Map, AstNode>? _forwardedModuleNodes; /// Modules forwarded by nested imports at each lexical scope level *beneath /// the global scope*. /// /// This is `null` until it's needed, since most environments won't ever use /// this. - List>> _nestedForwardedModules; + List>>? _nestedForwardedModules; /// Modules from [_modules], [_globalModules], and [_forwardedModules], in the /// order in which they were `@use`d. @@ -81,7 +81,7 @@ class Environment { /// /// The first element is the global scope, and each successive element is /// deeper in the tree. - final List> _variables; + final List> _variables; /// The nodes where each variable in [_variables] was defined. /// @@ -90,12 +90,12 @@ class Environment { /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - final List> _variableNodes; + final List>? _variableNodes; /// A map of variable names to their indices in [_variables]. /// /// This map is filled in as-needed, and may not be complete. - final Map _variableIndices; + final Map _variableIndices; /// A list of functions defined at each lexical scope level. /// @@ -125,8 +125,8 @@ class Environment { /// The content block passed to the lexically-enclosing mixin, or `null` if /// this is not in a mixin, or if no content block was passed. - UserDefinedCallable get content => _content; - UserDefinedCallable _content; + UserDefinedCallable? get content => _content; + UserDefinedCallable? _content; /// Whether the environment is lexically at the root of the document. bool get atRoot => _variables.length == 1; @@ -145,10 +145,10 @@ class Environment { /// /// This is cached to speed up repeated references to the same variable, as /// well as references to the last variable's [FileSpan]. - String _lastVariableName; + String? _lastVariableName; /// The index in [_variables] of the last variable that was accessed. - int _lastVariableIndex; + int? _lastVariableIndex; /// Creates an [Environment]. /// @@ -242,7 +242,7 @@ class Environment { /// [namespace], or if [namespace] is `null` and [module] defines a variable /// with the same name as a variable defined in this environment. void addModule(Module module, AstNode nodeWithSpan, - {String namespace}) { + {String? namespace}) { if (namespace == null) { _globalModules.add(module); _globalModuleNodes[module] = nodeWithSpan; @@ -354,7 +354,8 @@ class Environment { } // TODO: var - var forwardedModuleNodes = (_forwardedModuleNodes ??= {}); + Map, AstNode?> forwardedModuleNodes = + (_forwardedModuleNodes ??= {}); var forwardedVariableNames = forwarded.expand((module) => module.variables.keys).toSet(); @@ -376,13 +377,13 @@ class Environment { if (!shadowed.isEmpty) { _globalModules.add(shadowed); - _globalModuleNodes[shadowed] = _globalModuleNodes.remove(module); + _globalModuleNodes[shadowed] = _globalModuleNodes.remove(module)!; } } } // TODO: no ! - for (var module in forwardedModules.toList()) { + for (var module in forwardedModules!.toList()) { var shadowed = ShadowedModuleView.ifNecessary(module, variables: forwardedVariableNames, mixins: forwardedMixinNames, @@ -434,11 +435,11 @@ class Environment { /// /// Throws a [SassScriptException] if there is no module named [namespace], or /// if multiple global modules expose variables named [name]. - Value getVariable(String name, {String namespace}) { + Value? getVariable(String name, {String? namespace}) { if (namespace != null) return _getModule(namespace).variables[name]; if (_lastVariableName == name) { - return _variables[_lastVariableIndex][name] ?? + return _variables[_lastVariableIndex!][name] ?? _getVariableFromGlobalModule(name); } @@ -466,7 +467,7 @@ class Environment { /// Returns the value of the variable named [name] from a namespaceless /// module, or `null` if no such variable is declared in any namespaceless /// module. - Value _getVariableFromGlobalModule(String name) => + Value? _getVariableFromGlobalModule(String name) => _fromOneModule(name, "variable", (module) => module.variables[name]); /// Returns the node for the variable named [name], or `null` if no such @@ -477,7 +478,7 @@ class Environment { /// [FileSpan] so we can avoid calling [AstNode.span] if the span isn't /// required, since some nodes need to do real work to manufacture a source /// span. - AstNode getVariableNode(String name, {String namespace}) { + AstNode? getVariableNode(String name, {String? namespace}) { var variableNodes = _variableNodes; if (variableNodes == null) { throw StateError( @@ -485,10 +486,10 @@ class Environment { "passed in."); } - if (namespace != null) return _getModule(namespace).variableNodes[name]; + if (namespace != null) return _getModule(namespace).variableNodes![name]; if (_lastVariableName == name) { - return variableNodes[_lastVariableIndex][name] ?? + return variableNodes[_lastVariableIndex!][name] ?? _getVariableNodeFromGlobalModule(name); } @@ -517,11 +518,11 @@ class Environment { /// [FileSpan] so we can avoid calling [AstNode.span] if the span isn't /// required, since some nodes need to do real work to manufacture a source /// span. - AstNode _getVariableNodeFromGlobalModule(String name) { + AstNode? _getVariableNodeFromGlobalModule(String name) { // We don't need to worry about multiple modules defining the same variable, // because that's already been checked by [getVariable]. for (var module in _globalModules) { - var value = module.variableNodes[name]; + var value = module.variableNodes![name]; if (value != null) return value; } return null; @@ -534,7 +535,7 @@ class Environment { /// /// Throws a [SassScriptException] if there is no module named [namespace], or /// if multiple global modules expose functions named [name]. - bool globalVariableExists(String name, {String namespace}) { + bool globalVariableExists(String name, {String? namespace}) { if (namespace != null) { return _getModule(namespace).variables.containsKey(name); } @@ -544,7 +545,7 @@ class Environment { /// Returns the index of the last map in [_variables] that has a [name] key, /// or `null` if none exists. - int _variableIndex(String name) { + int? _variableIndex(String name) { for (var i = _variables.length - 1; i >= 0; i--) { if (_variables[i].containsKey(name)) return i; } @@ -569,8 +570,8 @@ class Environment { /// defined with the given namespace, if no variable with the given [name] is /// defined in module with the given namespace, or if no [namespace] is passed /// and multiple global modules define variables named [name]. - void setVariable(String name, Value /*!*/ value, AstNode /*?*/ nodeWithSpan, - {String namespace, bool global = false}) { + void setVariable(String name, Value value, AstNode? nodeWithSpan, + {String? namespace, bool global = false}) { if (namespace != null) { _getModule(namespace).setVariable(name, value, nodeWithSpan); return; @@ -616,7 +617,7 @@ class Environment { } var index = _lastVariableName == name - ? _lastVariableIndex /*!*/ + ? _lastVariableIndex! : _variableIndices.putIfAbsent( name, () => _variableIndex(name) ?? _variables.length - 1); if (!_inSemiGlobalScope && index == 0) { @@ -627,7 +628,7 @@ class Environment { _lastVariableName = name; _lastVariableIndex = index; _variables[index][name] = value; - _variableNodes?.andGet(index)[name] = nodeWithSpan; + _variableNodes?.andGet(index)![name] = nodeWithSpan!; } /// Sets the variable named [name] to [value], associated with @@ -639,14 +640,14 @@ class Environment { /// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - void setLocalVariable(String name, Value value, AstNode /*?*/ nodeWithSpan) { + void setLocalVariable(String name, Value value, AstNode? nodeWithSpan) { var index = _variables.length - 1; _lastVariableName = name; _lastVariableIndex = index; _variableIndices[name] = index; _variables[index][name] = value; if (nodeWithSpan != null) { - _variableNodes?.andGet(index)[name] = nodeWithSpan; + _variableNodes?.andGet(index)![name] = nodeWithSpan; } } @@ -655,7 +656,7 @@ class Environment { /// /// Throws a [SassScriptException] if there is no module named [namespace], or /// if multiple global modules expose functions named [name]. - Callable getFunction(String name, {String namespace}) { + Callable? getFunction(String name, {String? namespace}) { if (namespace != null) return _getModule(namespace).functions[name]; var index = _functionIndices[name]; @@ -673,12 +674,12 @@ class Environment { /// Returns the value of the function named [name] from a namespaceless /// module, or `null` if no such function is declared in any namespaceless /// module. - Callable _getFunctionFromGlobalModule(String name) => + Callable? _getFunctionFromGlobalModule(String name) => _fromOneModule(name, "function", (module) => module.functions[name]); /// Returns the index of the last map in [_functions] that has a [name] key, /// or `null` if none exists. - int _functionIndex(String name) { + int? _functionIndex(String name) { for (var i = _functions.length - 1; i >= 0; i--) { if (_functions[i].containsKey(name)) return i; } @@ -689,7 +690,7 @@ class Environment { /// /// Throws a [SassScriptException] if there is no module named [namespace], or /// if multiple global modules expose functions named [name]. - bool functionExists(String name, {String namespace}) => + bool functionExists(String name, {String? namespace}) => getFunction(name, namespace: namespace) != null; /// Sets the variable named [name] to [value] in the current scope. @@ -704,7 +705,7 @@ class Environment { /// /// Throws a [SassScriptException] if there is no module named [namespace], or /// if multiple global modules expose mixins named [name]. - Callable getMixin(String name, {String namespace}) { + Callable? getMixin(String name, {String? namespace}) { if (namespace != null) return _getModule(namespace).mixins[name]; var index = _mixinIndices[name]; @@ -722,12 +723,12 @@ class Environment { /// Returns the value of the mixin named [name] from a namespaceless /// module, or `null` if no such mixin is declared in any namespaceless /// module. - Callable _getMixinFromGlobalModule(String name) => + Callable? _getMixinFromGlobalModule(String name) => _fromOneModule(name, "mixin", (module) => module.mixins[name]); /// Returns the index of the last map in [_mixins] that has a [name] key, or /// `null` if none exists. - int _mixinIndex(String name) { + int? _mixinIndex(String name) { for (var i = _mixins.length - 1; i >= 0; i--) { if (_mixins[i].containsKey(name)) return i; } @@ -738,7 +739,7 @@ class Environment { /// /// Throws a [SassScriptException] if there is no module named [namespace], or /// if multiple global modules expose functions named [name]. - bool mixinExists(String name, {String namespace}) => + bool mixinExists(String name, {String? namespace}) => getMixin(name, namespace: namespace) != null; /// Sets the variable named [name] to [value] in the current scope. @@ -749,7 +750,7 @@ class Environment { } /// Sets [content] as [this.content] for the duration of [callback]. - void withContent(UserDefinedCallable content, void callback()) { + void withContent(UserDefinedCallable? content, void callback()) { var oldContent = _content; _content = content; callback(); @@ -827,7 +828,7 @@ class Environment { for (var i = 0; i < _variables.length; i++) { var values = _variables[i]; var nodes = - _variableNodes == null ? {} : _variableNodes[i]; + _variableNodes == null ? {} : _variableNodes![i]; // TODO: var nodes = _variableNodes.andGet(i) ?? {}; for (var entry in values.entries) { // Implicit configurations are never invalid, making [configurationSpan] @@ -884,8 +885,8 @@ class Environment { /// /// The [type] should be the singular name of the value type being returned. /// It's used to format an appropriate error message. - T _fromOneModule( - String name, String type, T /*?*/ callback(Module module)) { + T? _fromOneModule( + String name, String type, T? callback(Module module)) { var nestedForwardedModules = _nestedForwardedModules; if (nestedForwardedModules != null) { for (var modules in nestedForwardedModules.reversed) { @@ -896,21 +897,22 @@ class Environment { } } - T value; - Object identity; + T? value; + Object? identity; for (var module in _globalModules) { var valueInModule = callback(module); if (valueInModule == null) continue; - var identityFromModule = valueInModule is Callable + Object? identityFromModule = valueInModule is Callable ? valueInModule : module.variableIdentity(name); if (identityFromModule == identity) continue; if (value != null) { // TODO no !, as - var spans = _globalModuleNodes.entries.map( - (entry) => callback(entry.key).andThen((_) => entry.value.span)); + var spans = _globalModuleNodes.entries.map((entry) => + callback(entry.key) + .andThen(((_) => entry.value.span!) as FileSpan Function(T)?)); throw MultiSpanSassScriptException( 'This $type is available from multiple global modules.', @@ -929,11 +931,11 @@ class Environment { /// A module that represents the top-level members defined in an [Environment]. class _EnvironmentModule implements Module { - Uri get url => css?.span.sourceUrl; + Uri? get url => css?.span!.sourceUrl; final List> upstream; final Map variables; - final Map variableNodes; + final Map? variableNodes; final Map functions; final Map mixins; final Extender extender; @@ -954,7 +956,7 @@ class _EnvironmentModule implements Module { factory _EnvironmentModule( Environment environment, CssStylesheet css, Extender extender, - {Set> forwarded}) { + {Set>? forwarded}) { forwarded ??= const {}; return _EnvironmentModule._( environment, @@ -966,7 +968,7 @@ class _EnvironmentModule implements Module { environment._variableNodes.andThen((nodes) => _memberMap( // TODO: no ! nodes.first, - forwarded.map((module) => module.variableNodes /*!*/))), + forwarded!.map((module) => module.variableNodes!))), _memberMap(environment._functions.first, forwarded.map((module) => module.functions)), _memberMap(environment._mixins.first, @@ -1026,11 +1028,11 @@ class _EnvironmentModule implements Module { this.variableNodes, this.functions, this.mixins, - {@required this.transitivelyContainsCss, - @required this.transitivelyContainsExtensions}) + {required this.transitivelyContainsCss, + required this.transitivelyContainsExtensions}) : upstream = _environment._allModules; - void setVariable(String name, Value value, AstNode nodeWithSpan) { + void setVariable(String name, Value value, AstNode? nodeWithSpan) { var module = _modulesByVariable[name]; if (module != null) { module.setVariable(name, value, nodeWithSpan); diff --git a/lib/src/exception.dart b/lib/src/exception.dart index 3aaf7b3c1..347c70b6f 100644 --- a/lib/src/exception.dart +++ b/lib/src/exception.dart @@ -18,11 +18,11 @@ class SassException extends SourceSpanException { /// This includes [span]. Trace get trace => Trace([frameForSpan(span, "root stylesheet")]); - FileSpan /*?*/ get span => super.span as FileSpan; + FileSpan? get span => super.span as FileSpan?; - SassException(String message, FileSpan /*?*/ span) : super(message, span); + SassException(String message, FileSpan? span) : super(message, span); - String toString({Object color}) { + String toString({Object? color}) { var buffer = StringBuffer("Error: $message"); span?.highlight(color: color).andThen(buffer.write); @@ -83,14 +83,14 @@ class MultiSpanSassException extends SassException final String primaryLabel; final Map secondarySpans; - MultiSpanSassException(String message, FileSpan span, this.primaryLabel, - Map secondarySpans) + MultiSpanSassException(String message, FileSpan? span, this.primaryLabel, + Map secondarySpans) : secondarySpans = Map.unmodifiable(secondarySpans), super(message, span); - String toString({Object color, String secondaryColor}) { + String toString({Object? color, String? secondaryColor}) { var useColor = false; - String primaryColor; + String? primaryColor; if (color is String) { useColor = true; primaryColor = color; @@ -120,7 +120,7 @@ class MultiSpanSassException extends SassException class SassRuntimeException extends SassException { final Trace trace; - SassRuntimeException(String message, FileSpan span, this.trace) + SassRuntimeException(String message, FileSpan? span, this.trace) : super(message, span); } @@ -129,12 +129,8 @@ class MultiSpanSassRuntimeException extends MultiSpanSassException implements SassRuntimeException { final Trace trace; - MultiSpanSassRuntimeException( - String message, - FileSpan span, - String primaryLabel, - Map secondarySpans, - this.trace) + MultiSpanSassRuntimeException(String message, FileSpan? span, + String primaryLabel, Map secondarySpans, this.trace) : super(message, span, primaryLabel, secondarySpans); } @@ -145,7 +141,7 @@ class SassFormatException extends SassException int get offset => span?.start.offset; - SassFormatException(String message, FileSpan span) : super(message, span); + SassFormatException(String message, FileSpan? span) : super(message, span); } /// An exception thrown by SassScript. @@ -171,8 +167,8 @@ class MultiSpanSassScriptException extends SassScriptException { /// See [MultiSourceSpanException.secondarySpans]. final Map secondarySpans; - MultiSpanSassScriptException(String message, this.primaryLabel, - Map secondarySpans) + MultiSpanSassScriptException( + String message, this.primaryLabel, Map secondarySpans) : secondarySpans = Map.unmodifiable(secondarySpans), super(message); } diff --git a/lib/src/executable/compile_stylesheet.dart b/lib/src/executable/compile_stylesheet.dart index 2359a05d6..ed46039d6 100644 --- a/lib/src/executable/compile_stylesheet.dart +++ b/lib/src/executable/compile_stylesheet.dart @@ -30,7 +30,7 @@ import 'options.dart'; /// or that of a file it imports is more recent than [destination]'s /// modification time. Note that these modification times are cached by [graph]. Future compileStylesheet(ExecutableOptions options, StylesheetGraph graph, - String source, String destination, + String? source, String? destination, {bool ifModified = false}) async { var importer = FilesystemImporter('.'); if (ifModified) { @@ -121,7 +121,7 @@ Future compileStylesheet(ExecutableOptions options, StylesheetGraph graph, if (options.color) buffer.write('\u001b[32m'); var sourceName = source == null ? 'stdin' : p.prettyUri(p.toUri(source)); - var destinationName = p.prettyUri(p.toUri(destination)); + var destinationName = p.prettyUri(p.toUri(destination!)); buffer.write('Compiled $sourceName to $destinationName.'); if (options.color) buffer.write('\u001b[0m'); print(buffer); @@ -136,7 +136,7 @@ Future compileStylesheet(ExecutableOptions options, StylesheetGraph graph, /// /// Returns the source map comment to add to the end of the CSS file. String _writeSourceMap( - ExecutableOptions options, SingleMapping sourceMap, String destination) { + ExecutableOptions options, SingleMapping? sourceMap, String? destination) { if (sourceMap == null) return ""; if (destination != null) { @@ -157,7 +157,7 @@ String _writeSourceMap( } else { // [destination] can't be null here because --embed-source-map is // incompatible with writing to stdout. - var sourceMapPath = destination + '.map'; + var sourceMapPath = destination! + '.map'; ensureDir(p.dirname(sourceMapPath)); writeFile(sourceMapPath, sourceMapText); diff --git a/lib/src/executable/options.dart b/lib/src/executable/options.dart index ca33e8fa7..9431f46f0 100644 --- a/lib/src/executable/options.dart +++ b/lib/src/executable/options.dart @@ -130,7 +130,7 @@ class ExecutableOptions { final ArgResults _options; /// Whether to print the version of Sass and exit. - bool get version => _options['version'] as bool /*!*/; + bool get version => _options['version'] as bool; // TODO: // /// Whether to run an interactive shell. @@ -150,11 +150,11 @@ class ExecutableOptions { // }(); /// Whether to run an interactive shell. - /*late*/ bool /*!*/ get interactive { - if (_interactive != null) return _interactive; + /*late*/ bool get interactive { + if (_interactive != null) return _interactive!; // TODO: remove ? - var interactive = (_interactive = _options['interactive'] as bool); + var interactive = (_interactive = _options['interactive'] as bool?)!; if (!interactive) return false; var invalidOptions = [ @@ -169,26 +169,26 @@ class ExecutableOptions { return true; } - bool _interactive; + bool? _interactive; /// Whether to parse the source file with the indented syntax. /// /// This may be `null`, indicating that this should be determined by each /// stylesheet's extension. - bool get indented => _ifParsed('indented') as bool /*!*/; + bool get indented => _ifParsed('indented') as bool; /// Whether to use ANSI terminal colors. bool get color => _options.wasParsed('color') - ? _options['color'] as bool /*!*/ + ? _options['color'] as bool : supportsAnsiEscapes; /// Whether to use non-ASCII Unicode glyphs. bool get unicode => _options.wasParsed('unicode') - ? _options['unicode'] as bool /*!*/ + ? _options['unicode'] as bool : !term_glyph.ascii; /// Whether to silence normal output. - bool get quiet => _options['quiet'] as bool /*!*/; + bool get quiet => _options['quiet'] as bool; /// The logger to use to emit messages from Sass. Logger get logger => quiet ? Logger.quiet : Logger.stderr(color: color); @@ -200,35 +200,36 @@ class ExecutableOptions { /// Whether to include a `@charset` declaration or a BOM if the stylesheet /// contains any non-ASCII characters. - bool get charset => _options['charset'] as bool /*!*/; + bool get charset => _options['charset'] as bool; /// The set of paths Sass in which should look for imported files. - List get loadPaths => _options['load-path'] as List /*!*/; + List get loadPaths => _options['load-path'] as List; /// Whether to run the evaluator in asynchronous mode, for debugging purposes. - bool get asynchronous => _options['async'] as bool /*!*/; + bool get asynchronous => _options['async'] as bool; /// Whether to print the full Dart stack trace on exceptions. - bool get trace => _options['trace'] as bool /*!*/; + bool get trace => _options['trace'] as bool; /// Whether to update only files that have changed since the last compilation. - bool get update => _options['update'] as bool /*!*/; + bool get update => _options['update'] as bool; /// Whether to continuously watch the filesystem for changes. - bool get watch => _options['watch'] as bool /*!*/; + bool get watch => _options['watch'] as bool; /// Whether to manually poll for changes when watching. - bool get poll => _options['poll'] as bool /*!*/; + bool get poll => _options['poll'] as bool; /// Whether to stop compiling additional files once one file produces an /// error. - bool get stopOnError => _options['stop-on-error'] as bool /*!*/; + bool get stopOnError => _options['stop-on-error'] as bool; /// Whether to emit error messages as CSS stylesheets bool get emitErrorCss => - _options['error-css'] as bool /*!*/ ?? + _options['error-css'] as bool ?? // TODO: remove as - sourcesToDestinations.values.any((destination) => destination != null); + sourcesToDestinations.values.any( + ((destination) => destination != null) as bool Function(String?)); /// A map from source paths to the destination paths where the compiled CSS /// should be written. @@ -238,30 +239,30 @@ class ExecutableOptions { /// A `null` source indicates that a stylesheet should be read from standard /// input. A `null` destination indicates that a stylesheet should be written /// to standard output. - Map /*!*/ get sourcesToDestinations { + Map get sourcesToDestinations { _ensureSources(); - return _sourcesToDestinations; + return _sourcesToDestinations!; } - Map _sourcesToDestinations; + Map? _sourcesToDestinations; /// A map from source directories to the destination directories where the /// compiled CSS for stylesheets in the source directories should be written. /// /// Considers keys to be the same if they represent the same path on disk. - Map /*!*/ get sourceDirectoriesToDestinations { + Map get sourceDirectoriesToDestinations { _ensureSources(); return _sourceDirectoriesToDestinations; } - /*late final*/ Map _sourceDirectoriesToDestinations; + late final Map _sourceDirectoriesToDestinations; /// Ensure that both [sourcesToDestinations] and [sourceDirectories] have been /// computed. void _ensureSources() { if (_sourcesToDestinations != null) return; - var stdin = _options['stdin'] as bool /*!*/; + var stdin = _options['stdin'] as bool; if (_options.rest.isEmpty && !stdin) _fail("Compile Sass to CSS."); var directories = {}; @@ -324,8 +325,8 @@ class ExecutableOptions { } } - var map = p.PathMap< - String /*?*/ >(); // p.PathMap.of() doesn't support null keys. + var map = + p.PathMap(); // p.PathMap.of() doesn't support null keys. map[source] = destination; _sourcesToDestinations = UnmodifiableMapView(map); } @@ -338,7 +339,7 @@ class ExecutableOptions { // Track [seen] separately from `sourcesToDestinations.keys` because we want // to report errors for sources as users entered them, rather than after // directories have been resolved. - var seen = {}; + var seen = {}; var sourcesToDestinations = p.PathMap(); var sourceDirectoriesToDestinations = p.PathMap(); for (var argument in _options.rest) { @@ -402,8 +403,7 @@ class ExecutableOptions { /// Returns the sub-map of [sourcesToDestinations] for the given [source] and /// [destination] directories. - Map _listSourceDirectory( - String /*!*/ source, String /*!*/ destination) { + Map _listSourceDirectory(String source, String destination) { return { for (var path in listDir(source, recursive: true)) if (_isEntrypoint(path) && @@ -437,7 +437,7 @@ class ExecutableOptions { _fail("--embed-source-map isn't allowed with --no-source-map."); } } - if (!_writeToStdout) return _options['source-map'] as bool /*!*/; + if (!_writeToStdout) return _options['source-map'] as bool; if (_ifParsed('source-map-urls') == 'relative') { _fail( @@ -445,7 +445,7 @@ class ExecutableOptions { } if (_options['embed-source-map'] as bool) { - return _options['source-map'] as bool /*!*/; + return _options['source-map'] as bool; } else if (_ifParsed('source-map') == true) { _fail( "When printing to stdout, --source-map requires --embed-source-map."); @@ -461,10 +461,10 @@ class ExecutableOptions { } /// Whether to embed the generated source map as a data URL in the output CSS. - bool get embedSourceMap => _options['embed-source-map'] as bool /*!*/; + bool get embedSourceMap => _options['embed-source-map'] as bool; /// Whether to embed the source files in the generated source map. - bool get embedSources => _options['embed-sources'] as bool /*!*/; + bool get embedSources => _options['embed-sources'] as bool; /// Parses options from [args]. /// @@ -489,20 +489,20 @@ class ExecutableOptions { /// [destination]) according to the `source-map-urls` option. /// /// If [url] isn't a `file:` URL, returns it as-is. - Uri sourceMapUrl(Uri url, String destination) { + Uri sourceMapUrl(Uri url, String? destination) { if (url.scheme.isNotEmpty && url.scheme != 'file') return url; var path = p.fromUri(url); return p.toUri(_options['source-map-urls'] == 'relative' && !_writeToStdout // [destination] can't be null here because --source-map-urls=relative // is incompatible with writing to stdout. - ? p.relative(path, from: p.dirname(destination /*!*/)) + ? p.relative(path, from: p.dirname(destination!)) : p.absolute(path)); } /// Returns the value of [name] in [options] if it was explicitly provided by /// the user, and `null` otherwise. - Object _ifParsed(String name) => + Object? _ifParsed(String name) => _options.wasParsed(name) ? _options[name] : null; } diff --git a/lib/src/executable/repl.dart b/lib/src/executable/repl.dart index eb3e4fca4..7f0a732bd 100644 --- a/lib/src/executable/repl.dart +++ b/lib/src/executable/repl.dart @@ -52,7 +52,7 @@ void _logError(SassException error, StackTrace stackTrace, String line, Repl repl, ExecutableOptions options, TrackingLogger logger) { // If the error doesn't come from the repl line, or if something was logged // after the user's input, just print the error normally. - if (error.span.sourceUrl != null || + if (error.span!.sourceUrl != null || (!options.quiet && (logger.emittedDebug || logger.emittedWarning))) { print(error.toString(color: options.color)); return; @@ -62,17 +62,17 @@ void _logError(SassException error, StackTrace stackTrace, String line, var buffer = StringBuffer(); if (options.color) buffer.write("\u001b[31m"); - var spacesBeforeError = repl.prompt.length + error.span.start.column; - if (options.color && error.span.start.column < line.length) { + var spacesBeforeError = repl.prompt.length + error.span!.start.column; + if (options.color && error.span!.start.column < line.length) { // Position the cursor at the beginning of the error text. buffer.write("\u001b[1F\u001b[${spacesBeforeError}C"); // Rewrite the bad input, this time in red text. - buffer.writeln(error.span.text); + buffer.writeln(error.span!.text); } // Write arrows underneath the error text. buffer.write(" " * spacesBeforeError); - buffer.writeln("^" * math.max(1, error.span.length)); + buffer.writeln("^" * math.max(1, error.span!.length)); if (options.color) buffer.write("\u001b[0m"); buffer.writeln("Error: ${error.message}"); diff --git a/lib/src/executable/watch.dart b/lib/src/executable/watch.dart index fb318cf45..bcd3da179 100644 --- a/lib/src/executable/watch.dart +++ b/lib/src/executable/watch.dart @@ -90,7 +90,7 @@ class _Watcher { } /// Deletes the file at [path] and prints a message about it. - void _delete(String /*!*/ path) { + void _delete(String path) { try { deleteFile(path); var buffer = StringBuffer(); @@ -211,7 +211,7 @@ class _Watcher { return [ for (var entry in typeForPath.entries) - WatchEvent(entry.value, entry.key) + WatchEvent(entry.value, entry.key!) ]; }); } @@ -220,8 +220,7 @@ class _Watcher { /// necessary. /// /// Returns whether all recompilations succeeded. - Future _recompileDownstream( - Iterable nodes) async { + Future _recompileDownstream(Iterable nodes) async { var seen = {}; var toRecompile = Queue.of(nodes); @@ -257,7 +256,7 @@ class _Watcher { /// the CSS file it should be compiled to. /// /// Otherwise, returns `null`. - String _destinationFor(String source) { + String? _destinationFor(String source) { var destination = _sourcesToDestinations(_options)[source]; if (destination != null) return destination; if (p.basename(source).startsWith('_')) return null; @@ -279,10 +278,10 @@ class _Watcher { /// Exposes [options.sourcesToDestinations] as a non-nullable map, since stdin /// inputs and stdout outputs aren't allowed in `--watch` mode. Map _sourcesToDestinations(ExecutableOptions options) => - options.sourcesToDestinations.cast(); + options.sourcesToDestinations.cast(); /// Exposes [options.sourcesDirectoriesToDestinations] as a non-nullable map, /// since stdin inputs and stdout outputs aren't allowed in `--watch` mode. Map _sourceDirectoriesToDestinations( ExecutableOptions options) => - options.sourceDirectoriesToDestinations.cast(); + options.sourceDirectoriesToDestinations.cast(); diff --git a/lib/src/extend/empty_extender.dart b/lib/src/extend/empty_extender.dart index db47eb31a..a45d0633a 100644 --- a/lib/src/extend/empty_extender.dart +++ b/lib/src/extend/empty_extender.dart @@ -25,15 +25,15 @@ class EmptyExtender implements Extender { const []; ModifiableCssValue addSelector( - SelectorList selector, FileSpan /*!*/ span, - [List mediaContext]) { + SelectorList selector, FileSpan span, + [List? mediaContext]) { throw UnsupportedError( "addSelector() can't be called for a const Extender."); } void addExtension( CssValue extender, SimpleSelector target, ExtendRule extend, - [List mediaContext]) { + [List? mediaContext]) { throw UnsupportedError( "addExtension() can't be called for a const Extender."); } diff --git a/lib/src/extend/extender.dart b/lib/src/extend/extender.dart index 277b9a919..0677a980a 100644 --- a/lib/src/extend/extender.dart +++ b/lib/src/extend/extender.dart @@ -35,7 +35,7 @@ class Extender { /// A map from all extended simple selectors to the sources of those /// extensions. - final Map /*!*/ > _extensions; + final Map> _extensions; /// A map from all simple selectors in extenders to the extensions that those /// extenders define. @@ -168,8 +168,8 @@ class Extender { /// The [mediaContext] is the media query context in which the selector was /// defined, or `null` if it was defined at the top level of the document. ModifiableCssValue addSelector( - SelectorList selector, FileSpan /*?*/ span, - [List mediaContext]) { + SelectorList selector, FileSpan? span, + [List? mediaContext]) { var originalSelector = selector; if (!originalSelector.isInvisible) { for (var complex in originalSelector.components) { @@ -182,7 +182,7 @@ class Extender { selector = _extendList(originalSelector, _extensions, mediaContext); } on SassException catch (error) { throw SassException( - "From ${error.span.message('')}\n" + "From ${error.span!.message('')}\n" "${error.message}", span); } @@ -203,11 +203,11 @@ class Extender { for (var component in complex.components) { if (component is! CompoundSelector) continue; - for (var simple in (component as CompoundSelector).components) { + for (var simple in component.components) { _selectors.putIfAbsent(simple, () => {}).add(selector); if (simple is! PseudoSelector) return; - var selectorInPseudo = (simple as PseudoSelector).selector; + var selectorInPseudo = simple.selector; if (selectorInPseudo != null) { _registerSelector(selectorInPseudo, selector); } @@ -227,11 +227,11 @@ class Extender { /// context indicates no media queries. void addExtension( CssValue extender, SimpleSelector target, ExtendRule extend, - [List mediaContext]) { + [List? mediaContext]) { var selectors = _selectors[target]; var existingExtensions = _extensionsByExtender[target]; - Map newExtensions; + Map? newExtensions; var sources = _extensions.putIfAbsent(target, () => {}); for (var complex in extender.value.components) { var state = Extension( @@ -298,26 +298,23 @@ class Extender { /// .z.b {@extend .c} /// /// Returns `null` if there are no extensions to add. - Map> - _extendExistingExtensions( - List extensions, - Map /*!*/ > - newExtensions) { - Map> - additionalExtensions; + Map>? + _extendExistingExtensions(List extensions, + Map>? newExtensions) { + Map>? additionalExtensions; for (var extension in extensions.toList()) { - var sources = _extensions[extension.target] /*!*/; + var sources = _extensions[extension.target!]!; // [_extendExistingSelectors] would have thrown already. - List selectors; + List? selectors; try { selectors = _extendComplex( extension.extender, newExtensions, extension.mediaContext); if (selectors == null) continue; } on SassException catch (error) { throw SassException( - "From ${extension.extenderSpan.message('')}\n" + "From ${extension.extenderSpan!.message('')}\n" "${error.message}", error.span); } @@ -350,10 +347,10 @@ class Extender { } } - if (newExtensions.containsKey(extension.target)) { + if (newExtensions!.containsKey(extension.target)) { additionalExtensions ??= {}; var additionalSources = - additionalExtensions.putIfAbsent(extension.target, () => {}); + additionalExtensions.putIfAbsent(extension.target!, () => {}); additionalSources[complex] = withExtender; } } @@ -370,7 +367,7 @@ class Extender { /// Extend [extensions] using [newExtensions]. void _extendExistingSelectors(Set> selectors, - Map> newExtensions) { + Map?>? newExtensions) { for (var selector in selectors) { var oldValue = selector.value; try { @@ -381,7 +378,7 @@ class Extender { // TODO(nweiz): Make this a MultiSpanSassException. throw SassException( - "From ${selector.span.message('')}\n" + "From ${selector.span!.message('')}\n" "${error.message}", error.span); } @@ -400,15 +397,15 @@ class Extender { void addExtensions(Iterable extenders) { // Extensions already in [this] whose extenders are extended by // [extensions], and thus which need to be updated. - List extensionsToExtend; + List? extensionsToExtend; // Selectors that contain simple selectors that are extended by // [extensions], and thus which need to be extended themselves. - Set> selectorsToExtend; + Set>? selectorsToExtend; // An extension map with the same structure as [_extensions] that only // includes extensions from [extenders]. - Map> newExtensions; + Map?>? newExtensions; for (var extender in extenders) { if (extender.isEmpty) continue; @@ -421,23 +418,23 @@ class Extender { var extensionsForTarget = _extensionsByExtender[target]; if (extensionsForTarget != null) { extensionsToExtend ??= []; - extensionsToExtend.addAll(extensionsForTarget); + extensionsToExtend!.addAll(extensionsForTarget); } // Find existing selectors to extend. var selectorsForTarget = _selectors[target]; if (selectorsForTarget != null) { selectorsToExtend ??= {}; - selectorsToExtend.addAll(selectorsForTarget); + selectorsToExtend!.addAll(selectorsForTarget); } // Add [newSources] to [_extensions]. var existingSources = _extensions[target]; if (existingSources == null) { - _extensions[target] = extender._extensions[target]; + _extensions[target] = extender._extensions[target]!; if (extensionsForTarget != null || selectorsForTarget != null) { newExtensions ??= {}; - newExtensions[target] = extender._extensions[target]; + newExtensions![target] = extender._extensions[target]; } } else { newSources.forEach((extender, extension) { @@ -448,8 +445,8 @@ class Extender { if (extensionsForTarget != null || selectorsForTarget != null) { newExtensions ??= {}; - newExtensions - .putIfAbsent(target, () => {}) + newExtensions! + .putIfAbsent(target, () => {})! .putIfAbsent(extender, () => extension); } }); @@ -462,22 +459,25 @@ class Extender { if (extensionsToExtend != null) { // We can ignore the return value here because it's only useful for extend // loops, which can't exist across module boundaries. - _extendExistingExtensions(extensionsToExtend, newExtensions); + _extendExistingExtensions( + extensionsToExtend!, + newExtensions + as Map>?); } if (selectorsToExtend != null) { - _extendExistingSelectors(selectorsToExtend, newExtensions); + _extendExistingSelectors(selectorsToExtend!, newExtensions); } } /// Extends [list] using [extensions]. SelectorList _extendList( SelectorList list, - Map> extensions, - List mediaQueryContext) { + Map?>? extensions, + List? mediaQueryContext) { // This could be written more simply using [List.map], but we want to avoid // any allocations in the common case where no extends apply. - List extended; + List? extended; for (var i = 0; i < list.components.length; i++) { var complex = list.components[i]; var result = _extendComplex(complex, extensions, mediaQueryContext); @@ -496,10 +496,10 @@ class Extender { /// Extends [complex] using [extensions], and returns the contents of a /// [SelectorList]. - List _extendComplex( + List? _extendComplex( ComplexSelector complex, - Map> extensions, - List mediaQueryContext) { + Map?>? extensions, + List? mediaQueryContext) { // The complex selectors that each compound selector in [complex.components] // can expand to. // @@ -517,7 +517,7 @@ class Extender { // // This could be written more simply using [List.map], but we want to avoid // any allocations in the common case where no extends apply. - List> extendedNotExpanded; + List>? extendedNotExpanded; var isOriginal = _originals.contains(complex); for (var i = 0; i < complex.components.length; i++) { var component = complex.components[i]; @@ -549,7 +549,8 @@ class Extender { return paths(extendedNotExpanded).expand((path) { return weave(path.map((complex) => complex.components).toList()) .map((components) { - var outputComplex = ComplexSelector(components, + var outputComplex = ComplexSelector( + components as Iterable, lineBreak: complex.lineBreak || path.any((inputComplex) => inputComplex.lineBreak)); @@ -571,19 +572,19 @@ class Extender { /// /// The [inOriginal] parameter indicates whether this is in an original /// complex selector, meaning that [compound] should not be trimmed out. - List _extendCompound( + List? _extendCompound( CompoundSelector compound, - Map> extensions, - List mediaQueryContext, - {bool inOriginal}) { + Map?>? extensions, + List? mediaQueryContext, + {bool? inOriginal}) { // If there's more than one target and they all need to match, we track // which targets are actually extended. - var targetsUsed = _mode == ExtendMode.normal || extensions.length < 2 + var targetsUsed = _mode == ExtendMode.normal || extensions!.length < 2 ? null : {}; // The complex selectors produced from each component of [compound]. - List> options; + List>? options; for (var i = 0; i < compound.components.length; i++) { var simple = compound.components[i]; var extended = @@ -605,7 +606,7 @@ class Extender { // If [_mode] isn't [ExtendMode.normal] and we didn't use all the targets in // [extensions], extension fails for [compound]. - if (targetsUsed != null && targetsUsed.length != extensions.length) { + if (targetsUsed != null && targetsUsed.length != extensions!.length) { return null; } @@ -644,7 +645,7 @@ class Extender { // ] var first = _mode != ExtendMode.replace; var unifiedPaths = paths(options).map((path) { - List> complexes; + List>? complexes; if (first) { // The first path is always the original selector. We can't just // return [compound] directly because pseudo selectors may be @@ -661,7 +662,7 @@ class Extender { ]; } else { var toUnify = QueueList>(); - List originals; + List? originals; for (var state in path) { if (state.isOriginal) { originals ??= []; @@ -696,28 +697,28 @@ class Extender { // If we're preserving the original selector, mark the first unification as // such so [_trim] doesn't get rid of it. var isOriginal = (ComplexSelector _) => false; - if (inOriginal && _mode != ExtendMode.replace) { - var original = unifiedPaths.first.first; + if (inOriginal! && _mode != ExtendMode.replace) { + var original = unifiedPaths.first!.first; isOriginal = (complex) => complex == original; } return _trim( unifiedPaths .where((complexes) => complexes != null) - .expand((l) => l) + .expand((l) => l!) .toList(), isOriginal); } - Iterable> _extendSimple( + Iterable>? _extendSimple( SimpleSelector simple, - Map> extensions, - List mediaQueryContext, - Set targetsUsed) { + Map?>? extensions, + List? mediaQueryContext, + Set? targetsUsed) { // Extends [simple] without extending the contents of any selector pseudos // it contains. - List withoutPseudo(SimpleSelector simple) { - var extenders = extensions[simple]; + List? withoutPseudo(SimpleSelector simple) { + var extenders = extensions![simple]; if (extenders == null) return null; targetsUsed?.add(simple); if (_mode == ExtendMode.replace) return extenders.values.toList(); @@ -755,11 +756,11 @@ class Extender { /// Extends [pseudo] using [extensions], and returns a list of resulting /// pseudo selectors. - List _extendPseudo( + List? _extendPseudo( PseudoSelector pseudo, - Map> extensions, - List mediaQueryContext) { - var extended = _extendList(pseudo.selector, extensions, mediaQueryContext); + Map?>? extensions, + List? mediaQueryContext) { + var extended = _extendList(pseudo.selector!, extensions, mediaQueryContext); if (identical(extended, pseudo.selector)) return null; // For `:not()`, we usually want to get rid of any complex selectors because @@ -769,7 +770,7 @@ class Extender { // either way we aren't breaking anything that isn't already broken. Iterable complexes = extended.components; if (pseudo.normalizedName == "not" && - !pseudo.selector.components + !pseudo.selector!.components .any((complex) => complex.components.length > 1) && extended.components.any((complex) => complex.components.length == 1)) { complexes = extended.components @@ -794,7 +795,7 @@ class Extender { // supporting it properly would make this code and the code calling it // a lot more complicated, so it's not supported for now. if (innerPseudo.normalizedName != 'matches') return []; - return innerPseudo.selector.components; + return innerPseudo.selector!.components; case 'matches': case 'any': @@ -806,7 +807,7 @@ class Extender { // more complex cases that likely aren't worth the pain. if (innerPseudo.name != pseudo.name) return []; if (innerPseudo.argument != pseudo.argument) return []; - return innerPseudo.selector.components; + return innerPseudo.selector!.components; case 'has': case 'host': @@ -826,7 +827,7 @@ class Extender { // In order to support those browsers, we break up the contents of a `:not` // unless it originally contained a selector list. if (pseudo.normalizedName == 'not' && - pseudo.selector.components.length == 1) { + pseudo.selector!.components.length == 1) { var result = complexes .map((complex) => pseudo.withSelector(SelectorList([complex]))) .toList(); diff --git a/lib/src/extend/extension.dart b/lib/src/extend/extension.dart index 03e9e7a7c..31321daca 100644 --- a/lib/src/extend/extension.dart +++ b/lib/src/extend/extension.dart @@ -20,7 +20,7 @@ class Extension { /// The selector that's being extended. /// /// `null` for one-off extensions. - final SimpleSelector target; + final SimpleSelector? target; /// The minimum specificity required for any selector generated from this /// extender. @@ -35,25 +35,25 @@ class Extension { /// The media query context to which this extend is restricted, or `null` if /// it can apply within any context. - final List mediaContext; + final List? mediaContext; /// The span in which [extender] was defined. /// /// `null` for one-off extensions. - final FileSpan extenderSpan; + final FileSpan? extenderSpan; /// The span for an `@extend` rule that defined this extension. /// /// If any extend rule for this is extension is mandatory, this is guaranteed /// to be a span for a mandatory rule. - final FileSpan span; + final FileSpan? span; /// Creates a new extension. /// /// If [specificity] isn't passed, it defaults to `extender.maxSpecificity`. Extension(ComplexSelector extender, this.target, this.extenderSpan, this.span, this.mediaContext, - {int specificity, bool optional = false}) + {int? specificity, bool optional = false}) : extender = extender, specificity = specificity ?? extender.maxSpecificity, isOptional = optional, @@ -63,7 +63,7 @@ class Extension { /// /// If [specificity] isn't passed, it defaults to `extender.maxSpecificity`. Extension.oneOff(ComplexSelector extender, - {int specificity, this.isOriginal = false}) + {int? specificity, this.isOriginal = false}) : extender = extender, target = null, extenderSpan = null, @@ -74,7 +74,7 @@ class Extension { /// Asserts that the [mediaContext] for a selector is compatible with the /// query context for this extender. - void assertCompatibleMediaContext(List mediaContext) { + void assertCompatibleMediaContext(List? mediaContext) { if (this.mediaContext == null) return; if (mediaContext != null && listEquals(this.mediaContext, mediaContext)) { return; diff --git a/lib/src/extend/functions.dart b/lib/src/extend/functions.dart index 95445cf20..fb9c13a75 100644 --- a/lib/src/extend/functions.dart +++ b/lib/src/extend/functions.dart @@ -27,13 +27,13 @@ final _subselectorPseudos = {'matches', 'any', 'nth-child', 'nth-last-child'}; /// matched by both [complex1] and [complex2]. /// /// If no such list can be produced, returns `null`. -List> unifyComplex( - List> complexes) { +List>? unifyComplex( + List> complexes) { assert(complexes.isNotEmpty); if (complexes.length == 1) return complexes; - List unifiedBase; + List? unifiedBase; for (var complex in complexes) { var base = complex.last; if (base is CompoundSelector) { @@ -41,7 +41,7 @@ List> unifyComplex( unifiedBase = base.components; } else { for (var simple in base.components) { - unifiedBase = simple.unify(unifiedBase); + unifiedBase = simple.unify(unifiedBase!); if (unifiedBase == null) return null; } } @@ -53,23 +53,23 @@ List> unifyComplex( var complexesWithoutBases = complexes .map((complex) => complex.sublist(0, complex.length - 1)) .toList(); - complexesWithoutBases.last.add(CompoundSelector(unifiedBase)); - return weave(complexesWithoutBases); + complexesWithoutBases.last.add(CompoundSelector(unifiedBase!)); + return weave(complexesWithoutBases) as List>?; } /// Returns a [CompoundSelector] that matches only elements that are matched by /// both [compound1] and [compound2]. /// /// If no such selector can be produced, returns `null`. -CompoundSelector unifyCompound( +CompoundSelector? unifyCompound( List compound1, List compound2) { - var result = compound2; + List? result = compound2; for (var simple in compound1) { - result = simple.unify(result); + result = simple.unify(result!); if (result == null) return null; } - return CompoundSelector(result); + return CompoundSelector(result!); } /// Returns a [SimpleSelector] that matches only elements that are matched by @@ -77,10 +77,10 @@ CompoundSelector unifyCompound( /// [UniversalSelector]s or [TypeSelector]s. /// /// If no such selector can be produced, returns `null`. -SimpleSelector unifyUniversalAndElement( +SimpleSelector? unifyUniversalAndElement( SimpleSelector selector1, SimpleSelector selector2) { - String namespace1; - String name1; + String? namespace1; + String? name1; if (selector1 is UniversalSelector) { namespace1 = selector1.namespace; } else if (selector1 is TypeSelector) { @@ -91,8 +91,8 @@ SimpleSelector unifyUniversalAndElement( 'must be a UniversalSelector or a TypeSelector'); } - String namespace2; - String name2; + String? namespace2; + String? name2; if (selector2 is UniversalSelector) { namespace2 = selector2.namespace; } else if (selector2 is TypeSelector) { @@ -103,7 +103,7 @@ SimpleSelector unifyUniversalAndElement( 'must be a UniversalSelector or a TypeSelector'); } - String namespace; + String? namespace; if (namespace1 == namespace2 || namespace2 == '*') { namespace = namespace1; } else if (namespace1 == '*') { @@ -112,7 +112,7 @@ SimpleSelector unifyUniversalAndElement( return null; } - String name; + String? name; if (name1 == name2 || name2 == null) { name = name1; } else if (name1 == null || name1 == '*') { @@ -135,7 +135,7 @@ SimpleSelector unifyUniversalAndElement( /// output for very little gain. /// /// The selector `.D (.A .B)` is represented as the list `[[.D], [.A, .B]]`. -List> weave( +List> weave( List> complexes) { var prefixes = [complexes.first.toList()]; @@ -151,7 +151,8 @@ List> weave( } var parents = complex.take(complex.length - 1).toList(); - var newPrefixes = >[]; + List> newPrefixes = + >[]; for (var prefix in prefixes) { var parentPrefixes = _weaveParents(prefix, parents); if (parentPrefixes == null) continue; @@ -179,8 +180,8 @@ List> weave( /// identical to the intersection of all elements matched by `A X` and all /// elements matched by `B X`. Some `AB_i` are elided to reduce the size of /// the output. -Iterable> _weaveParents( - List parents1, +Iterable>? _weaveParents( + List parents1, List parents2) { var queue1 = Queue.of(parents1); var queue2 = Queue.of(parents2); @@ -206,7 +207,7 @@ Iterable> _weaveParents( var groups1 = _groupSelectors(queue1); var groups2 = _groupSelectors(queue2); - var lcs = longestCommonSubsequence>( + var lcs = longestCommonSubsequence>( groups2, groups1, select: (group1, group2) { if (listEquals(group1, group2)) return group1; if (group1.first is! CompoundSelector || @@ -217,17 +218,20 @@ Iterable> _weaveParents( if (complexIsParentSuperselector(group2, group1)) return group1; if (!_mustUnify(group1, group2)) return null; - var unified = unifyComplex([group1, group2]); + var unified = unifyComplex([ + group1 as List, + group2 as List + ]); if (unified == null) return null; if (unified.length > 1) return null; return unified.first; }); var choices = [ - >[initialCombinators] + >[initialCombinators] ]; for (var group in lcs) { - choices.add(_chunks>(groups1, groups2, + choices.add(_chunks>(groups1, groups2, (sequence) => complexIsParentSuperselector(sequence.first, group)) .map((chunk) => chunk.expand((group) => group)) .toList()); @@ -246,7 +250,7 @@ Iterable> _weaveParents( /// If the first element of [queue] has a `::root` selector, removes and returns /// that element. -CompoundSelector _firstIfRoot(Queue queue) { +CompoundSelector? _firstIfRoot(Queue queue) { if (queue.isEmpty) return null; var first = queue.first; if (first is CompoundSelector) { @@ -264,12 +268,12 @@ CompoundSelector _firstIfRoot(Queue queue) { /// /// If there are no combinators to be merged, returns an empty list. If the /// combinators can't be merged, returns `null`. -List _mergeInitialCombinators( - Queue components1, +List? _mergeInitialCombinators( + Queue components1, Queue components2) { - var combinators1 = []; + var combinators1 = []; while (components1.isNotEmpty && components1.first is Combinator) { - combinators1.add(components1.removeFirst() as Combinator); + combinators1.add(components1.removeFirst() as Combinator?); } var combinators2 = []; @@ -290,19 +294,19 @@ List _mergeInitialCombinators( /// /// If there are no combinators to be merged, returns an empty list. If the /// sequences can't be merged, returns `null`. -List>> _mergeFinalCombinators( - Queue components1, +List>>? _mergeFinalCombinators( + Queue components1, Queue components2, - [QueueList>> result]) { + [QueueList>>? result]) { result ??= QueueList(); if ((components1.isEmpty || components1.last is! Combinator) && (components2.isEmpty || components2.last is! Combinator)) { return result; } - var combinators1 = []; + var combinators1 = []; while (components1.isNotEmpty && components1.last is Combinator) { - combinators1.add(components1.removeLast() as Combinator); + combinators1.add(components1.removeLast() as Combinator?); } var combinators2 = []; @@ -330,12 +334,12 @@ List>> _mergeFinalCombinators( var combinator1 = combinators1.isEmpty ? null : combinators1.first; var combinator2 = combinators2.isEmpty ? null : combinators2.first; if (combinator1 != null && combinator2 != null) { - var compound1 = components1.removeLast() as CompoundSelector; + var compound1 = components1.removeLast() as CompoundSelector?; var compound2 = components2.removeLast() as CompoundSelector; if (combinator1 == Combinator.followingSibling && combinator2 == Combinator.followingSibling) { - if (compound1.isSuperselector(compound2)) { + if (compound1!.isSuperselector(compound2)) { result.addFirst([ [compound2, Combinator.followingSibling] ]); @@ -371,16 +375,17 @@ List>> _mergeFinalCombinators( (combinator1 == Combinator.nextSibling && combinator2 == Combinator.followingSibling)) { var followingSiblingSelector = - combinator1 == Combinator.followingSibling ? compound1 : compound2; + combinator1 == Combinator.followingSibling ? compound1! : compound2; var nextSiblingSelector = - combinator1 == Combinator.followingSibling ? compound2 : compound1; + combinator1 == Combinator.followingSibling ? compound2 : compound1!; if (followingSiblingSelector.isSuperselector(nextSiblingSelector)) { result.addFirst([ [nextSiblingSelector, Combinator.nextSibling] ]); } else { - var unified = unifyCompound(compound1.components, compound2.components); + var unified = + unifyCompound(compound1!.components, compound2.components); result.addFirst([ [ followingSiblingSelector, @@ -406,7 +411,7 @@ List>> _mergeFinalCombinators( ]); components2..add(compound2)..add(Combinator.child); } else if (combinator1 == combinator2) { - var unified = unifyCompound(compound1.components, compound2.components); + var unified = unifyCompound(compound1!.components, compound2.components); if (unified == null) return null; result.addFirst([ [unified, combinator1] @@ -446,8 +451,8 @@ List>> _mergeFinalCombinators( /// /// This is necessary when both selectors contain the same unique simple /// selector, such as an ID. -bool _mustUnify(List complex1, - List complex2) { +bool _mustUnify(List complex1, + List complex2) { var uniqueSelectors = { for (var component in complex1) if (component is CompoundSelector) @@ -519,12 +524,13 @@ List> paths(Iterable> choices) => choices.fold( /// /// For example, `(A B > C D + E ~ > G)` is grouped into /// `[(A) (B > C) (D + E ~ > G)]`. -QueueList> _groupSelectors( - Iterable complex) { - var groups = QueueList>(); +QueueList> _groupSelectors( + Iterable complex) { + QueueList> groups = + QueueList>(); var iterator = complex.iterator; if (!iterator.moveNext()) return groups; - var group = [iterator.current]; + var group = [iterator.current]; groups.add(group); while (iterator.moveNext()) { if (group.last is Combinator || iterator.current is Combinator) { @@ -558,8 +564,8 @@ bool listIsSuperselector( /// For example, `B` is not normally a superselector of `B A`, since it doesn't /// match elements that match `A`. However, it *is* a parent superselector, /// since `B X` is a superselector of `B A X`. -bool complexIsParentSuperselector(List complex1, - List complex2) { +bool complexIsParentSuperselector(List complex1, + List complex2) { // Try some simple heuristics to see if we can avoid allocations. if (complex1.first is Combinator) return false; if (complex2.first is Combinator) return false; @@ -575,8 +581,8 @@ bool complexIsParentSuperselector(List complex1, /// /// That is, whether [complex1] matches every element that [complex2] matches, as well /// as possibly additional elements. -bool complexIsSuperselector(List complex1, - List complex2) { +bool complexIsSuperselector(List complex1, + List complex2) { // Selectors with trailing operators are neither superselectors nor // subselectors. if (complex1.last is Combinator) return false; @@ -596,11 +602,11 @@ bool complexIsSuperselector(List complex1, // subselectors. if (complex1[i1] is Combinator) return false; if (complex2[i2] is Combinator) return false; - var compound1 = complex1[i1] as CompoundSelector; + var compound1 = complex1[i1] as CompoundSelector?; if (remaining1 == 1) { return compoundIsSuperselector( - compound1, complex2.last as CompoundSelector, + compound1!, complex2.last as CompoundSelector?, parents: complex2.take(complex2.length - 1).skip(i2)); } @@ -613,7 +619,7 @@ bool complexIsSuperselector(List complex1, for (; afterSuperselector < complex2.length; afterSuperselector++) { var compound2 = complex2[afterSuperselector - 1]; if (compound2 is CompoundSelector) { - if (compoundIsSuperselector(compound1, compound2, + if (compoundIsSuperselector(compound1!, compound2, parents: complex2.take(afterSuperselector - 1).skip(i2 + 1))) { break; } @@ -661,8 +667,8 @@ bool complexIsSuperselector(List complex1, /// relevant for pseudo selectors with selector arguments, where we may need to /// know if the parent selectors in the selector argument match [parents]. bool compoundIsSuperselector( - CompoundSelector compound1, CompoundSelector compound2, - {Iterable parents}) { + CompoundSelector compound1, CompoundSelector? compound2, + {Iterable? parents}) { // Every selector in [compound1.components] must have a matching selector in // [compound2.components]. for (var simple1 in compound1.components) { @@ -671,14 +677,14 @@ bool compoundIsSuperselector( parents: parents)) { return false; } - } else if (!_simpleIsSuperselectorOfCompound(simple1, compound2)) { + } else if (!_simpleIsSuperselectorOfCompound(simple1, compound2!)) { return false; } } // [compound1] can't be a superselector of a selector with non-selector // pseudo-elements that [compound2] doesn't share. - for (var simple2 in compound2.components) { + for (var simple2 in compound2!.components) { if (simple2 is PseudoSelector && simple2.isElement && simple2.selector == null && @@ -703,7 +709,7 @@ bool _simpleIsSuperselectorOfCompound( if (theirSimple is PseudoSelector && theirSimple.selector != null && _subselectorPseudos.contains(theirSimple.normalizedName)) { - return theirSimple.selector.components.every((complex) { + return theirSimple.selector!.components.every((complex) { if (complex.components.length != 1) return false; var compound = complex.components.single as CompoundSelector; return compound.components.contains(simple); @@ -725,31 +731,32 @@ bool _simpleIsSuperselectorOfCompound( /// relevant for pseudo selectors with selector arguments, where we may need to /// know if the parent selectors in the selector argument match [parents]. bool _selectorPseudoIsSuperselector( - PseudoSelector pseudo1, CompoundSelector compound2, - {Iterable parents}) { + PseudoSelector pseudo1, CompoundSelector? compound2, + {Iterable? parents}) { switch (pseudo1.normalizedName) { case 'matches': case 'any': - var pseudos = _selectorPseudosNamed(compound2, pseudo1.name); + var pseudos = _selectorPseudosNamed(compound2!, pseudo1.name); return pseudos.any((pseudo2) { - return pseudo1.selector.isSuperselector(pseudo2.selector); + return pseudo1.selector!.isSuperselector(pseudo2.selector!); }) || - pseudo1.selector.components.any((complex1) => complexIsSuperselector( + pseudo1.selector!.components.any((complex1) => complexIsSuperselector( complex1.components, [...?parents, compound2])); case 'has': case 'host': case 'host-context': - return _selectorPseudosNamed(compound2, pseudo1.name) - .any((pseudo2) => pseudo1.selector.isSuperselector(pseudo2.selector)); + return _selectorPseudosNamed(compound2!, pseudo1.name).any( + (pseudo2) => pseudo1.selector!.isSuperselector(pseudo2.selector!)); case 'slotted': - return _selectorPseudosNamed(compound2, pseudo1.name, isClass: false) - .any((pseudo2) => pseudo1.selector.isSuperselector(pseudo2.selector)); + return _selectorPseudosNamed(compound2!, pseudo1.name, isClass: false) + .any((pseudo2) => + pseudo1.selector!.isSuperselector(pseudo2.selector!)); case 'not': - return pseudo1.selector.components.every((complex) { - return compound2.components.any((simple2) { + return pseudo1.selector!.components.every((complex) { + return compound2!.components.any((simple2) { if (simple2 is TypeSelector) { var compound1 = complex.components.last; return compound1 is CompoundSelector && @@ -763,7 +770,7 @@ bool _selectorPseudoIsSuperselector( } else if (simple2 is PseudoSelector && simple2.name == pseudo1.name && simple2.selector != null) { - return listIsSuperselector(simple2.selector.components, [complex]); + return listIsSuperselector(simple2.selector!.components, [complex]); } else { return false; } @@ -771,16 +778,16 @@ bool _selectorPseudoIsSuperselector( }); case 'current': - return _selectorPseudosNamed(compound2, pseudo1.name) + return _selectorPseudosNamed(compound2!, pseudo1.name) .any((pseudo2) => pseudo1.selector == pseudo2.selector); case 'nth-child': case 'nth-last-child': - return compound2.components.any((pseudo2) => + return compound2!.components.any((pseudo2) => pseudo2 is PseudoSelector && pseudo2.name == pseudo1.name && pseudo2.argument == pseudo1.argument && - pseudo1.selector.isSuperselector(pseudo2.selector)); + pseudo1.selector!.isSuperselector(pseudo2.selector!)); default: throw "unreachable"; diff --git a/lib/src/extend/merged_extension.dart b/lib/src/extend/merged_extension.dart index 05fbbb216..f033e71cf 100644 --- a/lib/src/extend/merged_extension.dart +++ b/lib/src/extend/merged_extension.dart @@ -34,7 +34,7 @@ class MergedExtension extends Extension { right.mediaContext != null && !listEquals(left.mediaContext, right.mediaContext)) { throw SassException( - "From ${left.span.message('')}\n" + "From ${left.span!.message('')}\n" "You may not @extend the same selector from within different media " "queries.", right.span); diff --git a/lib/src/functions/color.dart b/lib/src/functions/color.dart index d57b3e4fe..8d193a2fb 100644 --- a/lib/src/functions/color.dart +++ b/lib/src/functions/color.dart @@ -458,7 +458,7 @@ SassColor _updateComponents(List arguments, /// /// [max] should be 255 for RGB channels, 1 for the alpha channel, and 100 /// for saturation, lightness, whiteness, and blackness. - num getParam(String name, num max, + num? getParam(String name, num max, {bool checkPercent = false, bool assertPercent = false}) { var number = keywords.remove(name)?.assertNumber(name); if (number == null) return null; @@ -503,14 +503,14 @@ SassColor _updateComponents(List arguments, } /// Updates [current] based on [param], clamped within [max]. - num updateValue(num current, num param, num max) { + num updateValue(num current, num? param, num max) { if (param == null) return current; if (change) return param; if (adjust) return (current + param).clamp(0, max); return current + (param > 0 ? max - current : current) * (param / 100); } - int updateRgb(int current, num param) => + int updateRgb(int current, num? param) => fuzzyRound(updateValue(current, param, 255)); if (hasRgb) { @@ -555,15 +555,18 @@ SassString _functionString(String name, Iterable arguments) => BuiltInCallable _removedColorFunction(String name, String argument, {bool negative = false}) => // TODO: no as - _function(name, r"$color, $amount", (arguments) { - throw SassScriptException( - "The function $name() isn't in the sass:color module.\n" - "\n" - "Recommendation: color.adjust(${arguments[0]}, \$$argument: " - "${negative ? '-' : ''}${arguments[1]})\n" - "\n" - "More info: https://sass-lang.com/documentation/functions/color#$name"); - }); + _function( + name, + r"$color, $amount", + (arguments) { + throw SassScriptException( + "The function $name() isn't in the sass:color module.\n" + "\n" + "Recommendation: color.adjust(${arguments[0]}, \$$argument: " + "${negative ? '-' : ''}${arguments[1]})\n" + "\n" + "More info: https://sass-lang.com/documentation/functions/color#$name"); + } as Value Function(List)); Value _rgb(String name, List arguments) { var alpha = arguments.length > 3 ? arguments[3] : null; @@ -640,7 +643,7 @@ Value _hsl(String name, List arguments) { } /// Prints a deprecation warning if [hue] has a unit other than `deg`. -void _checkAngle(SassNumber angle, [String name]) { +void _checkAngle(SassNumber angle, [String? name]) { if (!angle.hasUnits || angle.hasUnit('deg')) return; var message = StringBuffer() @@ -846,4 +849,5 @@ SassColor _transparentize(List arguments) { BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => // TODO: no as - BuiltInCallable.function(name, arguments, callback, url: "sass:color"); + BuiltInCallable.function(name, arguments as String, callback, + url: "sass:color"); diff --git a/lib/src/functions/list.dart b/lib/src/functions/list.dart index 4e188f630..6f4207393 100644 --- a/lib/src/functions/list.dart +++ b/lib/src/functions/list.dart @@ -135,4 +135,5 @@ final _isBracketed = _function("is-bracketed", r"$list", BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => // TODO: no as - BuiltInCallable.function(name, arguments, callback, url: "sass:list"); + BuiltInCallable.function(name, arguments as String, callback, + url: "sass:list"); diff --git a/lib/src/functions/map.dart b/lib/src/functions/map.dart index 6a5dcc57d..84eac46c4 100644 --- a/lib/src/functions/map.dart +++ b/lib/src/functions/map.dart @@ -103,7 +103,7 @@ final _deepRemove = if (nestedMap != null && nestedMap.contents?.containsKey(keys.last) ?? false) { // TODO: no ! - return SassMap(Map.of(nestedMap.contents)..remove(keys.last)); + return SassMap(Map.of(nestedMap!.contents)..remove(keys.last)); } return value; }); @@ -166,31 +166,31 @@ final _hasKey = _function("has-key", r"$map, $key, $keys...", (arguments) { /// /// If no keys are provided, this passes [map] directly to modify and returns /// the result. -Value _modify(SassMap map, Iterable keys, Value modify(Value old)) { +Value _modify(SassMap map, Iterable keys, Value? modify(Value? old)) { var keyIterator = keys.iterator; - SassMap _modifyNestedMap(SassMap map, [Value newValue]) { + SassMap _modifyNestedMap(SassMap map, [Value? newValue]) { // TODO: no as throughout var mutableMap = Map.of(map.contents); var key = keyIterator.current; if (!keyIterator.moveNext()) { mutableMap[key] = newValue ?? modify(mutableMap[key]); - return SassMap(mutableMap); + return SassMap(mutableMap as Map); } var nestedMap = mutableMap[key]?.tryMap(); if (nestedMap == null) { // We pass null to `modify` here to indicate there's no existing value. newValue = modify(null); - if (newValue == null) return SassMap(mutableMap); + if (newValue == null) return SassMap(mutableMap as Map); } nestedMap ??= const SassMap.empty(); mutableMap[key] = _modifyNestedMap(nestedMap, newValue); - return SassMap(mutableMap); + return SassMap(mutableMap as Map); } - return keyIterator.moveNext() ? _modifyNestedMap(map) : modify(map) /*!*/; + return keyIterator.moveNext() ? _modifyNestedMap(map) : modify(map)!; } /// Merges [map1] and [map2], with values in [map2] taking precedence. @@ -239,4 +239,5 @@ SassMap _deepMergeImpl(SassMap map1, SassMap map2) { BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => // TODO: no as - BuiltInCallable.function(name, arguments, callback, url: "sass:map"); + BuiltInCallable.function(name, arguments as String, callback, + url: "sass:map"); diff --git a/lib/src/functions/math.dart b/lib/src/functions/math.dart index 544148b3a..5970dacc4 100644 --- a/lib/src/functions/math.dart +++ b/lib/src/functions/math.dart @@ -57,7 +57,7 @@ final _clamp = _function("clamp", r"$min, $number, $max", (arguments) { final _floor = _numberFunction("floor", (value) => value.floor()); final _max = _function("max", r"$numbers...", (arguments) { - SassNumber max; + SassNumber? max; for (var value in arguments[0].asList) { var number = value.assertNumber(); if (max == null || max.lessThan(number).isTruthy) max = number; @@ -67,7 +67,7 @@ final _max = _function("max", r"$numbers...", (arguments) { }); final _min = _function("min", r"$numbers...", (arguments) { - SassNumber min; + SassNumber? min; for (var value in arguments[0].asList) { var number = value.assertNumber(); if (min == null || min.greaterThan(number).isTruthy) min = number; @@ -319,4 +319,5 @@ BuiltInCallable _numberFunction(String name, num transform(num value)) { BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => // TODO: no as - BuiltInCallable.function(name, arguments, callback, url: "sass:math"); + BuiltInCallable.function(name, arguments as String, callback, + url: "sass:math"); diff --git a/lib/src/functions/meta.dart b/lib/src/functions/meta.dart index 0b1b80c35..0fb263408 100644 --- a/lib/src/functions/meta.dart +++ b/lib/src/functions/meta.dart @@ -64,4 +64,5 @@ final global = UnmodifiableListView([ BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => // TODO: no as - BuiltInCallable.function(name, arguments, callback, url: "sass:meta"); + BuiltInCallable.function(name, arguments as String, callback, + url: "sass:meta"); diff --git a/lib/src/functions/selector.dart b/lib/src/functions/selector.dart index ed01482b9..0473f8c1c 100644 --- a/lib/src/functions/selector.dart +++ b/lib/src/functions/selector.dart @@ -130,7 +130,7 @@ final _parse = _function("parse", r"$selector", /// Adds a [ParentSelector] to the beginning of [compound], or returns `null` if /// that wouldn't produce a valid selector. -CompoundSelector _prependParent(CompoundSelector compound) { +CompoundSelector? _prependParent(CompoundSelector compound) { var first = compound.components.first; if (first is UniversalSelector) return null; if (first is TypeSelector) { @@ -149,4 +149,5 @@ CompoundSelector _prependParent(CompoundSelector compound) { BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => // TODO: no as - BuiltInCallable.function(name, arguments, callback, url: "sass:selector"); + BuiltInCallable.function(name, arguments as String, callback, + url: "sass:selector"); diff --git a/lib/src/functions/string.dart b/lib/src/functions/string.dart index 20a1cb58a..41dfb2723 100644 --- a/lib/src/functions/string.dart +++ b/lib/src/functions/string.dart @@ -173,4 +173,5 @@ int _codepointForIndex(int index, int lengthInCodepoints, BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => // TODO: no as - BuiltInCallable.function(name, arguments, callback, url: "sass:string"); + BuiltInCallable.function(name, arguments as String, callback, + url: "sass:string"); diff --git a/lib/src/import_cache.dart b/lib/src/import_cache.dart index c5835c127..5b9ba98e1 100644 --- a/lib/src/import_cache.dart +++ b/lib/src/import_cache.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_import_cache.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: 1196dc4a7cad43b056f32e5e76aeff4362973276 +// Checksum: 703b475d60d087c6a7944358a9cee6d1da44a958 // // ignore_for_file: unused_import @@ -38,11 +38,10 @@ class ImportCache { /// /// This cache isn't used for relative imports, because they're /// context-dependent. - final Map, Tuple3 /*?*/ > - _canonicalizeCache; + final Map, Tuple3?> _canonicalizeCache; /// The parsed stylesheets for each canonicalized import URL. - final Map _importCache; + final Map _importCache; /// The import results for each canonicalized import URL. final Map _resultsCache; @@ -65,10 +64,10 @@ class ImportCache { /// /// [`PackageConfig`]: https://pub.dev/documentation/package_config/latest/package_config.package_config/PackageConfig-class.html ImportCache( - {Iterable importers, - Iterable loadPaths, - PackageConfig packageConfig, - Logger logger}) + {Iterable? importers, + Iterable? loadPaths, + PackageConfig? packageConfig, + Logger? logger}) : _importers = _toImporters(importers, loadPaths, packageConfig), _logger = logger ?? const Logger.stderr(), _canonicalizeCache = {}, @@ -76,7 +75,7 @@ class ImportCache { _resultsCache = {}; /// Creates an import cache without any globally-available importers. - ImportCache.none({Logger logger}) + ImportCache.none({Logger? logger}) : _importers = const [], _logger = logger ?? const Logger.stderr(), _canonicalizeCache = {}, @@ -85,8 +84,8 @@ class ImportCache { /// Converts the user's [importers], [loadPaths], and [packageConfig] /// options into a single list of importers. - static List _toImporters(Iterable importers, - Iterable loadPaths, PackageConfig packageConfig) { + static List _toImporters(Iterable? importers, + Iterable? loadPaths, PackageConfig? packageConfig) { var sassPath = getEnvironmentVariable('SASS_PATH'); return [ ...?importers, @@ -111,8 +110,8 @@ class ImportCache { /// If any importers understand [url], returns that importer as well as the /// canonicalized URL and the original URL resolved relative to [baseUrl] if /// applicable. Otherwise, returns `null`. - Tuple3 canonicalize(Uri url, - {Importer baseImporter, Uri baseUrl, bool forImport = false}) { + Tuple3? canonicalize(Uri url, + {Importer? baseImporter, Uri? baseUrl, bool forImport = false}) { if (baseImporter != null) { var resolvedUrl = baseUrl?.resolveUri(url) ?? url; var canonicalUrl = _canonicalize(baseImporter, resolvedUrl, forImport); @@ -136,7 +135,7 @@ class ImportCache { /// Calls [importer.canonicalize] and prints a deprecation warning if it /// returns a relative URL. - Uri _canonicalize(Importer importer, Uri url, bool forImport) { + Uri? _canonicalize(Importer importer, Uri url, bool forImport) { var result = (forImport ? inImportRule(() => importer.canonicalize(url)) : importer.canonicalize(url)); @@ -158,8 +157,8 @@ Relative canonical URLs are deprecated and will eventually be disallowed. /// parsed stylesheet. Otherwise, returns `null`. /// /// Caches the result of the import and uses cached results if possible. - Tuple2 import(Uri url, - {Importer baseImporter, Uri baseUrl, bool forImport = false}) { + Tuple2? import(Uri url, + {Importer? baseImporter, Uri? baseUrl, bool forImport = false}) { var tuple = canonicalize(url, baseImporter: baseImporter, baseUrl: baseUrl, forImport: forImport); if (tuple == null) return null; @@ -178,8 +177,8 @@ Relative canonical URLs are deprecated and will eventually be disallowed. /// importers may return for legacy reasons. /// /// Caches the result of the import and uses cached results if possible. - Stylesheet importCanonical(Importer importer, Uri canonicalUrl, - [Uri originalUrl]) { + Stylesheet? importCanonical(Importer importer, Uri canonicalUrl, + [Uri? originalUrl]) { // TODO: no as return _importCache.putIfAbsent(canonicalUrl, () { var result = importer.load(canonicalUrl); diff --git a/lib/src/importer.dart b/lib/src/importer.dart index 23eefb769..6370db06d 100644 --- a/lib/src/importer.dart +++ b/lib/src/importer.dart @@ -31,9 +31,9 @@ abstract class Importer extends AsyncImporter { /// those created from Dart code with plain strings. static final Importer noOp = NoOpImporter(); - Uri /*?*/ canonicalize(Uri url); + Uri? canonicalize(Uri url); - ImporterResult /*?*/ load(Uri url); + ImporterResult? load(Uri url); DateTime modificationTime(Uri url) => DateTime.now(); diff --git a/lib/src/importer/async.dart b/lib/src/importer/async.dart index 02882bb2b..3d2591bac 100644 --- a/lib/src/importer/async.dart +++ b/lib/src/importer/async.dart @@ -72,7 +72,7 @@ abstract class AsyncImporter { /// same result. Calling [canonicalize] with a URL returned by [canonicalize] /// must return that URL. Calling [canonicalize] with a URL relative to one /// returned by [canonicalize] must return a meaningful result. - FutureOr /*!*/ canonicalize(Uri url); + FutureOr canonicalize(Uri url); /// Loads the Sass text for the given [url], or returns `null` if /// this importer can't find the stylesheet it refers to. @@ -93,7 +93,7 @@ abstract class AsyncImporter { /// will be used as the wrapped exception's message; otherwise, the exception /// object's `toString()` will be used. This means it's safe for importers to /// throw plain strings. - FutureOr /*!*/ load(Uri url); + FutureOr load(Uri url); /// Returns the time that the Sass file at [url] was last modified. /// diff --git a/lib/src/importer/filesystem.dart b/lib/src/importer/filesystem.dart index cb249bed5..caea86a02 100644 --- a/lib/src/importer/filesystem.dart +++ b/lib/src/importer/filesystem.dart @@ -21,13 +21,13 @@ class FilesystemImporter extends Importer { /// Creates an importer that loads files relative to [loadPath]. FilesystemImporter(String loadPath) : _loadPath = p.absolute(loadPath); - Uri /*?*/ canonicalize(Uri url) { + Uri? canonicalize(Uri url) { if (url.scheme != 'file' && url.scheme != '') return null; return resolveImportPath(p.join(_loadPath, p.fromUri(url))) .andThen((resolved) => p.toUri(io.canonicalize(resolved))); } - ImporterResult /*?*/ load(Uri url) { + ImporterResult? load(Uri url) { var path = p.fromUri(url); return ImporterResult(io.readFile(path), sourceMapUrl: url, syntax: Syntax.forPath(path)); diff --git a/lib/src/importer/no_op.dart b/lib/src/importer/no_op.dart index f7a1e8bac..e6261499d 100644 --- a/lib/src/importer/no_op.dart +++ b/lib/src/importer/no_op.dart @@ -9,8 +9,8 @@ import '../importer.dart'; /// This is used for stylesheets which don't support relative imports, such as /// those created from Dart code with plain strings. class NoOpImporter extends Importer { - Uri /*?*/ canonicalize(Uri url) => null; - ImporterResult /*?*/ load(Uri url) => null; + Uri? canonicalize(Uri url) => null; + ImporterResult? load(Uri url) => null; bool couldCanonicalize(Uri url, Uri canonicalUrl) => false; String toString() => "(unknown)"; diff --git a/lib/src/importer/node/implementation.dart b/lib/src/importer/node/implementation.dart index 9d9bbbc86..5f6fdea50 100644 --- a/lib/src/importer/node/implementation.dart +++ b/lib/src/importer/node/implementation.dart @@ -59,7 +59,7 @@ class NodeImporter { static Iterable _addSassPath(Iterable includePaths) sync* { yield* includePaths; // TODO: no ! - var sassPath = getEnvironmentVariable("SASS_PATH"); + var sassPath = getEnvironmentVariable("SASS_PATH")!; if (sassPath == null) return; yield* sassPath.split(isWindows ? ';' : ':'); } @@ -69,7 +69,7 @@ class NodeImporter { /// The [previous] URL is the URL of the stylesheet in which the import /// appeared. Returns the contents of the stylesheet and the URL to use as /// [previous] for imports within the loaded stylesheet. - Tuple2 load(String url, Uri /*?*/ previous, bool forImport) { + Tuple2? load(String url, Uri? previous, bool forImport) { var parsed = Uri.parse(url); if (parsed.scheme == '' || parsed.scheme == 'file') { var result = _resolveRelativePath(p.fromUri(parsed), previous, forImport); @@ -93,8 +93,8 @@ class NodeImporter { /// The [previous] URL is the URL of the stylesheet in which the import /// appeared. Returns the contents of the stylesheet and the URL to use as /// [previous] for imports within the loaded stylesheet. - Future> loadAsync( - String url, Uri /*?*/ previous, bool forImport) async { + Future?> loadAsync( + String url, Uri? previous, bool forImport) async { var parsed = Uri.parse(url); if (parsed.scheme == '' || parsed.scheme == 'file') { var result = _resolveRelativePath(p.fromUri(parsed), previous, forImport); @@ -117,8 +117,8 @@ class NodeImporter { /// /// Returns the stylesheet at that path and the URL used to load it, or `null` /// if loading failed. - Tuple2 _resolveRelativePath( - String path, Uri /*?*/ previous, bool forImport) { + Tuple2? _resolveRelativePath( + String path, Uri? previous, bool forImport) { if (p.isAbsolute(path)) return _tryPath(path, forImport); if (previous?.scheme != 'file') return null; @@ -127,7 +127,7 @@ class NodeImporter { } /// Converts [previous] to a string to pass to the importer function. - String _previousToString(Uri previous) { + String _previousToString(Uri? previous) { if (previous == null) return 'stdin'; if (previous.scheme == 'file') return p.fromUri(previous); return previous.toString(); @@ -138,7 +138,7 @@ class NodeImporter { /// /// Returns the stylesheet at that path and the URL used to load it, or `null` /// if loading failed. - Tuple2 _resolveLoadPathFromUrl(Uri url, bool forImport) => + Tuple2? _resolveLoadPathFromUrl(Uri url, bool forImport) => url.scheme == '' || url.scheme == 'file' ? _resolveLoadPath(p.fromUri(url), forImport) : null; @@ -148,7 +148,7 @@ class NodeImporter { /// /// Returns the stylesheet at that path and the URL used to load it, or `null` /// if loading failed. - Tuple2 _resolveLoadPath(String path, bool forImport) { + Tuple2? _resolveLoadPath(String path, bool forImport) { // 2: Filesystem imports relative to the working directory. var cwdResult = _tryPath(p.absolute(path), forImport); if (cwdResult != null) return cwdResult; @@ -166,7 +166,7 @@ class NodeImporter { /// /// Returns the stylesheet at that path and the URL used to load it, or `null` /// if loading failed. - Tuple2 _tryPath(String path, bool forImport) => (forImport + Tuple2? _tryPath(String path, bool forImport) => (forImport ? inImportRule(() => resolveImportPath(path)) : resolveImportPath(path)) .andThen((resolved) => @@ -174,13 +174,13 @@ class NodeImporter { /// Converts an importer's return [value] to a tuple that can be returned by /// [load]. - Tuple2 _handleImportResult( - String url, Uri /*?*/ previous, Object value, bool forImport) { + Tuple2? _handleImportResult( + String url, Uri? previous, Object value, bool forImport) { if (isJSError(value)) throw value; if (value is! NodeImporterResult) return null; // TODO: no var rename - var result = value as NodeImporterResult; + var result = value; var file = result.file; var contents = result.contents; if (file == null) { @@ -196,7 +196,7 @@ class NodeImporter { } /// Calls an importer that may or may not be asynchronous. - Future _callImporterAsync( + Future _callImporterAsync( JSFunction importer, String url, String previousString) async { var completer = Completer(); diff --git a/lib/src/importer/node/interface.dart b/lib/src/importer/node/interface.dart index 7dfebbb4b..65dea1d66 100644 --- a/lib/src/importer/node/interface.dart +++ b/lib/src/importer/node/interface.dart @@ -5,13 +5,13 @@ import 'package:tuple/tuple.dart'; class NodeImporter { - NodeImporter(Object /*!*/ context, Iterable includePaths, + NodeImporter(Object context, Iterable includePaths, Iterable importers); - Tuple2 load(String url, Uri /*?*/ previous, bool forImport) => + Tuple2 load(String url, Uri? previous, bool forImport) => throw ''; - Future /*?*/ > /*!*/ loadAsync( - String url, Uri /*?*/ previous, bool forImport) => + Future?> loadAsync( + String url, Uri? previous, bool forImport) => throw ''; } diff --git a/lib/src/importer/package.dart b/lib/src/importer/package.dart index c339a2f74..fee85228f 100644 --- a/lib/src/importer/package.dart +++ b/lib/src/importer/package.dart @@ -26,7 +26,7 @@ class PackageImporter extends Importer { /// [`PackageConfig`]: https://pub.dev/documentation/package_config/latest/package_config.package_config/PackageConfig-class.html PackageImporter(this._packageConfig); - Uri /*?*/ canonicalize(Uri url) { + Uri? canonicalize(Uri url) { if (url.scheme == 'file') return _filesystemImporter.canonicalize(url); if (url.scheme != 'package') return null; @@ -40,7 +40,7 @@ class PackageImporter extends Importer { return _filesystemImporter.canonicalize(resolved); } - ImporterResult /*?*/ load(Uri url) => _filesystemImporter.load(url); + ImporterResult? load(Uri url) => _filesystemImporter.load(url); DateTime modificationTime(Uri url) => _filesystemImporter.modificationTime(url); diff --git a/lib/src/importer/result.dart b/lib/src/importer/result.dart index 9fd53cc29..1b3f30dc6 100644 --- a/lib/src/importer/result.dart +++ b/lib/src/importer/result.dart @@ -23,7 +23,7 @@ class ImporterResult { /// automatically from [contents]. Uri get sourceMapUrl => _sourceMapUrl ?? Uri.dataFromString(contents, encoding: utf8); - final Uri _sourceMapUrl; + final Uri? _sourceMapUrl; /// The syntax to use to parse the stylesheet. final Syntax syntax; @@ -37,9 +37,9 @@ class ImporterResult { /// because old clients may still be passing the deprecated [indented] /// parameter instead. ImporterResult(this.contents, - {Uri sourceMapUrl, - Syntax syntax, - @Deprecated("Use the syntax parameter instead.") bool indented}) + {Uri? sourceMapUrl, + Syntax? syntax, + @Deprecated("Use the syntax parameter instead.") bool? indented}) : _sourceMapUrl = sourceMapUrl, syntax = syntax ?? (indented == true ? Syntax.sass : Syntax.scss) { if (sourceMapUrl?.scheme == '') { diff --git a/lib/src/importer/utils.dart b/lib/src/importer/utils.dart index 8e81134b9..a83fe0897 100644 --- a/lib/src/importer/utils.dart +++ b/lib/src/importer/utils.dart @@ -42,18 +42,20 @@ Future inImportRuleAsync(Future callback()) async { /// /// This tries to fill in extensions and partial prefixes and check for a /// directory default. If no file can be found, it returns `null`. -String resolveImportPath(String path) { +String? resolveImportPath(String path) { var extension = p.extension(path); if (extension == '.sass' || extension == '.scss' || extension == '.css') { - return _ifInImport(() => _exactlyOne( - // TODO: no !, as - _tryPath('${p.withoutExtension(path)}.import$extension'))) ?? + return _ifInImport((() => _exactlyOne( + // TODO: no !, as + _tryPath('${p.withoutExtension(path)}.import$extension'))!) + as String Function()) ?? _exactlyOne(_tryPath(path)); } return _ifInImport( // TODO: no !, as - () => _exactlyOne(_tryPathWithExtensions('$path.import'))) ?? + (() => _exactlyOne(_tryPathWithExtensions('$path.import'))!) as String + Function()) ?? _exactlyOne(_tryPathWithExtensions(path)) ?? _tryPathAsDirectory(path); } @@ -77,11 +79,12 @@ List _tryPath(String path) { /// index file exists. /// /// Otherwise, returns `null`. -String _tryPathAsDirectory(String path) { +String? _tryPathAsDirectory(String path) { if (!dirExists(path)) return null; - return _ifInImport(() => - _exactlyOne(_tryPathWithExtensions(p.join(path, 'index.import')))) ?? + return _ifInImport((() => _exactlyOne( + _tryPathWithExtensions(p.join(path, 'index.import')))!) + as String Function()) ?? _exactlyOne(_tryPathWithExtensions(p.join(path, 'index'))); } @@ -89,7 +92,7 @@ String _tryPathAsDirectory(String path) { /// /// If it contains no paths, returns `null`. If it contains more than one, /// throws an exception. -String _exactlyOne(List paths) { +String? _exactlyOne(List paths) { if (paths.isEmpty) return null; if (paths.length == 1) return paths.first; @@ -100,4 +103,4 @@ String _exactlyOne(List paths) { /// If [_inImportRule] is `true`, invokes callback and returns the result. /// /// Otherwise, returns `null`. -T _ifInImport(T callback()) => _inImportRule ? callback() : null; +T? _ifInImport(T callback()) => _inImportRule ? callback() : null; diff --git a/lib/src/interpolation_buffer.dart b/lib/src/interpolation_buffer.dart index 479a7b2e5..9fe3d890a 100644 --- a/lib/src/interpolation_buffer.dart +++ b/lib/src/interpolation_buffer.dart @@ -32,11 +32,11 @@ class InterpolationBuffer implements StringSink { _text.clear(); } - void write(Object obj) => _text.write(obj); - void writeAll(Iterable objects, [String separator = '']) => + void write(Object? obj) => _text.write(obj); + void writeAll(Iterable objects, [String separator = '']) => _text.writeAll(objects, separator); void writeCharCode(int character) => _text.writeCharCode(character); - void writeln([Object obj = '']) => _text.writeln(obj); + void writeln([Object? obj = '']) => _text.writeln(obj); /// Adds [expression] to this buffer. void add(Expression expression) { @@ -69,7 +69,7 @@ class InterpolationBuffer implements StringSink { /// Creates an [Interpolation] with the given [span] from the contents of this /// buffer. - Interpolation interpolation(FileSpan span) { + Interpolation interpolation(FileSpan? span) { return Interpolation( [..._contents, if (_text.isNotEmpty) _text.toString()], span); } diff --git a/lib/src/io.dart b/lib/src/io.dart index e950525da..c96f7a25a 100644 --- a/lib/src/io.dart +++ b/lib/src/io.dart @@ -24,7 +24,7 @@ final _realCaseCache = {}; bool get _couldBeCaseInsensitive => isWindows || isMacOS; /// Returns the canonical form of `path` on disk. -String canonicalize(String /*!*/ path) => _couldBeCaseInsensitive +String canonicalize(String path) => _couldBeCaseInsensitive ? _realCasePath(p.absolute(p.normalize(path))) : p.canonicalize(path); diff --git a/lib/src/io/interface.dart b/lib/src/io/interface.dart index 5efa32290..b83e361bc 100644 --- a/lib/src/io/interface.dart +++ b/lib/src/io/interface.dart @@ -13,7 +13,7 @@ class Stderr { /// by a newline. /// /// If [object] is `null`, just writes a newline. - void writeln([Object object]) {} + void writeln([Object? object]) {} /// Flushes any buffered text. void flush() {} @@ -88,10 +88,10 @@ DateTime modificationTime(String path) => throw ''; /// Returns the value of the environment variable with the given [name], or /// `null` if it's not set. -String /*?*/ getEnvironmentVariable(String name) => throw ''; +String? getEnvironmentVariable(String name) => throw ''; /// Gets and sets the exit code that the process will use when it exits. -int /*!*/ exitCode; +int exitCode; /// Recursively watches the directory at [path] for modifications. /// diff --git a/lib/src/io/node.dart b/lib/src/io/node.dart index 73e871ac0..2a14b5dd8 100644 --- a/lib/src/io/node.dart +++ b/lib/src/io/node.dart @@ -33,7 +33,7 @@ class Stderr { void write(Object object) => _stderr.write(object.toString()); - void writeln([Object object]) { + void writeln([Object? object]) { _stderr.write("${object ?? ''}\n"); } @@ -58,9 +58,10 @@ String readFile(String path) { } /// Wraps `fs.readFileSync` to throw a [FileSystemException]. -Object /*!*/ _readFile(String path, [String encoding]) => +Object _readFile(String path, [String? encoding]) => // TODO: no as - _systemErrorToFileSystemException(() => fs.readFileSync(path, encoding)); + _systemErrorToFileSystemException( + (() => fs.readFileSync(path, encoding)) as Object Function()); void writeFile(String path, String contents) => _systemErrorToFileSystemException(() => fs.writeFileSync(path, contents)); @@ -81,7 +82,7 @@ Future readStdin() async { assert(chunk != null); sink.add(chunk as List); })); - process.stdin.on('end', allowInterop(([Object _]) { + process.stdin.on('end', allowInterop(([Object? _]) { // Callback for 'end' receives no args. assert(_ == null); sink.close(); @@ -158,12 +159,12 @@ Iterable listDir(String path, {bool recursive = false}) { if (!recursive) { return fs .readdirSync(path) - .map((child) => p.join(path, child as String /*!*/)) + .map((child) => p.join(path, child as String)) .where((child) => !dirExists(child)); } else { Iterable list(String parent) => fs.readdirSync(parent).expand((child) { - var path = p.join(parent, child as String /*!*/); + var path = p.join(parent, child as String); return dirExists(path) ? list(path) : [path]; }); @@ -176,8 +177,8 @@ DateTime modificationTime(String path) => _systemErrorToFileSystemException(() => DateTime.fromMillisecondsSinceEpoch(fs.statSync(path).mtime.getTime())); -String getEnvironmentVariable(String name) => - getProperty(process.env, name) as String; +String? getEnvironmentVariable(String name) => + getProperty(process.env, name) as String?; /// Runs callback and converts any [JsSystemError]s it throws into /// [FileSystemException]s. @@ -216,7 +217,7 @@ Future> watchDir(String path, {bool poll = false}) { // Don't assign the controller until after the ready event fires. Otherwise, // Chokidar will give us a bunch of add events for files that already exist. - StreamController controller; + StreamController? controller; watcher ..on( 'add', @@ -238,7 +239,7 @@ Future> watchDir(String path, {bool poll = false}) { watcher.close(); }); // TODO: no ! - completer.complete(controller.stream); + completer.complete(controller!.stream); })); return completer.future; diff --git a/lib/src/io/vm.dart b/lib/src/io/vm.dart index aa5eaf064..d6ab96c1c 100644 --- a/lib/src/io/vm.dart +++ b/lib/src/io/vm.dart @@ -85,7 +85,7 @@ DateTime modificationTime(String path) { return stat.modified; } -String getEnvironmentVariable(String name) => io.Platform.environment[name]; +String? getEnvironmentVariable(String name) => io.Platform.environment[name]; Future> watchDir(String path, {bool poll = false}) async { var watcher = poll ? PollingDirectoryWatcher(path) : DirectoryWatcher(path); diff --git a/lib/src/logger.dart b/lib/src/logger.dart index 91a0dc828..8509ad68c 100644 --- a/lib/src/logger.dart +++ b/lib/src/logger.dart @@ -16,7 +16,7 @@ abstract class Logger { /// Creates a logger that prints warnings to standard error, with terminal /// colors if [color] is `true` (default `false`). - const factory Logger.stderr({bool color}) = StderrLogger; + const factory Logger.stderr({required bool color}) = StderrLogger; /// Emits a warning with the given [message]. /// @@ -26,7 +26,7 @@ abstract class Logger { /// a deprecation warning. Implementations should surface all this information /// to the end user. void warn(String message, - {FileSpan span, Trace trace, bool /*!*/ deprecation = false}); + {FileSpan? span, Trace? trace, bool deprecation = false}); /// Emits a debugging message associated with the given [span]. void debug(String message, SourceSpan span); @@ -35,6 +35,6 @@ abstract class Logger { /// A logger that emits no messages. class _QuietLogger implements Logger { void warn(String message, - {FileSpan span, Trace trace, bool /*!*/ deprecation = false}) {} + {FileSpan? span, Trace? trace, bool deprecation = false}) {} void debug(String message, SourceSpan span) {} } diff --git a/lib/src/logger/stderr.dart b/lib/src/logger/stderr.dart index cb73f70b8..d9d1f90d6 100644 --- a/lib/src/logger/stderr.dart +++ b/lib/src/logger/stderr.dart @@ -13,12 +13,12 @@ import '../utils.dart'; /// A logger that prints warnings to standard error. class StderrLogger implements Logger { /// Whether to use terminal colors in messages. - final bool /*!*/ color; + final bool color; const StderrLogger({this.color = false}); void warn(String message, - {FileSpan span, Trace trace, bool /*!*/ deprecation = false}) { + {FileSpan? span, Trace? trace, bool deprecation = false}) { if (color) { // Bold yellow. stderr.write('\u001b[33m\u001b[1m'); diff --git a/lib/src/logger/tracking.dart b/lib/src/logger/tracking.dart index 5d5099691..e7ec08696 100644 --- a/lib/src/logger/tracking.dart +++ b/lib/src/logger/tracking.dart @@ -22,7 +22,7 @@ class TrackingLogger implements Logger { TrackingLogger(this._logger); void warn(String message, - {FileSpan span, Trace trace, bool /*!*/ deprecation = false}) { + {FileSpan? span, Trace? trace, bool deprecation = false}) { _emittedWarning = true; _logger.warn(message, span: span, trace: trace, deprecation: deprecation); } diff --git a/lib/src/module.dart b/lib/src/module.dart index 947c5828e..a979804c2 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -16,13 +16,13 @@ abstract class Module { /// /// This may be `null` if the module was loaded from a string without a URL /// provided. - Uri get url; + Uri? get url; /// Modules that this module uses. List> get upstream; /// The module's variables. - Map /*!*/ get variables; + Map get variables; /// The nodes where each variable in [_variables] was defined. /// @@ -34,19 +34,19 @@ abstract class Module { /// /// Implementations must ensure that this has the same keys as [variables] if /// it's not `null`. - Map get variableNodes; + Map? get variableNodes; /// The module's functions. /// /// Implementations must ensure that each [AsyncCallable] is stored under its /// own name. - Map /*!*/ get functions; + Map get functions; /// The module's mixins. /// /// Implementations must ensure that each [AsyncCallable] is stored under its /// own name. - Map /*!*/ get mixins; + Map get mixins; /// The extensions defined in this module, which is also able to update /// [css]'s style rules in-place based on downstream extensions. @@ -71,13 +71,13 @@ abstract class Module { /// /// Throws a [SassScriptException] if this module doesn't define a variable /// named [name]. - void setVariable(String name, Value /*!*/ value, AstNode /*?*/ nodeWithSpan); + void setVariable(String name, Value value, AstNode? nodeWithSpan); /// Returns an opaque object that will be equal to another /// `variableIdentity()` return value for the same name in another module if /// and only if both modules expose identical definitions of the variable in /// question, as defined by the Sass spec. - Object variableIdentity(String /*!*/ name); + Object variableIdentity(String name); /// 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 5e70aac29..d1589e891 100644 --- a/lib/src/module/built_in.dart +++ b/lib/src/module/built_in.dart @@ -27,7 +27,9 @@ class BuiltInModule implements Module { bool get transitivelyContainsExtensions => false; BuiltInModule(String name, - {Iterable functions, Iterable mixins, Map variables}) + {Iterable? functions, + Iterable? mixins, + Map? variables}) : url = Uri(scheme: "sass", path: name), functions = _callableMap(functions), mixins = _callableMap(mixins), @@ -36,13 +38,13 @@ class BuiltInModule implements Module { /// Returns a map from [callables]' names to their values. static Map _callableMap( - Iterable callables) => + Iterable? callables) => UnmodifiableMapView(callables == null ? {} : UnmodifiableMapView( {for (var callable in callables) callable.name: callable})); - void setVariable(String name, Value value, AstNode nodeWithSpan) { + void setVariable(String name, Value value, AstNode? nodeWithSpan) { if (!variables.containsKey(name)) { throw SassScriptException("Undefined variable."); } diff --git a/lib/src/module/forwarded_view.dart b/lib/src/module/forwarded_view.dart index 739140669..79918dd96 100644 --- a/lib/src/module/forwarded_view.dart +++ b/lib/src/module/forwarded_view.dart @@ -22,7 +22,7 @@ class ForwardedModuleView implements Module { /// The rule that determines how this module's members should be exposed. final ForwardRule _rule; - Uri get url => _inner.url; + Uri? get url => _inner.url; List> get upstream => _inner.upstream; Extender get extender => _inner.extender; CssStylesheet get css => _inner.css; @@ -31,7 +31,7 @@ class ForwardedModuleView implements Module { _inner.transitivelyContainsExtensions; final Map variables; - final Map variableNodes; + final Map? variableNodes; final Map functions; final Map mixins; @@ -65,8 +65,8 @@ class ForwardedModuleView implements Module { /// [safelist], with the given [prefix], if given. /// /// Only one of [blocklist] or [safelist] may be non-`null`. - static Map /*!*/ _forwardedMap(Map /*!*/ map, - String prefix, Set safelist, Set blocklist) { + static Map _forwardedMap(Map map, String? prefix, + Set? safelist, Set? blocklist) { assert(safelist == null || blocklist == null); if (prefix == null && safelist == null && @@ -87,7 +87,7 @@ class ForwardedModuleView implements Module { return map; } - void setVariable(String name, Value value, AstNode nodeWithSpan) { + void setVariable(String name, Value value, AstNode? nodeWithSpan) { var shownVariables = _rule.shownVariables; var hiddenVariables = _rule.hiddenVariables; if (shownVariables != null && !shownVariables.contains(name)) { diff --git a/lib/src/module/shadowed_view.dart b/lib/src/module/shadowed_view.dart index 0071c5314..5aa500346 100644 --- a/lib/src/module/shadowed_view.dart +++ b/lib/src/module/shadowed_view.dart @@ -18,7 +18,7 @@ class ShadowedModuleView implements Module { /// The wrapped module. final Module _inner; - Uri get url => _inner.url; + Uri? get url => _inner.url; List> get upstream => _inner.upstream; Extender get extender => _inner.extender; CssStylesheet get css => _inner.css; @@ -39,11 +39,11 @@ class ShadowedModuleView implements Module { css.children.isEmpty; /// Like [ShadowedModuleView], but returns `null` if [inner] would be unchanged. - static ShadowedModuleView ifNecessary( + static ShadowedModuleView? ifNecessary( Module inner, - {Set variables, - Set functions, - Set mixins}) => + {Set? variables, + Set? functions, + Set? mixins}) => _needsBlocklist(inner.variables, variables) || _needsBlocklist(inner.functions, functions) || _needsBlocklist(inner.mixins, mixins) @@ -54,7 +54,7 @@ class ShadowedModuleView implements Module { /// Returns a view of [inner] that doesn't include the given [variables], /// [functions], or [mixins]. ShadowedModuleView(this._inner, - {Set variables, Set functions, Set mixins}) + {Set? variables, Set? functions, Set? mixins}) : variables = _shadowedMap(_inner.variables, variables), variableNodes = _shadowedMap(_inner.variableNodes, variables), functions = _shadowedMap(_inner.functions, functions), @@ -64,17 +64,18 @@ class ShadowedModuleView implements Module { this.functions, this.mixins); /// Returns a view of [map] with all keys in [blocklist] omitted. - static Map /*!*/ _shadowedMap /*!*/ >( - M /*!*/ map, Set blocklist) => + static Map _shadowedMap>( + M map, Set? blocklist) => map == null || blocklist == null || !_needsBlocklist(map, blocklist) ? map : LimitedMapView.blocklist(map, blocklist); /// Returns whether any of [map]'s keys are in [blocklist]. - static bool _needsBlocklist(Map map, Set blocklist) => + static bool _needsBlocklist( + Map map, Set? blocklist) => blocklist != null && map.isNotEmpty && blocklist.any(map.containsKey); - void setVariable(String name, Value value, AstNode nodeWithSpan) { + void setVariable(String name, Value value, AstNode? nodeWithSpan) { if (!variables.containsKey(name)) { throw SassScriptException("Undefined variable."); } else { diff --git a/lib/src/node.dart b/lib/src/node.dart index 3d60dba4e..65bb12708 100644 --- a/lib/src/node.dart +++ b/lib/src/node.dart @@ -67,7 +67,7 @@ void main() { /// /// [render]: https://github.com/sass/node-sass#options void _render( - RenderOptions options, void callback(Object error, RenderResult result)) { + RenderOptions options, void callback(Object? error, RenderResult? result)) { var fiber = options.fiber; if (fiber != null) { fiber.call(allowInterop(() { @@ -213,7 +213,7 @@ List _parseFunctions(RenderOptions options, DateTime start, var currentFiber = fiber.current; var jsArguments = [ ...arguments.map(wrapValue), - allowInterop(([Object result]) { + allowInterop(([Object? result]) { // Schedule a microtask so we don't try to resume the running fiber // if [importer] calls `done()` synchronously. scheduleMicrotask(() => currentFiber.run(result)); @@ -239,7 +239,7 @@ List _parseFunctions(RenderOptions options, DateTime start, var completer = Completer(); var jsArguments = [ ...arguments.map(wrapValue), - allowInterop(([Object result]) => completer.complete(result)) + allowInterop(([Object? result]) => completer.complete(result)) ]; var result = (callback as JSFunction).apply(context, jsArguments); return unwrapValue( @@ -253,23 +253,23 @@ List _parseFunctions(RenderOptions options, DateTime start, /// Parses [importer] and [includePaths] from [RenderOptions] into a /// [NodeImporter]. NodeImporter _parseImporter(RenderOptions options, DateTime start) { - List importers; + List importers; if (options.importer == null) { importers = []; - } else if (options.importer is List) { - importers = (options.importer as List).cast(); + } else if (options.importer is List) { + importers = (options.importer as List).cast(); } else { - importers = [options.importer as JSFunction /*!*/]; + importers = [options.importer as JSFunction]; } - RenderContext context; + late RenderContext context; if (importers.isNotEmpty) context = _contextWithOptions(options, start); var fiber = options.fiber; if (fiber != null) { importers = importers.map((importer) { return allowInteropCaptureThis( - (Object thisArg, String url, String previous, [Object _]) { + (Object thisArg, String url, String previous, [Object? _]) { var currentFiber = fiber.current; var result = call3(importer, thisArg, url, previous, allowInterop((Object result) { @@ -314,20 +314,20 @@ RenderContext _contextWithOptions(RenderOptions options, DateTime start) { } /// Parse [style] into an [OutputStyle]. -OutputStyle _parseOutputStyle(String style) { +OutputStyle _parseOutputStyle(String? style) { if (style == null || style == 'expanded') return OutputStyle.expanded; if (style == 'compressed') return OutputStyle.compressed; throw ArgumentError('Unsupported output style "$style".'); } /// Parses the indentation width into an [int]. -int _parseIndentWidth(Object width) { +int? _parseIndentWidth(Object? width) { if (width == null) return null; return width is int ? width : int.parse(width.toString()); } /// Parses the name of a line feed type into a [LineFeed]. -LineFeed _parseLineFeed(String str) { +LineFeed _parseLineFeed(String? str) { switch (str) { case 'cr': return LineFeed.cr; @@ -346,15 +346,15 @@ RenderResult _newRenderResult( var end = DateTime.now(); var css = result.css; - Uint8List sourceMapBytes; + Uint8List? sourceMapBytes; if (_enableSourceMaps(options)) { var sourceMapOption = options.sourceMap; var sourceMapPath = sourceMapOption is String ? sourceMapOption as String - : options.outFile /*!*/ + '.map'; + : options.outFile! + '.map'; var sourceMapDir = p.dirname(sourceMapPath); - var sourceMap = result.sourceMap /*!*/; + var sourceMap = result.sourceMap!; sourceMap.sourceRoot = options.sourceMapRoot; var outFile = options.outFile; if (outFile == null) { @@ -415,7 +415,7 @@ bool _enableSourceMaps(RenderOptions options) => /// Creates a [JsError] with the given fields added to it so it acts like a Node /// Sass error. JsError _newRenderError(String message, - {int line, int column, String file, int status}) { + {int? line, int? column, String? file, int? status}) { var error = JsError(message); setProperty(error, 'formatted', 'Error: $message'); if (line != null) setProperty(error, 'line', line); diff --git a/lib/src/node/chokidar.dart b/lib/src/node/chokidar.dart index 68fac0754..e94b9def8 100644 --- a/lib/src/node/chokidar.dart +++ b/lib/src/node/chokidar.dart @@ -12,10 +12,10 @@ class Chokidar { @JS() @anonymous class ChokidarOptions { - external bool /*?*/ get disableGlobbing; - external bool /*?*/ get usePolling; + external bool? get disableGlobbing; + external bool? get usePolling; - external factory ChokidarOptions({bool disableGlobbing, bool usePolling}); + external factory ChokidarOptions({bool? disableGlobbing, bool? usePolling}); } @JS() diff --git a/lib/src/node/fiber.dart b/lib/src/node/fiber.dart index 419f86b69..2bc3011ac 100644 --- a/lib/src/node/fiber.dart +++ b/lib/src/node/fiber.dart @@ -8,15 +8,15 @@ import 'package:js/js.dart'; @anonymous class FiberClass { // Work around sdk#31490. - external Fiber call(Object function()); + external Fiber call(Object? function()); external Fiber get current; - external Object yield([Object value]); + external Object yield([Object? value]); } @JS() @anonymous class Fiber { - external Object run([Object value]); + external Object run([Object? value]); } diff --git a/lib/src/node/function.dart b/lib/src/node/function.dart index 0ce234782..689de737b 100644 --- a/lib/src/node/function.dart +++ b/lib/src/node/function.dart @@ -6,11 +6,11 @@ import 'package:js/js.dart'; @JS("Function") class JSFunction implements Function { - external JSFunction(String arg1, [String arg2, String arg3]); + external JSFunction(String arg1, [String? arg2, String? arg3]); // Note that this just invokes the function with the given arguments, rather // than calling `Function.prototype.call()`. See sdk#31271. - external Object call([Object arg1, Object arg2, Object arg3]); + external Object call([Object? arg1, Object? arg2, Object? arg3]); - external Object apply(Object thisArg, [List args]); + external Object apply(Object thisArg, [List? args]); } diff --git a/lib/src/node/importer_result.dart b/lib/src/node/importer_result.dart index e41d096e5..b13d3302c 100644 --- a/lib/src/node/importer_result.dart +++ b/lib/src/node/importer_result.dart @@ -7,8 +7,8 @@ import 'package:js/js.dart'; @JS() @anonymous class NodeImporterResult { - external String /*?*/ get file; - external String /*?*/ get contents; + external String? get file; + external String? get contents; - external factory NodeImporterResult({String file, String contents}); + external factory NodeImporterResult({String? file, String? contents}); } diff --git a/lib/src/node/render_context.dart b/lib/src/node/render_context.dart index cf53ebfbb..b56e1c766 100644 --- a/lib/src/node/render_context.dart +++ b/lib/src/node/render_context.dart @@ -12,5 +12,5 @@ import 'render_context_options.dart'; class RenderContext { external RenderContextOptions get options; - external factory RenderContext({@required RenderContextOptions options}); + external factory RenderContext({required RenderContextOptions options}); } diff --git a/lib/src/node/render_context_options.dart b/lib/src/node/render_context_options.dart index 39c519d28..f197449f0 100644 --- a/lib/src/node/render_context_options.dart +++ b/lib/src/node/render_context_options.dart @@ -10,26 +10,26 @@ import 'render_result.dart'; @JS() @anonymous class RenderContextOptions { - external String /*?*/ get file; - external String /*?*/ get data; - external String /*?*/ get includePaths; - external int /*?*/ get precision; - external int /*?*/ get style; - external int /*?*/ get indentType; - external int /*?*/ get indentWidth; - external String /*?*/ get linefeed; - external RenderContext /*?*/ get context; - external set context(RenderContext /*?*/ value); - external RenderResult /*?*/ get result; + external String? get file; + external String? get data; + external String? get includePaths; + external int? get precision; + external int? get style; + external int? get indentType; + external int? get indentWidth; + external String? get linefeed; + external RenderContext? get context; + external set context(RenderContext? value); + external RenderResult? get result; external factory RenderContextOptions( - {String file, - String data, - String includePaths, - int precision, - int style, - int indentType, - int indentWidth, - String linefeed, - RenderResult result}); + {String? file, + String? data, + String? includePaths, + int? precision, + int? style, + int? indentType, + int? indentWidth, + String? linefeed, + RenderResult? result}); } diff --git a/lib/src/node/render_options.dart b/lib/src/node/render_options.dart index ca172fd25..9b6dea08d 100644 --- a/lib/src/node/render_options.dart +++ b/lib/src/node/render_options.dart @@ -9,40 +9,40 @@ import 'fiber.dart'; @JS() @anonymous class RenderOptions { - external String /*?*/ /*?*/ get file; - external String /*?*/ get data; - external Object /*?*/ get importer; - external Object /*?*/ get functions; - external List /*?*/ get includePaths; - external bool /*?*/ get indentedSyntax; - external bool /*?*/ get omitSourceMapUrl; - external String /*?*/ get outFile; - external String /*?*/ get outputStyle; - external String /*?*/ get indentType; - external Object /*?*/ get indentWidth; - external String /*?*/ get linefeed; - external FiberClass /*?*/ get fiber; - external Object /*?*/ get sourceMap; - external bool /*?*/ get sourceMapContents; - external bool /*?*/ get sourceMapEmbed; - external String /*?*/ get sourceMapRoot; + external String? /*?*/ get file; + external String? get data; + external Object? get importer; + external Object? get functions; + external List? get includePaths; + external bool? get indentedSyntax; + external bool? get omitSourceMapUrl; + external String? get outFile; + external String? get outputStyle; + external String? get indentType; + external Object? get indentWidth; + external String? get linefeed; + external FiberClass? get fiber; + external Object? get sourceMap; + external bool? get sourceMapContents; + external bool? get sourceMapEmbed; + external String? get sourceMapRoot; external factory RenderOptions( - {String file, - String data, - Object importer, - Object functions, - List includePaths, - bool indentedSyntax, - bool omitSourceMapUrl, - String outFile, - String outputStyle, - String indentType, - Object indentWidth, - String linefeed, - FiberClass fiber, - Object sourceMap, - bool sourceMapContents, - bool sourceMapEmbed, - String sourceMapRoot}); + {String? file, + String? data, + Object? importer, + Object? functions, + List? includePaths, + bool? indentedSyntax, + bool? omitSourceMapUrl, + String? outFile, + String? outputStyle, + String? indentType, + Object? indentWidth, + String? linefeed, + FiberClass? fiber, + Object? sourceMap, + bool? sourceMapContents, + bool? sourceMapEmbed, + String? sourceMapRoot}); } diff --git a/lib/src/node/render_result.dart b/lib/src/node/render_result.dart index 4d0814e21..5fb8e5757 100644 --- a/lib/src/node/render_result.dart +++ b/lib/src/node/render_result.dart @@ -9,27 +9,27 @@ import 'package:js/js.dart'; @JS() @anonymous class RenderResult { - external Uint8List /*?*/ get css; - external Uint8List /*?*/ get map; - external RenderResultStats /*?*/ get stats; + external Uint8List? get css; + external Uint8List? get map; + external RenderResultStats? get stats; external factory RenderResult( - {Uint8List css, Uint8List map, RenderResultStats stats}); + {Uint8List? css, Uint8List? map, RenderResultStats? stats}); } @JS() @anonymous class RenderResultStats { - external String /*?*/ get entry; - external int /*?*/ get start; - external int /*?*/ get end; - external int /*?*/ get duration; - external List /*?*/ get includedFiles; + external String? get entry; + external int? get start; + external int? get end; + external int? get duration; + external List? get includedFiles; external factory RenderResultStats( - {String entry, - int start, - int end, - int duration, - List includedFiles}); + {String? entry, + int? start, + int? end, + int? duration, + List? includedFiles}); } diff --git a/lib/src/node/types.dart b/lib/src/node/types.dart index 993459dfe..7bad3a94a 100644 --- a/lib/src/node/types.dart +++ b/lib/src/node/types.dart @@ -17,12 +17,12 @@ class Types { external set Error(Function function); external factory Types( - {Function Boolean, - Function Color, - Function List, - Function Map, - Function Null, - Function Number, - Function String, - Function Error}); + {Function? Boolean, + Function? Color, + Function? List, + Function? Map, + Function? Null, + Function? Number, + Function? String, + Function? Error}); } diff --git a/lib/src/node/utils.dart b/lib/src/node/utils.dart index 760815bf0..21f42cc7f 100644 --- a/lib/src/node/utils.dart +++ b/lib/src/node/utils.dart @@ -29,7 +29,7 @@ void jsThrow(Object error) => _jsThrow.call(error); final _jsThrow = JSFunction("error", "throw error;"); /// Returns whether or not [value] is the JS `undefined` value. -bool isUndefined(Object /*?*/ value) => _isUndefined.call(value) as bool; +bool isUndefined(Object? value) => _isUndefined.call(value) as bool; final _isUndefined = JSFunction("value", "return value === undefined;"); @@ -50,22 +50,21 @@ external Function get jsErrorConstructor; bool isJSError(Object value) => jsInstanceOf(value, jsErrorConstructor); /// Invokes [function] with [thisArg] as `this`. -Object /*?*/ call2( - JSFunction function, Object thisArg, Object arg1, Object arg2) => +Object? call2(JSFunction function, Object thisArg, Object arg1, Object arg2) => function.apply(thisArg, [arg1, arg2]); /// Invokes [function] with [thisArg] as `this`. -Object /*?*/ call3(JSFunction function, Object thisArg, Object arg1, - Object arg2, Object arg3) => +Object? call3(JSFunction function, Object thisArg, Object arg1, Object arg2, + Object arg3) => function.apply(thisArg, [arg1, arg2, arg3]); @JS("Object.keys") -external List _keys(Object object); +external List _keys(Object? object); /// Invokes [callback] for each key/value pair in [object]. -void jsForEach(Object object, void callback(Object key, Object value)) { +void jsForEach(Object? object, void callback(Object key, Object? value)) { for (var key in _keys(object)) { - callback(key, getProperty(object, key)); + callback(key, getProperty(object!, key)); } } @@ -85,25 +84,25 @@ Function createClass( } @JS("Object.getPrototypeOf") -external Object /*?*/ _getPrototypeOf(Object object); +external Object? _getPrototypeOf(Object object); @JS("Object.setPrototypeOf") -external void _setPrototypeOf(Object /*!*/ object, Object prototype); +external void _setPrototypeOf(Object object, Object prototype); @JS("Object.defineProperty") external void _defineProperty( - Object /*!*/ object, String name, _PropertyDescriptor prototype); + Object object, String name, _PropertyDescriptor prototype); @JS() @anonymous class _PropertyDescriptor { external Object get value; - external factory _PropertyDescriptor({Object value}); + external factory _PropertyDescriptor({Object? value}); } @JS("Object.create") -external Object _create(Object /*!*/ prototype); +external Object _create(Object prototype); /// Sets the name of `object`'s class to `name`. void setClassName(Object object, String name) { @@ -113,7 +112,7 @@ void setClassName(Object object, String name) { /// Injects [constructor] into the inheritance chain for [object]'s class. void injectSuperclass(Object object, Function constructor) { - var prototype = _getPrototypeOf(object); + var prototype = _getPrototypeOf(object)!; var parent = _getPrototypeOf(prototype); if (parent != null) { _setPrototypeOf(getProperty(constructor, 'prototype'), parent); @@ -122,7 +121,7 @@ void injectSuperclass(Object object, Function constructor) { } /// Returns whether [value] is truthy according to JavaScript. -bool isTruthy(Object value) => value != false && value != null; +bool isTruthy(Object? value) => value != false && value != null; @JS('Buffer.from') external Uint8List _buffer(String text, String encoding); diff --git a/lib/src/node/value/color.dart b/lib/src/node/value/color.dart index afc099f99..4de9ee78b 100644 --- a/lib/src/node/value/color.dart +++ b/lib/src/node/value/color.dart @@ -16,13 +16,13 @@ class _NodeSassColor { } /// Creates a new `sass.types.Color` object wrapping [value]. -Object /*!*/ newNodeSassColor(SassColor value) => +Object newNodeSassColor(SassColor value) => callConstructor(colorConstructor, [null, null, null, null, value]); /// The JS constructor for the `sass.types.Color` class. final Function colorConstructor = createClass('SassColor', (_NodeSassColor thisArg, num redOrArgb, - [num green, num blue, num alpha, SassColor dartValue]) { + [num? green, num? blue, num? alpha, SassColor? dartValue]) { if (dartValue != null) { thisArg.dartValue = dartValue; return; diff --git a/lib/src/node/value/list.dart b/lib/src/node/value/list.dart index 6a743cbe6..0f2065207 100644 --- a/lib/src/node/value/list.dart +++ b/lib/src/node/value/list.dart @@ -17,13 +17,13 @@ class _NodeSassList { } /// Creates a new `sass.types.List` object wrapping [value]. -Object /*!*/ newNodeSassList(SassList value) => +Object newNodeSassList(SassList value) => callConstructor(listConstructor, [null, null, value]); /// The JS constructor for the `sass.types.List` class. final Function listConstructor = createClass('SassList', (_NodeSassList thisArg, int length, - [bool commaSeparator, SassList dartValue]) { + [bool? commaSeparator, SassList? dartValue]) { thisArg.dartValue = dartValue ?? SassList(Iterable.generate(length, (_) => sassNull), (commaSeparator ?? true) ? ListSeparator.comma : ListSeparator.space); diff --git a/lib/src/node/value/map.dart b/lib/src/node/value/map.dart index 5ae600b99..45bf655f3 100644 --- a/lib/src/node/value/map.dart +++ b/lib/src/node/value/map.dart @@ -17,12 +17,12 @@ class _NodeSassMap { } /// Creates a new `sass.types.Map` object wrapping [value]. -Object /*!*/ newNodeSassMap(SassMap value) => +Object newNodeSassMap(SassMap value) => callConstructor(mapConstructor, [null, value]); /// The JS constructor for the `sass.types.Map` class. final Function mapConstructor = createClass('SassMap', - (_NodeSassMap thisArg, int length, [SassMap dartValue]) { + (_NodeSassMap thisArg, int length, [SassMap? dartValue]) { thisArg.dartValue = dartValue ?? SassMap(Map.fromIterables(Iterable.generate(length, (i) => SassNumber(i)), Iterable.generate(length, (_) => sassNull))); @@ -38,7 +38,7 @@ final Function mapConstructor = createClass('SassMap', var newKey = unwrapValue(key); // TODO: no var - var newMap = {}; + Map newMap = {}; var i = 0; for (var oldKey in thisArg.dartValue.contents.keys) { if (i == index) { @@ -53,7 +53,7 @@ final Function mapConstructor = createClass('SassMap', } // TODO: no as - thisArg.dartValue = SassMap(newMap); + thisArg.dartValue = SassMap(newMap as Map); }, 'setValue': (_NodeSassMap thisArg, int index, Object value) { var key = thisArg.dartValue.contents.keys.elementAt(index); diff --git a/lib/src/node/value/number.dart b/lib/src/node/value/number.dart index a299bdfd2..3e0c34fa3 100644 --- a/lib/src/node/value/number.dart +++ b/lib/src/node/value/number.dart @@ -16,12 +16,13 @@ class _NodeSassNumber { } /// Creates a new `sass.types.Number` object wrapping [value]. -Object /*!*/ newNodeSassNumber(SassNumber value) => +Object newNodeSassNumber(SassNumber value) => callConstructor(numberConstructor, [null, null, value]); /// The JS constructor for the `sass.types.Number` class. final Function numberConstructor = createClass('SassNumber', - (_NodeSassNumber thisArg, num value, [String unit, SassNumber dartValue]) { + (_NodeSassNumber thisArg, num value, + [String? unit, SassNumber? dartValue]) { thisArg.dartValue = dartValue ?? _parseNumber(value, unit); }, { 'getValue': (_NodeSassNumber thisArg) => thisArg.dartValue.value, @@ -42,7 +43,7 @@ final Function numberConstructor = createClass('SassNumber', /// Parses a [SassNumber] from [value] and [unit], using Node Sass's unit /// format. -SassNumber _parseNumber(num value, String unit) { +SassNumber _parseNumber(num value, String? unit) { if (unit == null || unit.isEmpty) return SassNumber(value); if (!unit.contains("*") && !unit.contains("/")) { return SassNumber(value, unit); diff --git a/lib/src/node/value/string.dart b/lib/src/node/value/string.dart index b188deb12..cd170adc6 100644 --- a/lib/src/node/value/string.dart +++ b/lib/src/node/value/string.dart @@ -16,12 +16,12 @@ class _NodeSassString { } /// Creates a new `sass.types.String` object wrapping [value]. -Object /*!*/ newNodeSassString(SassString value) => +Object newNodeSassString(SassString value) => callConstructor(stringConstructor, [null, value]); /// The JS constructor for the `sass.types.String` class. final Function stringConstructor = createClass('SassString', - (_NodeSassString thisArg, String value, [SassString dartValue]) { + (_NodeSassString thisArg, String value, [SassString? dartValue]) { thisArg.dartValue = dartValue ?? SassString(value, quotes: false); }, { 'getValue': (_NodeSassString thisArg) => thisArg.dartValue.text, diff --git a/lib/src/parse/at_root_query.dart b/lib/src/parse/at_root_query.dart index f4245245f..b0d0a395e 100644 --- a/lib/src/parse/at_root_query.dart +++ b/lib/src/parse/at_root_query.dart @@ -10,7 +10,7 @@ import 'parser.dart'; /// A parser for `@at-root` queries. class AtRootQueryParser extends Parser { - AtRootQueryParser(String contents, {Object url, Logger logger}) + AtRootQueryParser(String contents, {Object? url, Logger? logger}) : super(contents, url: url, logger: logger); AtRootQuery parse() { diff --git a/lib/src/parse/css.dart b/lib/src/parse/css.dart index 4976d73d4..14f0f03d5 100644 --- a/lib/src/parse/css.dart +++ b/lib/src/parse/css.dart @@ -27,7 +27,7 @@ final _disallowedFunctionNames = class CssParser extends ScssParser { bool get plainCss => true; - CssParser(String contents, {Object url, Logger logger}) + CssParser(String contents, {Object? url, Logger? logger}) : super(contents, url: url, logger: logger); void silentComment() { @@ -37,7 +37,7 @@ class CssParser extends ScssParser { scanner.spanFrom(start)); } - Statement atRule(Statement /*!*/ child(), {bool root = false}) { + Statement atRule(Statement child(), {bool root = false}) { // NOTE: this logic is largely duplicated in CssParser.atRule. Most changes // here should be mirrored there. @@ -106,7 +106,7 @@ class CssParser extends ScssParser { Expression identifierLike() { var start = scanner.state; var identifier = interpolatedIdentifier(); - var plain = identifier.asPlain; + var plain = identifier.asPlain!; var specialFunction = trySpecialFunction(plain.toLowerCase(), start); if (specialFunction != null) return specialFunction; diff --git a/lib/src/parse/keyframe_selector.dart b/lib/src/parse/keyframe_selector.dart index 0d3d30e7b..1229b6d61 100644 --- a/lib/src/parse/keyframe_selector.dart +++ b/lib/src/parse/keyframe_selector.dart @@ -10,7 +10,7 @@ import 'parser.dart'; /// A parser for `@keyframes` block selectors. class KeyframeSelectorParser extends Parser { - KeyframeSelectorParser(String contents, {Object url, Logger logger}) + KeyframeSelectorParser(String contents, {Object? url, Logger? logger}) : super(contents, url: url, logger: logger); List parse() { diff --git a/lib/src/parse/media_query.dart b/lib/src/parse/media_query.dart index eee235c25..d9f5cbbb3 100644 --- a/lib/src/parse/media_query.dart +++ b/lib/src/parse/media_query.dart @@ -11,7 +11,7 @@ import 'parser.dart'; /// A parser for `@media` queries. class MediaQueryParser extends Parser { - MediaQueryParser(String contents, {Object url, Logger logger}) + MediaQueryParser(String contents, {Object? url, Logger? logger}) : super(contents, url: url, logger: logger); List parse() { @@ -29,8 +29,8 @@ class MediaQueryParser extends Parser { /// Consumes a single media query. CssMediaQuery _mediaQuery() { // This is somewhat duplicated in StylesheetParser._mediaQuery. - String modifier; - String type; + String? modifier; + String? type; if (scanner.peekChar() != $lparen) { var identifier1 = identifier(); whitespace(); diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index 8796e5db2..592ce609a 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -28,11 +28,11 @@ class Parser { /// Parses [text] as a CSS identifier and returns the result. /// /// Throws a [SassFormatException] if parsing fails. - static String parseIdentifier(String text, {Logger logger}) => + static String parseIdentifier(String text, {Logger? logger}) => Parser(text, logger: logger)._parseIdentifier(); /// Returns whether [text] is a valid CSS identifier. - static bool isIdentifier(String text, {Logger logger}) { + static bool isIdentifier(String text, {Logger? logger}) { try { parseIdentifier(text, logger: logger); return true; @@ -44,11 +44,11 @@ class Parser { /// Returns whether [text] starts like a variable declaration. /// /// Ignores everything after the `:`. - static bool isVariableDeclarationLike(String text, {Logger logger}) => + static bool isVariableDeclarationLike(String text, {Logger? logger}) => Parser(text, logger: logger)._isVariableDeclarationLike(); @protected - Parser(String contents, {Object url, Logger logger}) + Parser(String contents, {Object? url, Logger? logger}) : scanner = SpanScanner(contents, sourceUrl: url), logger = logger ?? const Logger.stderr(); @@ -325,7 +325,7 @@ class Parser { case $lparen: case $lbrace: case $lbracket: - buffer.writeCharCode(next); + buffer.writeCharCode(next!); brackets.add(opposite(scanner.readChar())); wroteNewline = false; break; @@ -334,7 +334,7 @@ class Parser { case $rbrace: case $rbracket: if (brackets.isEmpty) break loop; - buffer.writeCharCode(next); + buffer.writeCharCode(next!); scanner.expectChar(brackets.removeLast()); wroteNewline = false; break; @@ -375,7 +375,7 @@ class Parser { /// Consumes a `url()` token if possible, and returns `null` otherwise. @protected - String tryUrl() { + String? tryUrl() { // NOTE: this logic is largely duplicated in ScssParser._tryUrlContents. // Most changes here should be mirrored there. @@ -514,7 +514,7 @@ class Parser { // // Returns whether or not the character was consumed. @protected - bool scanCharIf(bool condition(int character)) { + bool scanCharIf(bool condition(int? character)) { var next = scanner.peekChar(); if (!condition(next)) return false; scanner.readChar(); @@ -592,7 +592,7 @@ class Parser { /// /// [the CSS algorithm]: https://drafts.csswg.org/css-syntax-3/#would-start-an-identifier @protected - bool lookingAtIdentifier([int forward]) { + bool lookingAtIdentifier([int? forward]) { // See also [ScssParser._lookingAtInterpolatedIdentifier]. forward ??= 0; @@ -634,7 +634,7 @@ class Parser { /// Consumes an identifier and asserts that its name exactly matches [text]. @protected void expectIdentifier(String text, - {String name, bool caseSensitive = false}) { + {String? name, bool caseSensitive = false}) { name ??= '"$text"'; var start = scanner.position; @@ -657,7 +657,7 @@ class Parser { /// Prints a warning to standard error, associated with [span]. @protected - void warn(String message, FileSpan span) => logger.warn(message, span: span); + void warn(String message, FileSpan? span) => logger.warn(message, span: span); /// Throws an error associated with [span]. @protected @@ -681,7 +681,7 @@ class Parser { /// If [message] is passed, prints that as well. This is intended for use when /// debugging parser failures. @protected - void debug([Object message]) { + void debug([Object? message]) { if (message == null) { print(scanner.emptySpan.highlight(color: true)); } else { @@ -696,7 +696,7 @@ class Parser { try { return callback(); } on SourceSpanFormatException catch (error) { - var span = error.span as FileSpan /*!*/; + var span = error.span as FileSpan; if (startsWithIgnoreCase(error.message, "expected") && span.length == 0) { var startPosition = _firstNewlineBefore(span.start.offset); if (startPosition != span.start.offset) { @@ -718,7 +718,7 @@ class Parser { /// rather than the line where the problem actually occurred. int _firstNewlineBefore(int position) { var index = position - 1; - int lastNewline; + int? lastNewline; while (index >= 0) { var codeUnit = scanner.string.codeUnitAt(index); if (!isWhitespace(codeUnit)) return lastNewline ?? position; diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index cc5a1100b..ab3ca0727 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -14,32 +14,32 @@ import 'stylesheet.dart'; /// A parser for the indented syntax. class SassParser extends StylesheetParser { - int /*!*/ get currentIndentation => _currentIndentation; + int get currentIndentation => _currentIndentation; // TODO: var - int /*!*/ _currentIndentation = 0; + int _currentIndentation = 0; /// The indentation level of the next source line after the scanner's /// position, or `null` if that hasn't been computed yet. /// /// A source line is any line that's not entirely whitespace. - int _nextIndentation; + int? _nextIndentation; /// The beginning of the next source line after the scanner's position, or /// `null` if the next indentation hasn't been computed yet. /// /// A source line is any line that's not entirely whitespace. - LineScannerState _nextIndentationEnd; + LineScannerState? _nextIndentationEnd; /// Whether the document is indented using spaces or tabs. /// /// If this is `true`, the document is indented using spaces. If it's `false`, /// the document is indented using tabs. If it's `null`, we haven't yet seen /// the indentation character used by the document. - bool _spaces; + bool? _spaces; bool get indented => true; - SassParser(String contents, {Object url, Logger logger}) + SassParser(String contents, {Object? url, Logger? logger}) : super(contents, url: url, logger: logger); Interpolation styleRuleSelector() { @@ -55,12 +55,12 @@ class SassParser extends StylesheetParser { return buffer.interpolation(scanner.spanFrom(start)); } - void expectStatementSeparator([String name]) { + void expectStatementSeparator([String? name]) { if (!atEndOfStatement()) _expectNewline(); if (_peekIndentation() <= currentIndentation) return; scanner.error( "Nothing may be indented ${name == null ? 'here' : 'beneath a $name'}.", - position: _nextIndentationEnd.position); + position: _nextIndentationEnd!.position); } bool atEndOfStatement() { @@ -117,7 +117,7 @@ class SassParser extends StylesheetParser { } } - bool scanElse(int /*!*/ ifIndentation) { + bool scanElse(int ifIndentation) { if (_peekIndentation() != ifIndentation) return false; var start = scanner.state; var startIndentation = currentIndentation; @@ -143,7 +143,7 @@ class SassParser extends StylesheetParser { return children; } - List statements(Statement statement()) { + List statements(Statement? statement()) { var first = scanner.peekChar(); if (first == $tab || first == $space) { scanner.error("Indenting at the beginning of the document is illegal.", @@ -165,7 +165,7 @@ class SassParser extends StylesheetParser { /// This consumes children that are allowed at all levels of the document; the /// [child] parameter is called to consume any children that are specifically /// allowed in the caller's context. - Statement _child(Statement child()) { + Statement? _child(Statement? child()) { switch (scanner.peekChar()) { // Ignore empty lines. case $cr: @@ -374,7 +374,7 @@ class SassParser extends StylesheetParser { /// runs [body] to consume the next statement. void _whileIndentedLower(void body()) { var parentIndentation = currentIndentation; - int childIndentation; + int? childIndentation; while (_peekIndentation() > parentIndentation) { var indentation = _readIndentation(); childIndentation ??= indentation; @@ -391,19 +391,19 @@ class SassParser extends StylesheetParser { /// Consumes indentation whitespace and returns the indentation level of the /// next line. - int /*!*/ _readIndentation() { + int _readIndentation() { // TODO: This "!" is totally bogus - var nextIndentation = _nextIndentation; + var nextIndentation = _nextIndentation!; if (nextIndentation == null) _peekIndentation(); var currentIndentation = _currentIndentation = nextIndentation; - scanner.state = _nextIndentationEnd; + scanner.state = _nextIndentationEnd!; _nextIndentation = null; _nextIndentationEnd = null; return currentIndentation; } /// Returns the indentation level of the next line. - int /*!*/ _peekIndentation() { + int _peekIndentation() { var nextIndentation = _nextIndentation; if (nextIndentation != null) return nextIndentation; @@ -450,7 +450,7 @@ class SassParser extends StylesheetParser { _nextIndentation = nextIndentation; // TODO: no ! - if (nextIndentation > 0) _spaces ??= containsSpace; + if (nextIndentation! > 0) _spaces ??= containsSpace; _nextIndentationEnd = scanner.state; scanner.state = start; return nextIndentation; diff --git a/lib/src/parse/scss.dart b/lib/src/parse/scss.dart index dc50c9ee8..a03b7e6f5 100644 --- a/lib/src/parse/scss.dart +++ b/lib/src/parse/scss.dart @@ -15,12 +15,12 @@ class ScssParser extends StylesheetParser { bool get indented => false; int get currentIndentation => 0; - ScssParser(String contents, {Object url, Logger logger}) + ScssParser(String contents, {Object? url, Logger? logger}) : super(contents, url: url, logger: logger); Interpolation styleRuleSelector() => almostAnyValue(); - void expectStatementSeparator([String name]) { + void expectStatementSeparator([String? name]) { whitespaceWithoutComments(); if (scanner.isDone) return; var next = scanner.peekChar(); @@ -101,7 +101,7 @@ class ScssParser extends StylesheetParser { } } - List statements(Statement /*?*/ statement()) { + List statements(Statement? statement()) { var statements = []; whitespaceWithoutComments(); while (!scanner.isDone) { diff --git a/lib/src/parse/selector.dart b/lib/src/parse/selector.dart index a7011a878..ceb5d1d1b 100644 --- a/lib/src/parse/selector.dart +++ b/lib/src/parse/selector.dart @@ -33,8 +33,8 @@ class SelectorParser extends Parser { final bool _allowPlaceholder; SelectorParser(String contents, - {Object url, - Logger logger, + {Object? url, + Logger? logger, bool allowParent = true, bool allowPlaceholder = true}) : _allowParent = allowParent, @@ -158,7 +158,7 @@ class SelectorParser extends Parser { /// /// If [allowParent] is passed, it controls whether the parent selector `&` is /// allowed. Otherwise, it defaults to [_allowParent]. - SimpleSelector _simpleSelector({bool allowParent}) { + SimpleSelector _simpleSelector({bool? allowParent}) { var start = scanner.state; allowParent ??= _allowParent; switch (scanner.peekChar()) { @@ -307,8 +307,8 @@ class SelectorParser extends Parser { whitespace(); var unvendored = unvendor(name); - String argument; - SelectorList selector; + String? argument; + SelectorList? selector; if (element) { if (_selectorPseudoElements.contains(unvendored)) { selector = _selectorList(); diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 86c42465d..3441f0d16 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -45,7 +45,7 @@ abstract class StylesheetParser extends Parser { /// Whether the current mixin contains at least one `@content` rule. /// /// This is `null` unless [_inMixin] is `true`. - bool _mixinHasContent; + bool? _mixinHasContent; /// Whether the parser is currently parsing a content block passed to a mixin. var _inContentBlock = false; @@ -74,9 +74,9 @@ abstract class StylesheetParser extends Parser { /// The silent comment this parser encountered previously. @protected - SilentComment lastSilentComment; + SilentComment? lastSilentComment; - StylesheetParser(String contents, {Object url, Logger logger}) + StylesheetParser(String contents, {Object? url, Logger? logger}) : super(contents, url: url, logger: logger); // ## Statements @@ -122,7 +122,7 @@ abstract class StylesheetParser extends Parser { return arguments; }); - Expression /*!*/ parseExpression() => _parseSingleProduction(expression); + Expression parseExpression() => _parseSingleProduction(expression); VariableDeclaration parseVariableDeclaration() => _parseSingleProduction(() => lookingAtIdentifier() @@ -212,14 +212,14 @@ abstract class StylesheetParser extends Parser { /// used for the declaration. @protected VariableDeclaration variableDeclarationWithoutNamespace( - [String namespace, LineScannerState start]) { + [String? namespace, LineScannerState? start]) { var precedingComment = lastSilentComment; lastSilentComment = null; start ??= scanner.state; var name = variableName(); // TODO: no start! - if (namespace != null) _assertPublic(name, () => scanner.spanFrom(start)); + if (namespace != null) _assertPublic(name, () => scanner.spanFrom(start!)); if (plainCss) { error("Sass variables aren't allowed in plain CSS.", @@ -482,7 +482,7 @@ abstract class StylesheetParser extends Parser { /// Consumes a [StyleRule], optionally with a [buffer] that may contain some /// text that has already been parsed. - StyleRule _styleRule([InterpolationBuffer buffer, LineScannerState start]) { + StyleRule _styleRule([InterpolationBuffer? buffer, LineScannerState? start]) { _isUseAllowed = false; start ??= scanner.state; @@ -505,7 +505,7 @@ abstract class StylesheetParser extends Parser { _inStyleRule = wasInStyleRule; // TODO: no start! - return StyleRule(interpolation, children, scanner.spanFrom(start)); + return StyleRule(interpolation, children, scanner.spanFrom(start!)); }); } @@ -598,7 +598,7 @@ abstract class StylesheetParser extends Parser { /// If [root] is `true`, this parses at-rules that are allowed only at the /// root of the stylesheet. @protected - Statement /*!*/ atRule(Statement /*!*/ child(), {bool root = false}) { + Statement atRule(Statement child(), {bool root = false}) { // NOTE: this logic is largely duplicated in CssParser.atRule. Most changes // here should be mirrored there. @@ -719,7 +719,7 @@ abstract class StylesheetParser extends Parser { error( "@function rules may not contain " "${statement is StyleRule ? "style rules" : "declarations"}.", - statement.span); + statement.span!); } } @@ -934,7 +934,7 @@ abstract class StylesheetParser extends Parser { expectIdentifier("from"); whitespace(); - bool exclusive; + bool? exclusive; var from = expression(until: () { if (!lookingAtIdentifier()) return false; if (scanIdentifier("to")) { @@ -956,7 +956,7 @@ abstract class StylesheetParser extends Parser { _inControlDirective = wasInControlDirective; // TODO: no exclusive! - return ForRule(variable, from, to, children, span, exclusive: exclusive); + return ForRule(variable, from, to, children, span, exclusive: exclusive!); }); } @@ -967,7 +967,7 @@ abstract class StylesheetParser extends Parser { var url = _urlString(); whitespace(); - String prefix; + String? prefix; if (scanIdentifier("as")) { whitespace(); prefix = identifier(normalize: true); @@ -975,10 +975,10 @@ abstract class StylesheetParser extends Parser { whitespace(); } - Set shownMixinsAndFunctions; - Set /*?*/ shownVariables; - Set hiddenMixinsAndFunctions; - Set /*?*/ hiddenVariables; + Set? shownMixinsAndFunctions; + Set? shownVariables; + Set? hiddenMixinsAndFunctions; + Set? hiddenVariables; if (scanIdentifier("show")) { var members = _memberList(); shownMixinsAndFunctions = members.item1; @@ -999,11 +999,11 @@ abstract class StylesheetParser extends Parser { if (shownMixinsAndFunctions != null) { return ForwardRule.show( - url, shownMixinsAndFunctions, shownVariables, span, + url, shownMixinsAndFunctions, shownVariables!, span, prefix: prefix, configuration: configuration); } else if (hiddenMixinsAndFunctions != null) { return ForwardRule.hide( - url, hiddenMixinsAndFunctions, hiddenVariables, span, + url, hiddenMixinsAndFunctions, hiddenVariables!, span, prefix: prefix, configuration: configuration); } else { return ForwardRule(url, span, @@ -1047,7 +1047,7 @@ abstract class StylesheetParser extends Parser { whitespaceWithoutComments(); var clauses = [IfClause(condition, children)]; - ElseClause lastClause; + ElseClause? lastClause; while (scanElse(ifIndentation)) { whitespace(); @@ -1146,8 +1146,8 @@ abstract class StylesheetParser extends Parser { /// Consumes a supports condition and/or a media query after an `@import`. /// /// Returns `null` if neither type of query can be found. - Tuple2 tryImportQueries() { - SupportsCondition supports; + Tuple2? tryImportQueries() { + SupportsCondition? supports; if (scanIdentifier("supports")) { scanner.expectChar($lparen); var start = scanner.state; @@ -1180,7 +1180,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. IncludeRule _includeRule(LineScannerState start) { - String namespace; + String? namespace; var name = identifier(); if (scanner.scanChar($dot)) { namespace = name; @@ -1195,14 +1195,14 @@ abstract class StylesheetParser extends Parser { : ArgumentInvocation.empty(scanner.emptySpan); whitespace(); - ArgumentDeclaration contentArguments; + ArgumentDeclaration? contentArguments; if (scanIdentifier("using")) { whitespace(); contentArguments = _argumentDeclaration(); whitespace(); } - ContentBlock content; + ContentBlock? content; if (contentArguments != null || lookingAtChildren()) { contentArguments ??= ArgumentDeclaration.empty(span: scanner.emptySpan); @@ -1210,7 +1210,7 @@ abstract class StylesheetParser extends Parser { _inContentBlock = true; content = _withChildren(_statement, start, (children, span) { // TODO: no ! - return ContentBlock(contentArguments, children, span); + return ContentBlock(contentArguments!, children, span); }); _inContentBlock = wasInContentBlock; } else { @@ -1218,7 +1218,7 @@ abstract class StylesheetParser extends Parser { } var span = - scanner.spanFrom(start, start).expand((content ?? arguments).span); + scanner.spanFrom(start, start).expand((content ?? arguments).span!); return IncludeRule(name, arguments, span, namespace: namespace, content: content); } @@ -1258,7 +1258,7 @@ abstract class StylesheetParser extends Parser { _mixinHasContent = false; return _withChildren(_statement, start, (children, span) { - var hadContent = _mixinHasContent; + var hadContent = _mixinHasContent!; _inMixin = false; _mixinHasContent = null; @@ -1396,7 +1396,7 @@ relase. For details, see http://bit.ly/moz-document. /// default namespace from its URL. /// /// Returns `null` to indicate a `@use` rule without a URL. - String _useNamespace(Uri url, LineScannerState start) { + String? _useNamespace(Uri url, LineScannerState start) { if (scanIdentifier("as")) { whitespace(); return scanner.scanChar($asterisk) ? null : identifier(); @@ -1420,7 +1420,7 @@ relase. For details, see http://bit.ly/moz-document. /// `!default` flag. /// /// Returns `null` if there is no `with` clause. - List _configuration({bool allowGuarded = false}) { + List? _configuration({bool allowGuarded = false}) { if (!scanIdentifier("with")) return null; var variableNames = {}; @@ -1498,7 +1498,7 @@ relase. For details, see http://bit.ly/moz-document. var wasInUnknownAtRule = _inUnknownAtRule; _inUnknownAtRule = true; - Interpolation value; + Interpolation? value; var next = scanner.peekChar(); if (next != $exclamation && !atEndOfStatement()) value = almostAnyValue(); @@ -1535,13 +1535,13 @@ relase. For details, see http://bit.ly/moz-document. whitespace(); var arguments = []; var named = {}; - String restArgument; + String? restArgument; while (scanner.peekChar() == $dollar) { var variableStart = scanner.state; var name = variableName(); whitespace(); - Expression defaultValue; + Expression? defaultValue; if (scanner.scanChar($colon)) { whitespace(); defaultValue = _expressionUntilComma(); @@ -1556,7 +1556,7 @@ relase. For details, see http://bit.ly/moz-document. arguments.add(Argument(name, span: scanner.spanFrom(variableStart), defaultValue: defaultValue)); if (!named.add(name)) { - error("Duplicate argument.", arguments.last.span); + error("Duplicate argument.", arguments.last.span!); } if (!scanner.scanChar($comma)) break; @@ -1581,8 +1581,8 @@ relase. For details, see http://bit.ly/moz-document. var positional = []; var named = {}; - Expression rest; - Expression keywordRest; + Expression? rest; + Expression? keywordRest; while (_lookingAtExpression()) { var expression = _expressionUntilComma(singleEquals: !mixin); whitespace(); @@ -1605,7 +1605,7 @@ relase. For details, see http://bit.ly/moz-document. } } else if (named.isNotEmpty) { error("Positional arguments must come before keyword arguments.", - expression.span); + expression.span!); } else { positional.add(expression); } @@ -1632,12 +1632,12 @@ relase. For details, see http://bit.ly/moz-document. /// still be a valid expression. When it returns `true`, this returns the /// expression. @protected - Expression /*!*/ expression( - {bool bracketList = false, bool singleEquals = false, bool until()}) { + Expression expression( + {bool bracketList = false, bool singleEquals = false, bool until()?}) { // TODO: no ! throughout if (until != null && until()) scanner.error("Expected expression."); - LineScannerState beforeBracket; + late LineScannerState beforeBracket; if (bracketList) { beforeBracket = scanner.state; scanner.expectChar($lbracket); @@ -1652,19 +1652,19 @@ relase. For details, see http://bit.ly/moz-document. var start = scanner.state; var wasInParentheses = _inParentheses; - List commaExpressions; + List? commaExpressions; - List spaceExpressions; + List? spaceExpressions; // Operators whose right-hand operands are not fully parsed yet, in order of // appearance in the document. Because a low-precedence operator will cause // parsing to finish for all preceding higher-precedence operators, this is // naturally ordered from lowest to highest precedence. - List operators; + List? operators; // The left-hand sides of [operators]. `operands[n]` is the left-hand side // of `operators[n]`. - List operands; + List? operands; /// Whether the single expression parsed so far may be interpreted as /// slash-separated numbers. @@ -1677,7 +1677,7 @@ relase. For details, see http://bit.ly/moz-document. /// /// foo, bar /// ^ - Expression singleExpression = _singleExpression(); + Expression? singleExpression = _singleExpression(); // Resets the scanner state to the state it was at at the beginning of the // expression, except for [_inParentheses]. @@ -1696,20 +1696,20 @@ relase. For details, see http://bit.ly/moz-document. throw StateError("operators must be set for resolveOneOperation()."); } - var operator = operators.removeLast(); + var operator = operators!.removeLast(); if (operator != BinaryOperator.dividedBy) allowSlash = false; if (allowSlash && !_inParentheses) { singleExpression = BinaryOperationExpression.slash( - operands.removeLast(), singleExpression); + operands!.removeLast(), singleExpression!); } else { singleExpression = BinaryOperationExpression( - operator, operands.removeLast(), singleExpression); + operator, operands!.removeLast(), singleExpression!); } } void resolveOperations() { if (operators == null) return; - while (operators.isNotEmpty) { + while (operators!.isNotEmpty) { resolveOneOperation(); } } @@ -1730,7 +1730,7 @@ relase. For details, see http://bit.ly/moz-document. spaceExpressions ??= []; resolveOperations(); - spaceExpressions.add(singleExpression); + spaceExpressions!.add(singleExpression!); allowSlash = number; } else if (!number) { allowSlash = false; @@ -1752,13 +1752,13 @@ relase. For details, see http://bit.ly/moz-document. operators ??= []; operands ??= []; - while (operators.isNotEmpty && - operators.last.precedence >= operator.precedence) { + while (operators!.isNotEmpty && + operators!.last.precedence >= operator.precedence) { resolveOneOperation(); } - operators.add(operator); + operators!.add(operator); - operands.add(singleExpression); + operands!.add(singleExpression!); whitespace(); allowSlash = allowSlash && lookingAtNumber(); singleExpression = _singleExpression(); @@ -1769,9 +1769,9 @@ relase. For details, see http://bit.ly/moz-document. resolveOperations(); if (spaceExpressions != null) { - spaceExpressions.add(singleExpression); + spaceExpressions!.add(singleExpression!); singleExpression = - ListExpression(spaceExpressions, ListSeparator.space); + ListExpression(spaceExpressions!, ListSeparator.space); spaceExpressions = null; } } @@ -2006,7 +2006,7 @@ relase. For details, see http://bit.ly/moz-document. if (singleExpression == null) scanner.error("Expected expression."); resolveSpaceExpressions(); - commaExpressions.add(singleExpression); + commaExpressions!.add(singleExpression!); scanner.readChar(); allowSlash = true; singleExpression = null; @@ -2026,23 +2026,23 @@ relase. For details, see http://bit.ly/moz-document. if (commaExpressions != null) { resolveSpaceExpressions(); _inParentheses = wasInParentheses; - if (singleExpression != null) commaExpressions.add(singleExpression); - return ListExpression(commaExpressions, ListSeparator.comma, + if (singleExpression != null) commaExpressions!.add(singleExpression!); + return ListExpression(commaExpressions!, ListSeparator.comma, brackets: bracketList, span: bracketList ? scanner.spanFrom(beforeBracket) : null); } else if (bracketList && spaceExpressions != null) { resolveOperations(); return ListExpression( - spaceExpressions..add(singleExpression), ListSeparator.space, + spaceExpressions!..add(singleExpression!), ListSeparator.space, brackets: true, span: scanner.spanFrom(beforeBracket)); } else { resolveSpaceExpressions(); if (bracketList) { singleExpression = ListExpression( - [singleExpression], ListSeparator.undecided, + [singleExpression!], ListSeparator.undecided, brackets: true, span: scanner.spanFrom(beforeBracket)); } - return singleExpression; + return singleExpression!; } } @@ -2356,7 +2356,7 @@ relase. For details, see http://bit.ly/moz-document. UnaryOperationExpression _unaryOperation() { var start = scanner.state; // TODO: no ! - var operator = _unaryOperatorFor(scanner.readChar()); + var operator = _unaryOperatorFor(scanner.readChar())!; if (operator == null) { scanner.error("Expected unary operator.", position: scanner.position - 1); } else if (plainCss && operator != UnaryOperator.divide) { @@ -2371,7 +2371,7 @@ relase. For details, see http://bit.ly/moz-document. /// Returns the unsary operator corresponding to [character], or `null` if /// the character is not a unary operator. - UnaryOperator _unaryOperatorFor(int character) { + UnaryOperator? _unaryOperatorFor(int character) { switch (character) { case $plus: return UnaryOperator.plus; @@ -2399,7 +2399,7 @@ relase. For details, see http://bit.ly/moz-document. number += _tryDecimal(allowTrailingDot: scanner.position != start.position); number *= _tryExponent(); - String unit; + String? unit; if (scanner.scanChar($percent)) { unit = "%"; } else if (lookingAtIdentifier() && @@ -2619,7 +2619,7 @@ relase. For details, see http://bit.ly/moz-document. scanner.readChar(); if (plain == null) { - error("Interpolation isn't allowed in namespaces.", identifier.span); + error("Interpolation isn't allowed in namespaces.", identifier.span!); } if (scanner.peekChar() == $dollar) { @@ -2651,7 +2651,7 @@ relase. For details, see http://bit.ly/moz-document. /// Otherwise, returns `null`. [start] is the location before the beginning of /// [name]. @protected - Expression trySpecialFunction(String name, LineScannerState start) { + Expression? trySpecialFunction(String name, LineScannerState start) { var normalized = unvendor(name); InterpolationBuffer buffer; @@ -2862,7 +2862,7 @@ relase. For details, see http://bit.ly/moz-document. /// /// [start] is the position before the beginning of the name. [name] is the /// function's name; it defaults to `"url"`. - Interpolation _tryUrlContents(LineScannerState start, {String name}) { + Interpolation? _tryUrlContents(LineScannerState start, {String? name}) { // NOTE: this logic is largely duplicated in Parser.tryUrl. Most changes // here should be mirrored there. @@ -3102,7 +3102,7 @@ relase. For details, see http://bit.ly/moz-document. case $lparen: case $lbrace: case $lbracket: - buffer.writeCharCode(next); + buffer.writeCharCode(next!); brackets.add(opposite(scanner.readChar())); wroteNewline = false; break; @@ -3111,7 +3111,7 @@ relase. For details, see http://bit.ly/moz-document. case $rbrace: case $rbracket: if (brackets.isEmpty) break loop; - buffer.writeCharCode(next); + buffer.writeCharCode(next!); scanner.expectChar(brackets.removeLast()); wroteNewline = false; break; @@ -3330,7 +3330,7 @@ relase. For details, see http://bit.ly/moz-document. buffer.add(_expressionUntilComparison()); // TODO: no ! - if ((next == $langle || next == $rangle) && scanner.scanChar(next)) { + if ((next == $langle || next == $rangle) && scanner.scanChar(next!)) { buffer.writeCharCode($space); buffer.writeCharCode(next); if (scanner.scanChar($equal)) buffer.writeCharCode($equal); @@ -3370,7 +3370,7 @@ relase. For details, see http://bit.ly/moz-document. var condition = _supportsConditionInParens(); whitespace(); - String operator; + String? operator; while (lookingAtIdentifier()) { if (operator != null) { expectIdentifier(operator); @@ -3397,7 +3397,7 @@ relase. For details, see http://bit.ly/moz-document. if (_lookingAtInterpolatedIdentifier()) { var identifier = interpolatedIdentifier(); if (identifier.asPlain?.toLowerCase() == "not") { - error('"not" is not a valid identifier here.', identifier.span); + error('"not" is not a valid identifier here.', identifier.span!); } if (scanner.scanChar($lparen)) { @@ -3407,7 +3407,7 @@ relase. For details, see http://bit.ly/moz-document. return SupportsFunction(identifier, arguments, scanner.spanFrom(start)); } else if (identifier.contents.length != 1 || identifier.contents.first is! Expression) { - error("Expected @supports condition.", identifier.span); + error("Expected @supports condition.", identifier.span!); } else { return SupportsInterpolation( identifier.contents.first as Expression, scanner.spanFrom(start)); @@ -3482,7 +3482,7 @@ relase. For details, see http://bit.ly/moz-document. /// If [interpolation] is followed by `"and"` or `"or"`, parse it as a supports operation. /// /// Otherwise, return `null` without moving the scanner position. - SupportsOperation _trySupportsOperation( + SupportsOperation? _trySupportsOperation( Interpolation interpolation, LineScannerState start) { if (interpolation.contents.length != 1) return null; var expression = interpolation.contents.first; @@ -3491,8 +3491,8 @@ relase. For details, see http://bit.ly/moz-document. var beforeWhitespace = scanner.state; whitespace(); - SupportsOperation operation; - String operator; + SupportsOperation? operation; + String? operator; while (lookingAtIdentifier()) { if (operator != null) { expectIdentifier(operator); @@ -3508,9 +3508,7 @@ relase. For details, see http://bit.ly/moz-document. whitespace(); var right = _supportsConditionInParens(); operation = SupportsOperation( - operation ?? - SupportsInterpolation( - expression as Expression, interpolation.span), + operation ?? SupportsInterpolation(expression, interpolation.span), right, operator, scanner.spanFrom(start)); @@ -3586,8 +3584,8 @@ relase. For details, see http://bit.ly/moz-document. /// Consumes a block of [child] statements and passes them, as well as the /// span from [start] to the end of the child block, to [create]. - T _withChildren(Statement /*!*/ child(), LineScannerState start, - T create(List children, FileSpan span)) { + T _withChildren(Statement child(), LineScannerState start, + T create(List children, FileSpan span)) { var result = create(children(child), scanner.spanFrom(start)); whitespaceWithoutComments(); return result; @@ -3649,7 +3647,7 @@ relase. For details, see http://bit.ly/moz-document. /// /// This consumes whitespace, but nothing else, including comments. @protected - void expectStatementSeparator([String name]); + void expectStatementSeparator([String? name]); /// Whether the scanner is positioned at the end of a statement. @protected @@ -3675,12 +3673,12 @@ relase. For details, see http://bit.ly/moz-document. /// whitespace. This is necessary to ensure that the source span for the /// parent rule doesn't cover whitespace after the rule. @protected - List children(Statement /*!*/ child()); + List children(Statement child()); /// Consumes top-level statements. /// /// The [statement] callback may return `null`, indicating that a statement /// was consumed that shouldn't be added to the AST. @protected - List statements(Statement statement()); + List statements(Statement? statement()); } diff --git a/lib/src/stylesheet_graph.dart b/lib/src/stylesheet_graph.dart index 491470264..6db267906 100644 --- a/lib/src/stylesheet_graph.dart +++ b/lib/src/stylesheet_graph.dart @@ -37,7 +37,7 @@ class StylesheetGraph { /// /// Returns `true` if the import cache can't find a stylesheet at [url]. bool modifiedSince(Uri url, DateTime since, - [Importer baseImporter, Uri baseUrl]) { + [Importer? baseImporter, Uri? baseUrl]) { DateTime transitiveModificationTime(StylesheetNode node) { return _transitiveModificationTimes.putIfAbsent(node.canonicalUrl, () { var latest = node.importer.modificationTime(node.canonicalUrl); @@ -66,10 +66,11 @@ class StylesheetGraph { /// import [url] (resolved relative to [baseUrl] if it's passed). /// /// Returns `null` if the import cache can't find a stylesheet at [url]. - StylesheetNode _add(Uri url, [Importer baseImporter, Uri baseUrl]) { + StylesheetNode? _add(Uri url, [Importer? baseImporter, Uri? baseUrl]) { // TODO: no as - var tuple = _ignoreErrors(() => importCache.canonicalize(url, - baseImporter: baseImporter, baseUrl: baseUrl)); + var tuple = _ignoreErrors((() => importCache.canonicalize(url, + baseImporter: baseImporter, + baseUrl: baseUrl)!) as Tuple3 Function()); if (tuple == null) return null; addCanonical(tuple.item1, tuple.item2, tuple.item3); @@ -98,8 +99,9 @@ class StylesheetGraph { if (node != null) return const {}; // TODO: no as - var stylesheet = _ignoreErrors( - () => importCache.importCanonical(importer, canonicalUrl, originalUrl)); + var stylesheet = _ignoreErrors((() => + importCache.importCanonical(importer, canonicalUrl, originalUrl)!) + as Stylesheet Function()); if (stylesheet == null) return const {}; node = StylesheetNode._(stylesheet, importer, canonicalUrl, @@ -116,7 +118,7 @@ class StylesheetGraph { /// /// The first map contains stylesheets depended on via `@use` and `@forward` /// while the second map contains those depended on via `@import`. - Tuple2, Map> _upstreamNodes( + Tuple2, Map> _upstreamNodes( Stylesheet stylesheet, Importer baseImporter, Uri baseUrl) { var active = {baseUrl}; var tuple = findDependencies(stylesheet); @@ -149,8 +151,9 @@ class StylesheetGraph { importCache.clearImport(canonicalUrl); // TODO: no as - var stylesheet = _ignoreErrors( - () => importCache.importCanonical(node.importer, canonicalUrl)); + var stylesheet = _ignoreErrors((() => + importCache.importCanonical(node.importer, canonicalUrl)!) + as Stylesheet Function()); if (stylesheet == null) return false; node._stylesheet = stylesheet; @@ -225,18 +228,18 @@ class StylesheetGraph { /// If [forImport] is `true`, this re-runs canonicalization for /// [node.upstreamImports]. Otherwise, it re-runs canonicalization for /// [node.upstream]. - Map _recanonicalizeImportsForNode( + Map _recanonicalizeImportsForNode( StylesheetNode node, Importer importer, Uri canonicalUrl, - {@required bool forImport}) { + {required bool forImport}) { var map = forImport ? node.upstreamImports : node.upstream; - var newMap = {}; + var newMap = {}; map.forEach((url, upstream) { if (!importer.couldCanonicalize(url, canonicalUrl)) return; importCache.clearCanonicalize(url); // If the import produces a different canonicalized URL than it did // before, it changed and the stylesheet needs to be recompiled. - Tuple3 result; + Tuple3? result; try { result = importCache.canonicalize(url, baseImporter: node.importer, @@ -261,12 +264,14 @@ class StylesheetGraph { /// /// The [active] set should contain the canonical URLs that are currently /// being imported. It's used to detect circular imports. - StylesheetNode _nodeFor( + StylesheetNode? _nodeFor( Uri url, Importer baseImporter, Uri baseUrl, Set active, {bool forImport = false}) { // TODO: no as - var tuple = _ignoreErrors(() => importCache.canonicalize(url, - baseImporter: baseImporter, baseUrl: baseUrl, forImport: forImport)); + var tuple = _ignoreErrors((() => importCache.canonicalize(url, + baseImporter: baseImporter, + baseUrl: baseUrl, + forImport: forImport)!) as Tuple3 Function()); // If an import fails, let the evaluator surface that error rather than // surfacing it here. @@ -284,8 +289,9 @@ class StylesheetGraph { if (active.contains(canonicalUrl)) return null; // TODO: no as - var stylesheet = _ignoreErrors( - () => importCache.importCanonical(importer, canonicalUrl, resolvedUrl)); + var stylesheet = _ignoreErrors((() => + importCache.importCanonical(importer, canonicalUrl, resolvedUrl)!) + as Stylesheet Function()); if (stylesheet == null) return null; active.add(canonicalUrl); @@ -301,7 +307,7 @@ class StylesheetGraph { /// If [callback] throws any errors, ignores them and returns `null`. This is /// used to wrap calls to the import cache, since importer errors should be /// surfaced by the compilation process rather than the graph. - T _ignoreErrors(T callback()) { + T? _ignoreErrors(T callback()) { try { return callback(); } catch (_) { @@ -331,23 +337,23 @@ class StylesheetNode { /// the stylesheets those rules refer to. /// /// This may have `null` values, which indicate failed loads. - Map get upstream => UnmodifiableMapView(_upstream); - Map _upstream; + Map get upstream => UnmodifiableMapView(_upstream); + Map _upstream; /// A map from non-canonicalized `@import` URLs in [stylesheet] to the /// stylesheets those imports refer to. /// /// This may have `null` values, which indicate failed imports. - Map get upstreamImports => + Map get upstreamImports => UnmodifiableMapView(_upstreamImports); - Map _upstreamImports; + Map _upstreamImports; /// The stylesheets that import [stylesheet]. Set get downstream => UnmodifiableSetView(_downstream); final _downstream = {}; StylesheetNode._(this._stylesheet, this.importer, this.canonicalUrl, - Tuple2, Map> allUpstream) + Tuple2, Map> allUpstream) : _upstream = allUpstream.item1, _upstreamImports = allUpstream.item2 { for (var node in upstream.values.followedBy(upstreamImports.values)) { @@ -358,8 +364,8 @@ class StylesheetNode { /// Updates [upstream] and [upstreamImports] from [newUpstream] and /// [newUpstreamImports] and adjusts upstream nodes' [downstream] fields /// accordingly. - void _replaceUpstream(Map newUpstream, - Map newUpstreamImports) { + void _replaceUpstream(Map newUpstream, + Map newUpstreamImports) { var oldUpstream = {...upstream.values, ...upstreamImports.values}.removeNull(); var newUpstreamSet = diff --git a/lib/src/util/character.dart b/lib/src/util/character.dart index efa2eb950..5bf423740 100644 --- a/lib/src/util/character.dart +++ b/lib/src/util/character.dart @@ -11,15 +11,15 @@ import 'package:charcode/charcode.dart'; const _asciiCaseBit = 0x20; /// Returns whether [character] is an ASCII whitespace character. -bool isWhitespace(int character) => +bool isWhitespace(int? character) => isSpaceOrTab(character) || isNewline(character); /// Returns whether [character] is an ASCII newline. -bool isNewline(int character) => +bool isNewline(int? character) => character == $lf || character == $cr || character == $ff; /// Returns whether [character] is a space or a tab character. -bool isSpaceOrTab(int character) => character == $space || character == $tab; +bool isSpaceOrTab(int? character) => character == $space || character == $tab; /// Returns whether [character] is a letter or number. bool isAlphanumeric(int character) => @@ -31,7 +31,7 @@ bool isAlphabetic(int character) => (character >= $A && character <= $Z); /// Returns whether [character] is a number. -bool isDigit(int character) => +bool isDigit(int? character) => character != null && character >= $0 && character <= $9; /// Returns whether [character] is legal as the start of a Sass identifier. @@ -43,7 +43,7 @@ bool isName(int character) => isNameStart(character) || isDigit(character) || character == $minus; /// Returns whether [character] is a hexadeicmal digit. -bool isHex(int character) { +bool isHex(int? character) { if (character == null) return false; if (isDigit(character)) return true; if (character >= $a && character <= $f) return true; @@ -57,7 +57,7 @@ bool isHighSurrogate(int character) => // Returns whether [character] can start a simple selector other than a type // selector. -bool isSimpleSelectorStart(int character) => +bool isSimpleSelectorStart(int? character) => character == $asterisk || character == $lbracket || character == $dot || diff --git a/lib/src/util/limited_map_view.dart b/lib/src/util/limited_map_view.dart index 72759c406..50ad9f7ac 100644 --- a/lib/src/util/limited_map_view.dart +++ b/lib/src/util/limited_map_view.dart @@ -38,15 +38,15 @@ class LimitedMapView extends UnmodifiableMapBase { /// Returns a [LimitedMapView] that doesn't allow keys in [blocklist]. /// /// The [blocklist] must have the same notion of equality as the [map]. - LimitedMapView.blocklist(this._map, Set /*!*/ blocklist) + LimitedMapView.blocklist(this._map, Set blocklist) : _keys = { for (var key in _map.keys) if (!blocklist.contains(key)) key }; // TODO: no as - V operator [](Object key) => _keys.contains(key) ? _map[key] : null; - bool containsKey(Object key) => _keys.contains(key); + V? operator [](Object? key) => _keys.contains(key) ? _map[key as K] : null; + bool containsKey(Object? key) => _keys.contains(key); - V remove(Object key) => _keys.contains(key) ? _map.remove(key) : null; + V? remove(Object? key) => _keys.contains(key) ? _map.remove(key) : null; } diff --git a/lib/src/util/merged_map_view.dart b/lib/src/util/merged_map_view.dart index 3b5ed4830..b84f6ad51 100644 --- a/lib/src/util/merged_map_view.dart +++ b/lib/src/util/merged_map_view.dart @@ -33,7 +33,7 @@ class MergedMapView extends MapBase { /// Each map must have the default notion of equality. The underlying maps' /// values may change independently of this view, but their set of keys may /// not. - MergedMapView(Iterable /*!*/ > maps) { + MergedMapView(Iterable> maps) { for (var map in maps) { if (map is MergedMapView) { // Flatten nested merged views to avoid O(depth) overhead. @@ -46,7 +46,7 @@ class MergedMapView extends MapBase { } } - V operator [](Object key) => _mapsByKey[key].andGet(key); + V? operator [](Object? key) => _mapsByKey[key as K].andGet(key); operator []=(K key, V value) { var child = _mapsByKey[key]; @@ -57,7 +57,7 @@ class MergedMapView extends MapBase { child[key] = value; } - V /*?*/ remove(Object key) { + V? remove(Object? key) { throw UnsupportedError("Entries may not be removed from MergedMapView."); } @@ -65,5 +65,5 @@ class MergedMapView extends MapBase { throw UnsupportedError("Entries may not be removed from MergedMapView."); } - bool containsKey(Object key) => _mapsByKey.containsKey(key); + bool containsKey(Object? key) => _mapsByKey.containsKey(key); } diff --git a/lib/src/util/multi_dir_watcher.dart b/lib/src/util/multi_dir_watcher.dart index af24008fd..9543a1c08 100644 --- a/lib/src/util/multi_dir_watcher.dart +++ b/lib/src/util/multi_dir_watcher.dart @@ -38,7 +38,7 @@ class MultiDirWatcher { Future watch(String directory) { var isParentOfExistingDir = false; for (var entry in _watchers.entries.toList()) { - var existingDir = entry.key /*!*/; // dart-lang/path#100 + var existingDir = entry.key!; // dart-lang/path#100 var existingWatcher = entry.value; if (!isParentOfExistingDir && diff --git a/lib/src/util/no_source_map_buffer.dart b/lib/src/util/no_source_map_buffer.dart index 7e955ee7f..c69100883 100644 --- a/lib/src/util/no_source_map_buffer.dart +++ b/lib/src/util/no_source_map_buffer.dart @@ -17,17 +17,17 @@ class NoSourceMapBuffer implements SourceMapBuffer { int get length => _buffer.length; Map get sourceFiles => const {}; - T forSpan(SourceSpan span, T callback()) => callback(); - void write(Object object) => _buffer.write(object); - void writeAll(Iterable objects, [String separator = ""]) => + T forSpan(SourceSpan? span, T callback()) => callback(); + void write(Object? object) => _buffer.write(object); + void writeAll(Iterable objects, [String separator = ""]) => _buffer.writeAll(objects, separator); void writeCharCode(int charCode) => _buffer.writeCharCode(charCode); - void writeln([Object object = ""]) => _buffer.writeln(object); + void writeln([Object? object = ""]) => _buffer.writeln(object); String toString() => _buffer.toString(); void clear() => throw UnsupportedError("SourceMapBuffer.clear() is not supported."); - SingleMapping buildSourceMap({String prefix}) => throw UnsupportedError( + SingleMapping buildSourceMap({String? prefix}) => throw UnsupportedError( "NoSourceMapBuffer.buildSourceMap() is not supported."); } diff --git a/lib/src/util/nullable.dart b/lib/src/util/nullable.dart index cd59306d2..373baf4a3 100644 --- a/lib/src/util/nullable.dart +++ b/lib/src/util/nullable.dart @@ -2,39 +2,39 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. -extension NullableExtension on T /*?*/ { +extension NullableExtension on T? { /// If [this] is `null`, returns `null`. Otherwise, runs [fn] and returns its /// result. /// /// Based on Rust's `Option.and_then`. - V /*?*/ andThen(V Function(T value) fn) { + V? andThen(V Function(T value)? fn) { var self = this; // dart-lang/language#1520 - return self == null ? null : fn(self); + return self == null ? null : fn!(self); } } -extension NullableListExtension on List /*?*/ { +extension NullableListExtension on List? { /// If [this] is `null`, returns `null`. Otherwise, returns `this[index]`. /// /// This is the equivalent of `list?.[key]`, if such a thing existed. - T /*?*/ andGet(int index) { + T? andGet(int index) { var self = this; return self == null ? null : self[index]; } } -extension NullableMapExtension on Map /*?*/ { +extension NullableMapExtension on Map? { /// If [this] is `null`, returns `null`. Otherwise, returns `this[key]`. /// /// This is the equivalent of `map?.[key]`, if such a thing existed. - V /*?*/ andGet(Object key) { + V? andGet(Object? key) { var self = this; // TODO: no as - return self == null ? null : self[key]; + return self == null ? null : self[key as K]; } } -extension SetExtension on Set { +extension SetExtension on Set { /// Destructively removes the `null` element from this set, if it exists, and /// returns a view of it casted to a non-nullable type. Set removeNull() { diff --git a/lib/src/util/number.dart b/lib/src/util/number.dart index f2f341f90..c47ab44da 100644 --- a/lib/src/util/number.dart +++ b/lib/src/util/number.dart @@ -54,7 +54,7 @@ bool fuzzyIsInt(num number) { /// [int]. /// /// Otherwise, returns `null`. -int fuzzyAsInt(num number) => fuzzyIsInt(number) ? number.round() : null; +int? fuzzyAsInt(num number) => fuzzyIsInt(number) ? number.round() : null; /// Rounds [number] to the nearest integer. /// @@ -75,7 +75,7 @@ int fuzzyRound(num number) { /// /// If [number] is [fuzzyEquals] to [min] or [max], it's clamped to the /// appropriate value. -num fuzzyCheckRange(num number, num min, num max) { +num? fuzzyCheckRange(num number, num min, num max) { if (fuzzyEquals(number, min)) return min; if (fuzzyEquals(number, max)) return max; if (number > min && number < max) return number; @@ -86,7 +86,7 @@ num fuzzyCheckRange(num number, num min, num max) { /// /// If [number] is [fuzzyEquals] to [min] or [max], it's clamped to the /// appropriate value. [name] is used in error reporting. -num fuzzyAssertRange(num number, int min, int max, [String name]) { +num fuzzyAssertRange(num number, int min, int max, [String? name]) { var result = fuzzyCheckRange(number, min, max); if (result != null) return result; throw RangeError.range( diff --git a/lib/src/util/prefixed_map_view.dart b/lib/src/util/prefixed_map_view.dart index 29c0b3565..6f923db87 100644 --- a/lib/src/util/prefixed_map_view.dart +++ b/lib/src/util/prefixed_map_view.dart @@ -21,11 +21,11 @@ class PrefixedMapView extends UnmodifiableMapBase { /// Creates a new prefixed map view. PrefixedMapView(this._map, this._prefix); - V operator [](Object key) => key is String && key.startsWith(_prefix) + V? operator [](Object? key) => key is String && key.startsWith(_prefix) ? _map[key.substring(_prefix.length)] : null; - bool containsKey(Object key) => key is String && key.startsWith(_prefix) + bool containsKey(Object? key) => key is String && key.startsWith(_prefix) ? _map.containsKey(key.substring(_prefix.length)) : false; } @@ -33,7 +33,7 @@ class PrefixedMapView extends UnmodifiableMapBase { /// The implementation of [PrefixedMapViews.keys]. class _PrefixedKeys extends IterableBase { /// The view whose keys are being iterated over. - final PrefixedMapView _view; + final PrefixedMapView _view; int get length => _view.length; Iterator get iterator => @@ -41,5 +41,5 @@ class _PrefixedKeys extends IterableBase { _PrefixedKeys(this._view); - bool contains(Object key) => _view.containsKey(key); + bool contains(Object? key) => _view.containsKey(key); } diff --git a/lib/src/util/public_member_map_view.dart b/lib/src/util/public_member_map_view.dart index 641a7ebd3..e8110ed34 100644 --- a/lib/src/util/public_member_map_view.dart +++ b/lib/src/util/public_member_map_view.dart @@ -10,7 +10,7 @@ import '../utils.dart'; /// begin with `_` or `-`. /// /// Note that [PublicMemberMap.length] is *not* `O(1)`. -class PublicMemberMapView extends UnmodifiableMapBase { +class PublicMemberMapView extends UnmodifiableMapBase { /// The wrapped map. final Map _inner; @@ -18,10 +18,10 @@ class PublicMemberMapView extends UnmodifiableMapBase { PublicMemberMapView(this._inner); - bool containsKey(Object key) => + bool containsKey(Object? key) => key is String && isPublic(key) && _inner.containsKey(key); - V operator [](Object key) { + V? operator [](Object? key) { if (key is String && isPublic(key)) return _inner[key]; return null; } diff --git a/lib/src/util/source_map_buffer.dart b/lib/src/util/source_map_buffer.dart index 65877e169..fae869156 100644 --- a/lib/src/util/source_map_buffer.dart +++ b/lib/src/util/source_map_buffer.dart @@ -24,7 +24,7 @@ class SourceMapBuffer implements StringBuffer { for (var entry in _sourceFiles.entries) entry.key.toString(): entry.value }); - final _sourceFiles = {}; + final _sourceFiles = {}; /// The index of the current line in [_buffer]. var _line = 0; @@ -51,7 +51,7 @@ class SourceMapBuffer implements StringBuffer { /// [span.end]. /// /// Just calls `callback()` if [span] is `null`. - T forSpan(FileSpan span, T callback()) { + T forSpan(FileSpan? span, T callback()) { if (span == null) return callback(); var wasInSpan = _inSpan; @@ -95,7 +95,7 @@ class SourceMapBuffer implements StringBuffer { void clear() => throw UnsupportedError("SourceMapBuffer.clear() is not supported."); - void write(Object object) { + void write(Object? object) { var string = object.toString(); _buffer.write(string); @@ -108,7 +108,7 @@ class SourceMapBuffer implements StringBuffer { } } - void writeAll(Iterable objects, [String separator = ""]) => + void writeAll(Iterable objects, [String separator = ""]) => write(objects.join(separator)); void writeCharCode(int charCode) { @@ -120,7 +120,7 @@ class SourceMapBuffer implements StringBuffer { } } - void writeln([Object object = ""]) { + void writeln([Object? object = ""]) { // Special-case the common case. if (identical(object, "")) { _buffer.writeln(); @@ -164,7 +164,7 @@ class SourceMapBuffer implements StringBuffer { /// forward by the number of characters and lines in [prefix]. /// /// [SingleMapping.targetUrl] will be `null`. - SingleMapping buildSourceMap({String prefix}) { + SingleMapping buildSourceMap({String? prefix}) { if (prefix == null || prefix.isEmpty) { return SingleMapping.fromEntries(_entries); } diff --git a/lib/src/util/unprefixed_map_view.dart b/lib/src/util/unprefixed_map_view.dart index d7200eb16..708e0e22b 100644 --- a/lib/src/util/unprefixed_map_view.dart +++ b/lib/src/util/unprefixed_map_view.dart @@ -15,28 +15,28 @@ import 'dart:collection'; /// `@used with` to mark configured variables as used. class UnprefixedMapView extends UnmodifiableMapBase { /// The wrapped map. - final Map _map; + final Map _map; /// The prefix to remove from the map keys. - final String /*!*/ _prefix; + final String _prefix; Iterable get keys => _UnprefixedKeys(this); /// Creates a new unprefixed map view. UnprefixedMapView(this._map, this._prefix); - V operator [](Object key) => key is String ? _map[_prefix + key] : null; + V? operator [](Object? key) => key is String ? _map[_prefix + key] : null; - bool containsKey(Object key) => + bool containsKey(Object? key) => key is String ? _map.containsKey(_prefix + key) : false; - V remove(Object key) => key is String ? _map.remove(_prefix + key) : null; + V? remove(Object? key) => key is String ? _map.remove(_prefix + key) : null; } /// The implementation of [UnprefixedMapViews.keys]. class _UnprefixedKeys extends IterableBase { /// The view whose keys are being iterated over. - final UnprefixedMapView _view; + final UnprefixedMapView _view; Iterator get iterator => _view._map.keys .where((key) => key.startsWith(_view._prefix)) @@ -45,5 +45,5 @@ class _UnprefixedKeys extends IterableBase { _UnprefixedKeys(this._view); - bool contains(Object key) => _view.containsKey(key); + bool contains(Object? key) => _view.containsKey(key); } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 58ebd71be..d9740748b 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -18,7 +18,7 @@ import 'util/nullable.dart'; final _noSourceUrl = Uri.parse("-"); /// Converts [iter] into a sentence, separating each word with [conjunction]. -String toSentence(Iterable iter, [String conjunction]) { +String toSentence(Iterable iter, [String? conjunction]) { conjunction ??= "and"; if (iter.length == 1) return iter.first.toString(); return iter.take(iter.length - 1).join(", ") + " $conjunction ${iter.last}"; @@ -32,7 +32,7 @@ String indent(String string, int indentation) => /// /// By default, this just adds "s" to the end of [name] to get the plural. If /// [plural] is passed, that's used instead. -String pluralize(String name, int number, {String plural}) { +String pluralize(String name, int number, {String? plural}) { if (number == 1) return name; if (plural != null) return plural; return '${name}s'; @@ -70,7 +70,7 @@ String trimAscii(String string, {bool excludeEscape = false}) { return start == null ? "" : string.substring( - start, _lastNonWhitespace(string, excludeEscape: excludeEscape) + 1); + start, _lastNonWhitespace(string, excludeEscape: excludeEscape)! + 1); } /// Like [String.trimLeft], but only trims ASCII whitespace. @@ -90,7 +90,7 @@ String trimAsciiRight(String string, {bool excludeEscape = false}) { /// Returns the index of the first character in [string] that's not ASCII /// whitespace, or [null] if [string] is entirely spaces. -int _firstNonWhitespace(String string) { +int? _firstNonWhitespace(String string) { for (var i = 0; i < string.length; i++) { if (!isWhitespace(string.codeUnitAt(i))) return i; } @@ -102,7 +102,7 @@ int _firstNonWhitespace(String string) { /// /// If [excludeEscape] is `true`, this doesn't move past whitespace that's /// included in a CSS escape. -int _lastNonWhitespace(String string, {bool excludeEscape = false}) { +int? _lastNonWhitespace(String string, {bool excludeEscape = false}) { for (var i = string.length - 1; i >= 0; i--) { var codeUnit = string.codeUnitAt(i); if (!isWhitespace(codeUnit)) { @@ -149,7 +149,7 @@ List flattenVertically(Iterable> iterable) { /// Returns the first element of [iterable], or `null` if the iterable is empty. // TODO(nweiz): Use package:collection -T firstOrNull(Iterable iterable) { +T? firstOrNull(Iterable iterable) { var iterator = iterable.iterator; return iterator.moveNext() ? iterator.current : null; } @@ -188,8 +188,8 @@ int iterableHash(Iterable iterable) => const IterableEquality().hash(iterable); /// Returns whether [list1] and [list2] have the same contents. -bool listEquals(List list1, List list2) => - const ListEquality().equals(list1, list2); +bool listEquals(List? list1, List? list2) => + const ListEquality().equals(list1, list2); /// Returns a hash code for [list] that matches [listEquals]. int listHash(List list) => const ListEquality().hash(list); @@ -206,7 +206,7 @@ int mapHash(Map map) => /// /// By default, the frame's URL is set to `span.sourceUrl`. However, if [url] is /// passed, it's used instead. -Frame frameForSpan(SourceSpan /*?*/ span, String member, {Uri url}) => Frame( +Frame frameForSpan(SourceSpan? span, String member, {Uri? url}) => Frame( url ?? span?.sourceUrl ?? _noSourceUrl, span.andThen((span) => span.start.line + 1) ?? 1, span.andThen((span) => span.start.column + 1) ?? 1, @@ -217,7 +217,7 @@ Frame frameForSpan(SourceSpan /*?*/ span, String member, {Uri url}) => Frame( /// /// If [nodes] is empty, or if either the first or last node has a `null` span, /// returns `null`. -FileSpan spanForList(List nodes) { +FileSpan? spanForList(List nodes) { if (nodes.isEmpty) return null; var left = nodes.first.span; @@ -255,7 +255,7 @@ String unvendor(String name) { } /// Returns whether [string1] and [string2] are equal, ignoring ASCII case. -bool equalsIgnoreCase(String string1, String string2) { +bool equalsIgnoreCase(String? string1, String string2) { if (identical(string1, string2)) return true; if (string1 == null || string2 == null) return false; if (string1.length != string2.length) return false; @@ -297,15 +297,15 @@ void mapInPlace(List list, T function(T element)) { /// list. If it returns `null`, the elements are considered unequal; otherwise, /// it should return the element to include in the return value. List longestCommonSubsequence(List list1, List list2, - {T select(T element1, T element2)}) { + {T? select(T element1, T element2)?}) { select ??= (element1, element2) => element1 == element2 ? element1 : null; var lengths = List.generate( list1.length + 1, (_) => List.filled(list2.length + 1, 0), growable: false); - var selections = List>.generate( - list1.length, (_) => List.filled(list2.length, null), + var selections = List>.generate( + list1.length, (_) => List.filled(list2.length, null), growable: false); for (var i = 0; i < list1.length; i++) { @@ -335,9 +335,8 @@ List longestCommonSubsequence(List list1, List list2, /// /// By default, throws a [StateError] if no value matches. If [orElse] is /// passed, its return value is used instead. -T /*!*/ removeFirstWhere(List list, bool test(T value), - {T /*!*/ orElse()}) { - T toRemove; +T removeFirstWhere(List list, bool test(T value), {T orElse()?}) { + T? toRemove; for (var element in list) { if (!test(element)) continue; toRemove = element; @@ -397,7 +396,7 @@ Future> mapAsync( /// same key. Future putIfAbsentAsync( Map map, K key, Future ifAbsent()) async { - if (map.containsKey(key)) return map[key] /*!*/; + if (map.containsKey(key)) return map[key]!; var value = await ifAbsent(); map[key] = value; return value; diff --git a/lib/src/value.dart b/lib/src/value.dart index 92cd7e67f..bac1df40b 100644 --- a/lib/src/value.dart +++ b/lib/src/value.dart @@ -64,7 +64,7 @@ abstract class Value implements ext.Value { /// Returns Dart's `null` value if this is [sassNull], and returns [this] /// otherwise. - Value get realNull => this; + Value? get realNull => this; const Value(); @@ -74,7 +74,7 @@ abstract class Value implements ext.Value { /// It's not guaranteed to be stable across versions. T accept(ValueVisitor visitor); - int sassIndexToListIndex(ext.Value sassIndex, [String name]) { + int sassIndexToListIndex(ext.Value sassIndex, [String? name]) { var index = sassIndex.assertNumber(name).assertInt(name); if (index == 0) throw _exception("List index may not be 0.", name); if (index.abs() > lengthAsList) { @@ -86,24 +86,24 @@ abstract class Value implements ext.Value { return index < 0 ? lengthAsList + index : index - 1; } - SassBoolean assertBoolean([String name]) => + SassBoolean assertBoolean([String? name]) => throw _exception("$this is not a boolean.", name); - SassColor assertColor([String name]) => + SassColor assertColor([String? name]) => throw _exception("$this is not a color.", name); - SassFunction assertFunction([String name]) => + SassFunction assertFunction([String? name]) => throw _exception("$this is not a function reference.", name); - SassMap assertMap([String name]) => + SassMap assertMap([String? name]) => throw _exception("$this is not a map.", name); - SassMap tryMap() => null; + SassMap? tryMap() => null; - SassNumber assertNumber([String name]) => + SassNumber assertNumber([String? name]) => throw _exception("$this is not a number.", name); - SassString assertString([String name]) => + SassString assertString([String? name]) => throw _exception("$this is not a string.", name); /// Parses [this] as a selector list, in the same manner as the @@ -115,7 +115,7 @@ abstract class Value implements ext.Value { /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. - SelectorList assertSelector({String name, bool allowParent = false}) { + SelectorList assertSelector({String? name, bool allowParent = false}) { var string = _selectorString(name); try { return SelectorList.parse(string, allowParent: allowParent); @@ -135,7 +135,8 @@ abstract class Value implements ext.Value { /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. - SimpleSelector assertSimpleSelector({String name, bool allowParent = false}) { + SimpleSelector assertSimpleSelector( + {String? name, bool allowParent = false}) { var string = _selectorString(name); try { return SimpleSelector.parse(string, allowParent: allowParent); @@ -156,7 +157,7 @@ abstract class Value implements ext.Value { /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. CompoundSelector assertCompoundSelector( - {String name, bool allowParent = false}) { + {String? name, bool allowParent = false}) { var string = _selectorString(name); try { return CompoundSelector.parse(string, allowParent: allowParent); @@ -172,7 +173,7 @@ abstract class Value implements ext.Value { /// /// Throws a [SassScriptException] if [this] isn't a type or a structure that /// can be parsed as a selector. - String _selectorString([String name]) { + String _selectorString([String? name]) { var string = _selectorStringOrNull(); if (string != null) return string; @@ -187,7 +188,7 @@ abstract class Value implements ext.Value { /// /// Returns `null` if [this] isn't a type or a structure that can be parsed as /// a selector. - String _selectorStringOrNull() { + String? _selectorStringOrNull() { if (this is SassString) return (this as SassString).text; if (this is! SassList) return null; var list = this as SassList; @@ -222,7 +223,7 @@ abstract class Value implements ext.Value { /// Returns a new list containing [contents] that defaults to this value's /// separator and brackets. SassList changeListContents(Iterable contents, - {ListSeparator separator, bool brackets}) { + {ListSeparator? separator, bool? brackets}) { return SassList(contents, separator ?? this.separator, brackets: brackets ?? hasBrackets); } @@ -346,6 +347,6 @@ abstract class Value implements ext.Value { String toString() => serializeValue(this, inspect: true); /// Throws a [SassScriptException] with the given [message]. - SassScriptException _exception(String message, [String name]) => + SassScriptException _exception(String message, [String? name]) => SassScriptException(name == null ? message : "\$$name: $message"); } diff --git a/lib/src/value/argument_list.dart b/lib/src/value/argument_list.dart index f26b6cbb6..4e506ec00 100644 --- a/lib/src/value/argument_list.dart +++ b/lib/src/value/argument_list.dart @@ -23,8 +23,8 @@ class SassArgumentList extends SassList implements ext.SassArgumentList { bool get wereKeywordsAccessed => _wereKeywordsAccessed; var _wereKeywordsAccessed = false; - SassArgumentList(Iterable contents, - Map keywords, ListSeparator separator) + SassArgumentList(Iterable contents, Map keywords, + ListSeparator separator) : _keywords = Map.unmodifiable(keywords), super(contents, separator); } diff --git a/lib/src/value/boolean.dart b/lib/src/value/boolean.dart index cf2baa14a..8b755428f 100644 --- a/lib/src/value/boolean.dart +++ b/lib/src/value/boolean.dart @@ -23,7 +23,7 @@ class SassBoolean extends Value implements ext.SassBoolean { T accept(ValueVisitor visitor) => visitor.visitBoolean(this); - SassBoolean assertBoolean([String name]) => this; + SassBoolean assertBoolean([String? name]) => this; Value unaryNot() => value ? sassFalse : sassTrue; } diff --git a/lib/src/value/color.dart b/lib/src/value/color.dart index 171655744..7747d5c90 100644 --- a/lib/src/value/color.dart +++ b/lib/src/value/color.dart @@ -17,45 +17,45 @@ class SassColor extends Value implements ext.SassColor { int get red { if (_red == null) _hslToRgb(); - return _red; + return _red!; } - int _red; + int? _red; int get green { if (_green == null) _hslToRgb(); - return _green; + return _green!; } - int _green; + int? _green; int get blue { if (_blue == null) _hslToRgb(); - return _blue; + return _blue!; } - int _blue; + int? _blue; num get hue { if (_hue == null) _rgbToHsl(); - return _hue; + return _hue!; } - num _hue; + num? _hue; num get saturation { if (_saturation == null) _rgbToHsl(); - return _saturation; + return _saturation!; } - num _saturation; + num? _saturation; num get lightness { if (_lightness == null) _rgbToHsl(); - return _lightness; + return _lightness!; } - num _lightness; + num? _lightness; num get whiteness { // Because HWB is (currently) used much less frequently than HSL or RGB, we @@ -75,29 +75,29 @@ class SassColor extends Value implements ext.SassColor { /// The original string representation of this color, or `null` if one is /// unavailable. - String get original => originalSpan?.text; + String? get original => originalSpan?.text; /// The span tracking the location in which this color was originally defined. /// /// This is tracked as a span to avoid extra substring allocations. - final FileSpan originalSpan; + final FileSpan? originalSpan; SassColor.rgb(this._red, this._green, this._blue, - [num alpha, this.originalSpan]) + [num? alpha, this.originalSpan]) : alpha = alpha == null ? 1 : fuzzyAssertRange(alpha, 0, 1, "alpha") { RangeError.checkValueInInterval(red, 0, 255, "red"); RangeError.checkValueInInterval(green, 0, 255, "green"); RangeError.checkValueInInterval(blue, 0, 255, "blue"); } - SassColor.hsl(num hue, num saturation, num lightness, [num alpha]) + SassColor.hsl(num hue, num saturation, num lightness, [num? alpha]) : _hue = hue % 360, _saturation = fuzzyAssertRange(saturation, 0, 100, "saturation"), _lightness = fuzzyAssertRange(lightness, 0, 100, "lightness"), alpha = alpha == null ? 1 : fuzzyAssertRange(alpha, 0, 1, "alpha"), originalSpan = null; - factory SassColor.hwb(num hue, num whiteness, num blackness, [num alpha]) { + factory SassColor.hwb(num hue, num whiteness, num blackness, [num? alpha]) { // From https://www.w3.org/TR/css-color-4/#hwb-to-rgb var scaledHue = hue % 360 / 360; var scaledWhiteness = @@ -114,7 +114,7 @@ class SassColor extends Value implements ext.SassColor { var factor = 1 - scaledWhiteness - scaledBlackness; int toRgb(num hue) { // TODO: var - var channel = _hueToRgb(0, 1, hue) * factor + scaledWhiteness; + num channel = _hueToRgb(0, 1, hue) * factor + scaledWhiteness; return fuzzyRound(channel * 255); } @@ -132,24 +132,25 @@ class SassColor extends Value implements ext.SassColor { T accept(ValueVisitor visitor) => visitor.visitColor(this); - SassColor assertColor([String name]) => this; + SassColor assertColor([String? name]) => this; - SassColor changeRgb({int red, int green, int blue, num alpha}) => + SassColor changeRgb({int? red, int? green, int? blue, num? alpha}) => SassColor.rgb(red ?? this.red, green ?? this.green, blue ?? this.blue, alpha ?? this.alpha); - SassColor changeHsl({num hue, num saturation, num lightness, num alpha}) => + SassColor changeHsl( + {num? hue, num? saturation, num? lightness, num? alpha}) => SassColor.hsl(hue ?? this.hue, saturation ?? this.saturation, lightness ?? this.lightness, alpha ?? this.alpha); - SassColor changeHwb({num hue, num whiteness, num blackness, num alpha}) => + SassColor changeHwb({num? hue, num? whiteness, num? blackness, num? alpha}) => SassColor.hwb(hue ?? this.hue, whiteness ?? this.whiteness, blackness ?? this.blackness, alpha ?? this.alpha); SassColor changeAlpha(num alpha) => SassColor._(_red, _green, _blue, _hue, _saturation, _lightness, fuzzyAssertRange(alpha, 0, 1, "alpha")); - Value plus(Value /*!*/ other) { + Value plus(Value other) { if (other is! SassNumber && other is! SassColor) return super.plus(other); throw SassScriptException('Undefined operation "$this + $other".'); } @@ -166,7 +167,7 @@ class SassColor extends Value implements ext.SassColor { throw SassScriptException('Undefined operation "$this / $other".'); } - Value modulo(Value /*!*/ other) => + Value modulo(Value other) => throw SassScriptException('Undefined operation "$this % $other".'); bool operator ==(Object other) => diff --git a/lib/src/value/external/color.dart b/lib/src/value/external/color.dart index 13e7fc467..cf961bbb2 100644 --- a/lib/src/value/external/color.dart +++ b/lib/src/value/external/color.dart @@ -11,22 +11,22 @@ import 'value.dart'; @sealed abstract class SassColor extends Value { /// This color's red channel, between `0` and `255`. - int /*!*/ get red; + int get red; /// This color's green channel, between `0` and `255`. - int /*!*/ get green; + int get green; /// This color's blue channel, between `0` and `255`. - int /*!*/ get blue; + int get blue; /// This color's hue, between `0` and `360`. - num /*!*/ get hue; + num get hue; /// This color's saturation, a percentage between `0` and `100`. - num /*!*/ get saturation; + num get saturation; /// This color's lightness, a percentage between `0` and `100`. - num /*!*/ get lightness; + num get lightness; /// This color's whiteness, a percentage between `0` and `100`. num get whiteness; @@ -41,31 +41,31 @@ abstract class SassColor extends Value { /// /// Throws a [RangeError] if [red], [green], and [blue] aren't between `0` and /// `255`, or if [alpha] isn't between `0` and `1`. - factory SassColor.rgb(int red, int green, int blue, [num alpha]) = + factory SassColor.rgb(int red, int green, int blue, [num? alpha]) = internal.SassColor.rgb; /// Creates an HSL color. /// /// Throws a [RangeError] if [saturation] or [lightness] aren't between `0` /// and `100`, or if [alpha] isn't between `0` and `1`. - factory SassColor.hsl(num hue, num saturation, num lightness, [num alpha]) = + factory SassColor.hsl(num hue, num saturation, num lightness, [num? alpha]) = internal.SassColor.hsl; /// Creates an HWB color. /// /// Throws a [RangeError] if [whiteness] or [blackness] aren't between `0` and /// `100`, or if [alpha] isn't between `0` and `1`. - factory SassColor.hwb(num hue, num whiteness, num blackness, [num alpha]) = + factory SassColor.hwb(num hue, num whiteness, num blackness, [num? alpha]) = internal.SassColor.hwb; /// Changes one or more of this color's RGB channels and returns the result. - SassColor changeRgb({int red, int green, int blue, num alpha}); + SassColor changeRgb({int? red, int? green, int? blue, num? alpha}); /// Changes one or more of this color's HSL channels and returns the result. - SassColor changeHsl({num hue, num saturation, num lightness, num alpha}); + SassColor changeHsl({num? hue, num? saturation, num? lightness, num? alpha}); /// Changes one or more of this color's HWB channels and returns the result. - SassColor changeHwb({num hue, num whiteness, num blackness, num alpha}); + SassColor changeHwb({num? hue, num? whiteness, num? blackness, num? alpha}); /// Returns a new copy of this color with the alpha channel set to [alpha]. SassColor changeAlpha(num alpha); diff --git a/lib/src/value/external/list.dart b/lib/src/value/external/list.dart index daa447d8e..97e666ab0 100644 --- a/lib/src/value/external/list.dart +++ b/lib/src/value/external/list.dart @@ -13,14 +13,15 @@ import 'value.dart'; abstract class SassList extends Value { ListSeparator get separator; - bool /*!*/ get hasBrackets; + bool get hasBrackets; /// Returns an empty list with the given [separator] and [brackets]. /// /// The [separator] defaults to [ListSeparator.undecided], and [brackets] defaults to `false`. // TODO: No ? for brackets - const factory SassList.empty({ListSeparator separator, bool brackets}) = - internal.SassList.empty; + const factory SassList.empty( + {ListSeparator? separator, + required bool brackets}) = internal.SassList.empty; /// Returns an empty list with the given [separator] and [brackets]. factory SassList(Iterable contents, ListSeparator separator, diff --git a/lib/src/value/external/number.dart b/lib/src/value/external/number.dart index 628b9aea0..6385b9658 100644 --- a/lib/src/value/external/number.dart +++ b/lib/src/value/external/number.dart @@ -51,18 +51,18 @@ abstract class SassNumber extends Value { /// If [this] is an integer according to [isInt], returns [value] as an [int]. /// /// Otherwise, returns `null`. - int get asInt; + int? get asInt; /// Creates a number, optionally with a single numerator unit. /// /// This matches the numbers that can be written as literals. /// [SassNumber.withUnits] can be used to construct more complex units. - factory SassNumber(num value, [String unit]) = internal.SassNumber; + factory SassNumber(num value, [String? unit]) = internal.SassNumber; /// Creates a number with full [numeratorUnits] and [denominatorUnits]. factory SassNumber.withUnits(num value, - {List numeratorUnits, - List denominatorUnits}) = internal.SassNumber.withUnits; + {List? numeratorUnits, + List? denominatorUnits}) = internal.SassNumber.withUnits; /// Returns [value] as an [int], if it's an integer value according to /// [isInt]. @@ -70,7 +70,7 @@ abstract class SassNumber extends Value { /// Throws a [SassScriptException] if [value] isn't an integer. If this came /// from a function argument, [name] is the argument name (without the `$`). /// It's used for error reporting. - int assertInt([String name]); + int assertInt([String? name]); /// If [value] is between [min] and [max], returns it. /// @@ -78,7 +78,7 @@ abstract class SassNumber extends Value { /// appropriate value. Otherwise, this throws a [SassScriptException]. If this /// came from a function argument, [name] is the argument name (without the /// `$`). It's used for error reporting. - num valueInRange(num min, num max, [String name]); + num valueInRange(num min, num max, [String? name]); /// Returns whether [this] has [unit] as its only unit (and as a numerator). bool hasUnit(String unit); @@ -93,13 +93,13 @@ abstract class SassNumber extends Value { /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. - void assertUnit(String unit, [String name]); + void assertUnit(String unit, [String? name]); /// Throws a [SassScriptException] unless [this] has no units. /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. - void assertNoUnits([String name]); + void assertNoUnits([String? name]); /// Returns a copy of this number, converted to the same units as [other]. /// @@ -117,7 +117,7 @@ abstract class SassNumber extends Value { /// If this came from a function argument, [name] is the argument name /// (without the `$`) and [otherName] is the argument name for [other]. These /// are used for error reporting. - SassNumber coerceToMatch(SassNumber other, [String name, String otherName]); + SassNumber coerceToMatch(SassNumber other, [String? name, String? otherName]); /// Returns [value], converted to the same units as [other]. /// @@ -132,7 +132,7 @@ abstract class SassNumber extends Value { /// If this came from a function argument, [name] is the argument name /// (without the `$`) and [otherName] is the argument name for [other]. These /// are used for error reporting. - num coerceValueToMatch(SassNumber other, [String name, String otherName]); + num coerceValueToMatch(SassNumber other, [String? name, String? otherName]); /// Returns a copy of this number, converted to the same units as [other]. /// @@ -146,7 +146,8 @@ abstract class SassNumber extends Value { /// If this came from a function argument, [name] is the argument name /// (without the `$`) and [otherName] is the argument name for [other]. These /// are used for error reporting. - SassNumber convertToMatch(SassNumber other, [String name, String otherName]); + SassNumber convertToMatch(SassNumber other, + [String? name, String? otherName]); /// Returns [value], converted to the same units as [other]. /// @@ -157,7 +158,7 @@ abstract class SassNumber extends Value { /// If this came from a function argument, [name] is the argument name /// (without the `$`) and [otherName] is the argument name for [other]. These /// are used for error reporting. - num convertValueToMatch(SassNumber other, [String name, String otherName]); + num convertValueToMatch(SassNumber other, [String? name, String? otherName]); /// Returns a copy of this number, converted to the units represented by /// [newNumerators] and [newDenominators]. @@ -176,7 +177,7 @@ abstract class SassNumber extends Value { /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. SassNumber coerce(List newNumerators, List newDenominators, - [String name]); + [String? name]); /// Returns [value], converted to the units represented by [newNumerators] and /// [newDenominators]. @@ -192,14 +193,14 @@ abstract class SassNumber extends Value { /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. num coerceValue(List newNumerators, List newDenominators, - [String name]); + [String? name]); /// This has been renamed [coerceValue] for consistency with [coerceToMatch], /// [coerceValueToMatch], [convertToMatch], and [convertValueToMatch]. @deprecated num valueInUnits(List newNumerators, List newDenominators, - [String name]); + [String? name]); /// A shorthand for [coerceValue] with only one numerator unit. - num coerceValueToUnit(String unit, [String name]); + num coerceValueToUnit(String unit, [String? name]); } diff --git a/lib/src/value/external/string.dart b/lib/src/value/external/string.dart index 1f4d5d123..063399a72 100644 --- a/lib/src/value/external/string.dart +++ b/lib/src/value/external/string.dart @@ -25,10 +25,10 @@ abstract class SassString extends Value { /// contain characters that aren't valid in identifiers, such as /// `url(http://example.com)`. Unfortunately, it also means that we don't /// consider `foo` and `f\6F\6F` the same string. - String /*!*/ get text; + String get text; /// Whether this string has quotes. - bool /*!*/ get hasQuotes; + bool get hasQuotes; /// Sass's notion of the length of this string. /// @@ -41,19 +41,19 @@ abstract class SassString extends Value { /// /// This returns the same value as `text.runes.length`, but it's more /// efficient. - int /*!*/ get sassLength; + int get sassLength; /// Creates an empty string. /// /// The [quotes] argument defaults to `false`. // TODO: no required - factory SassString.empty({bool quotes}) = internal.SassString.empty; + factory SassString.empty({required bool quotes}) = internal.SassString.empty; /// Creates a string with the given [text]. /// /// The [quotes] argument defaults to `false`. // TODO: no ? - factory SassString(String text, {bool quotes}) = internal.SassString; + factory SassString(String text, {required bool quotes}) = internal.SassString; /// Converts [sassIndex] into a Dart-style index into [text]. /// @@ -76,7 +76,7 @@ abstract class SassString extends Value { /// number isn't an integer, or if that integer isn't a valid index for this /// string. If [sassIndex] came from a function argument, [name] is the /// argument name (without the `$`). It's used for error reporting. - int sassIndexToStringIndex(Value sassIndex, [String name]); + int sassIndexToStringIndex(Value sassIndex, [String? name]); /// Converts [sassIndex] into a Dart-style index into [text]`.runes`. /// @@ -89,5 +89,5 @@ abstract class SassString extends Value { /// number isn't an integer, or if that integer isn't a valid index for this /// string. If [sassIndex] came from a function argument, [name] is the /// argument name (without the `$`). It's used for error reporting. - int sassIndexToRuneIndex(Value sassIndex, [String name]); + int sassIndexToRuneIndex(Value sassIndex, [String? name]); } diff --git a/lib/src/value/external/value.dart b/lib/src/value/external/value.dart index 2d64bec1a..f6b19ec9f 100644 --- a/lib/src/value/external/value.dart +++ b/lib/src/value/external/value.dart @@ -53,7 +53,7 @@ abstract class Value { /// /// All SassScript values can be used as lists. Maps count as lists of pairs, /// and all other values count as single-value lists. - bool /*!*/ get hasBrackets; + bool get hasBrackets; /// This value as a list. /// @@ -63,7 +63,7 @@ abstract class Value { /// Returns Dart's `null` value if this is [sassNull], and returns [this] /// otherwise. - Value get realNull; + Value? get realNull; /// Converts [sassIndex] into a Dart-style index into the list returned by /// [asList]. @@ -75,7 +75,7 @@ abstract class Value { /// number isn't an integer, or if that integer isn't a valid index for /// [asList]. If [sassIndex] came from a function argument, [name] is the /// argument name (without the `$`). It's used for error reporting. - int sassIndexToListIndex(Value sassIndex, [String name]); + int sassIndexToListIndex(Value sassIndex, [String? name]); /// Throws a [SassScriptException] if [this] isn't a boolean. /// @@ -84,41 +84,41 @@ abstract class Value { /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. - SassBoolean assertBoolean([String name]); + SassBoolean assertBoolean([String? name]); /// Throws a [SassScriptException] if [this] isn't a color. /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. - SassColor assertColor([String name]); + SassColor assertColor([String? name]); /// Throws a [SassScriptException] if [this] isn't a function reference. /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. - SassFunction assertFunction([String name]); + SassFunction assertFunction([String? name]); /// Throws a [SassScriptException] if [this] isn't a map. /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. - SassMap assertMap([String name]); + SassMap assertMap([String? name]); /// Returns [this] as a [SassMap] if it is one (including empty lists, which /// count as empty maps) or returns `null` if it's not. - SassMap tryMap(); + SassMap? tryMap(); /// Throws a [SassScriptException] if [this] isn't a number. /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. - SassNumber assertNumber([String name]); + SassNumber assertNumber([String? name]); /// Throws a [SassScriptException] if [this] isn't a string. /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. - SassString assertString([String name]); + SassString assertString([String? name]); /// Returns a valid CSS representation of [this]. /// diff --git a/lib/src/value/function.dart b/lib/src/value/function.dart index 431c5332d..3f86a2620 100644 --- a/lib/src/value/function.dart +++ b/lib/src/value/function.dart @@ -14,7 +14,7 @@ class SassFunction extends Value implements internal.SassFunction { T accept(ValueVisitor visitor) => visitor.visitFunction(this); - SassFunction assertFunction([String name]) => this; + SassFunction assertFunction([String? name]) => this; bool operator ==(Object other) => other is SassFunction && callable == other.callable; diff --git a/lib/src/value/list.dart b/lib/src/value/list.dart index fca8b8e69..4e973465e 100644 --- a/lib/src/value/list.dart +++ b/lib/src/value/list.dart @@ -27,13 +27,12 @@ class SassList extends Value implements ext.SassList { int get lengthAsList => asList.length; - const SassList.empty({ListSeparator separator, bool brackets = false}) + const SassList.empty({ListSeparator? separator, bool brackets = false}) : _contents = const [], separator = separator ?? ListSeparator.undecided, hasBrackets = brackets; - SassList(Iterable contents, this.separator, - {bool brackets = false}) + SassList(Iterable contents, this.separator, {bool brackets = false}) : _contents = List.unmodifiable(contents), hasBrackets = brackets { if (separator == ListSeparator.undecided && asList.length > 1) { @@ -44,10 +43,10 @@ class SassList extends Value implements ext.SassList { T accept(ValueVisitor visitor) => visitor.visitList(this); - SassMap assertMap([String name]) => + SassMap assertMap([String? name]) => asList.isEmpty ? const SassMap.empty() : super.assertMap(name); - SassMap tryMap() => asList.isEmpty ? const SassMap.empty() : null; + SassMap? tryMap() => asList.isEmpty ? const SassMap.empty() : null; bool operator ==(Object other) => (other is SassList && @@ -80,7 +79,7 @@ class ListSeparator { /// /// If the separator of a list has not been decided, this value will be /// `null`. - final String /*?*/ separator; + final String? separator; const ListSeparator._(this._name, this.separator); diff --git a/lib/src/value/map.dart b/lib/src/value/map.dart index 5883c4964..1609fb2ef 100644 --- a/lib/src/value/map.dart +++ b/lib/src/value/map.dart @@ -26,12 +26,11 @@ class SassMap extends Value implements ext.SassMap { /// Returns an empty map. const SassMap.empty() : contents = const {}; - SassMap(Map contents) - : contents = Map.unmodifiable(contents); + SassMap(Map contents) : contents = Map.unmodifiable(contents); T accept(ValueVisitor visitor) => visitor.visitMap(this); - SassMap assertMap([String name]) => this; + SassMap assertMap([String? name]) => this; SassMap tryMap() => this; diff --git a/lib/src/value/null.dart b/lib/src/value/null.dart index 68f4f6a2a..183ac43d1 100644 --- a/lib/src/value/null.dart +++ b/lib/src/value/null.dart @@ -16,7 +16,7 @@ class SassNull extends Value { bool get isBlank => true; - Value get realNull => null; + Value? get realNull => null; const SassNull._(); diff --git a/lib/src/value/number.dart b/lib/src/value/number.dart index a0c8038bb..c3b242327 100644 --- a/lib/src/value/number.dart +++ b/lib/src/value/number.dart @@ -167,22 +167,22 @@ abstract class SassNumber extends Value implements ext.SassNumber { /// The representation of this number as two slash-separated numbers, if it /// has one. - final Tuple2 asSlash; + final Tuple2? asSlash; bool get isInt => fuzzyIsInt(value); - int get asInt => fuzzyAsInt(value); + int? get asInt => fuzzyAsInt(value); /// Returns a human readable string representation of this number's units. String get unitString => hasUnits ? _unitString(numeratorUnits, denominatorUnits) : ''; - factory SassNumber(num value, [String unit]) => unit == null + factory SassNumber(num value, [String? unit]) => unit == null ? UnitlessSassNumber(value) : SingleUnitSassNumber(value, unit); factory SassNumber.withUnits(num value, - {List numeratorUnits, List denominatorUnits}) { + {List? numeratorUnits, List? denominatorUnits}) { if (denominatorUnits == null || denominatorUnits.isEmpty) { if (numeratorUnits == null || numeratorUnits.isEmpty) { return UnitlessSassNumber(value); @@ -219,15 +219,15 @@ abstract class SassNumber extends Value implements ext.SassNumber { /// [numerator] and [denominator]. SassNumber withSlash(SassNumber numerator, SassNumber denominator); - SassNumber assertNumber([String name]) => this; + SassNumber assertNumber([String? name]) => this; - int assertInt([String name]) { + int assertInt([String? name]) { var integer = fuzzyAsInt(value); if (integer != null) return integer; throw _exception("$this is not an int.", name); } - num valueInRange(num min, num max, [String name]) { + num valueInRange(num min, num max, [String? name]) { var result = fuzzyCheckRange(value, min, max); if (result != null) return result; throw _exception( @@ -235,35 +235,35 @@ abstract class SassNumber extends Value implements ext.SassNumber { name); } - void assertUnit(String unit, [String name]) { + void assertUnit(String unit, [String? name]) { if (hasUnit(unit)) return; throw _exception('Expected $this to have unit "$unit".', name); } - void assertNoUnits([String name]) { + void assertNoUnits([String? name]) { if (!hasUnits) return; throw _exception('Expected $this to have no units.', name); } SassNumber coerceToMatch(ext.SassNumber other, - [String name, String otherName]) => + [String? name, String? otherName]) => SassNumber.withUnits(coerceValueToMatch(other, name, otherName), numeratorUnits: other.numeratorUnits, denominatorUnits: other.denominatorUnits); num coerceValueToMatch(ext.SassNumber other, - [String name, String otherName]) => + [String? name, String? otherName]) => _coerceOrConvertValue(other.numeratorUnits, other.denominatorUnits, coerceUnitless: true, name: name, other: other, otherName: otherName); SassNumber convertToMatch(ext.SassNumber other, - [String name, String otherName]) => + [String? name, String? otherName]) => SassNumber.withUnits(convertValueToMatch(other, name, otherName), numeratorUnits: other.numeratorUnits, denominatorUnits: other.denominatorUnits); num convertValueToMatch(ext.SassNumber other, - [String name, String otherName]) => + [String? name, String? otherName]) => _coerceOrConvertValue(other.numeratorUnits, other.denominatorUnits, coerceUnitless: false, name: name, @@ -271,21 +271,21 @@ abstract class SassNumber extends Value implements ext.SassNumber { otherName: otherName); SassNumber coerce(List newNumerators, List newDenominators, - [String name]) => + [String? name]) => SassNumber.withUnits(coerceValue(newNumerators, newDenominators, name), numeratorUnits: newNumerators, denominatorUnits: newDenominators); num coerceValue(List newNumerators, List newDenominators, - [String name]) => + [String? name]) => _coerceOrConvertValue(newNumerators, newDenominators, coerceUnitless: true, name: name); - num coerceValueToUnit(String unit, [String name]) => + num coerceValueToUnit(String unit, [String? name]) => coerceValue([unit], [], name); @deprecated num valueInUnits(List newNumerators, List newDenominators, - [String name]) => + [String? name]) => coerceValue(newNumerators, newDenominators, name); /// Converts [value] to [newNumerators] and [newDenominators]. @@ -300,10 +300,10 @@ abstract class SassNumber extends Value implements ext.SassNumber { /// error reporting. num _coerceOrConvertValue( List newNumerators, List newDenominators, - {@required bool coerceUnitless, - String name, - ext.SassNumber other, - String otherName}) { + {required bool coerceUnitless, + String? name, + ext.SassNumber? other, + String? otherName}) { assert( other == null || (listEquals(other.numeratorUnits, newNumerators) && @@ -338,7 +338,7 @@ abstract class SassNumber extends Value implements ext.SassNumber { // and make it clear exactly which units are convertible. return _exception( "Expected $this to have ${a(type)} unit " - "(${_unitsByType[type].join(', ')}).", + "(${_unitsByType[type]!.join(', ')}).", name); } } @@ -361,7 +361,7 @@ abstract class SassNumber extends Value implements ext.SassNumber { value *= factor; return true; // TODO: no as - }, orElse: () => throw _compatibilityException()); + }, orElse: (() => throw _compatibilityException()) as String Function()?); } var oldDenominators = denominatorUnits.toList(); @@ -372,7 +372,7 @@ abstract class SassNumber extends Value implements ext.SassNumber { value /= factor; return true; // TODO: no as - }, orElse: () => throw _compatibilityException()); + }, orElse: (() => throw _compatibilityException()) as String Function()?); } if (oldNumerators.isNotEmpty || oldDenominators.isNotEmpty) { @@ -530,28 +530,30 @@ abstract class SassNumber extends Value implements ext.SassNumber { var newNumerators = []; var mutableOtherDenominators = otherDenominators.toList(); for (var numerator in numeratorUnits) { - removeFirstWhere(mutableOtherDenominators, (denominator) { + removeFirstWhere(mutableOtherDenominators, (denominator) { var factor = conversionFactor(numerator, denominator); if (factor == null) return false; value /= factor; return true; - }, orElse: () { - newNumerators.add(numerator); - return null; - }); + }, + orElse: () { + newNumerators.add(numerator); + return null; + } as String Function()?); } var mutableDenominatorUnits = denominatorUnits.toList(); for (var numerator in otherNumerators) { - removeFirstWhere(mutableDenominatorUnits, (denominator) { + removeFirstWhere(mutableDenominatorUnits, (denominator) { var factor = conversionFactor(numerator, denominator); if (factor == null) return false; value /= factor; return true; - }, orElse: () { - newNumerators.add(numerator); - return null; - }); + }, + orElse: () { + newNumerators.add(numerator); + return null; + } as String Function()?); } return SassNumber.withUnits(value, @@ -574,7 +576,7 @@ abstract class SassNumber extends Value implements ext.SassNumber { /// /// Equivalently, `1unit2 * conversionFactor(unit1, unit2) = 1unit1`. @protected - num conversionFactor(String /*!*/ unit1, String /*!*/ unit2) { + num? conversionFactor(String unit1, String unit2) { if (unit1 == unit2) return 1; var innerMap = _conversions[unit1]; if (innerMap == null) return null; @@ -633,12 +635,12 @@ abstract class SassNumber extends Value implements ext.SassNumber { if (units.isEmpty) return units; if (units.length == 1) { var type = _typesByUnit[units.first]; - return type == null ? units : [_unitsByType[type].first]; + return type == null ? units : [_unitsByType[type]!.first]; } return units.map((unit) { var type = _typesByUnit[unit]; - return type == null ? unit : _unitsByType[type].first; + return type == null ? unit : _unitsByType[type]!.first; }).toList() ..sort(); } @@ -660,6 +662,6 @@ abstract class SassNumber extends Value implements ext.SassNumber { } /// Throws a [SassScriptException] with the given [message]. - SassScriptException _exception(String message, [String name]) => + SassScriptException _exception(String message, [String? name]) => SassScriptException(name == null ? message : "\$$name: $message"); } diff --git a/lib/src/value/number/complex.dart b/lib/src/value/number/complex.dart index 3b3014cd6..8fca749d5 100644 --- a/lib/src/value/number/complex.dart +++ b/lib/src/value/number/complex.dart @@ -24,7 +24,7 @@ class ComplexSassNumber extends SassNumber { List.unmodifiable(denominatorUnits)); ComplexSassNumber._(num value, this.numeratorUnits, this.denominatorUnits, - [Tuple2 asSlash]) + [Tuple2? asSlash]) : super.protected(value, asSlash) { assert(numeratorUnits.length > 1 || denominatorUnits.isNotEmpty); } diff --git a/lib/src/value/number/single_unit.dart b/lib/src/value/number/single_unit.dart index 182a24fb7..7bc08760a 100644 --- a/lib/src/value/number/single_unit.dart +++ b/lib/src/value/number/single_unit.dart @@ -27,7 +27,7 @@ class SingleUnitSassNumber extends SassNumber { bool get hasUnits => true; SingleUnitSassNumber(num value, this._unit, - [Tuple2 asSlash]) + [Tuple2? asSlash]) : super.protected(value, asSlash); SassNumber withValue(num value) => SingleUnitSassNumber(value, _unit); @@ -40,21 +40,21 @@ class SingleUnitSassNumber extends SassNumber { bool compatibleWithUnit(String unit) => conversionFactor(_unit, unit) != null; SassNumber coerceToMatch(ext.SassNumber other, - [String name, String otherName]) => + [String? name, String? otherName]) => convertToMatch(other, name, otherName); num coerceValueToMatch(ext.SassNumber other, - [String name, String otherName]) => + [String? name, String? otherName]) => convertValueToMatch(other, name, otherName); SassNumber convertToMatch(ext.SassNumber other, - [String name, String otherName]) => + [String? name, String? otherName]) => (other is SingleUnitSassNumber ? _coerceToUnit(other._unit) : null) ?? // Call this to generate a consistent error message. super.convertToMatch(other, name, otherName); num convertValueToMatch(ext.SassNumber other, - [String name, String otherName]) => + [String? name, String? otherName]) => (other is SingleUnitSassNumber ? _coerceValueToUnit(other._unit) : null) ?? @@ -62,7 +62,7 @@ class SingleUnitSassNumber extends SassNumber { super.convertValueToMatch(other, name, otherName); SassNumber coerce(List newNumerators, List newDenominators, - [String name]) => + [String? name]) => (newNumerators.length == 1 && newDenominators.isEmpty ? _coerceToUnit(newNumerators[0]) : null) ?? @@ -70,36 +70,36 @@ class SingleUnitSassNumber extends SassNumber { super.coerce(newNumerators, newDenominators, name); num coerceValue(List newNumerators, List newDenominators, - [String name]) => + [String? name]) => (newNumerators.length == 1 && newDenominators.isEmpty ? _coerceValueToUnit(newNumerators[0]) : null) ?? // Call this to generate a consistent error message. super.coerceValue(newNumerators, newDenominators, name); - num coerceValueToUnit(String unit, [String name]) => + num coerceValueToUnit(String unit, [String? name]) => _coerceValueToUnit(unit) ?? // Call this to generate a consistent error message. super.coerceValueToUnit(unit, name); /// A shorthand for [coerce] with only one numerator unit, except that it /// returns `null` if coercion fails. - SassNumber _coerceToUnit(String unit) { + SassNumber? _coerceToUnit(String unit) { if (_unit == unit) return this; return conversionFactor(unit, _unit) .andThen((factor) => SingleUnitSassNumber(value * factor, unit)); } /// Like [coerceValueToUnit], except that it returns `null` if coercion fails. - num _coerceValueToUnit(String unit) => + num? _coerceValueToUnit(String unit) => conversionFactor(unit, _unit).andThen((factor) => value * factor); SassNumber multiplyUnits( num value, List otherNumerators, List otherDenominators) { var newNumerators = otherNumerators; var mutableOtherDenominators = otherDenominators.toList(); - removeFirstWhere(mutableOtherDenominators, (denominator) { - var factor = conversionFactor(denominator, _unit); + removeFirstWhere(mutableOtherDenominators, (denominator) { + var factor = conversionFactor(denominator!, _unit); if (factor == null) return false; value *= factor; return true; diff --git a/lib/src/value/number/unitless.dart b/lib/src/value/number/unitless.dart index b9fcdb5cb..b826a69de 100644 --- a/lib/src/value/number/unitless.dart +++ b/lib/src/value/number/unitless.dart @@ -19,7 +19,7 @@ class UnitlessSassNumber extends SassNumber { bool get hasUnits => false; - UnitlessSassNumber(num value, [Tuple2 asSlash]) + UnitlessSassNumber(num value, [Tuple2? asSlash]) : super.protected(value, asSlash); SassNumber withValue(num value) => UnitlessSassNumber(value); @@ -32,37 +32,37 @@ class UnitlessSassNumber extends SassNumber { bool compatibleWithUnit(String unit) => true; SassNumber coerceToMatch(ext.SassNumber other, - [String name, String otherName]) => + [String? name, String? otherName]) => (other as SassNumber).withValue(value); num coerceValueToMatch(ext.SassNumber other, - [String name, String otherName]) => + [String? name, String? otherName]) => value; SassNumber convertToMatch(ext.SassNumber other, - [String name, String otherName]) => + [String? name, String? otherName]) => other.hasUnits // Call this to generate a consistent error message. ? super.convertToMatch(other, name, otherName) : this; num convertValueToMatch(ext.SassNumber other, - [String name, String otherName]) => + [String? name, String? otherName]) => other.hasUnits // Call this to generate a consistent error message. ? super.convertValueToMatch(other, name, otherName) : value; SassNumber coerce(List newNumerators, List newDenominators, - [String name]) => + [String? name]) => SassNumber.withUnits(value, numeratorUnits: newNumerators, denominatorUnits: newDenominators); num coerceValue(List newNumerators, List newDenominators, - [String name]) => + [String? name]) => value; - num coerceValueToUnit(String unit, [String name]) => value; + num coerceValueToUnit(String unit, [String? name]) => value; SassBoolean greaterThan(Value other) { if (other is SassNumber) { diff --git a/lib/src/value/string.dart b/lib/src/value/string.dart index 3a96dabd7..c7d0e964c 100644 --- a/lib/src/value/string.dart +++ b/lib/src/value/string.dart @@ -26,10 +26,10 @@ class SassString extends Value implements ext.SassString { int get sassLength { _sassLength ??= text.runes.length; - return _sassLength; + return _sassLength!; } - int _sassLength; + int? _sassLength; bool get isSpecialNumber { if (hasQuotes) return false; @@ -91,11 +91,11 @@ class SassString extends Value implements ext.SassString { SassString(this.text, {bool quotes = true}) : hasQuotes = quotes; - int sassIndexToStringIndex(ext.Value sassIndex, [String name]) => + int sassIndexToStringIndex(ext.Value sassIndex, [String? name]) => codepointIndexToCodeUnitIndex( text, sassIndexToRuneIndex(sassIndex, name)); - int sassIndexToRuneIndex(ext.Value sassIndex, [String name]) { + int sassIndexToRuneIndex(ext.Value sassIndex, [String? name]) { var index = sassIndex.assertNumber(name).assertInt(name); if (index == 0) throw _exception("String index may not be 0.", name); if (index.abs() > sassLength) { @@ -109,9 +109,9 @@ class SassString extends Value implements ext.SassString { T accept(ValueVisitor visitor) => visitor.visitString(this); - SassString assertString([String name]) => this; + SassString assertString([String? name]) => this; - Value plus(Value /*!*/ other) { + Value plus(Value other) { if (other is SassString) { return SassString(text + other.text, quotes: hasQuotes); } else { @@ -124,6 +124,6 @@ class SassString extends Value implements ext.SassString { int get hashCode => text.hashCode; /// Throws a [SassScriptException] with the given [message]. - SassScriptException _exception(String message, [String name]) => + SassScriptException _exception(String message, [String? name]) => SassScriptException(name == null ? message : "\$$name: $message"); } diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 0510b0194..06639cf90 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -72,11 +72,11 @@ typedef _ScopeCallback = Future Function( /// /// Throws a [SassRuntimeException] if evaluation fails. Future evaluateAsync(Stylesheet stylesheet, - {AsyncImportCache importCache, - NodeImporter nodeImporter, - AsyncImporter importer, - Iterable functions, - Logger logger, + {AsyncImportCache? importCache, + NodeImporter? nodeImporter, + AsyncImporter? importer, + Iterable? functions, + Logger? logger, bool sourceMap = false}) => _EvaluateVisitor( importCache: importCache, @@ -93,23 +93,23 @@ class AsyncEvaluator { final _EvaluateVisitor _visitor; /// The importer to use to resolve `@use` rules in [_visitor]. - final AsyncImporter _importer; + final AsyncImporter? _importer; /// Creates an evaluator. /// /// Arguments are the same as for [evaluateAsync]. AsyncEvaluator( - {AsyncImportCache importCache, - AsyncImporter importer, - Iterable functions, - Logger logger}) + {AsyncImportCache? importCache, + AsyncImporter? importer, + Iterable? functions, + Logger? logger}) : _visitor = _EvaluateVisitor( importCache: importCache, functions: functions, logger: logger), _importer = importer; Future use(UseRule use) => _visitor.runStatement(_importer, use); - Future evaluate(Expression expression) => + Future evaluate(Expression expression) => _visitor.runExpression(_importer, expression); Future setVariable(VariableDeclaration declaration) => @@ -119,15 +119,15 @@ class AsyncEvaluator { /// A visitor that executes Sass code to produce a CSS tree. class _EvaluateVisitor implements - StatementVisitor /*!*/ >, - ExpressionVisitor>, + StatementVisitor>, + ExpressionVisitor>, CssVisitor> { /// The import cache used to import other stylesheets. - final AsyncImportCache _importCache; + final AsyncImportCache? _importCache; /// The Node Sass-compatible importer to use when loading new Sass files when /// compiled to Node.js. - final NodeImporter _nodeImporter; + final NodeImporter? _nodeImporter; /// Built-in functions that are globally-acessible, even under the new module /// system. @@ -137,14 +137,14 @@ class _EvaluateVisitor final _builtInModules = {}; /// All modules that have been loaded and evaluated so far. - final _modules = {}; + final _modules = {}; /// A map from canonical module URLs to the nodes whose spans indicate where /// those modules were originally loaded. /// /// This is not guaranteed to have a node for every module in [_modules]. For /// example, the entrypoint module was not loaded by a node. - final _moduleNodes = {}; + final _moduleNodes = {}; /// The logger to use to print warnings. final Logger _logger; @@ -159,16 +159,16 @@ class _EvaluateVisitor /// /// This doesn't take into consideration any intermediate `@at-root` rules. In /// the common case where those rules are relevant, use [_styleRule] instead. - ModifiableCssStyleRule _styleRuleIgnoringAtRoot; + ModifiableCssStyleRule? _styleRuleIgnoringAtRoot; /// The current media queries, if any. - List _mediaQueries; + List? _mediaQueries; /// The current parent node in the output CSS tree. - /*late*/ ModifiableCssParentNode _parent; + late ModifiableCssParentNode _parent; /// The name of the current declaration parent. - String _declarationName; + String? _declarationName; /// The human-readable name of the current stack frame. var _member = "root stylesheet"; @@ -179,12 +179,12 @@ class _EvaluateVisitor /// [AstNode] rather than a [FileSpan] so we can avoid calling [AstNode.span] /// if the span isn't required, since some nodes need to do real work to /// manufacture a source span. - AstNode _callableNode; + AstNode? _callableNode; /// The span for the current import that's being resolved. /// /// This is used to produce warnings for importers. - FileSpan _importSpan; + FileSpan? _importSpan; /// Whether we're currently executing a function. var _inFunction = false; @@ -192,7 +192,7 @@ class _EvaluateVisitor /// Whether we're currently building the output of an unknown at rule. var _inUnknownAtRule = false; - ModifiableCssStyleRule get _styleRule => + ModifiableCssStyleRule? get _styleRule => _atRootExcludingStyleRule ? null : _styleRuleIgnoringAtRoot; /// Whether we're directly within an `@at-root` rule that excludes style @@ -218,7 +218,7 @@ class _EvaluateVisitor /// entrypoint module). /// /// This is used to ensure that we don't get into an infinite load loop. - final _activeModules = {}; + final _activeModules = {}; /// The dynamic call stack representing function invocations, mixin /// invocations, and imports surrounding the current context. @@ -241,17 +241,17 @@ class _EvaluateVisitor /// /// If this is `null`, relative imports aren't supported in the current /// stylesheet. - AsyncImporter _importer; + AsyncImporter? _importer; /// The stylesheet that's currently being evaluated. - /*late*/ Stylesheet _stylesheet; + late Stylesheet _stylesheet; /// The root stylesheet node. - /*late*/ ModifiableCssStylesheet _root; + late ModifiableCssStylesheet _root; /// The first index in [_root.children] after the initial block of CSS /// imports. - /*late*/ int _endOfImports; + late int _endOfImports; /// Plain-CSS imports that didn't appear in the initial block of CSS imports. /// @@ -260,11 +260,11 @@ class _EvaluateVisitor /// /// This is `null` unless there are any out-of-order imports in the current /// stylesheet. - /*late*/ List _outOfOrderImports; + late List? _outOfOrderImports; /// The extender that tracks extensions and style rules for the current /// module. - /*late*/ Extender _extender; + late Extender _extender; /// The configuration for the current module. /// @@ -275,10 +275,10 @@ class _EvaluateVisitor /// /// Most arguments are the same as those to [evaluateAsync]. _EvaluateVisitor( - {AsyncImportCache importCache, - NodeImporter nodeImporter, - Iterable functions, - Logger logger, + {AsyncImportCache? importCache, + NodeImporter? nodeImporter, + Iterable? functions, + Logger? logger, bool sourceMap = false}) : _importCache = nodeImporter == null ? importCache ?? AsyncImportCache.none(logger: logger) @@ -373,7 +373,7 @@ class _EvaluateVisitor var callable = css ? PlainCssCallable(name.text) : _addExceptionSpan( - _callableNode /*!*/, + _callableNode!, () => _getFunction(name.text.replaceAll("_", "-"), namespace: module?.text)); if (callable != null) return SassFunction(callable); @@ -386,7 +386,7 @@ class _EvaluateVisitor var function = arguments[0]; var args = arguments[1] as SassArgumentList; - var callableNode = _callableNode /*!*/; + var callableNode = _callableNode!; var invocation = ArgumentInvocation([], {}, callableNode.span, rest: ValueExpression(args, callableNode.span), keywordRest: args.keywords.isEmpty @@ -404,7 +404,7 @@ class _EvaluateVisitor "in Dart Sass 2.0.0. Use call(get-function($function)) instead.", deprecation: true); - var callableNode = _callableNode /*!*/; + var callableNode = _callableNode!; var expression = FunctionExpression( Interpolation([function.text], callableNode.span), invocation, @@ -415,7 +415,7 @@ class _EvaluateVisitor var callable = function.assertFunction("function").callable; if (callable is AsyncCallable) { return await _runFunctionCallable( - invocation, callable, _callableNode /*!*/); + invocation, callable, _callableNode!); } else { throw SassScriptException( "The function ${callable.name} is asynchronous.\n" @@ -430,7 +430,7 @@ class _EvaluateVisitor var url = Uri.parse(arguments[0].assertString("url").text); var withMap = arguments[1].realNull?.assertMap("with")?.contents; - var callableNode = _callableNode /*!*/; + var callableNode = _callableNode!; var configuration = const Configuration.empty(); if (withMap != null) { var values = {}; @@ -471,8 +471,7 @@ class _EvaluateVisitor } } - Future run( - AsyncImporter importer, Stylesheet /*!*/ node) async { + Future run(AsyncImporter? importer, Stylesheet node) async { return _withWarnCallback(() async { var url = node.span?.sourceUrl; if (url != null) { @@ -492,11 +491,11 @@ class _EvaluateVisitor }); } - Future runExpression(AsyncImporter importer, Expression expression) => + Future runExpression(AsyncImporter? importer, Expression expression) => _withWarnCallback(() => _withFakeStylesheet( importer, expression, () => expression.accept(this))); - Future runStatement(AsyncImporter importer, Statement statement) => + Future runStatement(AsyncImporter? importer, Statement statement) => _withWarnCallback(() => _withFakeStylesheet( importer, statement, () => statement.accept(this))); @@ -511,8 +510,8 @@ class _EvaluateVisitor /// Runs [callback] with [importer] as [_importer] and a fake [_stylesheet] /// with [nodeWithSpan]'s source span. - Future _withFakeStylesheet(AsyncImporter importer, AstNode nodeWithSpan, - FutureOr /*!*/ callback()) async { + Future _withFakeStylesheet(AsyncImporter? importer, + AstNode nodeWithSpan, FutureOr callback()) async { var oldImporter = _importer; _importer = importer; var oldStylesheet = _stylesheet; @@ -542,10 +541,10 @@ class _EvaluateVisitor /// /// The [stackFrame] and [nodeWithSpan] are used for the name and location of /// the stack frame for the duration of the [callback]. - Future _loadModule(Uri url, String stackFrame, - AstNode /*!*/ nodeWithSpan, void callback(Module module), - {Uri baseUrl, - Configuration configuration, + Future _loadModule(Uri url, String stackFrame, AstNode nodeWithSpan, + void callback(Module module), + {Uri? baseUrl, + Configuration? configuration, bool namesInErrors = false}) async { var builtInModule = _builtInModules[url]; if (builtInModule != null) { @@ -619,14 +618,14 @@ class _EvaluateVisitor /// If [namesInErrors] is `true`, this includes the names of modules in errors /// relating to them. This should only be `true` if the names won't be obvious /// from the source span. - Future _execute(AsyncImporter importer, Stylesheet stylesheet, - {Configuration configuration, - AstNode nodeWithSpan, + Future _execute(AsyncImporter? importer, Stylesheet stylesheet, + {Configuration? configuration, + AstNode? nodeWithSpan, bool namesInErrors = false}) async { var url = stylesheet.span?.sourceUrl; // TODO: no ! - var alreadyLoaded = _modules[url]; + var alreadyLoaded = _modules[url!]; if (alreadyLoaded != null) { var currentConfiguration = configuration ?? _configuration; if (currentConfiguration is ExplicitConfiguration) { @@ -654,7 +653,7 @@ class _EvaluateVisitor } var environment = AsyncEnvironment(sourceMap: _sourceMap); - CssStylesheet css; + late CssStylesheet css; var extender = Extender(); await _withEnvironment(environment, () async { var oldImporter = _importer; @@ -724,7 +723,7 @@ class _EvaluateVisitor return [ ..._root.children.take(_endOfImports), // TODO: no ! - ..._outOfOrderImports, + ..._outOfOrderImports!, ..._root.children.skip(_endOfImports) ]; } @@ -779,7 +778,7 @@ class _EvaluateVisitor // so that by the time we're processing a module we've already filled in all // its downstream extenders and we can use them to extend that module. // TODO: var - var downstreamExtenders = >{}; + Map> downstreamExtenders = >{}; /// Extensions that haven't yet been satisfied by some upstream module. This /// adds extensions when they're defined but not satisfied, and removes them @@ -873,14 +872,14 @@ class _EvaluateVisitor // ## Statements - Future visitStylesheet(Stylesheet node) async { + Future visitStylesheet(Stylesheet node) async { for (var child in node.children) { await child.accept(this); } return null; } - Future visitAtRootRule(AtRootRule node) async { + Future visitAtRootRule(AtRootRule node) async { var query = AtRootQuery.defaultQuery; var unparsedQuery = node.query; if (unparsedQuery != null) { @@ -894,7 +893,7 @@ class _EvaluateVisitor var included = []; while (parent is! CssStylesheet) { if (!query.excludes(parent)) included.add(parent); - parent = parent.parent /*!*/; + parent = parent.parent!; } var root = _trimIncluded(included); @@ -945,20 +944,20 @@ class _EvaluateVisitor if (nodes.isEmpty) return _root; var parent = _parent; - int innermostContiguous; + int? innermostContiguous; var i = 0; for (; i < nodes.length; i++) { while (parent != nodes[i]) { innermostContiguous = null; - parent = parent.parent /*!*/; + parent = parent.parent!; } innermostContiguous ??= i; - parent = parent.parent /*!*/; + parent = parent.parent!; } if (parent != _root) return _root; // TODO: no ! - var root = nodes[innermostContiguous]; + var root = nodes[innermostContiguous!]; nodes.removeRange(innermostContiguous, nodes.length); return root; } @@ -1023,7 +1022,7 @@ class _EvaluateVisitor Future visitContentBlock(ContentBlock node) => throw UnsupportedError( "Evaluation handles @include and its content block together."); - Future visitContentRule(ContentRule node) async { + Future visitContentRule(ContentRule node) async { var content = _environment.content; if (content == null) return null; @@ -1037,14 +1036,14 @@ class _EvaluateVisitor return null; } - Future visitDebugRule(DebugRule node) async { + Future visitDebugRule(DebugRule node) async { var value = await node.expression.accept(this); _logger.debug( value is SassString ? value.text : value.toString(), node.span); return null; } - Future visitDeclaration(Declaration node) async { + Future visitDeclaration(Declaration node) async { if (_styleRule == null && !_inUnknownAtRule && !_inKeyframes) { throw _exception( "Declarations may only be used within style rules.", node.span); @@ -1055,7 +1054,7 @@ class _EvaluateVisitor name = CssValue("$_declarationName-${name.value}", name.span); } var cssValue = await node.value.andThen( - (value) async => CssValue(await value.accept(this), value.span)); + (value) async => CssValue(await value.accept(this), value.span))!; // 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. @@ -1064,7 +1063,7 @@ class _EvaluateVisitor _parent.addChild(ModifiableCssDeclaration(name, cssValue, node.span, parsedAsCustomProperty: node.isCustomProperty, valueSpanForMap: - _sourceMap ? null : _expressionNode(node.value).span)); + _sourceMap ? null : _expressionNode(node.value!).span)); } else if (name.value.startsWith('--') && cssValue != null) { throw _exception( "Custom property values may not be empty.", cssValue.span); @@ -1087,7 +1086,7 @@ class _EvaluateVisitor /// Returns whether [value] is an empty list. bool _isEmptyList(Value value) => value.asList.isEmpty; - Future visitEachRule(EachRule node) async { + Future visitEachRule(EachRule node) async { var list = await node.list.accept(this); var nodeWithSpan = _expressionNode(node.list); var setVariables = node.variables.length == 1 @@ -1107,7 +1106,7 @@ class _EvaluateVisitor /// Destructures [value] and assigns it to [variables], as in an `@each` /// statement. void _setMultipleVariables( - List variables, Value value, AstNode /*!*/ nodeWithSpan) { + List variables, Value value, AstNode nodeWithSpan) { var list = value.asList; var minLength = math.min(variables.length, list.length); for (var i = 0; i < minLength; i++) { @@ -1124,7 +1123,7 @@ class _EvaluateVisitor (await node.expression.accept(this)).toString(), node.span); } - Future visitExtendRule(ExtendRule node) async { + Future visitExtendRule(ExtendRule node) async { var styleRule = _styleRule; if (styleRule == null || _declarationName != null) { throw _exception( @@ -1166,7 +1165,7 @@ class _EvaluateVisitor return null; } - Future visitAtRule(AtRule node) async { + Future visitAtRule(AtRule node) async { // NOTE: this logic is largely duplicated in [visitCssAtRule]. Most changes // here should be mirrored there. @@ -1178,7 +1177,7 @@ class _EvaluateVisitor var name = await _interpolationToValue(node.name); var value = await node.value.andThen((value) => - _interpolationToValue(value, trim: true, warnForColor: true)); + _interpolationToValue(value, trim: true, warnForColor: true))!; if (node.children == null) { _parent.addChild( @@ -1221,7 +1220,7 @@ class _EvaluateVisitor return null; } - Future visitForRule(ForRule node) async { + Future visitForRule(ForRule node) async { var fromNumber = await _addExceptionSpanAsync( node.from, () async => (await node.from.accept(this)).assertNumber()); var toNumber = await _addExceptionSpanAsync( @@ -1255,7 +1254,7 @@ class _EvaluateVisitor }, semiGlobal: true); } - Future visitForwardRule(ForwardRule node) async { + Future visitForwardRule(ForwardRule node) async { var oldConfiguration = _configuration; var adjustedConfiguration = oldConfiguration.throughForward(node); @@ -1315,7 +1314,7 @@ class _EvaluateVisitor /// [downstream], unless they match a name in [except]. void _removeUsedConfiguration( Configuration upstream, Configuration downstream, - {@required Set except}) { + {required Set except}) { for (var name in upstream.values.keys.toList()) { if (except.contains(name)) continue; if (!downstream.values.containsKey(name)) upstream.remove(name); @@ -1331,7 +1330,7 @@ class _EvaluateVisitor /// variable in the error message. This should only be `true` if the name /// won't be obvious from the source span. void _assertConfigurationIsEmpty(Configuration configuration, - {Set only, bool nameInError = false}) { + {Set? only, bool nameInError = false}) { configuration.values.forEach((name, value) { if (only != null && !only.contains(name)) return; @@ -1344,13 +1343,13 @@ class _EvaluateVisitor }); } - Future visitFunctionRule(FunctionRule node) async { + Future visitFunctionRule(FunctionRule node) async { _environment.setFunction(UserDefinedCallable(node, _environment.closure())); return null; } - Future visitIfRule(IfRule node) async { - IfRuleClause clause = node.lastClause; + Future visitIfRule(IfRule node) async { + IfRuleClause? clause = node.lastClause; for (var clauseToCheck in node.clauses) { if ((await clauseToCheck.expression.accept(this)).isTruthy) { clause = clauseToCheck; @@ -1362,13 +1361,13 @@ class _EvaluateVisitor return await _environment.scope( () => _handleReturn( // TODO: no ! - clause.children, + clause!.children, (child) => child.accept(this)), semiGlobal: true, when: clause.hasDeclarations); } - Future visitImportRule(ImportRule node) async { + Future visitImportRule(ImportRule node) async { for (var import in node.imports) { if (import is DynamicImport) { await _visitDynamicImport(import); @@ -1416,7 +1415,7 @@ class _EvaluateVisitor return; } - List children; + late List children; var environment = _environment.forImport(); await _withEnvironment(environment, () async { var oldImporter = _importer; @@ -1480,9 +1479,9 @@ class _EvaluateVisitor /// /// This first tries loading [url] relative to [baseUrl], which defaults to /// `_stylesheet.span.sourceUrl`. - Future> _loadStylesheet( - String url, FileSpan span, - {Uri baseUrl, bool forImport = false}) async { + Future> _loadStylesheet( + String url, FileSpan? span, + {Uri? baseUrl, bool forImport = false}) async { try { assert(_importSpan == null); _importSpan = span; @@ -1509,14 +1508,14 @@ class _EvaluateVisitor } on SassException catch (error) { throw _exception(error.message, error.span); } catch (error) { - String message; + String? message; try { // TODO: as String! - message = error.message as String; + message = error.message as String?; } catch (_) { message = error.toString(); } - throw _exception(message); + throw _exception(message!); } finally { _importSpan = null; } @@ -1525,9 +1524,10 @@ class _EvaluateVisitor /// Imports a stylesheet using [_nodeImporter]. /// /// Returns the [Stylesheet], or `null` if the import failed. - Future _importLikeNode(String originalUrl, bool forImport) async { - var result = await _nodeImporter.loadAsync( - originalUrl, _stylesheet.span?.sourceUrl, forImport); + Future _importLikeNode( + String originalUrl, bool forImport) async { + var result = await _nodeImporter! + .loadAsync(originalUrl, _stylesheet.span?.sourceUrl, forImport); if (result == null) return null; var contents = result.item1; @@ -1549,9 +1549,9 @@ class _EvaluateVisitor var supports = await import.supports.andThen((supports) async => CssValue( "supports(${supports is SupportsDeclaration ? "${await _evaluateToCss(supports.name)}: " "${await _evaluateToCss(supports.value)}" : await supports.andThen(_visitSupportsCondition)})", - supports.span)); + supports.span))!; var rawMedia = import.media; - var mediaQuery = await rawMedia.andThen(_visitMediaQueries); + var mediaQuery = await rawMedia.andThen(_visitMediaQueries)!; var node = ModifiableCssImport(url, import.span, supports: supports, media: mediaQuery); @@ -1567,7 +1567,7 @@ class _EvaluateVisitor return null; } - Future visitIncludeRule(IncludeRule node) async { + Future visitIncludeRule(IncludeRule node) async { var mixin = _addExceptionSpan(node, () => _environment.getMixin(node.name, namespace: node.namespace)); if (mixin == null) { @@ -1614,12 +1614,12 @@ class _EvaluateVisitor return null; } - Future visitMixinRule(MixinRule node) async { + Future visitMixinRule(MixinRule node) async { _environment.setMixin(UserDefinedCallable(node, _environment.closure())); return null; } - Future visitLoudComment(LoudComment node) async { + Future visitLoudComment(LoudComment node) async { // NOTE: this logic is largely duplicated in [visitCssComment]. Most changes // here should be mirrored there. @@ -1635,7 +1635,7 @@ class _EvaluateVisitor return null; } - Future visitMediaRule(MediaRule node) async { + Future visitMediaRule(MediaRule node) async { // NOTE: this logic is largely duplicated in [visitCssMediaRule]. Most // changes here should be mirrored there. @@ -1645,8 +1645,9 @@ class _EvaluateVisitor } var queries = await _visitMediaQueries(node.query); - var mergedQueries = _mediaQueries - .andThen((mediaQueries) => _mergeMediaQueries(mediaQueries, queries)); + var mergedQueries = _mediaQueries.andThen(((mediaQueries) => + _mergeMediaQueries(mediaQueries, queries)!) + as List Function(List)?); if (mergedQueries != null && mergedQueries.isEmpty) return null; await _withParent( @@ -1697,7 +1698,7 @@ class _EvaluateVisitor /// Returns the empty list if there are no contexts that match both [queries1] /// and [queries2], or `null` if there are contexts that can't be represented /// by media queries. - List _mergeMediaQueries( + List? _mergeMediaQueries( Iterable queries1, Iterable queries2) { var queries = []; for (var query1 in queries1) { @@ -1714,9 +1715,9 @@ class _EvaluateVisitor Future visitReturnRule(ReturnRule node) => node.expression.accept(this); - Future visitSilentComment(SilentComment node) async => null; + Future visitSilentComment(SilentComment node) async => null; - Future visitStyleRule(StyleRule node) async { + Future visitStyleRule(StyleRule node) async { // NOTE: this logic is largely duplicated in [visitCssStyleRule]. Most // changes here should be mirrored there. @@ -1785,7 +1786,7 @@ class _EvaluateVisitor return null; } - Future visitSupportsRule(SupportsRule node) async { + Future visitSupportsRule(SupportsRule node) async { // NOTE: this logic is largely duplicated in [visitCssSupportsRule]. Most // changes here should be mirrored there. @@ -1854,7 +1855,7 @@ class _EvaluateVisitor /// [SupportsOperation], and is used to determine whether parentheses are /// necessary if [condition] is also a [SupportsOperation]. Future _parenthesize(SupportsCondition condition, - [String operator]) async { + [String? operator]) async { if ((condition is SupportsNegation) || (condition is SupportsOperation && (operator == null || operator != condition.operator))) { @@ -1864,7 +1865,7 @@ class _EvaluateVisitor } } - Future visitVariableDeclaration(VariableDeclaration node) async { + Future visitVariableDeclaration(VariableDeclaration node) async { if (node.isGuarded) { if (node.namespace == null && _environment.atRoot) { var override = _configuration.remove(node.name); @@ -1908,7 +1909,7 @@ class _EvaluateVisitor return null; } - Future visitUseRule(UseRule node) async { + Future visitUseRule(UseRule node) async { var configuration = node.configuration.isEmpty ? const Configuration.empty() : Configuration({ @@ -1927,7 +1928,7 @@ class _EvaluateVisitor return null; } - Future visitWarnRule(WarnRule node) async { + Future visitWarnRule(WarnRule node) async { var value = await _addExceptionSpanAsync(node, () => node.expression.accept(this)); _logger.warn( @@ -1936,7 +1937,7 @@ class _EvaluateVisitor return null; } - Future visitWhileRule(WhileRule node) { + Future visitWhileRule(WhileRule node) { return _environment.scope(() async { while ((await node.condition.accept(this)).isTruthy) { var result = await _handleReturn( @@ -1949,7 +1950,8 @@ class _EvaluateVisitor // ## Expressions - Future visitBinaryOperationExpression(BinaryOperationExpression node) { + Future visitBinaryOperationExpression( + BinaryOperationExpression node) { return _addExceptionSpanAsync(node, () async { var left = await node.left.accept(this); switch (node.operator) { @@ -2055,10 +2057,9 @@ class _EvaluateVisitor _verifyArguments(positional.length, named, IfExpression.declaration, node); // ignore: prefer_is_empty - var condition = positional.length > 0 ? positional[0] : named["condition"]; - var ifTrue = positional.length > 1 ? positional[1] : named["if-true"] /*!*/; - var ifFalse = - positional.length > 2 ? positional[2] : named["if-false"] /*!*/; + var condition = positional.length > 0 ? positional[0] : named["condition"]!; + var ifTrue = positional.length > 1 ? positional[1] : named["if-true"]!; + var ifFalse = positional.length > 2 ? positional[2] : named["if-false"]!; return await ((await condition.accept(this)).isTruthy ? ifTrue : ifFalse) .accept(this); @@ -2106,7 +2107,7 @@ class _EvaluateVisitor Future visitFunctionExpression(FunctionExpression node) async { var plainName = node.name.asPlain; - AsyncCallable function; + AsyncCallable? function; if (plainName != null) { function = _addExceptionSpan( node, @@ -2137,7 +2138,7 @@ class _EvaluateVisitor /// Like `_environment.getFunction`, but also returns built-in /// globally-available functions. - AsyncCallable _getFunction(String name, {String namespace}) { + AsyncCallable? _getFunction(String name, {String? namespace}) { var local = _environment.getFunction(name, namespace: namespace); if (local != null || namespace != null) return local; return _builtInFunctions[name]; @@ -2145,7 +2146,7 @@ class _EvaluateVisitor /// Evaluates the arguments in [arguments] as applied to [callable], and /// invokes [run] in a scope with those arguments defined. - Future _runUserDefinedCallable( + Future _runUserDefinedCallable( ArgumentInvocation arguments, UserDefinedCallable callable, AstNode nodeWithSpan, @@ -2178,15 +2179,15 @@ class _EvaluateVisitor i++) { var argument = declaredArguments[i]; var value = evaluated.named.remove(argument.name) ?? - await argument.defaultValue.accept(this); + await argument.defaultValue!.accept(this); _environment.setLocalVariable( argument.name, value.withoutSlash(), evaluated.namedNodes.andGet(argument.name) ?? - _expressionNode(argument.defaultValue)); + _expressionNode(argument.defaultValue!)); } - SassArgumentList argumentList; + SassArgumentList? argumentList; var restArgument = callable.declaration.arguments.restArgument; if (restArgument != null) { var rest = evaluated.positional.length > declaredArguments.length @@ -2225,7 +2226,7 @@ class _EvaluateVisitor /// Evaluates [arguments] as applied to [callable]. Future _runFunctionCallable(ArgumentInvocation arguments, - AsyncCallable callable, AstNode nodeWithSpan) async { + AsyncCallable? callable, AstNode nodeWithSpan) async { if (callable is AsyncBuiltInCallable) { return (await _runBuiltInCallable(arguments, callable, nodeWithSpan)) .withoutSlash(); @@ -2260,10 +2261,10 @@ class _EvaluateVisitor } // TODO: no ! - var rest = await arguments.rest?.accept(this); + var rest = (await arguments.rest?.accept(this))!; if (rest != null) { if (!first) buffer.write(", "); - buffer.write(_serialize(rest, arguments.rest)); + buffer.write(_serialize(rest, arguments.rest!)); } buffer.writeCharCode($rparen); @@ -2295,10 +2296,10 @@ class _EvaluateVisitor i++) { var argument = declaredArguments[i]; evaluated.positional.add(evaluated.named.remove(argument.name) ?? - await argument.defaultValue?.accept(this)); + (await argument.defaultValue?.accept(this))!); } - SassArgumentList argumentList; + SassArgumentList? argumentList; if (overload.restArgument != null) { var rest = const []; if (evaluated.positional.length > declaredArguments.length) { @@ -2332,14 +2333,14 @@ class _EvaluateVisitor throw MultiSpanSassRuntimeException(error.message, error.span, error.primaryLabel, error.secondarySpans, _stackTrace(error.span)); } catch (error) { - String message; + String? message; try { // TODO: as String! - message = error.message as String; + message = error.message as String?; } catch (_) { message = error.toString(); } - throw _exception(message, nodeWithSpan.span); + throw _exception(message!, nodeWithSpan.span); } _callableNode = oldCallableNode; @@ -2362,7 +2363,7 @@ class _EvaluateVisitor /// If [trackSpans] is `true`, this tracks the source spans of the arguments /// being passed in. It defaults to [_sourceMap]. Future<_ArgumentResults> _evaluateArguments(ArgumentInvocation arguments, - {bool trackSpans}) async { + {bool? trackSpans}) async { trackSpans ??= _sourceMap; var positional = [ @@ -2641,8 +2642,9 @@ class _EvaluateVisitor } // TODO: no !, no as - var mergedQueries = _mediaQueries.andThen( - (mediaQueries) => _mergeMediaQueries(mediaQueries, node.queries)); + var mergedQueries = _mediaQueries.andThen(((mediaQueries) => + _mergeMediaQueries(mediaQueries, node.queries)!) + as List Function(List)?); if (mergedQueries != null && mergedQueries.isEmpty) return null; await _withParent( @@ -2752,8 +2754,8 @@ class _EvaluateVisitor /// /// Returns the value returned by [callback], or `null` if it only ever /// returned `null`. - Future _handleReturn( - List list, Future callback(T value)) async { + Future _handleReturn( + List list, Future callback(T value)) async { for (var value in list) { var result = await callback(value); if (result != null) return result; @@ -2843,7 +2845,7 @@ class _EvaluateVisitor /// This returns an [AstNode] rather than a [FileSpan] so we can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - AstNode _expressionNode(Expression /*!*/ expression) { + AstNode _expressionNode(Expression expression) { // If we aren't making a source map this doesn't matter, but we still return // the expression so we don't have to make the type (and everything // downstream of it) nullable. @@ -2868,13 +2870,13 @@ class _EvaluateVisitor /// Runs [callback] in a new environment scope unless [scopeWhen] is false. Future _withParent( S node, Future callback(), - {bool through(CssNode node), bool scopeWhen = true}) async { + {bool through(CssNode node)?, bool scopeWhen = true}) async { // TODO: no as - _addChild(node, through: through); + _addChild(node as ModifiableCssNode, through: through); var oldParent = _parent; // TODO: no as - _parent = node; + _parent = node as ModifiableCssParentNode; var result = await _environment.scope(callback, when: scopeWhen); _parent = oldParent; @@ -2886,26 +2888,26 @@ class _EvaluateVisitor /// If [through] is passed, [node] is added as a child of the first parent for /// which [through] returns `false` instead. That parent is copied unless it's the /// lattermost child of its parent. - void _addChild(ModifiableCssNode node, {bool through(CssNode node)}) { + void _addChild(ModifiableCssNode node, {bool through(CssNode node)?}) { // Go up through parents that match [through]. var parent = _parent; if (through != null) { while (through(parent)) { - parent = parent.parent /*!*/; + parent = parent.parent!; } // If the parent has a (visible) following sibling, we shouldn't add to // the parent. Instead, we should create a copy and add it after the // interstitial sibling. if (parent.hasFollowingSibling) { - var grandparent = parent.parent; + var grandparent = parent.parent!; parent = parent.copyWithoutChildren(); grandparent.addChild(parent); } } // TODO: no as - parent.addChild(node); + parent.addChild(node as ModifiableCssNode); } /// Runs [callback] with [rule] as the current style rule. @@ -2920,7 +2922,7 @@ class _EvaluateVisitor /// Runs [callback] with [queries] as the current media queries. Future _withMediaQueries( - List queries, Future callback()) async { + List? queries, Future callback()) async { var oldMediaQueries = _mediaQueries; _mediaQueries = queries; var result = await callback(); @@ -2949,15 +2951,14 @@ class _EvaluateVisitor /// Creates a new stack frame with location information from [member] and /// [span]. - Frame _stackFrame(String member, FileSpan /*?*/ span) => frameForSpan( - span, member, + Frame _stackFrame(String member, FileSpan? span) => frameForSpan(span, member, url: span?.sourceUrl.andThen((url) => _importCache?.humanize(url) ?? url)); /// Returns a stack trace at the current point. /// /// If [span] is passed, it's used for the innermost stack frame. - Trace _stackTrace([FileSpan span]) { + Trace _stackTrace([FileSpan? span]) { var frames = [ ..._stack.map((tuple) => _stackFrame(tuple.item1, tuple.item2.span)), if (span != null) _stackFrame(_member, span) @@ -2966,14 +2967,14 @@ class _EvaluateVisitor } /// Emits a warning with the given [message] about the given [span]. - void _warn(String message, FileSpan span, {bool deprecation = false}) => + void _warn(String message, FileSpan? span, {bool deprecation = false}) => _logger.warn(message, span: span, trace: _stackTrace(span), deprecation: deprecation); /// Returns a [SassRuntimeException] with the given [message]. /// /// If [span] is passed, it's used for the innermost stack frame. - SassRuntimeException _exception(String message, [FileSpan span]) => + SassRuntimeException _exception(String message, [FileSpan? span]) => SassRuntimeException( message, span ?? _stack.last.item2.span, _stackTrace(span)); @@ -2982,7 +2983,7 @@ class _EvaluateVisitor /// /// The primary span is taken from the current stack trace span. SassRuntimeException _multiSpanException(String message, String primaryLabel, - Map secondaryLabels) => + Map secondaryLabels) => MultiSpanSassRuntimeException(message, _stack.last.item2.span, primaryLabel, secondaryLabels, _stackTrace()); @@ -2997,7 +2998,7 @@ class _EvaluateVisitor /// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - T _adjustParseError(AstNode /*!*/ nodeWithSpan, T callback()) { + T _adjustParseError(AstNode nodeWithSpan, T callback()) { try { return callback(); } on SassFormatException catch (error) { @@ -3025,7 +3026,7 @@ class _EvaluateVisitor /// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - T _addExceptionSpan(AstNode /*!*/ nodeWithSpan, T callback()) { + T _addExceptionSpan(AstNode nodeWithSpan, T callback()) { try { return callback(); } on MultiSpanSassScriptException catch (error) { @@ -3042,7 +3043,7 @@ class _EvaluateVisitor /// Like [_addExceptionSpan], but for an asynchronous [callback]. Future _addExceptionSpanAsync( - AstNode /*!*/ nodeWithSpan, Future callback()) async { + AstNode nodeWithSpan, Future callback()) async { try { return await callback(); } on MultiSpanSassScriptException catch (error) { @@ -3161,7 +3162,7 @@ class EvaluateResult { /// The result of evaluating arguments to a function or mixin. class _ArgumentResults { /// Arguments passed by position. - final List positional; + final List positional; /// The [AstNode]s that hold the spans for each [positional] argument, or /// `null` if source span tracking is disabled. @@ -3169,7 +3170,7 @@ class _ArgumentResults { /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - final List positionalNodes; + final List? positionalNodes; /// Arguments passed by name. final Map named; @@ -3180,7 +3181,7 @@ class _ArgumentResults { /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - final Map namedNodes; + final Map? namedNodes; /// The separator used for the rest argument list, if any. final ListSeparator separator; diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 31285c28c..ea85bd8e9 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: 370eeeb96dca6b66ab00f7cfb71c3a18c46e22e6 +// Checksum: c527823c670e27437320a11255efb5e86f1b712d // // ignore_for_file: unused_import @@ -80,11 +80,11 @@ typedef _ScopeCallback = void Function(void Function() callback); /// /// Throws a [SassRuntimeException] if evaluation fails. EvaluateResult evaluate(Stylesheet stylesheet, - {ImportCache importCache, - NodeImporter nodeImporter, - Importer importer, - Iterable functions, - Logger logger, + {ImportCache? importCache, + NodeImporter? nodeImporter, + Importer? importer, + Iterable? functions, + Logger? logger, bool sourceMap = false}) => _EvaluateVisitor( importCache: importCache, @@ -101,23 +101,23 @@ class Evaluator { final _EvaluateVisitor _visitor; /// The importer to use to resolve `@use` rules in [_visitor]. - final Importer _importer; + final Importer? _importer; /// Creates an evaluator. /// /// Arguments are the same as for [evaluate]. Evaluator( - {ImportCache importCache, - Importer importer, - Iterable functions, - Logger logger}) + {ImportCache? importCache, + Importer? importer, + Iterable? functions, + Logger? logger}) : _visitor = _EvaluateVisitor( importCache: importCache, functions: functions, logger: logger), _importer = importer; void use(UseRule use) => _visitor.runStatement(_importer, use); - Value /*!*/ evaluate(Expression expression) => + Value evaluate(Expression expression) => _visitor.runExpression(_importer, expression); void setVariable(VariableDeclaration declaration) => @@ -127,15 +127,15 @@ class Evaluator { /// A visitor that executes Sass code to produce a CSS tree. class _EvaluateVisitor implements - StatementVisitor, - ExpressionVisitor, + StatementVisitor, + ExpressionVisitor, CssVisitor { /// The import cache used to import other stylesheets. - final ImportCache _importCache; + final ImportCache? _importCache; /// The Node Sass-compatible importer to use when loading new Sass files when /// compiled to Node.js. - final NodeImporter _nodeImporter; + final NodeImporter? _nodeImporter; /// Built-in functions that are globally-acessible, even under the new module /// system. @@ -145,14 +145,14 @@ class _EvaluateVisitor final _builtInModules = >{}; /// All modules that have been loaded and evaluated so far. - final _modules = >{}; + final _modules = >{}; /// A map from canonical module URLs to the nodes whose spans indicate where /// those modules were originally loaded. /// /// This is not guaranteed to have a node for every module in [_modules]. For /// example, the entrypoint module was not loaded by a node. - final _moduleNodes = {}; + final _moduleNodes = {}; /// The logger to use to print warnings. final Logger _logger; @@ -167,16 +167,16 @@ class _EvaluateVisitor /// /// This doesn't take into consideration any intermediate `@at-root` rules. In /// the common case where those rules are relevant, use [_styleRule] instead. - ModifiableCssStyleRule _styleRuleIgnoringAtRoot; + ModifiableCssStyleRule? _styleRuleIgnoringAtRoot; /// The current media queries, if any. - List _mediaQueries; + List? _mediaQueries; /// The current parent node in the output CSS tree. - /*late*/ ModifiableCssParentNode _parent; + late ModifiableCssParentNode _parent; /// The name of the current declaration parent. - String _declarationName; + String? _declarationName; /// The human-readable name of the current stack frame. var _member = "root stylesheet"; @@ -187,12 +187,12 @@ class _EvaluateVisitor /// [AstNode] rather than a [FileSpan] so we can avoid calling [AstNode.span] /// if the span isn't required, since some nodes need to do real work to /// manufacture a source span. - AstNode _callableNode; + AstNode? _callableNode; /// The span for the current import that's being resolved. /// /// This is used to produce warnings for importers. - FileSpan _importSpan; + FileSpan? _importSpan; /// Whether we're currently executing a function. var _inFunction = false; @@ -200,7 +200,7 @@ class _EvaluateVisitor /// Whether we're currently building the output of an unknown at rule. var _inUnknownAtRule = false; - ModifiableCssStyleRule get _styleRule => + ModifiableCssStyleRule? get _styleRule => _atRootExcludingStyleRule ? null : _styleRuleIgnoringAtRoot; /// Whether we're directly within an `@at-root` rule that excludes style @@ -226,7 +226,7 @@ class _EvaluateVisitor /// entrypoint module). /// /// This is used to ensure that we don't get into an infinite load loop. - final _activeModules = {}; + final _activeModules = {}; /// The dynamic call stack representing function invocations, mixin /// invocations, and imports surrounding the current context. @@ -249,17 +249,17 @@ class _EvaluateVisitor /// /// If this is `null`, relative imports aren't supported in the current /// stylesheet. - Importer _importer; + Importer? _importer; /// The stylesheet that's currently being evaluated. - /*late*/ Stylesheet _stylesheet; + late Stylesheet _stylesheet; /// The root stylesheet node. - /*late*/ ModifiableCssStylesheet _root; + late ModifiableCssStylesheet _root; /// The first index in [_root.children] after the initial block of CSS /// imports. - /*late*/ int _endOfImports; + late int _endOfImports; /// Plain-CSS imports that didn't appear in the initial block of CSS imports. /// @@ -268,11 +268,11 @@ class _EvaluateVisitor /// /// This is `null` unless there are any out-of-order imports in the current /// stylesheet. - /*late*/ List _outOfOrderImports; + late List? _outOfOrderImports; /// The extender that tracks extensions and style rules for the current /// module. - /*late*/ Extender _extender; + late Extender _extender; /// The configuration for the current module. /// @@ -283,10 +283,10 @@ class _EvaluateVisitor /// /// Most arguments are the same as those to [evaluate]. _EvaluateVisitor( - {ImportCache importCache, - NodeImporter nodeImporter, - Iterable functions, - Logger logger, + {ImportCache? importCache, + NodeImporter? nodeImporter, + Iterable? functions, + Logger? logger, bool sourceMap = false}) : _importCache = nodeImporter == null ? importCache ?? ImportCache.none(logger: logger) @@ -381,7 +381,7 @@ class _EvaluateVisitor var callable = css ? PlainCssCallable(name.text) : _addExceptionSpan( - _callableNode /*!*/, + _callableNode!, () => _getFunction(name.text.replaceAll("_", "-"), namespace: module?.text)); if (callable != null) return SassFunction(callable); @@ -393,7 +393,7 @@ class _EvaluateVisitor var function = arguments[0]; var args = arguments[1] as SassArgumentList; - var callableNode = _callableNode /*!*/; + var callableNode = _callableNode!; var invocation = ArgumentInvocation([], {}, callableNode.span, rest: ValueExpression(args, callableNode.span), keywordRest: args.keywords.isEmpty @@ -411,7 +411,7 @@ class _EvaluateVisitor "in Dart Sass 2.0.0. Use call(get-function($function)) instead.", deprecation: true); - var callableNode = _callableNode /*!*/; + var callableNode = _callableNode!; var expression = FunctionExpression( Interpolation([function.text], callableNode.span), invocation, @@ -421,8 +421,7 @@ class _EvaluateVisitor var callable = function.assertFunction("function").callable; if (callable is Callable) { - return _runFunctionCallable( - invocation, callable, _callableNode /*!*/); + return _runFunctionCallable(invocation, callable, _callableNode!); } else { throw SassScriptException( "The function ${callable.name} is asynchronous.\n" @@ -436,7 +435,7 @@ class _EvaluateVisitor var url = Uri.parse(arguments[0].assertString("url").text); var withMap = arguments[1].realNull?.assertMap("with")?.contents; - var callableNode = _callableNode /*!*/; + var callableNode = _callableNode!; var configuration = const Configuration.empty(); if (withMap != null) { var values = {}; @@ -477,7 +476,7 @@ class _EvaluateVisitor } } - EvaluateResult run(Importer importer, Stylesheet /*!*/ node) { + EvaluateResult run(Importer? importer, Stylesheet node) { return _withWarnCallback(() { var url = node.span?.sourceUrl; if (url != null) { @@ -497,11 +496,11 @@ class _EvaluateVisitor }); } - Value runExpression(Importer importer, Expression expression) => + Value runExpression(Importer? importer, Expression expression) => _withWarnCallback(() => _withFakeStylesheet( importer, expression, () => expression.accept(this))); - void runStatement(Importer importer, Statement statement) => + void runStatement(Importer? importer, Statement statement) => _withWarnCallback(() => _withFakeStylesheet( importer, statement, () => statement.accept(this))); @@ -517,7 +516,7 @@ class _EvaluateVisitor /// Runs [callback] with [importer] as [_importer] and a fake [_stylesheet] /// with [nodeWithSpan]'s source span. T _withFakeStylesheet( - Importer importer, AstNode nodeWithSpan, T /*!*/ callback()) { + Importer? importer, AstNode nodeWithSpan, T callback()) { var oldImporter = _importer; _importer = importer; var oldStylesheet = _stylesheet; @@ -547,9 +546,11 @@ class _EvaluateVisitor /// /// The [stackFrame] and [nodeWithSpan] are used for the name and location of /// the stack frame for the duration of the [callback]. - void _loadModule(Uri url, String stackFrame, AstNode /*!*/ nodeWithSpan, + void _loadModule(Uri url, String stackFrame, AstNode nodeWithSpan, void callback(Module module), - {Uri baseUrl, Configuration configuration, bool namesInErrors = false}) { + {Uri? baseUrl, + Configuration? configuration, + bool namesInErrors = false}) { var builtInModule = _builtInModules[url]; if (builtInModule != null) { if (configuration is ExplicitConfiguration) { @@ -622,14 +623,14 @@ class _EvaluateVisitor /// If [namesInErrors] is `true`, this includes the names of modules in errors /// relating to them. This should only be `true` if the names won't be obvious /// from the source span. - Module _execute(Importer importer, Stylesheet stylesheet, - {Configuration configuration, - AstNode nodeWithSpan, + Module _execute(Importer? importer, Stylesheet stylesheet, + {Configuration? configuration, + AstNode? nodeWithSpan, bool namesInErrors = false}) { var url = stylesheet.span?.sourceUrl; // TODO: no ! - var alreadyLoaded = _modules[url]; + var alreadyLoaded = _modules[url!]; if (alreadyLoaded != null) { var currentConfiguration = configuration ?? _configuration; if (currentConfiguration is ExplicitConfiguration) { @@ -657,7 +658,7 @@ class _EvaluateVisitor } var environment = Environment(sourceMap: _sourceMap); - CssStylesheet css; + late CssStylesheet css; var extender = Extender(); _withEnvironment(environment, () { var oldImporter = _importer; @@ -727,7 +728,7 @@ class _EvaluateVisitor return [ ..._root.children.take(_endOfImports), // TODO: no ! - ..._outOfOrderImports, + ..._outOfOrderImports!, ..._root.children.skip(_endOfImports) ]; } @@ -782,7 +783,7 @@ class _EvaluateVisitor // so that by the time we're processing a module we've already filled in all // its downstream extenders and we can use them to extend that module. // TODO: var - var downstreamExtenders = >{}; + Map> downstreamExtenders = >{}; /// Extensions that haven't yet been satisfied by some upstream module. This /// adds extensions when they're defined but not satisfied, and removes them @@ -876,14 +877,14 @@ class _EvaluateVisitor // ## Statements - Value visitStylesheet(Stylesheet node) { + Value? visitStylesheet(Stylesheet node) { for (var child in node.children) { child.accept(this); } return null; } - Value visitAtRootRule(AtRootRule node) { + Value? visitAtRootRule(AtRootRule node) { var query = AtRootQuery.defaultQuery; var unparsedQuery = node.query; if (unparsedQuery != null) { @@ -896,7 +897,7 @@ class _EvaluateVisitor var included = []; while (parent is! CssStylesheet) { if (!query.excludes(parent)) included.add(parent); - parent = parent.parent /*!*/; + parent = parent.parent!; } var root = _trimIncluded(included); @@ -947,20 +948,20 @@ class _EvaluateVisitor if (nodes.isEmpty) return _root; var parent = _parent; - int innermostContiguous; + int? innermostContiguous; var i = 0; for (; i < nodes.length; i++) { while (parent != nodes[i]) { innermostContiguous = null; - parent = parent.parent /*!*/; + parent = parent.parent!; } innermostContiguous ??= i; - parent = parent.parent /*!*/; + parent = parent.parent!; } if (parent != _root) return _root; // TODO: no ! - var root = nodes[innermostContiguous]; + var root = nodes[innermostContiguous!]; nodes.removeRange(innermostContiguous, nodes.length); return root; } @@ -1025,7 +1026,7 @@ class _EvaluateVisitor Value visitContentBlock(ContentBlock node) => throw UnsupportedError( "Evaluation handles @include and its content block together."); - Value visitContentRule(ContentRule node) { + Value? visitContentRule(ContentRule node) { var content = _environment.content; if (content == null) return null; @@ -1039,14 +1040,14 @@ class _EvaluateVisitor return null; } - Value visitDebugRule(DebugRule node) { + Value? visitDebugRule(DebugRule node) { var value = node.expression.accept(this); _logger.debug( value is SassString ? value.text : value.toString(), node.span); return null; } - Value visitDeclaration(Declaration node) { + Value? visitDeclaration(Declaration node) { if (_styleRule == null && !_inUnknownAtRule && !_inKeyframes) { throw _exception( "Declarations may only be used within style rules.", node.span); @@ -1056,8 +1057,8 @@ class _EvaluateVisitor if (_declarationName != null) { name = CssValue("$_declarationName-${name.value}", name.span); } - var cssValue = - node.value.andThen((value) => CssValue(value.accept(this), value.span)); + var cssValue = node.value + .andThen((value) => CssValue(value.accept(this), value.span))!; // 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. @@ -1066,7 +1067,7 @@ class _EvaluateVisitor _parent.addChild(ModifiableCssDeclaration(name, cssValue, node.span, parsedAsCustomProperty: node.isCustomProperty, valueSpanForMap: - _sourceMap ? null : _expressionNode(node.value).span)); + _sourceMap ? null : _expressionNode(node.value!).span)); } else if (name.value.startsWith('--') && cssValue != null) { throw _exception( "Custom property values may not be empty.", cssValue.span); @@ -1089,7 +1090,7 @@ class _EvaluateVisitor /// Returns whether [value] is an empty list. bool _isEmptyList(Value value) => value.asList.isEmpty; - Value visitEachRule(EachRule node) { + Value? visitEachRule(EachRule node) { var list = node.list.accept(this); var nodeWithSpan = _expressionNode(node.list); var setVariables = node.variables.length == 1 @@ -1109,7 +1110,7 @@ class _EvaluateVisitor /// Destructures [value] and assigns it to [variables], as in an `@each` /// statement. void _setMultipleVariables( - List variables, Value value, AstNode /*!*/ nodeWithSpan) { + List variables, Value value, AstNode nodeWithSpan) { var list = value.asList; var minLength = math.min(variables.length, list.length); for (var i = 0; i < minLength; i++) { @@ -1125,7 +1126,7 @@ class _EvaluateVisitor throw _exception(node.expression.accept(this).toString(), node.span); } - Value visitExtendRule(ExtendRule node) { + Value? visitExtendRule(ExtendRule node) { var styleRule = _styleRule; if (styleRule == null || _declarationName != null) { throw _exception( @@ -1166,7 +1167,7 @@ class _EvaluateVisitor return null; } - Value visitAtRule(AtRule node) { + Value? visitAtRule(AtRule node) { // NOTE: this logic is largely duplicated in [visitCssAtRule]. Most changes // here should be mirrored there. @@ -1178,7 +1179,7 @@ class _EvaluateVisitor var name = _interpolationToValue(node.name); var value = node.value.andThen((value) => - _interpolationToValue(value, trim: true, warnForColor: true)); + _interpolationToValue(value, trim: true, warnForColor: true))!; if (node.children == null) { _parent.addChild( @@ -1220,7 +1221,7 @@ class _EvaluateVisitor return null; } - Value visitForRule(ForRule node) { + Value? visitForRule(ForRule node) { var fromNumber = _addExceptionSpan( node.from, () => node.from.accept(this).assertNumber()); var toNumber = @@ -1254,7 +1255,7 @@ class _EvaluateVisitor }, semiGlobal: true); } - Value visitForwardRule(ForwardRule node) { + Value? visitForwardRule(ForwardRule node) { var oldConfiguration = _configuration; var adjustedConfiguration = oldConfiguration.throughForward(node); @@ -1314,7 +1315,7 @@ class _EvaluateVisitor /// [downstream], unless they match a name in [except]. void _removeUsedConfiguration( Configuration upstream, Configuration downstream, - {@required Set except}) { + {required Set except}) { for (var name in upstream.values.keys.toList()) { if (except.contains(name)) continue; if (!downstream.values.containsKey(name)) upstream.remove(name); @@ -1330,7 +1331,7 @@ class _EvaluateVisitor /// variable in the error message. This should only be `true` if the name /// won't be obvious from the source span. void _assertConfigurationIsEmpty(Configuration configuration, - {Set only, bool nameInError = false}) { + {Set? only, bool nameInError = false}) { configuration.values.forEach((name, value) { if (only != null && !only.contains(name)) return; @@ -1343,13 +1344,13 @@ class _EvaluateVisitor }); } - Value visitFunctionRule(FunctionRule node) { + Value? visitFunctionRule(FunctionRule node) { _environment.setFunction(UserDefinedCallable(node, _environment.closure())); return null; } - Value visitIfRule(IfRule node) { - IfRuleClause clause = node.lastClause; + Value? visitIfRule(IfRule node) { + IfRuleClause? clause = node.lastClause; for (var clauseToCheck in node.clauses) { if (clauseToCheck.expression.accept(this).isTruthy) { clause = clauseToCheck; @@ -1361,13 +1362,13 @@ class _EvaluateVisitor return _environment.scope( () => _handleReturn( // TODO: no ! - clause.children, + clause!.children, (child) => child.accept(this)), semiGlobal: true, when: clause.hasDeclarations); } - Value visitImportRule(ImportRule node) { + Value? visitImportRule(ImportRule node) { for (var import in node.imports) { if (import is DynamicImport) { _visitDynamicImport(import); @@ -1414,7 +1415,7 @@ class _EvaluateVisitor return; } - List children; + late List children; var environment = _environment.forImport(); _withEnvironment(environment, () { var oldImporter = _importer; @@ -1478,8 +1479,8 @@ class _EvaluateVisitor /// /// This first tries loading [url] relative to [baseUrl], which defaults to /// `_stylesheet.span.sourceUrl`. - Tuple2 _loadStylesheet(String url, FileSpan span, - {Uri baseUrl, bool forImport = false}) { + Tuple2 _loadStylesheet(String url, FileSpan? span, + {Uri? baseUrl, bool forImport = false}) { try { assert(_importSpan == null); _importSpan = span; @@ -1506,14 +1507,14 @@ class _EvaluateVisitor } on SassException catch (error) { throw _exception(error.message, error.span); } catch (error) { - String message; + String? message; try { // TODO: as String! - message = error.message as String; + message = error.message as String?; } catch (_) { message = error.toString(); } - throw _exception(message); + throw _exception(message!); } finally { _importSpan = null; } @@ -1522,9 +1523,9 @@ class _EvaluateVisitor /// Imports a stylesheet using [_nodeImporter]. /// /// Returns the [Stylesheet], or `null` if the import failed. - Stylesheet _importLikeNode(String originalUrl, bool forImport) { - var result = - _nodeImporter.load(originalUrl, _stylesheet.span?.sourceUrl, forImport); + Stylesheet? _importLikeNode(String originalUrl, bool forImport) { + var result = _nodeImporter! + .load(originalUrl, _stylesheet.span?.sourceUrl, forImport); if (result == null) return null; var contents = result.item1; @@ -1546,9 +1547,9 @@ class _EvaluateVisitor var supports = import.supports.andThen((supports) => CssValue( "supports(${supports is SupportsDeclaration ? "${_evaluateToCss(supports.name)}: " "${_evaluateToCss(supports.value)}" : supports.andThen(_visitSupportsCondition)})", - supports.span)); + supports.span))!; var rawMedia = import.media; - var mediaQuery = rawMedia.andThen(_visitMediaQueries); + var mediaQuery = rawMedia.andThen(_visitMediaQueries)!; var node = ModifiableCssImport(url, import.span, supports: supports, media: mediaQuery); @@ -1564,7 +1565,7 @@ class _EvaluateVisitor return null; } - Value visitIncludeRule(IncludeRule node) { + Value? visitIncludeRule(IncludeRule node) { var mixin = _addExceptionSpan(node, () => _environment.getMixin(node.name, namespace: node.namespace)); if (mixin == null) { @@ -1610,12 +1611,12 @@ class _EvaluateVisitor return null; } - Value visitMixinRule(MixinRule node) { + Value? visitMixinRule(MixinRule node) { _environment.setMixin(UserDefinedCallable(node, _environment.closure())); return null; } - Value visitLoudComment(LoudComment node) { + Value? visitLoudComment(LoudComment node) { // NOTE: this logic is largely duplicated in [visitCssComment]. Most changes // here should be mirrored there. @@ -1631,7 +1632,7 @@ class _EvaluateVisitor return null; } - Value visitMediaRule(MediaRule node) { + Value? visitMediaRule(MediaRule node) { // NOTE: this logic is largely duplicated in [visitCssMediaRule]. Most // changes here should be mirrored there. @@ -1641,8 +1642,9 @@ class _EvaluateVisitor } var queries = _visitMediaQueries(node.query); - var mergedQueries = _mediaQueries - .andThen((mediaQueries) => _mergeMediaQueries(mediaQueries, queries)); + var mergedQueries = _mediaQueries.andThen(((mediaQueries) => + _mergeMediaQueries(mediaQueries, queries)!) + as List Function(List)?); if (mergedQueries != null && mergedQueries.isEmpty) return null; _withParent(ModifiableCssMediaRule(mergedQueries ?? queries, node.span), @@ -1691,7 +1693,7 @@ class _EvaluateVisitor /// Returns the empty list if there are no contexts that match both [queries1] /// and [queries2], or `null` if there are contexts that can't be represented /// by media queries. - List _mergeMediaQueries( + List? _mergeMediaQueries( Iterable queries1, Iterable queries2) { var queries = []; for (var query1 in queries1) { @@ -1707,9 +1709,9 @@ class _EvaluateVisitor Value visitReturnRule(ReturnRule node) => node.expression.accept(this); - Value visitSilentComment(SilentComment node) => null; + Value? visitSilentComment(SilentComment node) => null; - Value visitStyleRule(StyleRule node) { + Value? visitStyleRule(StyleRule node) { // NOTE: this logic is largely duplicated in [visitCssStyleRule]. Most // changes here should be mirrored there. @@ -1778,7 +1780,7 @@ class _EvaluateVisitor return null; } - Value visitSupportsRule(SupportsRule node) { + Value? visitSupportsRule(SupportsRule node) { // NOTE: this logic is largely duplicated in [visitCssSupportsRule]. Most // changes here should be mirrored there. @@ -1845,7 +1847,7 @@ class _EvaluateVisitor /// If [operator] is passed, it's the operator for the surrounding /// [SupportsOperation], and is used to determine whether parentheses are /// necessary if [condition] is also a [SupportsOperation]. - String _parenthesize(SupportsCondition condition, [String operator]) { + String _parenthesize(SupportsCondition condition, [String? operator]) { if ((condition is SupportsNegation) || (condition is SupportsOperation && (operator == null || operator != condition.operator))) { @@ -1855,7 +1857,7 @@ class _EvaluateVisitor } } - Value visitVariableDeclaration(VariableDeclaration node) { + Value? visitVariableDeclaration(VariableDeclaration node) { if (node.isGuarded) { if (node.namespace == null && _environment.atRoot) { var override = _configuration.remove(node.name); @@ -1899,7 +1901,7 @@ class _EvaluateVisitor return null; } - Value visitUseRule(UseRule node) { + Value? visitUseRule(UseRule node) { var configuration = node.configuration.isEmpty ? const Configuration.empty() : Configuration({ @@ -1918,7 +1920,7 @@ class _EvaluateVisitor return null; } - Value visitWarnRule(WarnRule node) { + Value? visitWarnRule(WarnRule node) { var value = _addExceptionSpan(node, () => node.expression.accept(this)); _logger.warn( value is SassString ? value.text : _serialize(value, node.expression), @@ -1926,7 +1928,7 @@ class _EvaluateVisitor return null; } - Value visitWhileRule(WhileRule node) { + Value? visitWhileRule(WhileRule node) { return _environment.scope(() { while (node.condition.accept(this).isTruthy) { var result = _handleReturn( @@ -1939,7 +1941,7 @@ class _EvaluateVisitor // ## Expressions - Value visitBinaryOperationExpression(BinaryOperationExpression node) { + Value? visitBinaryOperationExpression(BinaryOperationExpression node) { return _addExceptionSpan(node, () { var left = node.left.accept(this); switch (node.operator) { @@ -2044,10 +2046,9 @@ class _EvaluateVisitor _verifyArguments(positional.length, named, IfExpression.declaration, node); // ignore: prefer_is_empty - var condition = positional.length > 0 ? positional[0] : named["condition"]; - var ifTrue = positional.length > 1 ? positional[1] : named["if-true"] /*!*/; - var ifFalse = - positional.length > 2 ? positional[2] : named["if-false"] /*!*/; + var condition = positional.length > 0 ? positional[0] : named["condition"]!; + var ifTrue = positional.length > 1 ? positional[1] : named["if-true"]!; + var ifFalse = positional.length > 2 ? positional[2] : named["if-false"]!; return (condition.accept(this).isTruthy ? ifTrue : ifFalse).accept(this); } @@ -2092,7 +2093,7 @@ class _EvaluateVisitor Value visitFunctionExpression(FunctionExpression node) { var plainName = node.name.asPlain; - Callable function; + Callable? function; if (plainName != null) { function = _addExceptionSpan( node, @@ -2123,7 +2124,7 @@ class _EvaluateVisitor /// Like `_environment.getFunction`, but also returns built-in /// globally-available functions. - Callable _getFunction(String name, {String namespace}) { + Callable? _getFunction(String name, {String? namespace}) { var local = _environment.getFunction(name, namespace: namespace); if (local != null || namespace != null) return local; return _builtInFunctions[name]; @@ -2131,7 +2132,7 @@ class _EvaluateVisitor /// Evaluates the arguments in [arguments] as applied to [callable], and /// invokes [run] in a scope with those arguments defined. - V _runUserDefinedCallable( + V _runUserDefinedCallable( ArgumentInvocation arguments, UserDefinedCallable callable, AstNode nodeWithSpan, @@ -2164,15 +2165,15 @@ class _EvaluateVisitor i++) { var argument = declaredArguments[i]; var value = evaluated.named.remove(argument.name) ?? - argument.defaultValue.accept(this); + argument.defaultValue!.accept(this); _environment.setLocalVariable( argument.name, value.withoutSlash(), evaluated.namedNodes.andGet(argument.name) ?? - _expressionNode(argument.defaultValue)); + _expressionNode(argument.defaultValue!)); } - SassArgumentList argumentList; + SassArgumentList? argumentList; var restArgument = callable.declaration.arguments.restArgument; if (restArgument != null) { var rest = evaluated.positional.length > declaredArguments.length @@ -2211,7 +2212,7 @@ class _EvaluateVisitor /// Evaluates [arguments] as applied to [callable]. Value _runFunctionCallable( - ArgumentInvocation arguments, Callable callable, AstNode nodeWithSpan) { + ArgumentInvocation arguments, Callable? callable, AstNode nodeWithSpan) { if (callable is BuiltInCallable) { return _runBuiltInCallable(arguments, callable, nodeWithSpan) .withoutSlash(); @@ -2244,10 +2245,10 @@ class _EvaluateVisitor } // TODO: no ! - var rest = arguments.rest?.accept(this); + var rest = arguments.rest?.accept(this)!; if (rest != null) { if (!first) buffer.write(", "); - buffer.write(_serialize(rest, arguments.rest)); + buffer.write(_serialize(rest, arguments.rest!)); } buffer.writeCharCode($rparen); @@ -2279,10 +2280,10 @@ class _EvaluateVisitor i++) { var argument = declaredArguments[i]; evaluated.positional.add(evaluated.named.remove(argument.name) ?? - argument.defaultValue?.accept(this)); + argument.defaultValue?.accept(this)!); } - SassArgumentList argumentList; + SassArgumentList? argumentList; if (overload.restArgument != null) { var rest = const []; if (evaluated.positional.length > declaredArguments.length) { @@ -2316,14 +2317,14 @@ class _EvaluateVisitor throw MultiSpanSassRuntimeException(error.message, error.span, error.primaryLabel, error.secondarySpans, _stackTrace(error.span)); } catch (error) { - String message; + String? message; try { // TODO: as String! - message = error.message as String; + message = error.message as String?; } catch (_) { message = error.toString(); } - throw _exception(message, nodeWithSpan.span); + throw _exception(message!, nodeWithSpan.span); } _callableNode = oldCallableNode; @@ -2346,7 +2347,7 @@ class _EvaluateVisitor /// If [trackSpans] is `true`, this tracks the source spans of the arguments /// being passed in. It defaults to [_sourceMap]. _ArgumentResults _evaluateArguments(ArgumentInvocation arguments, - {bool trackSpans}) { + {bool? trackSpans}) { trackSpans ??= _sourceMap; var positional = [ @@ -2624,8 +2625,9 @@ class _EvaluateVisitor } // TODO: no !, no as - var mergedQueries = _mediaQueries.andThen( - (mediaQueries) => _mergeMediaQueries(mediaQueries, node.queries)); + var mergedQueries = _mediaQueries.andThen(((mediaQueries) => + _mergeMediaQueries(mediaQueries, node.queries)!) + as List Function(List)?); if (mergedQueries != null && mergedQueries.isEmpty) return null; _withParent( @@ -2733,7 +2735,7 @@ class _EvaluateVisitor /// /// Returns the value returned by [callback], or `null` if it only ever /// returned `null`. - Value _handleReturn(List list, Value callback(T value)) { + Value? _handleReturn(List list, Value? callback(T value)) { for (var value in list) { var result = callback(value); if (result != null) return result; @@ -2820,7 +2822,7 @@ class _EvaluateVisitor /// This returns an [AstNode] rather than a [FileSpan] so we can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - AstNode _expressionNode(Expression /*!*/ expression) { + AstNode _expressionNode(Expression expression) { // If we aren't making a source map this doesn't matter, but we still return // the expression so we don't have to make the type (and everything // downstream of it) nullable. @@ -2844,13 +2846,13 @@ class _EvaluateVisitor /// /// Runs [callback] in a new environment scope unless [scopeWhen] is false. T _withParent(S node, T callback(), - {bool through(CssNode node), bool scopeWhen = true}) { + {bool through(CssNode node)?, bool scopeWhen = true}) { // TODO: no as - _addChild(node, through: through); + _addChild(node as ModifiableCssNode, through: through); var oldParent = _parent; // TODO: no as - _parent = node; + _parent = node as ModifiableCssParentNode; var result = _environment.scope(callback, when: scopeWhen); _parent = oldParent; @@ -2862,26 +2864,26 @@ class _EvaluateVisitor /// If [through] is passed, [node] is added as a child of the first parent for /// which [through] returns `false` instead. That parent is copied unless it's the /// lattermost child of its parent. - void _addChild(ModifiableCssNode node, {bool through(CssNode node)}) { + void _addChild(ModifiableCssNode node, {bool through(CssNode node)?}) { // Go up through parents that match [through]. var parent = _parent; if (through != null) { while (through(parent)) { - parent = parent.parent /*!*/; + parent = parent.parent!; } // If the parent has a (visible) following sibling, we shouldn't add to // the parent. Instead, we should create a copy and add it after the // interstitial sibling. if (parent.hasFollowingSibling) { - var grandparent = parent.parent; + var grandparent = parent.parent!; parent = parent.copyWithoutChildren(); grandparent.addChild(parent); } } // TODO: no as - parent.addChild(node); + parent.addChild(node as ModifiableCssNode); } /// Runs [callback] with [rule] as the current style rule. @@ -2894,7 +2896,7 @@ class _EvaluateVisitor } /// Runs [callback] with [queries] as the current media queries. - T _withMediaQueries(List queries, T callback()) { + T _withMediaQueries(List? queries, T callback()) { var oldMediaQueries = _mediaQueries; _mediaQueries = queries; var result = callback(); @@ -2922,15 +2924,14 @@ class _EvaluateVisitor /// Creates a new stack frame with location information from [member] and /// [span]. - Frame _stackFrame(String member, FileSpan /*?*/ span) => frameForSpan( - span, member, + Frame _stackFrame(String member, FileSpan? span) => frameForSpan(span, member, url: span?.sourceUrl.andThen((url) => _importCache?.humanize(url) ?? url)); /// Returns a stack trace at the current point. /// /// If [span] is passed, it's used for the innermost stack frame. - Trace _stackTrace([FileSpan span]) { + Trace _stackTrace([FileSpan? span]) { var frames = [ ..._stack.map((tuple) => _stackFrame(tuple.item1, tuple.item2.span)), if (span != null) _stackFrame(_member, span) @@ -2939,14 +2940,14 @@ class _EvaluateVisitor } /// Emits a warning with the given [message] about the given [span]. - void _warn(String message, FileSpan span, {bool deprecation = false}) => + void _warn(String message, FileSpan? span, {bool deprecation = false}) => _logger.warn(message, span: span, trace: _stackTrace(span), deprecation: deprecation); /// Returns a [SassRuntimeException] with the given [message]. /// /// If [span] is passed, it's used for the innermost stack frame. - SassRuntimeException _exception(String message, [FileSpan span]) => + SassRuntimeException _exception(String message, [FileSpan? span]) => SassRuntimeException( message, span ?? _stack.last.item2.span, _stackTrace(span)); @@ -2955,7 +2956,7 @@ class _EvaluateVisitor /// /// The primary span is taken from the current stack trace span. SassRuntimeException _multiSpanException(String message, String primaryLabel, - Map secondaryLabels) => + Map secondaryLabels) => MultiSpanSassRuntimeException(message, _stack.last.item2.span, primaryLabel, secondaryLabels, _stackTrace()); @@ -2970,7 +2971,7 @@ class _EvaluateVisitor /// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - T _adjustParseError(AstNode /*!*/ nodeWithSpan, T callback()) { + T _adjustParseError(AstNode nodeWithSpan, T callback()) { try { return callback(); } on SassFormatException catch (error) { @@ -2998,7 +2999,7 @@ class _EvaluateVisitor /// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - T _addExceptionSpan(AstNode /*!*/ nodeWithSpan, T callback()) { + T _addExceptionSpan(AstNode nodeWithSpan, T callback()) { try { return callback(); } on MultiSpanSassScriptException catch (error) { @@ -3101,7 +3102,7 @@ class _ImportedCssVisitor implements ModifiableCssVisitor { /// The result of evaluating arguments to a function or mixin. class _ArgumentResults { /// Arguments passed by position. - final List positional; + final List positional; /// The [AstNode]s that hold the spans for each [positional] argument, or /// `null` if source span tracking is disabled. @@ -3109,7 +3110,7 @@ class _ArgumentResults { /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - final List positionalNodes; + final List? positionalNodes; /// Arguments passed by name. final Map named; @@ -3120,7 +3121,7 @@ class _ArgumentResults { /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - final Map namedNodes; + final Map? namedNodes; /// The separator used for the rest argument list, if any. final ListSeparator separator; diff --git a/lib/src/visitor/interface/expression.dart b/lib/src/visitor/interface/expression.dart index 6ca7d3801..6c78dd3ef 100644 --- a/lib/src/visitor/interface/expression.dart +++ b/lib/src/visitor/interface/expression.dart @@ -8,7 +8,7 @@ import '../../ast/sass.dart'; /// /// [visitors]: https://en.wikipedia.org/wiki/Visitor_pattern abstract class ExpressionVisitor { - T visitBinaryOperationExpression(BinaryOperationExpression node); + T? visitBinaryOperationExpression(BinaryOperationExpression node); T visitBooleanExpression(BooleanExpression node); T visitColorExpression(ColorExpression node); T visitFunctionExpression(FunctionExpression node); diff --git a/lib/src/visitor/interface/statement.dart b/lib/src/visitor/interface/statement.dart index eaf387d82..2f12dc196 100644 --- a/lib/src/visitor/interface/statement.dart +++ b/lib/src/visitor/interface/statement.dart @@ -8,31 +8,31 @@ import '../../ast/sass.dart'; /// /// [visitors]: https://en.wikipedia.org/wiki/Visitor_pattern abstract class StatementVisitor { - T visitAtRootRule(AtRootRule node); - T visitAtRule(AtRule node); + T? visitAtRootRule(AtRootRule node); + T? visitAtRule(AtRule node); T visitContentBlock(ContentBlock node); - T visitContentRule(ContentRule node); - T visitDebugRule(DebugRule node); - T visitDeclaration(Declaration node); - T visitEachRule(EachRule node); + T? visitContentRule(ContentRule node); + T? visitDebugRule(DebugRule node); + T? visitDeclaration(Declaration node); + T? visitEachRule(EachRule node); T visitErrorRule(ErrorRule node); - T visitExtendRule(ExtendRule node); - T visitForRule(ForRule node); - T visitForwardRule(ForwardRule node); - T visitFunctionRule(FunctionRule node); - T visitIfRule(IfRule node); - T visitImportRule(ImportRule node); - T visitIncludeRule(IncludeRule node); - T visitLoudComment(LoudComment node); - T visitMediaRule(MediaRule node); - T visitMixinRule(MixinRule node); + T? visitExtendRule(ExtendRule node); + T? visitForRule(ForRule node); + T? visitForwardRule(ForwardRule node); + T? visitFunctionRule(FunctionRule node); + T? visitIfRule(IfRule node); + T? visitImportRule(ImportRule node); + T? visitIncludeRule(IncludeRule node); + T? visitLoudComment(LoudComment node); + T? visitMediaRule(MediaRule node); + T? visitMixinRule(MixinRule node); T visitReturnRule(ReturnRule node); - T visitSilentComment(SilentComment node); - T visitStyleRule(StyleRule node); - T visitStylesheet(Stylesheet node); - T visitSupportsRule(SupportsRule node); - T visitUseRule(UseRule node); - T visitVariableDeclaration(VariableDeclaration node); - T visitWarnRule(WarnRule node); - T visitWhileRule(WhileRule node); + T? visitSilentComment(SilentComment node); + T? visitStyleRule(StyleRule node); + T? visitStylesheet(Stylesheet node); + T? visitSupportsRule(SupportsRule node); + T? visitUseRule(UseRule node); + T? visitVariableDeclaration(VariableDeclaration node); + T? visitWarnRule(WarnRule node); + T? visitWhileRule(WhileRule node); } diff --git a/lib/src/visitor/serialize.dart b/lib/src/visitor/serialize.dart index 0af80302f..a48a2f8e9 100644 --- a/lib/src/visitor/serialize.dart +++ b/lib/src/visitor/serialize.dart @@ -44,11 +44,11 @@ import 'interface/value.dart'; /// If [charset] is `true`, this will include a `@charset` declaration or a BOM /// if the stylesheet contains any non-ASCII characters. SerializeResult serialize(CssNode node, - {OutputStyle style, + {OutputStyle? style, bool inspect = false, bool useSpaces = true, - int indentWidth, - LineFeed lineFeed, + int? indentWidth, + LineFeed? lineFeed, bool sourceMap = false, bool charset = true}) { indentWidth ??= 2; @@ -137,12 +137,12 @@ class _SerializeVisitor bool get _isCompressed => _style == OutputStyle.compressed; _SerializeVisitor( - {OutputStyle style, + {OutputStyle? style, bool inspect = false, bool quote = true, bool useSpaces = true, - int indentWidth, - LineFeed lineFeed, + int? indentWidth, + LineFeed? lineFeed, bool sourceMap = true}) : _buffer = sourceMap ? SourceMapBuffer() : NoSourceMapBuffer(), _style = style ?? OutputStyle.expanded, @@ -155,7 +155,7 @@ class _SerializeVisitor } void visitCssStylesheet(CssStylesheet node) { - CssNode previous; + CssNode? previous; for (var i = 0; i < node.children.length; i++) { var child = node.children[i]; if (_isInvisible(child)) continue; @@ -405,12 +405,12 @@ class _SerializeVisitor /// /// Returns `null` if [text] contains no newlines, and -1 if it contains /// newlines but no lines are indented. - int _minimumIndentation(String text) { + int? _minimumIndentation(String text) { var scanner = LineScanner(text); while (!scanner.isDone && scanner.readChar() != $lf) {} if (scanner.isDone) return scanner.peekChar(-1) == $lf ? -1 : null; - int min; + int? min; while (!scanner.isDone) { while (!scanner.isDone) { var next = scanner.peekChar(); @@ -429,7 +429,7 @@ class _SerializeVisitor /// [_indentation] for each non-empty line after the first. /// /// Compresses trailing empty lines of [text] into a single trailing space. - void _writeWithIndent(String text, int /*!*/ minimumIndentation) { + void _writeWithIndent(String text, int minimumIndentation) { var scanner = LineScanner(text); // Write the first line as-is. @@ -697,8 +697,8 @@ class _SerializeVisitor /// Otherwise, returns [text] as-is. String _removeExponent(String text) { // Don't allocate this until we know [text] contains exponent notation. - StringBuffer buffer; - int exponent; + StringBuffer? buffer; + late int exponent; for (var i = 0; i < text.length; i++) { var codeUnit = text.codeUnitAt(i); if (codeUnit != $e) continue; @@ -962,7 +962,7 @@ class _SerializeVisitor } void visitComplexSelector(ComplexSelector complex) { - ComplexSelectorComponent lastComponent; + ComplexSelectorComponent? lastComponent; for (var component in complex.components) { if (lastComponent != null && !_omitSpacesAround(lastComponent) && @@ -1000,7 +1000,7 @@ class _SerializeVisitor _buffer.write(id.name); } - void visitSelectorList(SelectorList /*!*/ list) { + void visitSelectorList(SelectorList list) { var complexes = _inspect ? list.components : list.components.where((complex) => !complex.isInvisible); @@ -1085,7 +1085,7 @@ class _SerializeVisitor } _writeLineFeed(); - CssNode previous; + CssNode? previous; _indent(() { for (var i = 0; i < children.length; i++) { var child = children[i]; @@ -1095,7 +1095,7 @@ class _SerializeVisitor if (_requiresSemicolon(previous)) _buffer.writeCharCode($semicolon); _writeLineFeed(); // TODO: no ! - if (previous.isGroupEnd) _writeLineFeed(); + if (previous!.isGroupEnd) _writeLineFeed(); } previous = child; @@ -1112,7 +1112,7 @@ class _SerializeVisitor } /// Whether [node] requires a semicolon to be written after it. - bool _requiresSemicolon(CssNode node) => + bool _requiresSemicolon(CssNode? node) => node is CssParentNode ? node.isChildless : node is! CssComment; /// Writes a line feed, unless this emitting compressed CSS. @@ -1241,13 +1241,13 @@ class SerializeResult { /// The source map indicating how the source files map to [css]. /// /// This is `null` if source mapping was disabled for this compilation. - final SingleMapping sourceMap; + final SingleMapping? sourceMap; /// A map from source file URLs to the corresponding [SourceFile]s. /// /// This can be passed to [sourceMap]'s [Mapping.spanFor] method. It's `null` /// if source mapping was disabled for this compilation. - final Map sourceFiles; + final Map? sourceFiles; SerializeResult(this.css, {this.sourceMap, this.sourceFiles}); } diff --git a/pubspec.yaml b/pubspec.yaml index a76c7a9ef..ed9f76ba7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,7 +9,7 @@ executables: sass: sass environment: - sdk: '>=2.6.0 <3.0.0' + sdk: '>=2.12.0 <3.0.0' dependencies: args: ^2.0.0 diff --git a/test/cli/dart_test.dart b/test/cli/dart_test.dart index 5e1f654d2..27a14c626 100644 --- a/test/cli/dart_test.dart +++ b/test/cli/dart_test.dart @@ -30,6 +30,6 @@ void main() { void ensureSnapshotUpToDate() => pkg.ensureExecutableUpToDate("sass"); Future runSass(Iterable arguments, - {Map environment}) => + {Map? environment}) => pkg.start("sass", arguments, environment: environment, workingDirectory: d.sandbox, encoding: utf8); diff --git a/test/cli/node_test.dart b/test/cli/node_test.dart index 7d1e4f751..9169cc6aa 100644 --- a/test/cli/node_test.dart +++ b/test/cli/node_test.dart @@ -31,7 +31,7 @@ void main() { } Future runSass(Iterable arguments, - {Map environment}) => + {Map? environment}) => pkg.start("sass", arguments, environment: environment, workingDirectory: d.sandbox, diff --git a/test/cli/shared.dart b/test/cli/shared.dart index 736686da3..fb945ad7b 100644 --- a/test/cli/shared.dart +++ b/test/cli/shared.dart @@ -13,11 +13,11 @@ 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, - {Map environment})) { + {Map? environment})) { /// Runs the executable on [arguments] plus an output file, then verifies that /// the contents of the output file match [expected]. Future expectCompiles(List arguments, Object expected, - {Map environment}) async { + {Map? environment}) async { var sass = await runSass([...arguments, "out.css", "--no-source-map"], environment: environment); await sass.shouldExit(0); diff --git a/test/cli/shared/source_maps.dart b/test/cli/shared/source_maps.dart index 35130a94a..5da4857d2 100644 --- a/test/cli/shared/source_maps.dart +++ b/test/cli/shared/source_maps.dart @@ -18,7 +18,7 @@ import '../../utils.dart'; /// Defines test that are shared between the Dart and Node.js CLI test suites. void sharedTests(Future runSass(Iterable arguments)) { group("for a simple compilation", () { - Map map; + late Map map; setUp(() async { await d.file("test.scss", "a {b: 1 + 2}").create(); @@ -35,7 +35,7 @@ void sharedTests(Future runSass(Iterable arguments)) { }); test("contains mappings", () { - /*late*/ SingleMapping sourceMap; + late SingleMapping sourceMap; sass.compileString("a {b: 1 + 2}", sourceMap: (map) => sourceMap = map); expect(map, containsPair("mappings", sourceMap.toJson()["mappings"])); }); @@ -278,7 +278,7 @@ void sharedTests(Future runSass(Iterable arguments)) { await d.file("test.scss", "a {b: 1 + 2}").create(); }); - Map map; + Map? map; group("with the target in the same directory", () { setUp(() async { await (await runSass(["--embed-source-map", "test.scss", "out.css"])) @@ -288,7 +288,7 @@ void sharedTests(Future runSass(Iterable arguments)) { }); test("contains mappings in the generated CSS", () { - SingleMapping sourceMap; + late SingleMapping sourceMap; sass.compileString("a {b: 1 + 2}", sourceMap: (map) => sourceMap = map); expect(map, containsPair("mappings", sourceMap.toJson()["mappings"])); }); @@ -360,4 +360,4 @@ void sharedTests(Future runSass(Iterable arguments)) { /// Reads the file at [path] within [d.sandbox] and JSON-decodes it. Map _readJson(String path) => - jsonDecode(readFile(d.path(path))) as Map /*!*/; + jsonDecode(readFile(d.path(path))) as Map; diff --git a/test/dart_api/function_test.dart b/test/dart_api/function_test.dart index eb29bf7ec..83852579f 100644 --- a/test/dart_api/function_test.dart +++ b/test/dart_api/function_test.dart @@ -78,7 +78,10 @@ void main() { expect(() { compileString('a {b: foo()}', // TODO: no as - functions: [Callable("foo", "", (arguments) => throw "heck")]); + functions: [ + Callable("foo", "", + ((arguments) => throw "heck") as Value Function(List)) + ]); }, throwsA(const TypeMatcher())); }); @@ -112,15 +115,18 @@ void main() { test("supports keyword arguments", () { var css = compileString(r'a {b: foo($bar: 1)}', functions: [ - Callable("foo", r"$args...", expectAsync1((arguments) { - expect(arguments, hasLength(1)); - var list = arguments[0] as SassArgumentList; - expect(list.asList, hasLength(0)); - expect(list.keywords, contains("bar")); - expect(list.keywords["bar"].assertNumber().value, equals(1)); - return list.keywords["bar"]; - // TODO: no as - })) + Callable( + "foo", + r"$args...", + expectAsync1((arguments) { + expect(arguments, hasLength(1)); + var list = arguments[0] as SassArgumentList; + expect(list.asList, hasLength(0)); + expect(list.keywords, contains("bar")); + expect(list.keywords["bar"]!.assertNumber().value, equals(1)); + return list.keywords["bar"]!; + // TODO: no as + } as Value Function(List))) ]); expect(css, equalsIgnoringWhitespace("a { b: 1; }")); diff --git a/test/dart_api/importer_test.dart b/test/dart_api/importer_test.dart index a1c9b57d8..a2648b765 100644 --- a/test/dart_api/importer_test.dart +++ b/test/dart_api/importer_test.dart @@ -92,7 +92,7 @@ void main() { }); test("uses an importer's source map URL", () { - /*late*/ SingleMapping map; + late SingleMapping map; compileString('@import "orange";', importers: [ TestImporter((url) => Uri.parse("u:$url"), (url) { @@ -107,7 +107,7 @@ void main() { }); test("uses a data: source map URL if the importer doesn't provide one", () { - /*late*/ SingleMapping map; + late SingleMapping map; compileString('@import "orange";', importers: [ TestImporter((url) => Uri.parse("u:$url"), (url) { @@ -126,11 +126,13 @@ void main() { test("wraps an error in canonicalize()", () { expect(() { compileString('@import "orange";', importers: [ - TestImporter((url) { - throw "this import is bad actually"; - }, expectAsync1((_) => null, count: 0)) // TODO: no as + TestImporter( + (url) { + throw "this import is bad actually"; + } as Uri Function(Uri), + expectAsync1((_) => null, count: 0)) // TODO: no as ]); - }, throwsA(predicate((error) { + }, throwsA(predicate((dynamic error) { // TODO: no dynamic expect(error, const TypeMatcher()); expect( @@ -146,7 +148,7 @@ void main() { throw "this import is bad actually"; }) ]); - }, throwsA(predicate((error) { + }, throwsA(predicate((dynamic error) { // TODO: no dynamic expect(error, const TypeMatcher()); expect( @@ -162,7 +164,7 @@ void main() { throw FormatException("bad format somehow"); }) ]); - }, throwsA(predicate((error) { + }, throwsA(predicate((dynamic error) { // TODO: no dynamic expect(error, const TypeMatcher()); // FormatException.toString() starts with "FormatException:", but @@ -177,7 +179,7 @@ void main() { compileString('@import "orange";', importers: [ TestImporter((url) => Uri.parse("u:$url"), (url) => null) ]); - }, throwsA(predicate((error) { + }, throwsA(predicate((dynamic error) { // TODO: no dynamic expect(error, const TypeMatcher()); expect(error.toString(), diff --git a/test/dart_api/logger_test.dart b/test/dart_api/logger_test.dart index 9b0bd61d9..842120506 100644 --- a/test/dart_api/logger_test.dart +++ b/test/dart_api/logger_test.dart @@ -21,7 +21,8 @@ void main() { compileString(''' @mixin foo {@warn heck} @include foo; - ''', logger: _TestLogger.withWarn((message, {span, trace, deprecation}) { + ''', logger: _TestLogger.withWarn((message, + {span, required trace, deprecation}) { expect(message, equals("heck")); expect(span, isNull); expect(trace.frames.first.member, equals('foo()')); @@ -78,8 +79,8 @@ void main() { test("with a parser warning passes the message and span", () { var mustBeCalled = expectAsync0(() {}); - compileString('a {b: c && d}', - logger: _TestLogger.withWarn((message, {span, trace, deprecation}) { + compileString('a {b: c && d}', logger: + _TestLogger.withWarn((message, {required span, trace, deprecation}) { expect(message, contains('"&&" means two copies')); expect(span.start.line, equals(0)); @@ -98,7 +99,8 @@ void main() { compileString(''' @mixin foo {#{blue} {x: y}} @include foo; - ''', logger: _TestLogger.withWarn((message, {span, trace, deprecation}) { + ''', logger: _TestLogger.withWarn((message, + {required span, required trace, deprecation}) { expect(message, contains("color value blue")); expect(span.start.line, equals(0)); @@ -124,7 +126,8 @@ void main() { warn("heck"); return sassNull; })) - ], logger: _TestLogger.withWarn((message, {span, trace, deprecation}) { + ], logger: _TestLogger.withWarn((message, + {required span, required trace, deprecation}) { expect(message, equals("heck")); expect(span.start.line, equals(0)); @@ -148,7 +151,8 @@ void main() { warn("heck"); return sassNull; })) - ], logger: _TestLogger.withWarn((message, {span, trace, deprecation}) { + ], logger: _TestLogger.withWarn((message, + {required span, required trace, deprecation}) { expect(message, equals("heck")); expect(span.start.line, equals(0)); @@ -173,7 +177,8 @@ void main() { warn("heck"); return sassNull; })) - ], logger: _TestLogger.withWarn((message, {span, trace, deprecation}) { + ], logger: _TestLogger.withWarn((message, + {required span, required trace, deprecation}) { expect(message, equals("heck")); expect(span.start.line, equals(0)); @@ -195,7 +200,8 @@ void main() { warn("heck"); return ImporterResult("", indented: false); }) - ], logger: _TestLogger.withWarn((message, {span, trace, deprecation}) { + ], logger: _TestLogger.withWarn((message, + {required span, required trace, deprecation}) { expect(message, equals("heck")); expect(span.start.line, equals(0)); @@ -232,7 +238,7 @@ void main() { /// A [Logger] whose [warn] and [debug] methods are provided by callbacks. class _TestLogger implements Logger { final void Function(String, - {FileSpan span, Trace trace, bool /*!*/ deprecation}) _warn; + {FileSpan? span, Trace? trace, required bool deprecation}) _warn; final void Function(String, SourceSpan) _debug; _TestLogger.withWarn(this._warn) : _debug = const Logger.stderr().debug; @@ -240,7 +246,7 @@ class _TestLogger implements Logger { _TestLogger.withDebug(this._debug) : _warn = const Logger.stderr().warn; void warn(String message, - {FileSpan span, Trace trace, bool deprecation = false}) => + {FileSpan? span, Trace? trace, bool deprecation = false}) => _warn(message, span: span, trace: trace, deprecation: deprecation); void debug(String message, SourceSpan span) => _debug(message, span); } diff --git a/test/dart_api/test_importer.dart b/test/dart_api/test_importer.dart index f84a2bb79..5dac8e0d7 100644 --- a/test/dart_api/test_importer.dart +++ b/test/dart_api/test_importer.dart @@ -8,7 +8,7 @@ import 'package:sass/sass.dart'; /// closures. class TestImporter extends Importer { final Uri Function(Uri url) _canonicalize; - final ImporterResult /*!*/ Function(Uri url) _load; + final ImporterResult Function(Uri url) _load; TestImporter(this._canonicalize, this._load); diff --git a/test/dart_api/value/boolean_test.dart b/test/dart_api/value/boolean_test.dart index 18acc71f1..606cfab7d 100644 --- a/test/dart_api/value/boolean_test.dart +++ b/test/dart_api/value/boolean_test.dart @@ -12,7 +12,7 @@ import 'utils.dart'; void main() { group("true", () { - /*late*/ Value /*!*/ value; + late Value value; setUp(() => value = parseValue("true")); test("is truthy", () { @@ -38,7 +38,7 @@ void main() { }); group("false", () { - /*late*/ Value /*!*/ value; + late Value value; setUp(() => value = parseValue("false")); test("is falsey", () { diff --git a/test/dart_api/value/color_test.dart b/test/dart_api/value/color_test.dart index 1fda28a75..c3a8ca240 100644 --- a/test/dart_api/value/color_test.dart +++ b/test/dart_api/value/color_test.dart @@ -13,7 +13,7 @@ import 'utils.dart'; void main() { group("an RGB color", () { - SassColor value; + late SassColor value; setUp(() => value = parseValue("#123456") as SassColor); test("has RGB channels", () { @@ -191,7 +191,7 @@ void main() { }); group("an HSL color", () { - SassColor value; + late SassColor value; setUp(() => value = parseValue("hsl(120, 42%, 42%)") as SassColor); test("has RGB channels", () { @@ -267,7 +267,7 @@ void main() { }); group("new SassColor.hwb()", () { - SassColor value; + late SassColor value; setUp(() => value = SassColor.hwb(120, 42, 42)); test("has RGB channels", () { diff --git a/test/dart_api/value/function_test.dart b/test/dart_api/value/function_test.dart index bca96635a..0b107b25b 100644 --- a/test/dart_api/value/function_test.dart +++ b/test/dart_api/value/function_test.dart @@ -12,7 +12,7 @@ import 'utils.dart'; void main() { group("a function value", () { - SassFunction value; + late SassFunction value; setUp(() => value = parseValue("get-function('red')") as SassFunction); test("has a callable with the given name", () { diff --git a/test/dart_api/value/list_test.dart b/test/dart_api/value/list_test.dart index 9db1dce88..b0ddcaa20 100644 --- a/test/dart_api/value/list_test.dart +++ b/test/dart_api/value/list_test.dart @@ -12,7 +12,7 @@ import 'utils.dart'; void main() { group("a comma-separated list", () { - Value value; + late Value value; setUp(() => value = parseValue("a, b, c")); test("is comma-separated", () { @@ -125,7 +125,7 @@ void main() { }); group("a single-element list", () { - Value value; + late Value value; setUp(() => value = parseValue("[1]")); test("has an undecided separator", () { @@ -152,7 +152,7 @@ void main() { }); group("an empty list", () { - Value value; + late Value value; setUp(() => value = parseValue("()")); test("has an undecided separator", () { @@ -169,7 +169,7 @@ void main() { test("counts as an empty map", () { expect(value.assertMap().contents, isEmpty); - expect(value.tryMap().contents, isEmpty); + expect(value.tryMap()!.contents, isEmpty); }); test("isn't any other type", () { @@ -191,7 +191,7 @@ void main() { }); group("a scalar value", () { - Value value; + late Value value; setUp(() => value = parseValue("blue")); test("has an undecided separator", () { diff --git a/test/dart_api/value/map_test.dart b/test/dart_api/value/map_test.dart index 23e241cb8..8c92c50a5 100644 --- a/test/dart_api/value/map_test.dart +++ b/test/dart_api/value/map_test.dart @@ -12,7 +12,7 @@ import 'utils.dart'; void main() { group("a map with contents", () { - SassMap value; + late SassMap value; setUp(() => value = parseValue("(a: b, c: d)") as SassMap); test("has an undecided separator", () { @@ -141,7 +141,7 @@ void main() { }); group("an empty map", () { - SassMap value; + late SassMap value; setUp(() => value = parseValue("map-remove((a: b), a)") as SassMap); test("has an undecided separator", () { diff --git a/test/dart_api/value/null_test.dart b/test/dart_api/value/null_test.dart index 6e3e9295d..329f45f4d 100644 --- a/test/dart_api/value/null_test.dart +++ b/test/dart_api/value/null_test.dart @@ -11,7 +11,7 @@ import 'package:sass/sass.dart'; import 'utils.dart'; void main() { - Value value; + late Value value; setUp(() => value = parseValue("null")); test("is falsey", () { diff --git a/test/dart_api/value/number_test.dart b/test/dart_api/value/number_test.dart index 6ab1138db..89e1eea46 100644 --- a/test/dart_api/value/number_test.dart +++ b/test/dart_api/value/number_test.dart @@ -14,7 +14,7 @@ import 'utils.dart'; void main() { group("a unitless integer", () { - SassNumber value; + late SassNumber value; setUp(() => value = parseValue("123") as SassNumber); test("has the correct value", () { @@ -139,7 +139,7 @@ void main() { }); group("a unitless double", () { - SassNumber value; + late SassNumber value; setUp(() => value = parseValue("123.456") as SassNumber); test("has the correct value", () { @@ -154,7 +154,7 @@ void main() { }); group("a unitless fuzzy integer", () { - SassNumber value; + late SassNumber value; setUp(() => value = parseValue("123.000000000001") as SassNumber); test("has the correct value", () { @@ -197,7 +197,7 @@ void main() { }); group("an integer with a single numerator unit", () { - SassNumber value; + late SassNumber value; setUp(() => value = parseValue("123px") as SassNumber); test("has that unit", () { @@ -312,7 +312,7 @@ void main() { }); group("an integer with numerator and denominator units", () { - SassNumber value; + late SassNumber value; setUp(() => value = parseValue("123px / 5ms") as SassNumber); test("has those units", () { diff --git a/test/dart_api/value/string_test.dart b/test/dart_api/value/string_test.dart index 3e8c3642e..f625ce8b3 100644 --- a/test/dart_api/value/string_test.dart +++ b/test/dart_api/value/string_test.dart @@ -12,7 +12,7 @@ import 'utils.dart'; void main() { group("an unquoted ASCII string", () { - SassString value; + late SassString value; setUp(() => value = parseValue("foobar") as SassString); test("has the correct text", () { @@ -125,7 +125,7 @@ void main() { }); group("a quoted ASCII string", () { - SassString value; + late SassString value; setUp(() => value = parseValue('"foobar"') as SassString); test("has the correct text", () { @@ -143,7 +143,7 @@ void main() { }); group("an unquoted Unicde", () { - SassString value; + late SassString value; setUp(() => value = parseValue("a👭b👬c") as SassString); test("sassLength returns the length", () { diff --git a/test/dart_api/value/utils.dart b/test/dart_api/value/utils.dart index 1635e034c..51ed209cd 100644 --- a/test/dart_api/value/utils.dart +++ b/test/dart_api/value/utils.dart @@ -9,7 +9,7 @@ import 'package:sass/src/exception.dart'; /// Parses [source] by way of a function call. Value parseValue(String source) { - /*late*/ Value value; + late Value value; compileString("a {b: foo(($source))}", functions: [ Callable("foo", r"$arg", expectAsync1((arguments) { expect(arguments, hasLength(1)); @@ -27,7 +27,7 @@ final throwsSassScriptException = /// Like [equals], but asserts that the hash codes of the values are the same as /// well. // TODO: no dynamic -Matcher equalsWithHash(Object expected) => predicate((actual) { +Matcher equalsWithHash(Object expected) => predicate((dynamic actual) { expect(actual, equals(expected)); expect(actual.hashCode, equals(expected.hashCode), reason: "Expected $actual's hash code to equal $expected's."); diff --git a/test/doc_comments_test.dart b/test/doc_comments_test.dart index 9abdbcb87..7e20c5311 100644 --- a/test/doc_comments_test.dart +++ b/test/doc_comments_test.dart @@ -16,7 +16,7 @@ void main() { final variable = stylesheet.children.whereType().first; - expect(variable.comment.docComment, equals('Results my vary.')); + expect(variable.comment!.docComment, equals('Results my vary.')); }); test('attach to function rules', () { @@ -29,7 +29,7 @@ void main() { final stylesheet = Stylesheet.parseScss(contents); final function = stylesheet.children.whereType().first; - expect(function.comment.docComment, equals('A fun function!')); + expect(function.comment!.docComment, equals('A fun function!')); }); test('attach to mixin rules', () { @@ -43,7 +43,7 @@ void main() { final stylesheet = Stylesheet.parseScss(contents); final mix = stylesheet.children.whereType().first; - expect(mix.comment.docComment, equals('Mysterious mixin.')); + expect(mix.comment!.docComment, equals('Mysterious mixin.')); }); test('are null when there are no triple-slash comments', () { @@ -54,7 +54,7 @@ void main() { final variable = stylesheet.children.whereType().first; - expect(variable.comment.docComment, isNull); + expect(variable.comment!.docComment, isNull); }); test('are not carried over across members', () { @@ -75,8 +75,8 @@ void main() { final mix = stylesheet.children.whereType().first; final function = stylesheet.children.whereType().first; - expect(mix.comment.docComment, equals('Mysterious mixin.')); - expect(function.comment.docComment, equals('A fun function!')); + expect(mix.comment!.docComment, equals('Mysterious mixin.')); + expect(function.comment!.docComment, equals('A fun function!')); }); test('do not include double-slash comments', () { @@ -92,7 +92,7 @@ void main() { final variable = stylesheet.children.whereType().first; - expect(variable.comment.docComment, equals('Line 1\nLine 2\nLine 3')); + expect(variable.comment!.docComment, equals('Line 1\nLine 2\nLine 3')); }); }); @@ -105,7 +105,7 @@ $vary: 5.16em'''; final variable = stylesheet.children.whereType().first; - expect(variable.comment.docComment, equals('Results my vary.')); + expect(variable.comment!.docComment, equals('Results my vary.')); }); test('attach to function rules', () { @@ -117,7 +117,7 @@ $vary: 5.16em'''; final stylesheet = Stylesheet.parseSass(contents); final function = stylesheet.children.whereType().first; - expect(function.comment.docComment, equals('A fun function!')); + expect(function.comment!.docComment, equals('A fun function!')); }); test('attach to mixin rules', () { @@ -130,7 +130,7 @@ $vary: 5.16em'''; final stylesheet = Stylesheet.parseSass(contents); final mix = stylesheet.children.whereType().first; - expect(mix.comment.docComment, equals('Mysterious mixin.')); + expect(mix.comment!.docComment, equals('Mysterious mixin.')); }); test('are null when there are no triple-slash comments', () { @@ -141,7 +141,7 @@ $vary: 5.16em'''; final variable = stylesheet.children.whereType().first; - expect(variable.comment.docComment, isNull); + expect(variable.comment!.docComment, isNull); }); test('are not carried over across members', () { @@ -160,8 +160,8 @@ $vary: 5.16em'''; final mix = stylesheet.children.whereType().first; final function = stylesheet.children.whereType().first; - expect(mix.comment.docComment, equals('Mysterious mixin.')); - expect(function.comment.docComment, equals('A fun function!')); + expect(mix.comment!.docComment, equals('Mysterious mixin.')); + expect(function.comment!.docComment, equals('A fun function!')); }); test('do not include double-slash comments', () { @@ -176,7 +176,7 @@ $vary: 5.16em'''; final variable = stylesheet.children.whereType().first; - expect(variable.comment.docComment, equals('Line 1\nLine 2')); + expect(variable.comment!.docComment, equals('Line 1\nLine 2')); }); test('are compacted into one from adjacent comments', () { @@ -192,7 +192,7 @@ $vary: 5.16em'''; stylesheet.children.whereType().first; expect(stylesheet.children.length, equals(2)); - expect(variable.comment.docComment, + expect(variable.comment!.docComment, equals('Line 1\nLine 2\nLine 3\nLine 4')); }); }); diff --git a/test/double_check_test.dart b/test/double_check_test.dart index fd26193d8..fa10d1feb 100644 --- a/test/double_check_test.dart +++ b/test/double_check_test.dart @@ -25,7 +25,7 @@ void main() { "Run pub run grinder to update it."; var target = File(targetPath).readAsStringSync(); - var match = checksumPattern.firstMatch(target); // TODO: no ! + var match = checksumPattern.firstMatch(target)!; // TODO: no ! if (match == null) fail(message); var source = File(sourcePath).readAsBytesSync(); @@ -48,7 +48,7 @@ void main() { var pubspec = loadYaml(File("pubspec.yaml").readAsStringSync(), sourceUrl: Uri(path: "pubspec.yaml")) as Map; expect(pubspec, containsPair("version", isA())); - var pubspecVersion = pubspec["version"] as String /*!*/; + var pubspecVersion = pubspec["version"] as String; expect(pubspecVersion, anyOf(equals(changelogVersion), equals("$changelogVersion-dev"))); diff --git a/test/hybrid.dart b/test/hybrid.dart index a40f12d95..004cd0a75 100644 --- a/test/hybrid.dart +++ b/test/hybrid.dart @@ -8,8 +8,7 @@ import 'package:test/test.dart'; /// Creates a directory in the system temp directory and returns its path. Future createTempDir() async => (await runHybridExpression( - '(await Directory.systemTemp.createTemp("dart_sass_")).path')) - as String /*!*/; + '(await Directory.systemTemp.createTemp("dart_sass_")).path')) as String; /// Writes [text] to [path]. Future writeTextFile(String path, String text) => runHybridExpression( @@ -26,7 +25,8 @@ Future deleteDirectory(String path) => /// Runs [expression], which may be asynchronous, in a hybrid isolate. /// /// Returns the result of [expression] if it's JSON-serializable. -Future runHybridExpression(String expression, [Object message]) async { +Future runHybridExpression(String expression, + [Object? message]) async { var channel = spawnHybridCode(''' import 'dart:async'; import 'dart:convert'; diff --git a/test/node_api/api.dart b/test/node_api/api.dart index 772bed676..f446a5d70 100644 --- a/test/node_api/api.dart +++ b/test/node_api/api.dart @@ -117,9 +117,9 @@ class NodeSassList { class NodeSassMap { external Constructor get constructor; external Object getValue(int index); - external void setValue(int index, Object value); + external void setValue(int index, Object? value); external Object getKey(int index); - external void setKey(int index, Object value); + external void setKey(int index, Object? value); external int getLength(); } diff --git a/test/node_api/function_test.dart b/test/node_api/function_test.dart index 8eee066ff..2b7cdbfd3 100644 --- a/test/node_api/function_test.dart +++ b/test/node_api/function_test.dart @@ -131,8 +131,8 @@ void main() { renderSync(RenderOptions( data: "a {b: last(1px, 2em)}", functions: jsify({ - r"last($value1, $value2)": - allowInterop(expectAsync2((value1, value2) => value2)) + r"last($value1, $value2)": allowInterop( + expectAsync2((dynamic value1, dynamic value2) => value2)) }))), equalsIgnoringWhitespace("a { b: 2em; }")); }); @@ -142,8 +142,8 @@ void main() { renderSync(RenderOptions( data: r"a {b: last($value2: 1px, $value1: 2em)}", functions: jsify({ - r"last($value1, $value2)": - allowInterop(expectAsync2((value1, value2) => value2)) + r"last($value1, $value2)": allowInterop( + expectAsync2((dynamic value1, dynamic value2) => value2)) }))), equalsIgnoringWhitespace("a { b: 1px; }")); }); @@ -153,8 +153,8 @@ void main() { renderSync(RenderOptions( data: "a {b: last((1px 2em)...)}", functions: jsify({ - r"last($value1, $value2)": - allowInterop(expectAsync2((value1, value2) => value2)) + r"last($value1, $value2)": allowInterop( + expectAsync2((dynamic value1, dynamic value2) => value2)) }))), equalsIgnoringWhitespace("a { b: 2em; }")); }); @@ -188,7 +188,7 @@ void main() { }); group('this', () { - String sassPath; + late String sassPath; setUp(() async { sassPath = p.join(sandbox, 'test.scss'); }); @@ -312,7 +312,7 @@ void main() { data: 'a {b: foo()}', functions: jsify({ 'foo': allowInteropCaptureThis(expectAsync1((RenderContext this_) { - expect(this_.options.result.stats.start, + expect(this_.options.result!.stats!.start, greaterThanOrEqualTo(start.millisecondsSinceEpoch)); return callConstructor(sass.types.Number, [12]); })) @@ -325,7 +325,7 @@ void main() { data: 'a {b: foo()}', functions: jsify({ 'foo': allowInteropCaptureThis(expectAsync1((RenderContext this_) { - expect(this_.options.result.stats.entry, equals('data')); + expect(this_.options.result!.stats!.entry, equals('data')); return callConstructor(sass.types.Number, [12]); })) }), @@ -338,7 +338,7 @@ void main() { file: sassPath, functions: jsify({ 'foo': allowInteropCaptureThis(expectAsync1((RenderContext this_) { - expect(this_.options.result.stats.entry, equals(sassPath)); + expect(this_.options.result!.stats!.entry, equals(sassPath)); return callConstructor(sass.types.Number, [12]); })) }), @@ -371,7 +371,7 @@ void main() { render(RenderOptions( data: "a {b: foo()}", functions: jsify({ - "foo": allowInterop((void done(Object result)) { + "foo": allowInterop((void done(Object? result)) { Timer(Duration.zero, () { done(callConstructor(sass.types.Number, [1])); }); @@ -415,7 +415,7 @@ void main() { var error = await renderError(RenderOptions( data: "a {b: foo()}", functions: jsify({ - "foo": allowInterop((void done(Object result)) { + "foo": allowInterop((void done(Object? result)) { Timer(Duration.zero, () { done(callConstructor(sass.types.Error, ["aw beans"])); }); @@ -428,7 +428,7 @@ void main() { var error = await renderError(RenderOptions( data: "a {b: foo()}", functions: jsify({ - "foo": allowInterop((void done(Object result)) { + "foo": allowInterop((void done(Object? result)) { Timer(Duration.zero, () { done(null); }); @@ -483,7 +483,7 @@ void main() { render(RenderOptions( data: "a {b: foo()}", functions: jsify({ - "foo": allowInterop((void done(Object result)) { + "foo": allowInterop((void done(Object? result)) { Timer(Duration.zero, () { done(callConstructor(sass.types.Number, [1])); }); @@ -520,7 +520,7 @@ void main() { var error = await renderError(RenderOptions( data: "a {b: foo()}", functions: jsify({ - "foo": allowInterop((void done(Object result)) { + "foo": allowInterop((void done(Object? result)) { Timer(Duration.zero, () { done(null); }); @@ -553,7 +553,8 @@ void main() { renderSync(RenderOptions( data: "a {b: call(id(get-function('str-length')), 'foo')}", functions: jsify({ - r"id($value)": allowInterop(expectAsync1((value) => value)) + r"id($value)": + allowInterop(expectAsync1((dynamic value) => value)) }))), equalsIgnoringWhitespace("a { b: 3; }")); }); diff --git a/test/node_api/importer_test.dart b/test/node_api/importer_test.dart index 2d4f9f18c..1677da9fd 100644 --- a/test/node_api/importer_test.dart +++ b/test/node_api/importer_test.dart @@ -27,7 +27,7 @@ void main() { setUpAll(ensureNpmPackage); useSandbox(); - /*late*/ String sassPath; + late String sassPath; setUp(() async { sassPath = p.join(sandbox, 'test.scss'); @@ -141,7 +141,7 @@ void main() { importer: allowInterop(expectAsync2((void _, void __) { return NodeImporterResult(contents: '', file: 'bar'); })))); - expect(result.stats.includedFiles, equals(['bar'])); + expect(result.stats!.includedFiles, equals(['bar'])); }); }); @@ -244,7 +244,7 @@ void main() { file: basePath, importer: allowInterop( (void _, void __) => NodeImporterResult(file: 'test')))); - expect(result.stats.includedFiles, equals([basePath, sassPath])); + expect(result.stats!.includedFiles, equals([basePath, sassPath])); }); test("is resolved relative to include paths", () async { @@ -276,7 +276,7 @@ void main() { }); group("in the sandbox directory", () { - String oldWorkingDirectory; + late String oldWorkingDirectory; setUp(() { oldWorkingDirectory = currentPath; process.chdir(sandbox); @@ -329,7 +329,7 @@ void main() { test("is the exact imported text", () { renderSync(RenderOptions( data: "@import 'foo'", - importer: allowInterop(expectAsync2((url, _) { + importer: allowInterop(expectAsync2((dynamic url, dynamic _) { expect(url, equals('foo')); return NodeImporterResult(contents: ''); })))); @@ -339,7 +339,7 @@ void main() { test("doesn't remove ./", () { renderSync(RenderOptions( data: "@import './foo'", - importer: allowInterop(expectAsync2((url, _) { + importer: allowInterop(expectAsync2((dynamic url, dynamic _) { expect(url, equals('./foo')); return NodeImporterResult(contents: ''); })))); @@ -348,7 +348,7 @@ void main() { test("isn't resolved relative to the current file", () { renderSync(RenderOptions( data: "@import 'foo/bar'", - importer: allowInterop(expectAsync2((url, _) { + importer: allowInterop(expectAsync2((dynamic url, dynamic _) { if (url == 'foo/bar') { return NodeImporterResult(contents: "@import 'baz'"); } else { @@ -364,7 +364,7 @@ void main() { importer: allowInterop(expectAsync2((void _, void __) { return NodeImporterResult(contents: ''); })))); - expect(result.stats.includedFiles, equals(['foo'])); + expect(result.stats!.includedFiles, equals(['foo'])); }); }); @@ -375,7 +375,7 @@ void main() { renderSync(RenderOptions( file: importPath, - importer: allowInterop(expectAsync2((_, prev) { + importer: allowInterop(expectAsync2((dynamic _, dynamic prev) { expect(prev, equals(p.absolute(importPath))); return NodeImporterResult(contents: ''); })))); @@ -391,7 +391,7 @@ void main() { renderSync(RenderOptions( file: import1Path, - importer: allowInterop(expectAsync2((url, prev) { + importer: allowInterop(expectAsync2((dynamic url, dynamic prev) { if (url == 'foo') { return NodeImporterResult(file: 'import2'); } else { @@ -405,7 +405,7 @@ void main() { test('is "stdin" for string stylesheets', () async { renderSync(RenderOptions( data: '@import "foo"', - importer: allowInterop(expectAsync2((_, prev) { + importer: allowInterop(expectAsync2((dynamic _, dynamic prev) { expect(prev, equals('stdin')); return NodeImporterResult(contents: ''); })))); @@ -413,11 +413,11 @@ void main() { test("is the imported string for imports from importers", () async { renderSync(RenderOptions(data: '@import "foo"', importer: [ - allowInterop(expectAsync2((url, _) { + allowInterop(expectAsync2((dynamic url, dynamic _) { if (url != "foo") return null; return NodeImporterResult(contents: '@import "bar"'); }, count: 2)), - allowInterop(expectAsync2((url, prev) { + allowInterop(expectAsync2((dynamic url, dynamic prev) { expect(url, equals("bar")); expect(prev, equals("foo")); return NodeImporterResult(contents: ''); @@ -431,7 +431,7 @@ void main() { renderSync(RenderOptions( data: '@import "foo"', importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, _, __) { + expectAsync3((RenderContext this_, dynamic _, dynamic __) { var options = this_.options; expect(options.includePaths, equals(p.current)); expect(options.precision, equals(SassNumber.precision)); @@ -448,7 +448,7 @@ void main() { renderSync(RenderOptions( data: '@import "foo"', importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, _, __) { + expectAsync3((RenderContext this_, dynamic _, dynamic __) { expect(this_.options.data, equals('@import "foo"')); expect(this_.options.file, isNull); return NodeImporterResult(contents: ''); @@ -460,7 +460,7 @@ void main() { renderSync(RenderOptions( file: sassPath, importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, _, __) { + expectAsync3((RenderContext this_, dynamic _, dynamic __) { expect(this_.options.data, isNull); expect(this_.options.file, equals(sassPath)); return NodeImporterResult(contents: ''); @@ -472,7 +472,7 @@ void main() { data: '@import "foo"', includePaths: [sandbox], importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, _, __) { + expectAsync3((RenderContext this_, dynamic _, dynamic __) { expect(this_.options.includePaths, equals("${p.current}${isWindows ? ';' : ':'}$sandbox")); return NodeImporterResult(contents: ''); @@ -485,7 +485,7 @@ void main() { data: '@import "foo"', indentWidth: 5, importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, _, __) { + expectAsync3((RenderContext this_, dynamic _, dynamic __) { expect(this_.options.indentWidth, equals(5)); return NodeImporterResult(contents: ''); })))); @@ -496,7 +496,7 @@ void main() { data: '@import "foo"', indentType: 'tab', importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, _, __) { + expectAsync3((RenderContext this_, dynamic _, dynamic __) { expect(this_.options.indentType, equals(1)); return NodeImporterResult(contents: ''); })))); @@ -507,7 +507,7 @@ void main() { data: '@import "foo"', linefeed: 'cr', importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, _, __) { + expectAsync3((RenderContext this_, dynamic _, dynamic __) { expect(this_.options.linefeed, equals('\r')); return NodeImporterResult(contents: ''); })))); @@ -518,7 +518,7 @@ void main() { renderSync(RenderOptions( data: '@import "foo"', importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, _, __) { + expectAsync3((RenderContext this_, dynamic _, dynamic __) { expect(this_.options.context, same(this_)); return NodeImporterResult(contents: ''); })))); @@ -530,8 +530,8 @@ void main() { renderSync(RenderOptions( data: '@import "foo"', importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, _, __) { - expect(this_.options.result.stats.start, + expectAsync3((RenderContext this_, dynamic _, dynamic __) { + expect(this_.options.result!.stats!.start, greaterThanOrEqualTo(start.millisecondsSinceEpoch)); return NodeImporterResult(contents: ''); })))); @@ -541,8 +541,8 @@ void main() { renderSync(RenderOptions( data: '@import "foo"', importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, _, __) { - expect(this_.options.result.stats.entry, equals('data')); + expectAsync3((RenderContext this_, dynamic _, dynamic __) { + expect(this_.options.result!.stats!.entry, equals('data')); return NodeImporterResult(contents: ''); })))); }); @@ -552,8 +552,8 @@ void main() { renderSync(RenderOptions( file: sassPath, importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, _, __) { - expect(this_.options.result.stats.entry, equals(sassPath)); + expectAsync3((RenderContext this_, dynamic _, dynamic __) { + expect(this_.options.result!.stats!.entry, equals(sassPath)); return NodeImporterResult(contents: ''); })))); }); diff --git a/test/node_api/intercept_stdout.dart b/test/node_api/intercept_stdout.dart index 54a8b76a7..dad528fcf 100644 --- a/test/node_api/intercept_stdout.dart +++ b/test/node_api/intercept_stdout.dart @@ -7,7 +7,7 @@ import 'dart:async'; import 'package:js/js.dart'; typedef _InterceptStdout = void Function() Function( - String Function(String), String Function(String)); + String Function(String)?, String Function(String)); @JS('require') external _InterceptStdout _require(String name); @@ -19,8 +19,8 @@ final _interceptStdout = _require("intercept-stdout"); /// /// Note that the piped text is not necessarily separated by lines. Stream interceptStderr() { - void Function() unhook; - StreamController controller; + late void Function() unhook; + late StreamController controller; controller = StreamController(onListen: () { unhook = _interceptStdout(null, allowInterop((text) { controller.add(text); diff --git a/test/node_api/source_map_test.dart b/test/node_api/source_map_test.dart index fcd506843..26fc3e310 100644 --- a/test/node_api/source_map_test.dart +++ b/test/node_api/source_map_test.dart @@ -28,17 +28,17 @@ void main() { useSandbox(); group("a basic invocation", () { - String css; - /*late*/ Map /*!*/ map; + late String css; + late Map map; setUp(() { var result = sass.renderSync( RenderOptions(data: "a {b: c}", sourceMap: true, outFile: "out.css")); - css = utf8.decode(result.css); + css = utf8.decode(result.css!); map = _jsonUtf8.decode(result.map) as Map; }); test("includes correct mappings", () { - /*late*/ SingleMapping /*!*/ expectedMap; + late SingleMapping expectedMap; dart_sass.compileString("a {b: c}", sourceMap: (map) => expectedMap = map); expectedMap.targetUrl = "out.css"; @@ -136,21 +136,21 @@ void main() { var result = sass.renderSync(RenderOptions(data: "a {b: c}", outFile: "out.css")); expect(result.map, isNull); - expect(utf8.decode(result.css), isNot(contains("/*#"))); + expect(utf8.decode(result.css!), isNot(contains("/*#"))); }); test("with sourceMap: false", () { var result = sass.renderSync(RenderOptions( data: "a {b: c}", sourceMap: false, outFile: "out.css")); expect(result.map, isNull); - expect(utf8.decode(result.css), isNot(contains("/*#"))); + expect(utf8.decode(result.css!), isNot(contains("/*#"))); }); test("without outFile", () { var result = sass.renderSync(RenderOptions(data: "a {b: c}", sourceMap: true)); expect(result.map, isNull); - expect(utf8.decode(result.css), isNot(contains("/*#"))); + expect(utf8.decode(result.css!), isNot(contains("/*#"))); }); }); @@ -158,7 +158,7 @@ void main() { test("emits a source map", () { var result = sass.renderSync( RenderOptions(data: "a {b: c}", sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map) as Map /*!*/; + var map = _jsonUtf8.decode(result.map) as Map; expect(map, containsPair("sources", ["stdin"])); }); @@ -168,7 +168,7 @@ void main() { var result = sass.renderSync(RenderOptions( file: p.join(sandbox, "test.scss"), sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map) as Map /*!*/; + var map = _jsonUtf8.decode(result.map) as Map; expect( map, containsPair( @@ -182,7 +182,7 @@ void main() { var result = sass.renderSync(RenderOptions( file: p.join(sandbox, "test"), sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map) as Map /*!*/; + var map = _jsonUtf8.decode(result.map) as Map; expect( map, containsPair( @@ -192,7 +192,7 @@ void main() { test("derives the target URL from stdin", () { var result = sass.renderSync( RenderOptions(data: "a {b: c}", sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map) as Map /*!*/; + var map = _jsonUtf8.decode(result.map) as Map; expect(map, containsPair("file", "stdin.css")); }); @@ -219,7 +219,7 @@ void main() { outFile: "out.css", omitSourceMapUrl: true)); expect(result.map, isNotNull); - expect(utf8.decode(result.css), isNot(contains("/*#"))); + expect(utf8.decode(result.css!), isNot(contains("/*#"))); }); group("with a string sourceMap", () { @@ -227,15 +227,15 @@ void main() { var result = sass.renderSync(RenderOptions( data: "a {b: c}", sourceMap: "map", outFile: "out.css")); expect(result.map, isNotNull); - expect( - utf8.decode(result.css), endsWith("\n\n/*# sourceMappingURL=map */")); + expect(utf8.decode(result.css!), + endsWith("\n\n/*# sourceMappingURL=map */")); }); test("makes the source map comment relative to the outfile", () { var result = sass.renderSync(RenderOptions( data: "a {b: c}", sourceMap: "map", outFile: "dir/out.css")); expect(result.map, isNotNull); - expect(utf8.decode(result.css), + expect(utf8.decode(result.css!), endsWith("\n\n/*# sourceMappingURL=../map */")); }); @@ -250,8 +250,8 @@ void main() { var result = sass.renderSync(RenderOptions( data: "a {b: c}", sourceMap: p.absolute("map"), outFile: "out.css")); expect(result.map, isNotNull); - expect( - utf8.decode(result.css), endsWith("\n\n/*# sourceMappingURL=map */")); + expect(utf8.decode(result.css!), + endsWith("\n\n/*# sourceMappingURL=map */")); }); test("makes the sources list relative to the map location", () async { @@ -300,7 +300,7 @@ void main() { outFile: "out.css", sourceMapEmbed: true)); - var map = embeddedSourceMap(utf8.decode(result.css)); + var map = embeddedSourceMap(utf8.decode(result.css!)); expect(map, equals(_jsonUtf8.decode(result.map))); }); @@ -332,4 +332,4 @@ void main() { /// Renders [options] and returns the decoded source map. Map _renderSourceMap(RenderOptions options) => - _jsonUtf8.decode(sass.renderSync(options).map) as Map /*!*/; + _jsonUtf8.decode(sass.renderSync(options).map) as Map; diff --git a/test/node_api/utils.dart b/test/node_api/utils.dart index 5a830fa70..0ccff7174 100644 --- a/test/node_api/utils.dart +++ b/test/node_api/utils.dart @@ -27,7 +27,7 @@ String get sandbox { "field."); } -String _sandbox; +String? _sandbox; void useSandbox() { setUp(() async { @@ -41,7 +41,7 @@ void useSandbox() { /// Validates that a [RenderError]'s `toString()` and `message` both equal /// [text]. -Matcher toStringAndMessageEqual(String text) => predicate((error) { +Matcher toStringAndMessageEqual(String text) => predicate((dynamic error) { expect(error.toString(), equals("Error: $text")); expect(error.message, equals(text)); expect(error.formatted, equals("Error: $text")); @@ -54,7 +54,7 @@ Future render(RenderOptions options) { sass.render(options, allowInterop(Zone.current.bindBinaryCallbackGuarded((error, result) { expect(error, isNull); - completer.complete(utf8.decode(result.css)); + completer.complete(utf8.decode(result.css!)); }))); return completer.future; } @@ -73,7 +73,7 @@ Future renderError(RenderOptions options) { /// Returns the result of rendering via [options] as a string. String renderSync(RenderOptions options) => - utf8.decode(sass.renderSync(options).css); + utf8.decode(sass.renderSync(options).css!); /// Like [renderSync], but goes through the untyped JS API. /// @@ -81,7 +81,7 @@ String renderSync(RenderOptions options) => /// type errors. String renderSyncJS(Map options) { var result = _renderSyncJS.call(sass, jsify(options)) as RenderResult; - return utf8.decode(result.css); + return utf8.decode(result.css!); } final _renderSyncJS = @@ -111,7 +111,7 @@ void runTestInSandbox() { } /// Sets the environment variable [name] to [value] within this process. -void setEnvironmentVariable(String name, String value) { +void setEnvironmentVariable(String name, String? value) { setProperty(_environment, name, value); } diff --git a/test/node_api/value/color_test.dart b/test/node_api/value/color_test.dart index 0ba05979c..012ef1909 100644 --- a/test/node_api/value/color_test.dart +++ b/test/node_api/value/color_test.dart @@ -18,7 +18,7 @@ import 'utils.dart'; void main() { group("from a parameter", () { - NodeSassColor color; + late NodeSassColor color; setUp(() { color = parseValue("rgba(42, 84, 126, 0.42)"); }); diff --git a/test/node_api/value/list_test.dart b/test/node_api/value/list_test.dart index 8865fdc77..20129d847 100644 --- a/test/node_api/value/list_test.dart +++ b/test/node_api/value/list_test.dart @@ -16,7 +16,7 @@ import 'utils.dart'; void main() { group("an argument list", () { - /*late*/ NodeSassList args; + late NodeSassList args; setUp(() { renderSync(RenderOptions( data: "a {b: foo(1, 'a', blue)}", @@ -47,7 +47,7 @@ void main() { group("a list", () { group("from a parameter", () { - NodeSassList list; + late NodeSassList list; setUp(() { list = parseValue("1, 'a', blue"); }); diff --git a/test/node_api/value/map_test.dart b/test/node_api/value/map_test.dart index 973bc9c63..c80c50bd3 100644 --- a/test/node_api/value/map_test.dart +++ b/test/node_api/value/map_test.dart @@ -16,7 +16,7 @@ import 'utils.dart'; void main() { group("from a parameter", () { - NodeSassMap map; + late NodeSassMap map; setUp(() { map = parseValue("(a: 2, 1: blue, red: b)"); }); diff --git a/test/node_api/value/null_test.dart b/test/node_api/value/null_test.dart index c0c14b49d..a13320d21 100644 --- a/test/node_api/value/null_test.dart +++ b/test/node_api/value/null_test.dart @@ -14,7 +14,7 @@ import 'utils.dart'; void main() { group("from a parameter", () { - NodeSassNull value; + late NodeSassNull value; setUp(() { value = parseValue("null"); }); diff --git a/test/node_api/value/number_test.dart b/test/node_api/value/number_test.dart index 64929a2fb..a431baac6 100644 --- a/test/node_api/value/number_test.dart +++ b/test/node_api/value/number_test.dart @@ -16,7 +16,7 @@ import 'utils.dart'; void main() { group("from a parameter", () { - NodeSassNumber number; + late NodeSassNumber number; setUp(() { number = parseValue("1px"); }); diff --git a/test/node_api/value/string_test.dart b/test/node_api/value/string_test.dart index 321c39858..e385646b3 100644 --- a/test/node_api/value/string_test.dart +++ b/test/node_api/value/string_test.dart @@ -16,7 +16,7 @@ import 'utils.dart'; void main() { group("from a parameter", () { - NodeSassString string; + late NodeSassString string; setUp(() { string = parseValue("abc"); }); diff --git a/test/node_api/value/utils.dart b/test/node_api/value/utils.dart index 2c900db37..6a3dc8ff2 100644 --- a/test/node_api/value/utils.dart +++ b/test/node_api/value/utils.dart @@ -14,7 +14,7 @@ import '../utils.dart'; /// Parses [source] by way of a function call. T parseValue(String source) { - /*late*/ T value; + late T value; renderSync(RenderOptions( data: "a {b: foo(($source))}", functions: jsify({ @@ -29,5 +29,5 @@ T parseValue(String source) { /// A matcher that matches values that are JS `instanceof` [type]. Matcher isJSInstanceOf(Object type) => predicate( // TODO: no dynamic - (value) => jsInstanceOf(value, type), + (dynamic value) => jsInstanceOf(value, type), "to be an instance of $type"); diff --git a/test/node_api_test.dart b/test/node_api_test.dart index ede14ca27..6c4fd490b 100644 --- a/test/node_api_test.dart +++ b/test/node_api_test.dart @@ -24,7 +24,7 @@ void main() { setUpAll(ensureNpmPackage); useSandbox(); - /*late*/ String sassPath; + late String sassPath; setUp(() async { sassPath = p.join(sandbox, 'test.scss'); @@ -302,31 +302,31 @@ a { group("the result object", () { test("includes the filename", () { var result = sass.renderSync(RenderOptions(file: sassPath)); - expect(result.stats.entry, equals(sassPath)); + expect(result.stats!.entry, equals(sassPath)); }); test("includes data without a filename", () { var result = sass.renderSync(RenderOptions(data: 'a {b: c}')); - expect(result.stats.entry, equals('data')); + expect(result.stats!.entry, equals('data')); }); test("includes timing information", () { - var stats = sass.renderSync(RenderOptions(file: sassPath)).stats /*!*/; + var stats = sass.renderSync(RenderOptions(file: sassPath)).stats!; expect(stats.start, const TypeMatcher()); expect(stats.end, const TypeMatcher()); expect(stats.start, lessThanOrEqualTo(stats.end)); - expect(stats.duration, equals(stats.end - stats.start)); + expect(stats.duration, equals(stats.end! - stats.start!)); }); group("has includedFiles which", () { test("contains the root path if available", () { var result = sass.renderSync(RenderOptions(file: sassPath)); - expect(result.stats.includedFiles, equals([sassPath])); + expect(result.stats!.includedFiles, equals([sassPath])); }); test("doesn't contain the root path if it's not available", () { var result = sass.renderSync(RenderOptions(data: 'a {b: c}')); - expect(result.stats.includedFiles, isEmpty); + expect(result.stats!.includedFiles, isEmpty); }); test("contains imported paths", () async { @@ -334,7 +334,7 @@ a { await writeTextFile(importerPath, '@import "test"'); var result = sass.renderSync(RenderOptions(file: importerPath)); - expect(result.stats.includedFiles, + expect(result.stats!.includedFiles, unorderedEquals([importerPath, sassPath])); }); @@ -343,14 +343,14 @@ a { await writeTextFile(importerPath, '@import "test"; @import "test";'); var result = sass.renderSync(RenderOptions(file: importerPath)); - expect(result.stats.includedFiles, + expect(result.stats!.includedFiles, unorderedEquals([importerPath, sassPath])); }); }); }); group("the error object", () { - /*late*/ RenderError error; + late RenderError error; group("for a parse error in a file", () { setUp(() async { await writeTextFile(sassPath, "a {b: }"); diff --git a/test/source_map_test.dart b/test/source_map_test.dart index ca557c48b..044ce54ef 100644 --- a/test/source_map_test.dart +++ b/test/source_map_test.dart @@ -681,10 +681,10 @@ void main() { $map: (a: b); x {y: $map} """, sourceMap: (_) {}); - }, throwsA(predicate((untypedError) { + }, throwsA(predicate((dynamic untypedError) { // TODO: no dynamic var error = untypedError as SourceSpanException; - expect(error.span.text, equals(r"$map")); + expect(error.span!.text, equals(r"$map")); return true; }))); }); @@ -711,14 +711,14 @@ void main() { /// /// This also re-indents the input strings with [_reindent]. void _expectSourceMap(String sass, String scss, String css, - {Importer importer, OutputStyle style}) { + {Importer? importer, OutputStyle? style}) { _expectSassSourceMap(sass, css, importer: importer, style: style); _expectScssSourceMap(scss, css, importer: importer, style: style); } /// Like [_expectSourceMap], but with only SCSS source. void _expectScssSourceMap(String scss, String css, - {Importer importer, OutputStyle style}) { + {Importer? importer, OutputStyle? style}) { var scssTuple = _extractLocations(_reindent(scss)); var scssText = scssTuple.item1; var scssLocations = _tuplesToMap(scssTuple.item2); @@ -727,7 +727,7 @@ void _expectScssSourceMap(String scss, String css, var cssText = cssTuple.item1; var cssLocations = cssTuple.item2; - SingleMapping scssMap; + late SingleMapping scssMap; var scssOutput = compileString(scssText, sourceMap: (map) => scssMap = map, importer: importer, style: style); expect(scssOutput, equals(cssText)); @@ -736,7 +736,7 @@ void _expectScssSourceMap(String scss, String css, /// Like [_expectSourceMap], but with only indented source. void _expectSassSourceMap(String sass, String css, - {Importer importer, OutputStyle style}) { + {Importer? importer, OutputStyle? style}) { var sassTuple = _extractLocations(_reindent(sass)); var sassText = sassTuple.item1; var sassLocations = _tuplesToMap(sassTuple.item2); @@ -745,7 +745,7 @@ void _expectSassSourceMap(String sass, String css, var cssText = cssTuple.item1; var cssLocations = cssTuple.item2; - SingleMapping sassMap; + late SingleMapping sassMap; var sassOutput = compileString(sassText, indented: true, sourceMap: (map) => sassMap = map, @@ -831,7 +831,7 @@ void _expectMapMatches( for (var tuple in targetLocations) { var name = tuple.item1; var expectedTarget = tuple.item2; - var expectedSource = sourceLocations[name]; + var expectedSource = sourceLocations[name]!; if (!entryIter.moveNext()) { fail('Missing mapping "$name", expected ' diff --git a/test/utils.dart b/test/utils.dart index a032bcba2..dd228516e 100644 --- a/test/utils.dart +++ b/test/utils.dart @@ -26,10 +26,10 @@ Future get tick => Map embeddedSourceMap(String css) { expect(css, matches(_sourceMapCommentRegExp)); - var match = _sourceMapCommentRegExp.firstMatch(css); - var data = Uri.parse(match[1]).data; + var match = _sourceMapCommentRegExp.firstMatch(css)!; + var data = Uri.parse(match[1]!).data!; expect(data.mimeType, equals("application/json")); - return jsonDecode(data.contentAsString()) as Map /*!*/; + return jsonDecode(data.contentAsString()) as Map; } // Like `p.prettyUri()`, but for a non-URL path. diff --git a/tool/grind.dart b/tool/grind.dart index 04d6ee196..ea53f7699 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -33,8 +33,8 @@ void main(List args) { as Map; pkg.npmReadme.fn = () => _readAndResolveMarkdown("package/README.npm.md"); pkg.standaloneName.value = "dart-sass"; - pkg.githubUser.fn = () => Platform.environment["GH_USER"] /*!*/; - pkg.githubPassword.fn = () => Platform.environment["GH_TOKEN"] /*!*/; + pkg.githubUser.fn = () => Platform.environment["GH_USER"]!; + pkg.githubPassword.fn = () => Platform.environment["GH_TOKEN"]!; pkg.githubReleaseNotes.fn = () => "To install Sass ${pkg.version}, download one of the packages below " @@ -110,7 +110,7 @@ final _readAndResolveRegExp = RegExp( String _readAndResolveMarkdown(String path) => File(path) .readAsStringSync() .replaceAllMapped(_readAndResolveRegExp, (match) { - String included; + late String included; try { included = File(p.join(p.dirname(path), p.fromUri(match[1]))) .readAsStringSync(); @@ -118,7 +118,7 @@ String _readAndResolveMarkdown(String path) => File(path) _matchError(match, error.toString(), url: p.toUri(path)); } - Match headerMatch; + late Match headerMatch; try { headerMatch = "# ${match[2]}".allMatches(included).first; } on StateError { @@ -141,7 +141,7 @@ String _readAndResolveMarkdown(String path) => File(path) }); /// Throws a nice [SourceSpanException] associated with [match]. -void _matchError(Match match, String message, {Object url}) { +void _matchError(Match match, String message, {Object? url}) { var file = SourceFile.fromString(match.input, url: url); throw SourceSpanException(message, file.span(match.start, match.end)); } diff --git a/tool/grind/benchmark.dart b/tool/grind/benchmark.dart index 6ad412b67..2cf7159a4 100644 --- a/tool/grind/benchmark.dart +++ b/tool/grind/benchmark.dart @@ -71,7 +71,7 @@ Future benchmarkGenerate() async { /// it's written after [text]. If the file already exists and is the expected /// length, it's not written. Future _writeNTimes(String path, String text, num times, - {String header, String footer}) async { + {String? header, String? footer}) async { var file = File(path); var expectedLength = (header == null ? 0 : header.length + 1) + (text.length + 1) * times + @@ -183,7 +183,7 @@ I ran five instances of each configuration and recorded the fastest time. buffer.writeln("Running on a file containing $description:"); buffer.writeln(); - Duration sasscTime; + Duration? sasscTime; if (!libsassIncompatible.contains(info[1])) { sasscTime = await _benchmark(p.join(sassc, 'bin', 'sassc'), [path]); buffer.writeln("* sassc: ${_formatTime(sasscTime)}"); // TODO: no ! @@ -246,7 +246,7 @@ Future _benchmark(String executable, List arguments) async { // chance to warm up at the OS level. await _benchmarkOnce(executable, arguments); - /*late*/ Duration lowest; + late Duration lowest; for (var i = 0; i < 5; i++) { var duration = await _benchmarkOnce(executable, arguments); if (lowest == null || duration < lowest) lowest = duration; @@ -264,15 +264,15 @@ Future _benchmarkOnce( } var match = RegExp(r"(\d+)m(\d+)\.(\d+)s") - .firstMatch(result.stderr as String); // TODO: no ! + .firstMatch(result.stderr as String)!; // TODO: no ! if (match == null) { fail("Process didn't print the expected format:\n${result.stderr}"); } return Duration( - minutes: int.parse(match[1]), - seconds: int.parse(match[2]), - milliseconds: int.parse(match[3])); + minutes: int.parse(match[1]!), + seconds: int.parse(match[2]!), + milliseconds: int.parse(match[3]!)); } String _formatTime(Duration duration) => diff --git a/tool/grind/synchronize.dart b/tool/grind/synchronize.dart index acea62dab..452e16df4 100644 --- a/tool/grind/synchronize.dart +++ b/tool/grind/synchronize.dart @@ -197,7 +197,7 @@ class _Visitor extends RecursiveAstVisitor { _buffer.write("void"); } } else if (node.name.name == "Module") { - _skip(node.name.beginToken); + _skipNode(node); _buffer.write("Module"); } else { super.visitTypeName(node); @@ -206,7 +206,7 @@ class _Visitor extends RecursiveAstVisitor { /// Writes [_source] to [_buffer] up to the beginning of [token], then puts /// [_position] after [token] so it doesn't get written. - void _skip(Token token) { + void _skip(Token? token) { if (token == null) return; _buffer.write(_source.substring(_position, token.offset)); _position = token.end; diff --git a/tool/grind/utils.dart b/tool/grind/utils.dart index 3d43378e9..6d877fae1 100644 --- a/tool/grind/utils.dart +++ b/tool/grind/utils.dart @@ -24,8 +24,8 @@ void ensureBuild() { /// Returns the environment variable named [name], or throws an exception if it /// can't be found. -String /*!*/ environment(String name) { - var value = Platform.environment[name]; // TODO: no ! +String environment(String name) { + var value = Platform.environment[name]!; // TODO: no ! if (value == null) fail("Required environment variable $name not found."); return value; } From e85d9a7afc2eca39af5266f2d43537313fbd5152 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 16 Mar 2021 20:51:32 -0700 Subject: [PATCH 10/19] Fix automated migrator errors This gets the project analyzer-clean, but the tests are still failing. --- bin/sass.dart | 6 +- lib/src/ast/css/modifiable/declaration.dart | 1 - lib/src/ast/css/modifiable/node.dart | 3 +- lib/src/ast/css/modifiable/value.dart | 2 +- lib/src/ast/sass/statement/at_rule.dart | 2 + lib/src/ast/sass/statement/parent.dart | 2 - lib/src/ast/selector/complex.dart | 27 ++- lib/src/ast/selector/compound.dart | 18 +- lib/src/ast/selector/pseudo.dart | 8 +- lib/src/async_compile.dart | 7 +- lib/src/async_environment.dart | 27 ++- lib/src/async_import_cache.dart | 7 +- lib/src/callable.dart | 8 +- lib/src/callable/async.dart | 6 +- lib/src/callable/async_built_in.dart | 6 +- lib/src/callable/built_in.dart | 12 +- lib/src/compile.dart | 7 +- lib/src/environment.dart | 29 ++- lib/src/exception.dart | 4 +- lib/src/executable/options.dart | 39 +--- lib/src/extend/empty_extender.dart | 2 +- lib/src/extend/extender.dart | 16 +- lib/src/extend/functions.dart | 179 +++++++++--------- lib/src/functions/color.dart | 36 ++-- lib/src/functions/list.dart | 4 +- lib/src/functions/map.dart | 49 +++-- lib/src/functions/math.dart | 4 +- lib/src/functions/meta.dart | 4 +- lib/src/functions/selector.dart | 4 +- lib/src/functions/string.dart | 4 +- lib/src/import_cache.dart | 9 +- lib/src/importer/filesystem.dart | 1 - lib/src/importer/node/implementation.dart | 9 +- lib/src/importer/node/interface.dart | 2 +- lib/src/importer/utils.dart | 17 +- lib/src/io/interface.dart | 3 +- lib/src/io/node.dart | 27 ++- lib/src/logger.dart | 2 +- lib/src/module/forwarded_view.dart | 9 +- lib/src/module/shadowed_view.dart | 12 +- lib/src/node.dart | 20 +-- lib/src/node/render_context.dart | 49 ++++- lib/src/node/render_context_options.dart | 35 ---- lib/src/node/render_result.dart | 28 +-- lib/src/node/utils.dart | 15 +- lib/src/node/value.dart | 2 +- lib/src/node/value/boolean.dart | 2 +- lib/src/node/value/color.dart | 3 +- lib/src/node/value/list.dart | 2 +- lib/src/node/value/map.dart | 16 +- lib/src/node/value/number.dart | 2 +- lib/src/node/value/string.dart | 2 +- lib/src/parse/css.dart | 1 - lib/src/parse/parser.dart | 8 +- lib/src/parse/sass.dart | 32 +--- lib/src/parse/selector.dart | 1 - lib/src/parse/stylesheet.dart | 189 +++++++++++--------- lib/src/stylesheet_graph.dart | 32 ++-- lib/src/util/limited_map_view.dart | 3 +- lib/src/util/nullable.dart | 7 +- lib/src/utils.dart | 2 +- lib/src/value.dart | 2 +- lib/src/value/color.dart | 5 +- lib/src/value/external/list.dart | 6 +- lib/src/value/external/string.dart | 6 +- lib/src/value/number.dart | 6 +- lib/src/value/string.dart | 9 +- lib/src/visitor/async_evaluate.dart | 101 +++++------ lib/src/visitor/evaluate.dart | 104 +++++------ lib/src/visitor/serialize.dart | 10 +- test/dart_api/function_test.dart | 26 +-- test/dart_api/importer_test.dart | 21 +-- test/dart_api/logger_test.dart | 59 +++--- test/dart_api/test_importer.dart | 8 +- test/dart_api/value/utils.dart | 3 +- test/double_check_test.dart | 2 +- test/hybrid.dart | 2 +- test/node_api/function_test.dart | 23 ++- test/node_api/importer_test.dart | 52 +++--- test/node_api/source_map_test.dart | 36 ++-- test/node_api/utils.dart | 6 +- test/node_api/value/utils.dart | 4 +- test/node_api_test.dart | 16 +- test/source_map_test.dart | 6 +- test/utils.dart | 5 + tool/grind.dart | 2 +- tool/grind/benchmark.dart | 12 +- tool/grind/utils.dart | 2 +- 88 files changed, 712 insertions(+), 887 deletions(-) delete mode 100644 lib/src/node/render_context_options.dart diff --git a/bin/sass.dart b/bin/sass.dart index cc6bc4c4c..a456d6c81 100644 --- a/bin/sass.dart +++ b/bin/sass.dart @@ -35,7 +35,7 @@ Future main(List args) async { } } - late ExecutableOptions options; + ExecutableOptions? options; try { options = ExecutableOptions.parse(args); term_glyph.ascii = !options.unicode; @@ -68,7 +68,9 @@ Future main(List args) async { // dart-lang/sdk#33400. () { try { - if (destination != null && !options.emitErrorCss) { + if (destination != null && + // dart-lang/sdk#45348 + !options!.emitErrorCss) { deleteFile(destination); } } on FileSystemException { diff --git a/lib/src/ast/css/modifiable/declaration.dart b/lib/src/ast/css/modifiable/declaration.dart index 680594dd3..989f99ed3 100644 --- a/lib/src/ast/css/modifiable/declaration.dart +++ b/lib/src/ast/css/modifiable/declaration.dart @@ -2,7 +2,6 @@ // 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 '../../../value.dart'; diff --git a/lib/src/ast/css/modifiable/node.dart b/lib/src/ast/css/modifiable/node.dart index 0d5faa583..3b493c88a 100644 --- a/lib/src/ast/css/modifiable/node.dart +++ b/lib/src/ast/css/modifiable/node.dart @@ -71,7 +71,8 @@ abstract class ModifiableCssNode extends CssNode { parent._children.removeAt(_indexInParent!); for (var i = _indexInParent!; i < parent._children.length; i++) { - parent._children[i]._indexInParent--; + var child = parent._children[i]; + child._indexInParent = child._indexInParent! - 1; } _parent = null; } diff --git a/lib/src/ast/css/modifiable/value.dart b/lib/src/ast/css/modifiable/value.dart index b63ca3c2b..f345feac6 100644 --- a/lib/src/ast/css/modifiable/value.dart +++ b/lib/src/ast/css/modifiable/value.dart @@ -7,7 +7,7 @@ import 'package:source_span/source_span.dart'; import '../value.dart'; /// A modifiable version of [CssValue] for use in the evaluation step. -class ModifiableCssValue implements CssValue { +class ModifiableCssValue implements CssValue { T value; final FileSpan? span; diff --git a/lib/src/ast/sass/statement/at_rule.dart b/lib/src/ast/sass/statement/at_rule.dart index 5e6bce3ba..ff273e3dc 100644 --- a/lib/src/ast/sass/statement/at_rule.dart +++ b/lib/src/ast/sass/statement/at_rule.dart @@ -27,6 +27,8 @@ class AtRule extends ParentStatement { String toString() { var buffer = StringBuffer("@$name"); if (value != null) buffer.write(" $value"); + + var children = this.children; return children == null ? "$buffer;" : "$buffer {${children.join(" ")}}"; } } diff --git a/lib/src/ast/sass/statement/parent.dart b/lib/src/ast/sass/statement/parent.dart index 4fca22fec..0c0fd383e 100644 --- a/lib/src/ast/sass/statement/parent.dart +++ b/lib/src/ast/sass/statement/parent.dart @@ -9,8 +9,6 @@ import 'import_rule.dart'; import 'mixin_rule.dart'; import 'variable_declaration.dart'; -// TODO: make this generic over nullable children. - /// A [Statement] that can have child statements. /// /// This has a generic parameter so that its subclasses can choose whether or diff --git a/lib/src/ast/selector/complex.dart b/lib/src/ast/selector/complex.dart index 2bf8ce314..0bf9076aa 100644 --- a/lib/src/ast/selector/complex.dart +++ b/lib/src/ast/selector/complex.dart @@ -33,10 +33,10 @@ class ComplexSelector extends Selector { /// can have a range of possible specificities. int get minSpecificity { if (_minSpecificity == null) _computeSpecificity(); - return _minSpecificity; + return _minSpecificity!; } - late final int _minSpecificity; + int? _minSpecificity; /// The maximum possible specificity that this selector can have. /// @@ -44,18 +44,13 @@ class ComplexSelector extends Selector { /// can have a range of possible specificities. int get maxSpecificity { if (_maxSpecificity == null) _computeSpecificity(); - return _maxSpecificity; + return _maxSpecificity!; } - late final int _maxSpecificity; + int? _maxSpecificity; - // TODO: make late - bool get isInvisible { - return _isInvisible ??= components.any( - (component) => component is CompoundSelector && component.isInvisible); - } - - bool? _isInvisible; + late final bool isInvisible = components.any( + (component) => component is CompoundSelector && component.isInvisible); ComplexSelector(Iterable components, {this.lineBreak = false}) @@ -76,14 +71,16 @@ class ComplexSelector extends Selector { /// Computes [_minSpecificity] and [_maxSpecificity]. void _computeSpecificity() { - _minSpecificity = 0; - _maxSpecificity = 0; + var minSpecificity = 0; + var maxSpecificity = 0; for (var component in components) { if (component is CompoundSelector) { - _minSpecificity += component.minSpecificity; - _maxSpecificity += component.maxSpecificity; + minSpecificity += component.minSpecificity; + maxSpecificity += component.maxSpecificity; } } + _minSpecificity = minSpecificity; + _maxSpecificity = maxSpecificity; } int get hashCode => listHash(components); diff --git a/lib/src/ast/selector/compound.dart b/lib/src/ast/selector/compound.dart index 26556bad8..f5241250e 100644 --- a/lib/src/ast/selector/compound.dart +++ b/lib/src/ast/selector/compound.dart @@ -19,18 +19,16 @@ class CompoundSelector extends Selector implements ComplexSelectorComponent { /// This is never empty. final List components; - // TODO: late for specificities - /// The minimum possible specificity that this selector can have. /// /// Pseudo selectors that contain selectors, like `:not()` and `:matches()`, /// can have a range of possible specificities. int get minSpecificity { if (_minSpecificity == null) _computeSpecificity(); - return _minSpecificity; + return _minSpecificity!; } - late final int _minSpecificity; + int? _minSpecificity; /// The maximum possible specificity that this selector can have. /// @@ -41,7 +39,7 @@ class CompoundSelector extends Selector implements ComplexSelectorComponent { return _maxSpecificity!; } - late final int? _maxSpecificity; + int? _maxSpecificity; bool get isInvisible => components.any((component) => component.isInvisible); @@ -77,12 +75,14 @@ class CompoundSelector extends Selector implements ComplexSelectorComponent { /// Computes [_minSpecificity] and [_maxSpecificity]. void _computeSpecificity() { - _minSpecificity = 0; - _maxSpecificity = 0; + var minSpecificity = 0; + var maxSpecificity = 0; for (var simple in components) { - _minSpecificity += simple.minSpecificity; - _maxSpecificity += simple.maxSpecificity; + minSpecificity += simple.minSpecificity; + maxSpecificity += simple.maxSpecificity; } + _minSpecificity = minSpecificity; + _maxSpecificity = maxSpecificity; } int get hashCode => listHash(components); diff --git a/lib/src/ast/selector/pseudo.dart b/lib/src/ast/selector/pseudo.dart index 3bf2f9a23..bbf21295c 100644 --- a/lib/src/ast/selector/pseudo.dart +++ b/lib/src/ast/selector/pseudo.dart @@ -59,8 +59,6 @@ class PseudoSelector extends SimpleSelector { /// both non-`null`, the selector follows the argument. final SelectorList? selector; - // TODO: late - int get minSpecificity { if (_minSpecificity == null) _computeSpecificity(); return _minSpecificity!; @@ -175,6 +173,8 @@ class PseudoSelector extends SimpleSelector { minSpecificity = math.max(_minSpecificity!, complex.minSpecificity); maxSpecificity = math.max(_maxSpecificity!, complex.maxSpecificity); } + _minSpecificity = minSpecificity; + _maxSpecificity = maxSpecificity; } else { // This is higher than any selector's specificity can actually be. var minSpecificity = math.pow(super.minSpecificity, 3) as int; @@ -183,9 +183,9 @@ class PseudoSelector extends SimpleSelector { minSpecificity = math.min(_minSpecificity!, complex.minSpecificity); maxSpecificity = math.max(_maxSpecificity!, complex.maxSpecificity); } + _minSpecificity = minSpecificity; + _maxSpecificity = maxSpecificity; } - _minSpecificity = minSpecificity; - _maxSpecificity = maxSpecificity; } T accept(SelectorVisitor visitor) => visitor.visitPseudoSelector(this); diff --git a/lib/src/async_compile.dart b/lib/src/async_compile.dart index a812ff9c5..812f0aa6d 100644 --- a/lib/src/async_compile.dart +++ b/lib/src/async_compile.dart @@ -42,8 +42,8 @@ Future compileAsync(String path, if (nodeImporter == null && (syntax == null || syntax == Syntax.forPath(path))) { importCache ??= AsyncImportCache.none(logger: logger); - stylesheet = await importCache.importCanonical( - FilesystemImporter('.'), p.toUri(canonicalize(path)), p.toUri(path)); + stylesheet = (await importCache.importCanonical( + FilesystemImporter('.'), p.toUri(canonicalize(path)), p.toUri(path)))!; } else { stylesheet = Stylesheet.parse( readFile(path), syntax ?? Syntax.forPath(path), @@ -51,8 +51,7 @@ Future compileAsync(String path, } return await _compileStylesheet( - // TODO: no ! - stylesheet!, + stylesheet, logger, importCache, nodeImporter, diff --git a/lib/src/async_environment.dart b/lib/src/async_environment.dart index cfa0917f2..ed13a4638 100644 --- a/lib/src/async_environment.dart +++ b/lib/src/async_environment.dart @@ -4,7 +4,6 @@ import 'dart:collection'; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:source_span/source_span.dart'; @@ -342,12 +341,11 @@ class AsyncEnvironment { !_globalModules.contains(module)) module }; - forwardedModules = _forwardedModules = {}; + } else { + forwardedModules = _forwardedModules ??= {}; } - // TODO: var - Map, AstNode?> forwardedModuleNodes = - (_forwardedModuleNodes ??= {}); + var forwardedModuleNodes = _forwardedModuleNodes ??= {}; var forwardedVariableNames = forwarded.expand((module) => module.variables.keys).toSet(); @@ -374,8 +372,7 @@ class AsyncEnvironment { } } - // TODO: no ! - for (var module in forwardedModules!.toList()) { + for (var module in forwardedModules.toList()) { var shadowed = ShadowedModuleView.ifNecessary(module, variables: forwardedVariableNames, mixins: forwardedMixinNames, @@ -386,7 +383,7 @@ class AsyncEnvironment { if (!shadowed.isEmpty) { forwardedModules.add(shadowed); forwardedModuleNodes[shadowed] = - forwardedModuleNodes.remove(module); + forwardedModuleNodes.remove(module)!; } } } @@ -821,9 +818,7 @@ class AsyncEnvironment { var configuration = {}; for (var i = 0; i < _variables.length; i++) { var values = _variables[i]; - var nodes = - _variableNodes == null ? {} : _variableNodes![i]; - // TODO: var nodes = _variableNodes.andGet(i) ?? {}; + var nodes = _variableNodes.andGet(i) ?? {}; for (var entry in values.entries) { // Implicit configurations are never invalid, making [configurationSpan] // unnecessary, so we pass null here to avoid having to compute it. @@ -902,10 +897,8 @@ class AsyncEnvironment { if (identityFromModule == identity) continue; if (value != null) { - // TODO no !, as - var spans = _globalModuleNodes.entries.map((entry) => - callback(entry.key) - .andThen(((_) => entry.value.span!) as FileSpan Function(T)?)); + var spans = _globalModuleNodes.entries.map( + (entry) => callback(entry.key).andThen((_) => entry.value.span)); throw MultiSpanSassScriptException( 'This $type is available from multiple global modules.', @@ -924,7 +917,7 @@ class AsyncEnvironment { /// A module that represents the top-level members defined in an [Environment]. class _EnvironmentModule implements Module { - Uri? get url => css?.span!.sourceUrl; + Uri? get url => css.span?.sourceUrl; final List upstream; final Map variables; @@ -959,8 +952,8 @@ class _EnvironmentModule implements Module { _memberMap(environment._variables.first, forwarded.map((module) => module.variables)), environment._variableNodes.andThen((nodes) => _memberMap( - // TODO: no ! nodes.first, + // `forwarded!` due to dart-lang/sdk#45348 forwarded!.map((module) => module.variableNodes!))), _memberMap(environment._functions.first, forwarded.map((module) => module.functions)), diff --git a/lib/src/async_import_cache.dart b/lib/src/async_import_cache.dart index 4263c88dd..2d25a0e2b 100644 --- a/lib/src/async_import_cache.dart +++ b/lib/src/async_import_cache.dart @@ -117,7 +117,6 @@ class AsyncImportCache { } } - // TODO: no as return await putIfAbsentAsync(_canonicalizeCache, Tuple2(url, forImport), () async { for (var importer in _importers) { @@ -181,7 +180,6 @@ Relative canonical URLs are deprecated and will eventually be disallowed. /// Caches the result of the import and uses cached results if possible. Future importCanonical(AsyncImporter importer, Uri canonicalUrl, [Uri? originalUrl]) async { - // TODO: no as return await putIfAbsentAsync(_importCache, canonicalUrl, () async { var result = await importer.load(canonicalUrl); if (result == null) return null; @@ -202,13 +200,12 @@ Relative canonical URLs are deprecated and will eventually be disallowed. /// Returns [canonicalUrl] as-is if it hasn't been loaded by this cache. Uri humanize(Uri canonicalUrl) { // Display the URL with the shortest path length. - var url = minBy( + var url = minBy( _canonicalizeCache.values .whereNotNull() .where((tuple) => tuple.item2 == canonicalUrl) .map((tuple) => tuple.item3), - // TODO: no Uri - (Uri url) => url.path.length); + (url) => url.path.length); if (url == null) return canonicalUrl; // Use the canonicalized basename so that we display e.g. diff --git a/lib/src/callable.dart b/lib/src/callable.dart index c984e94f1..89beb9601 100644 --- a/lib/src/callable.dart +++ b/lib/src/callable.dart @@ -68,8 +68,7 @@ abstract class Callable extends AsyncCallable { @Deprecated('Use `Callable.function` instead.') factory Callable(String name, String arguments, ext.Value callback(List arguments)) => - // TODO: no as - Callable.function(name, arguments as String, callback); + Callable.function(name, arguments, callback); /// Creates a function with the given [name] and [arguments] that runs /// [callback] when called. @@ -116,8 +115,5 @@ abstract class Callable extends AsyncCallable { factory Callable.function(String name, String arguments, ext.Value callback(List arguments)) => BuiltInCallable.function( - // TODO: no as - name, - arguments as String, - (arguments) => callback(arguments) as Value); + name, arguments, (arguments) => callback(arguments) as Value); } diff --git a/lib/src/callable/async.dart b/lib/src/callable/async.dart index be5cde0a5..f4413bf81 100644 --- a/lib/src/callable/async.dart +++ b/lib/src/callable/async.dart @@ -26,8 +26,7 @@ abstract class AsyncCallable { @Deprecated('Use `AsyncCallable.function` instead.') factory AsyncCallable(String name, String arguments, FutureOr callback(List arguments)) => - // TODO: no as - AsyncCallable.function(name, arguments as String, callback); + AsyncCallable.function(name, arguments, callback); /// Creates a callable with the given [name] and [arguments] that runs /// [callback] when called. @@ -38,8 +37,7 @@ abstract class AsyncCallable { /// See [new Callable] for more details. factory AsyncCallable.function(String name, String arguments, FutureOr callback(List arguments)) => - // TODO: no as - AsyncBuiltInCallable.function(name, arguments as String, (arguments) { + AsyncBuiltInCallable.function(name, arguments, (arguments) { var result = callback(arguments); if (result is ext.Value) return result as Value; return result.then((value) => value as Value); diff --git a/lib/src/callable/async_built_in.dart b/lib/src/callable/async_built_in.dart index 26017ca4d..6751c0889 100644 --- a/lib/src/callable/async_built_in.dart +++ b/lib/src/callable/async_built_in.dart @@ -59,7 +59,11 @@ class AsyncBuiltInCallable implements AsyncCallable { ArgumentDeclaration.parse('@mixin $name($arguments) {', url: url), (arguments) async { await callback(arguments); - return null; + // We could encode the fact that functions return values and mixins + // don't in the type system, but that would get very messy very + // quickly so it's easier to just return Sass's `null` for mixins and + // simply ignore it at the call site. + return sassNull; }); /// Creates a callable with a single [arguments] declaration and a single diff --git a/lib/src/callable/built_in.dart b/lib/src/callable/built_in.dart index d0a79f930..1b4a2aeb9 100644 --- a/lib/src/callable/built_in.dart +++ b/lib/src/callable/built_in.dart @@ -61,8 +61,7 @@ class BuiltInCallable implements Callable, AsyncBuiltInCallable { /// [callback]. BuiltInCallable.parsed(this.name, ArgumentDeclaration arguments, Value callback(List arguments)) - // TODO: no as - : _overloads = [Tuple2(arguments as ArgumentDeclaration, callback)]; + : _overloads = [Tuple2(arguments, callback)]; /// Creates a function with multiple implementations. /// @@ -74,9 +73,7 @@ class BuiltInCallable implements Callable, AsyncBuiltInCallable { /// If passed, [url] is the URL of the module in which the function is /// defined. BuiltInCallable.overloadedFunction( - // TODO: use _Callback - this.name, - Map arguments)> overloads, + this.name, Map overloads, {Object? url}) : _overloads = [ for (var entry in overloads.entries) @@ -96,7 +93,7 @@ class BuiltInCallable implements Callable, AsyncBuiltInCallable { /// [ArgumentDeclaration]. Tuple2 callbackFor( int positional, Set names) { - Tuple2 fuzzyMatch; + Tuple2? fuzzyMatch; int? minMismatchDistance; for (var overload in _overloads) { @@ -117,7 +114,8 @@ class BuiltInCallable implements Callable, AsyncBuiltInCallable { fuzzyMatch = overload; } - return fuzzyMatch; + if (fuzzyMatch != null) return fuzzyMatch; + throw StateError("BuiltInCallable $name may not have empty overloads."); } /// Returns a copy of this callable with the given [name]. diff --git a/lib/src/compile.dart b/lib/src/compile.dart index a883d2749..7888370b9 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: 8053f105aefbe04ac104613c7eab7251d7798655 +// Checksum: 399932f6eea5cbf7a1c7851ca9e1b5455e14564c // // ignore_for_file: unused_import @@ -53,7 +53,7 @@ CompileResult compile(String path, (syntax == null || syntax == Syntax.forPath(path))) { importCache ??= ImportCache.none(logger: logger); stylesheet = importCache.importCanonical( - FilesystemImporter('.'), p.toUri(canonicalize(path)), p.toUri(path)); + FilesystemImporter('.'), p.toUri(canonicalize(path)), p.toUri(path))!; } else { stylesheet = Stylesheet.parse( readFile(path), syntax ?? Syntax.forPath(path), @@ -61,8 +61,7 @@ CompileResult compile(String path, } return _compileStylesheet( - // TODO: no ! - stylesheet!, + stylesheet, logger, importCache, nodeImporter, diff --git a/lib/src/environment.dart b/lib/src/environment.dart index ae567c88b..e34646c70 100644 --- a/lib/src/environment.dart +++ b/lib/src/environment.dart @@ -5,13 +5,12 @@ // DO NOT EDIT. This file was generated from async_environment.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: d53a246eab6683937dcd59a335320de94b316f34 +// Checksum: 9d25f8e34c8e566be536523b873c21757b591bfc // // ignore_for_file: unused_import import 'dart:collection'; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:source_span/source_span.dart'; @@ -350,12 +349,11 @@ class Environment { !_globalModules.contains(module)) module }; - forwardedModules = _forwardedModules = {}; + } else { + forwardedModules = _forwardedModules ??= {}; } - // TODO: var - Map, AstNode?> forwardedModuleNodes = - (_forwardedModuleNodes ??= {}); + var forwardedModuleNodes = _forwardedModuleNodes ??= {}; var forwardedVariableNames = forwarded.expand((module) => module.variables.keys).toSet(); @@ -382,8 +380,7 @@ class Environment { } } - // TODO: no ! - for (var module in forwardedModules!.toList()) { + for (var module in forwardedModules.toList()) { var shadowed = ShadowedModuleView.ifNecessary(module, variables: forwardedVariableNames, mixins: forwardedMixinNames, @@ -394,7 +391,7 @@ class Environment { if (!shadowed.isEmpty) { forwardedModules.add(shadowed); forwardedModuleNodes[shadowed] = - forwardedModuleNodes.remove(module); + forwardedModuleNodes.remove(module)!; } } } @@ -827,9 +824,7 @@ class Environment { var configuration = {}; for (var i = 0; i < _variables.length; i++) { var values = _variables[i]; - var nodes = - _variableNodes == null ? {} : _variableNodes![i]; - // TODO: var nodes = _variableNodes.andGet(i) ?? {}; + var nodes = _variableNodes.andGet(i) ?? {}; for (var entry in values.entries) { // Implicit configurations are never invalid, making [configurationSpan] // unnecessary, so we pass null here to avoid having to compute it. @@ -909,10 +904,8 @@ class Environment { if (identityFromModule == identity) continue; if (value != null) { - // TODO no !, as - var spans = _globalModuleNodes.entries.map((entry) => - callback(entry.key) - .andThen(((_) => entry.value.span!) as FileSpan Function(T)?)); + var spans = _globalModuleNodes.entries.map( + (entry) => callback(entry.key).andThen((_) => entry.value.span)); throw MultiSpanSassScriptException( 'This $type is available from multiple global modules.', @@ -931,7 +924,7 @@ class Environment { /// A module that represents the top-level members defined in an [Environment]. class _EnvironmentModule implements Module { - Uri? get url => css?.span!.sourceUrl; + Uri? get url => css.span?.sourceUrl; final List> upstream; final Map variables; @@ -966,8 +959,8 @@ class _EnvironmentModule implements Module { _memberMap(environment._variables.first, forwarded.map((module) => module.variables)), environment._variableNodes.andThen((nodes) => _memberMap( - // TODO: no ! nodes.first, + // `forwarded!` due to dart-lang/sdk#45348 forwarded!.map((module) => module.variableNodes!))), _memberMap(environment._functions.first, forwarded.map((module) => module.functions)), diff --git a/lib/src/exception.dart b/lib/src/exception.dart index 347c70b6f..2c4a6830b 100644 --- a/lib/src/exception.dart +++ b/lib/src/exception.dart @@ -137,9 +137,9 @@ class MultiSpanSassRuntimeException extends MultiSpanSassException /// An exception thrown when Sass parsing has failed. class SassFormatException extends SassException implements SourceSpanFormatException { - String get source => span?.file.getText(0); + String? get source => span?.file.getText(0); - int get offset => span?.start.offset; + int? get offset => span?.start.offset; SassFormatException(String message, FileSpan? span) : super(message, span); } diff --git a/lib/src/executable/options.dart b/lib/src/executable/options.dart index 9431f46f0..96e508a56 100644 --- a/lib/src/executable/options.dart +++ b/lib/src/executable/options.dart @@ -7,7 +7,6 @@ import 'dart:collection'; import 'package:args/args.dart'; import 'package:charcode/charcode.dart'; import 'package:collection/collection.dart'; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:term_glyph/term_glyph.dart' as term_glyph; import 'package:tuple/tuple.dart'; @@ -123,8 +122,7 @@ class ExecutableOptions { static String get usage => _parser.usage; /// Shorthand for throwing a [UsageException] with the given [message]. - @alwaysThrows - static void _fail(String message) => throw UsageException(message); + static Never _fail(String message) => throw UsageException(message); /// The parsed options passed by the user to the executable. final ArgResults _options; @@ -132,30 +130,9 @@ class ExecutableOptions { /// Whether to print the version of Sass and exit. bool get version => _options['version'] as bool; -// TODO: -// /// Whether to run an interactive shell. -// late final bool interactive = () { -// if (!(_options['interactive'] as bool)) return false; - -// var invalidOptions = [ -// 'stdin', 'indented', 'style', 'source-map', 'source-map-urls', // -// 'embed-sources', 'embed-source-map', 'update', 'watch' -// ]; -// for (var option in invalidOptions) { -// if (_options.wasParsed(option)) { -// throw UsageException("--$option isn't allowed with --interactive."); -// } -// } -// return true; -// }(); - /// Whether to run an interactive shell. - /*late*/ bool get interactive { - if (_interactive != null) return _interactive!; - - // TODO: remove ? - var interactive = (_interactive = _options['interactive'] as bool?)!; - if (!interactive) return false; + late final bool interactive = () { + if (!(_options['interactive'] as bool)) return false; var invalidOptions = [ 'stdin', 'indented', 'style', 'source-map', 'source-map-urls', // @@ -167,9 +144,7 @@ class ExecutableOptions { } } return true; - } - - bool? _interactive; + }(); /// Whether to parse the source file with the indented syntax. /// @@ -226,10 +201,8 @@ class ExecutableOptions { /// Whether to emit error messages as CSS stylesheets bool get emitErrorCss => - _options['error-css'] as bool ?? - // TODO: remove as - sourcesToDestinations.values.any( - ((destination) => destination != null) as bool Function(String?)); + _options['error-css'] as bool? ?? + sourcesToDestinations.values.any((destination) => destination != null); /// A map from source paths to the destination paths where the compiled CSS /// should be written. diff --git a/lib/src/extend/empty_extender.dart b/lib/src/extend/empty_extender.dart index a45d0633a..bdef0d717 100644 --- a/lib/src/extend/empty_extender.dart +++ b/lib/src/extend/empty_extender.dart @@ -25,7 +25,7 @@ class EmptyExtender implements Extender { const []; ModifiableCssValue addSelector( - SelectorList selector, FileSpan span, + SelectorList selector, FileSpan? span, [List? mediaContext]) { throw UnsupportedError( "addSelector() can't be called for a const Extender."); diff --git a/lib/src/extend/extender.dart b/lib/src/extend/extender.dart index 0677a980a..57f22daf7 100644 --- a/lib/src/extend/extender.dart +++ b/lib/src/extend/extender.dart @@ -181,8 +181,11 @@ class Extender { try { selector = _extendList(originalSelector, _extensions, mediaContext); } on SassException catch (error) { + var span = error.span; + if (span == null) rethrow; + throw SassException( - "From ${error.span!.message('')}\n" + "From ${span.message('')}\n" "${error.message}", span); } @@ -313,8 +316,11 @@ class Extender { extension.extender, newExtensions, extension.mediaContext); if (selectors == null) continue; } on SassException catch (error) { + var extenderSpan = extension.extenderSpan; + if (extenderSpan == null) rethrow; + throw SassException( - "From ${extension.extenderSpan!.message('')}\n" + "From ${extenderSpan.message('')}\n" "${error.message}", error.span); } @@ -490,8 +496,7 @@ class Extender { } if (extended == null) return list; - return SelectorList(_trim(extended, _originals.contains) - .where((complex) => complex != null)); + return SelectorList(_trim(extended, _originals.contains)); } /// Extends [complex] using [extensions], and returns the contents of a @@ -549,8 +554,7 @@ class Extender { return paths(extendedNotExpanded).expand((path) { return weave(path.map((complex) => complex.components).toList()) .map((components) { - var outputComplex = ComplexSelector( - components as Iterable, + var outputComplex = ComplexSelector(components, lineBreak: complex.lineBreak || path.any((inputComplex) => inputComplex.lineBreak)); diff --git a/lib/src/extend/functions.dart b/lib/src/extend/functions.dart index fb9c13a75..c5ac3a997 100644 --- a/lib/src/extend/functions.dart +++ b/lib/src/extend/functions.dart @@ -54,7 +54,7 @@ List>? unifyComplex( .map((complex) => complex.sublist(0, complex.length - 1)) .toList(); complexesWithoutBases.last.add(CompoundSelector(unifiedBase!)); - return weave(complexesWithoutBases) as List>?; + return weave(complexesWithoutBases); } /// Returns a [CompoundSelector] that matches only elements that are matched by @@ -135,7 +135,7 @@ SimpleSelector? unifyUniversalAndElement( /// output for very little gain. /// /// The selector `.D (.A .B)` is represented as the list `[[.D], [.A, .B]]`. -List> weave( +List> weave( List> complexes) { var prefixes = [complexes.first.toList()]; @@ -151,8 +151,7 @@ List> weave( } var parents = complex.take(complex.length - 1).toList(); - List> newPrefixes = - >[]; + var newPrefixes = >[]; for (var prefix in prefixes) { var parentPrefixes = _weaveParents(prefix, parents); if (parentPrefixes == null) continue; @@ -180,8 +179,8 @@ List> weave( /// identical to the intersection of all elements matched by `A X` and all /// elements matched by `B X`. Some `AB_i` are elided to reduce the size of /// the output. -Iterable>? _weaveParents( - List parents1, +Iterable>? _weaveParents( + List parents1, List parents2) { var queue1 = Queue.of(parents1); var queue2 = Queue.of(parents2); @@ -207,7 +206,7 @@ Iterable>? _weaveParents( var groups1 = _groupSelectors(queue1); var groups2 = _groupSelectors(queue2); - var lcs = longestCommonSubsequence>( + var lcs = longestCommonSubsequence>( groups2, groups1, select: (group1, group2) { if (listEquals(group1, group2)) return group1; if (group1.first is! CompoundSelector || @@ -218,20 +217,17 @@ Iterable>? _weaveParents( if (complexIsParentSuperselector(group2, group1)) return group1; if (!_mustUnify(group1, group2)) return null; - var unified = unifyComplex([ - group1 as List, - group2 as List - ]); + var unified = unifyComplex([group1, group2]); if (unified == null) return null; if (unified.length > 1) return null; return unified.first; }); var choices = [ - >[initialCombinators] + >[initialCombinators] ]; for (var group in lcs) { - choices.add(_chunks>(groups1, groups2, + choices.add(_chunks>(groups1, groups2, (sequence) => complexIsParentSuperselector(sequence.first, group)) .map((chunk) => chunk.expand((group) => group)) .toList()); @@ -250,7 +246,7 @@ Iterable>? _weaveParents( /// If the first element of [queue] has a `::root` selector, removes and returns /// that element. -CompoundSelector? _firstIfRoot(Queue queue) { +CompoundSelector? _firstIfRoot(Queue queue) { if (queue.isEmpty) return null; var first = queue.first; if (first is CompoundSelector) { @@ -268,12 +264,12 @@ CompoundSelector? _firstIfRoot(Queue queue) { /// /// If there are no combinators to be merged, returns an empty list. If the /// combinators can't be merged, returns `null`. -List? _mergeInitialCombinators( - Queue components1, +List? _mergeInitialCombinators( + Queue components1, Queue components2) { - var combinators1 = []; + var combinators1 = []; while (components1.isNotEmpty && components1.first is Combinator) { - combinators1.add(components1.removeFirst() as Combinator?); + combinators1.add(components1.removeFirst() as Combinator); } var combinators2 = []; @@ -294,19 +290,19 @@ List? _mergeInitialCombinators( /// /// If there are no combinators to be merged, returns an empty list. If the /// sequences can't be merged, returns `null`. -List>>? _mergeFinalCombinators( - Queue components1, +List>>? _mergeFinalCombinators( + Queue components1, Queue components2, - [QueueList>>? result]) { + [QueueList>>? result]) { result ??= QueueList(); if ((components1.isEmpty || components1.last is! Combinator) && (components2.isEmpty || components2.last is! Combinator)) { return result; } - var combinators1 = []; + var combinators1 = []; while (components1.isNotEmpty && components1.last is Combinator) { - combinators1.add(components1.removeLast() as Combinator?); + combinators1.add(components1.removeLast() as Combinator); } var combinators2 = []; @@ -334,12 +330,12 @@ List>>? _mergeFinalCombinators( var combinator1 = combinators1.isEmpty ? null : combinators1.first; var combinator2 = combinators2.isEmpty ? null : combinators2.first; if (combinator1 != null && combinator2 != null) { - var compound1 = components1.removeLast() as CompoundSelector?; + var compound1 = components1.removeLast() as CompoundSelector; var compound2 = components2.removeLast() as CompoundSelector; if (combinator1 == Combinator.followingSibling && combinator2 == Combinator.followingSibling) { - if (compound1!.isSuperselector(compound2)) { + if (compound1.isSuperselector(compound2)) { result.addFirst([ [compound2, Combinator.followingSibling] ]); @@ -375,17 +371,16 @@ List>>? _mergeFinalCombinators( (combinator1 == Combinator.nextSibling && combinator2 == Combinator.followingSibling)) { var followingSiblingSelector = - combinator1 == Combinator.followingSibling ? compound1! : compound2; + combinator1 == Combinator.followingSibling ? compound1 : compound2; var nextSiblingSelector = - combinator1 == Combinator.followingSibling ? compound2 : compound1!; + combinator1 == Combinator.followingSibling ? compound2 : compound1; if (followingSiblingSelector.isSuperselector(nextSiblingSelector)) { result.addFirst([ [nextSiblingSelector, Combinator.nextSibling] ]); } else { - var unified = - unifyCompound(compound1!.components, compound2.components); + var unified = unifyCompound(compound1.components, compound2.components); result.addFirst([ [ followingSiblingSelector, @@ -411,7 +406,7 @@ List>>? _mergeFinalCombinators( ]); components2..add(compound2)..add(Combinator.child); } else if (combinator1 == combinator2) { - var unified = unifyCompound(compound1!.components, compound2.components); + var unified = unifyCompound(compound1.components, compound2.components); if (unified == null) return null; result.addFirst([ [unified, combinator1] @@ -440,7 +435,7 @@ List>>? _mergeFinalCombinators( components1.removeLast(); } result.addFirst([ - [components2.removeLast(), combinator2] + [components2.removeLast(), combinator2!] ]); return _mergeFinalCombinators(components1, components2, result); } @@ -451,8 +446,8 @@ List>>? _mergeFinalCombinators( /// /// This is necessary when both selectors contain the same unique simple /// selector, such as an ID. -bool _mustUnify(List complex1, - List complex2) { +bool _mustUnify(List complex1, + List complex2) { var uniqueSelectors = { for (var component in complex1) if (component is CompoundSelector) @@ -524,13 +519,12 @@ List> paths(Iterable> choices) => choices.fold( /// /// For example, `(A B > C D + E ~ > G)` is grouped into /// `[(A) (B > C) (D + E ~ > G)]`. -QueueList> _groupSelectors( - Iterable complex) { - QueueList> groups = - QueueList>(); +QueueList> _groupSelectors( + Iterable complex) { + var groups = QueueList>(); var iterator = complex.iterator; if (!iterator.moveNext()) return groups; - var group = [iterator.current]; + var group = [iterator.current]; groups.add(group); while (iterator.moveNext()) { if (group.last is Combinator || iterator.current is Combinator) { @@ -564,8 +558,8 @@ bool listIsSuperselector( /// For example, `B` is not normally a superselector of `B A`, since it doesn't /// match elements that match `A`. However, it *is* a parent superselector, /// since `B X` is a superselector of `B A X`. -bool complexIsParentSuperselector(List complex1, - List complex2) { +bool complexIsParentSuperselector(List complex1, + List complex2) { // Try some simple heuristics to see if we can avoid allocations. if (complex1.first is Combinator) return false; if (complex2.first is Combinator) return false; @@ -581,8 +575,8 @@ bool complexIsParentSuperselector(List complex1, /// /// That is, whether [complex1] matches every element that [complex2] matches, as well /// as possibly additional elements. -bool complexIsSuperselector(List complex1, - List complex2) { +bool complexIsSuperselector(List complex1, + List complex2) { // Selectors with trailing operators are neither superselectors nor // subselectors. if (complex1.last is Combinator) return false; @@ -602,11 +596,11 @@ bool complexIsSuperselector(List complex1, // subselectors. if (complex1[i1] is Combinator) return false; if (complex2[i2] is Combinator) return false; - var compound1 = complex1[i1] as CompoundSelector?; + var compound1 = complex1[i1] as CompoundSelector; if (remaining1 == 1) { return compoundIsSuperselector( - compound1!, complex2.last as CompoundSelector?, + compound1, complex2.last as CompoundSelector, parents: complex2.take(complex2.length - 1).skip(i2)); } @@ -619,7 +613,7 @@ bool complexIsSuperselector(List complex1, for (; afterSuperselector < complex2.length; afterSuperselector++) { var compound2 = complex2[afterSuperselector - 1]; if (compound2 is CompoundSelector) { - if (compoundIsSuperselector(compound1!, compound2, + if (compoundIsSuperselector(compound1, compound2, parents: complex2.take(afterSuperselector - 1).skip(i2 + 1))) { break; } @@ -667,8 +661,8 @@ bool complexIsSuperselector(List complex1, /// relevant for pseudo selectors with selector arguments, where we may need to /// know if the parent selectors in the selector argument match [parents]. bool compoundIsSuperselector( - CompoundSelector compound1, CompoundSelector? compound2, - {Iterable? parents}) { + CompoundSelector compound1, CompoundSelector compound2, + {Iterable? parents}) { // Every selector in [compound1.components] must have a matching selector in // [compound2.components]. for (var simple1 in compound1.components) { @@ -677,14 +671,14 @@ bool compoundIsSuperselector( parents: parents)) { return false; } - } else if (!_simpleIsSuperselectorOfCompound(simple1, compound2!)) { + } else if (!_simpleIsSuperselectorOfCompound(simple1, compound2)) { return false; } } // [compound1] can't be a superselector of a selector with non-selector // pseudo-elements that [compound2] doesn't share. - for (var simple2 in compound2!.components) { + for (var simple2 in compound2.components) { if (simple2 is PseudoSelector && simple2.isElement && simple2.selector == null && @@ -706,17 +700,16 @@ bool _simpleIsSuperselectorOfCompound( if (simple == theirSimple) return true; // Some selector pseudoclasses can match normal selectors. - if (theirSimple is PseudoSelector && - theirSimple.selector != null && - _subselectorPseudos.contains(theirSimple.normalizedName)) { - return theirSimple.selector!.components.every((complex) { - if (complex.components.length != 1) return false; - var compound = complex.components.single as CompoundSelector; - return compound.components.contains(simple); - }); - } else { - return false; - } + if (theirSimple is! PseudoSelector) return false; + var selector = theirSimple.selector; + if (selector == null) return false; + if (!_subselectorPseudos.contains(theirSimple.normalizedName)) return false; + + return selector.components.every((complex) { + if (complex.components.length != 1) return false; + var compound = complex.components.single as CompoundSelector; + return compound.components.contains(simple); + }); }); } @@ -731,32 +724,31 @@ bool _simpleIsSuperselectorOfCompound( /// relevant for pseudo selectors with selector arguments, where we may need to /// know if the parent selectors in the selector argument match [parents]. bool _selectorPseudoIsSuperselector( - PseudoSelector pseudo1, CompoundSelector? compound2, - {Iterable? parents}) { + PseudoSelector pseudo1, CompoundSelector compound2, + {Iterable? parents}) { + var selector1 = pseudo1.selector!; switch (pseudo1.normalizedName) { case 'matches': case 'any': - var pseudos = _selectorPseudosNamed(compound2!, pseudo1.name); - return pseudos.any((pseudo2) { - return pseudo1.selector!.isSuperselector(pseudo2.selector!); - }) || - pseudo1.selector!.components.any((complex1) => complexIsSuperselector( + var selectors = _selectorPseudoArgs(compound2, pseudo1.name); + return selectors + .any((selector2) => selector1.isSuperselector(selector2)) || + selector1.components.any((complex1) => complexIsSuperselector( complex1.components, [...?parents, compound2])); case 'has': case 'host': case 'host-context': - return _selectorPseudosNamed(compound2!, pseudo1.name).any( - (pseudo2) => pseudo1.selector!.isSuperselector(pseudo2.selector!)); + return _selectorPseudoArgs(compound2, pseudo1.name) + .any((selector2) => selector1.isSuperselector(selector2)); case 'slotted': - return _selectorPseudosNamed(compound2!, pseudo1.name, isClass: false) - .any((pseudo2) => - pseudo1.selector!.isSuperselector(pseudo2.selector!)); + return _selectorPseudoArgs(compound2, pseudo1.name, isClass: false) + .any((selector2) => selector1.isSuperselector(selector2)); case 'not': - return pseudo1.selector!.components.every((complex) { - return compound2!.components.any((simple2) { + return selector1.components.every((complex) { + return compound2.components.any((simple2) { if (simple2 is TypeSelector) { var compound1 = complex.components.last; return compound1 is CompoundSelector && @@ -768,9 +760,10 @@ bool _selectorPseudoIsSuperselector( compound1.components.any( (simple1) => simple1 is IDSelector && simple1 != simple2); } else if (simple2 is PseudoSelector && - simple2.name == pseudo1.name && - simple2.selector != null) { - return listIsSuperselector(simple2.selector!.components, [complex]); + simple2.name == pseudo1.name) { + var selector2 = simple2.selector; + if (selector2 == null) return false; + return listIsSuperselector(selector2.components, [complex]); } else { return false; } @@ -778,27 +771,31 @@ bool _selectorPseudoIsSuperselector( }); case 'current': - return _selectorPseudosNamed(compound2!, pseudo1.name) - .any((pseudo2) => pseudo1.selector == pseudo2.selector); + return _selectorPseudoArgs(compound2, pseudo1.name) + .any((selector2) => selector1 == selector2); case 'nth-child': case 'nth-last-child': - return compound2!.components.any((pseudo2) => - pseudo2 is PseudoSelector && - pseudo2.name == pseudo1.name && - pseudo2.argument == pseudo1.argument && - pseudo1.selector!.isSuperselector(pseudo2.selector!)); + return compound2.components.any((pseudo2) { + if (pseudo2 is! PseudoSelector) return false; + if (pseudo2.name != pseudo1.name) return false; + if (pseudo2.argument == pseudo1.argument) return false; + var selector2 = pseudo2.selector; + if (selector2 == null) return false; + return selector1.isSuperselector(selector2); + }); default: throw "unreachable"; } } -/// Returns all pseudo selectors in [compound] that have a selector argument, -/// and that have the given [name]. -Iterable _selectorPseudosNamed( +/// Returns all the selector arguments of pseudo selectors in [compound] with +/// the given [name]. +Iterable _selectorPseudoArgs( CompoundSelector compound, String name, {bool isClass = true}) => - compound.components.whereType().where((pseudo) => - pseudo.isClass == isClass && - pseudo.selector != null && - pseudo.name == name); + compound.components + .whereType() + .where((pseudo) => pseudo.isClass == isClass && pseudo.name == name) + .map((pseudo) => pseudo.selector) + .whereNotNull(); diff --git a/lib/src/functions/color.dart b/lib/src/functions/color.dart index 8d193a2fb..910465e2f 100644 --- a/lib/src/functions/color.dart +++ b/lib/src/functions/color.dart @@ -30,10 +30,7 @@ final global = UnmodifiableListView([ r"$color, $alpha": (arguments) => _rgbTwoArg("rgb", arguments), r"$channels": (arguments) { var parsed = _parseChannels( - // TODO: no ! - "rgb", - [r"$red", r"$green", r"$blue"], - arguments.first); + "rgb", [r"$red", r"$green", r"$blue"], arguments.first); return parsed is SassString ? parsed : _rgb("rgb", parsed as List); } }), @@ -44,10 +41,7 @@ final global = UnmodifiableListView([ r"$color, $alpha": (arguments) => _rgbTwoArg("rgba", arguments), r"$channels": (arguments) { var parsed = _parseChannels( - // TODO: no ! - "rgba", - [r"$red", r"$green", r"$blue"], - arguments.first); + "rgba", [r"$red", r"$green", r"$blue"], arguments.first); return parsed is SassString ? parsed : _rgb("rgba", parsed as List); @@ -554,19 +548,15 @@ SassString _functionString(String name, Iterable arguments) => /// value to [argument], with a leading minus sign if [negative] is `true`. BuiltInCallable _removedColorFunction(String name, String argument, {bool negative = false}) => - // TODO: no as - _function( - name, - r"$color, $amount", - (arguments) { - throw SassScriptException( - "The function $name() isn't in the sass:color module.\n" - "\n" - "Recommendation: color.adjust(${arguments[0]}, \$$argument: " - "${negative ? '-' : ''}${arguments[1]})\n" - "\n" - "More info: https://sass-lang.com/documentation/functions/color#$name"); - } as Value Function(List)); + _function(name, r"$color, $amount", (arguments) { + throw SassScriptException( + "The function $name() isn't in the sass:color module.\n" + "\n" + "Recommendation: color.adjust(${arguments[0]}, \$$argument: " + "${negative ? '-' : ''}${arguments[1]})\n" + "\n" + "More info: https://sass-lang.com/documentation/functions/color#$name"); + }); Value _rgb(String name, List arguments) { var alpha = arguments.length > 3 ? arguments[3] : null; @@ -848,6 +838,4 @@ SassColor _transparentize(List arguments) { /// `sass:color`. BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => - // TODO: no as - BuiltInCallable.function(name, arguments as String, callback, - url: "sass:color"); + BuiltInCallable.function(name, arguments, callback, url: "sass:color"); diff --git a/lib/src/functions/list.dart b/lib/src/functions/list.dart index 6f4207393..eb8e1a462 100644 --- a/lib/src/functions/list.dart +++ b/lib/src/functions/list.dart @@ -134,6 +134,4 @@ final _isBracketed = _function("is-bracketed", r"$list", /// Like [new BuiltInCallable.function], but always sets the URL to `sass:list`. BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => - // TODO: no as - BuiltInCallable.function(name, arguments as String, callback, - url: "sass:list"); + BuiltInCallable.function(name, arguments, callback, url: "sass:list"); diff --git a/lib/src/functions/map.dart b/lib/src/functions/map.dart index 84eac46c4..d211e3dd8 100644 --- a/lib/src/functions/map.dart +++ b/lib/src/functions/map.dart @@ -81,7 +81,7 @@ final _merge = BuiltInCallable.overloadedFunction("merge", { } var map2 = args.last.assertMap("map2"); return _modify(map1, args.take(args.length - 1), (oldValue) { - var nestedMap = oldValue?.tryMap(); + var nestedMap = oldValue.tryMap(); if (nestedMap == null) return map2; return SassMap({...nestedMap.contents, ...map2.contents}); }); @@ -99,14 +99,12 @@ final _deepRemove = var map = arguments[0].assertMap("map"); var keys = [arguments[1], ...arguments[2].asList]; return _modify(map, keys.take(keys.length - 1), (value) { - var nestedMap = value?.tryMap(); - if (nestedMap != null && nestedMap.contents?.containsKey(keys.last) ?? - false) { - // TODO: no ! - return SassMap(Map.of(nestedMap!.contents)..remove(keys.last)); + var nestedMap = value.tryMap(); + if (nestedMap != null && nestedMap.contents.containsKey(keys.last)) { + return SassMap(Map.of(nestedMap.contents)..remove(keys.last)); } return value; - }); + }, addNesting: false); }); final _remove = BuiltInCallable.overloadedFunction("remove", { @@ -159,38 +157,35 @@ final _hasKey = _function("has-key", r"$map, $key, $keys...", (arguments) { /// /// If more than one key is provided, this means the map targeted for update is /// nested within [map]. The multiple [keys] form a path of nested maps that -/// leads to the targeted map. If any value along the path is not a map, and -/// `modify(null)` returns null, this inserts a new map at that key and -/// overwrites the current value. Otherwise, this fails and returns [map] with -/// no changes. +/// leads to the targeted value, which is passed to [modify]. +/// +/// If any value along the path (other than the last one) is not a map and +/// [addNesting] is `true`, this creates nested maps to match [keys] and passes +/// [sassNull] to [modify]. Otherwise, this fails and returns [map] with no +/// changes. /// /// If no keys are provided, this passes [map] directly to modify and returns /// the result. -Value _modify(SassMap map, Iterable keys, Value? modify(Value? old)) { +Value _modify(SassMap map, Iterable keys, Value modify(Value old), + {bool addNesting = true}) { var keyIterator = keys.iterator; - SassMap _modifyNestedMap(SassMap map, [Value? newValue]) { - // TODO: no as throughout + SassMap _modifyNestedMap(SassMap map) { var mutableMap = Map.of(map.contents); var key = keyIterator.current; if (!keyIterator.moveNext()) { - mutableMap[key] = newValue ?? modify(mutableMap[key]); - return SassMap(mutableMap as Map); + mutableMap[key] = modify(mutableMap[key] ?? sassNull); + return SassMap(mutableMap); } var nestedMap = mutableMap[key]?.tryMap(); - if (nestedMap == null) { - // We pass null to `modify` here to indicate there's no existing value. - newValue = modify(null); - if (newValue == null) return SassMap(mutableMap as Map); - } + if (nestedMap == null && !addNesting) return SassMap(mutableMap); - nestedMap ??= const SassMap.empty(); - mutableMap[key] = _modifyNestedMap(nestedMap, newValue); - return SassMap(mutableMap as Map); + mutableMap[key] = _modifyNestedMap(nestedMap ?? const SassMap.empty()); + return SassMap(mutableMap); } - return keyIterator.moveNext() ? _modifyNestedMap(map) : modify(map)!; + return keyIterator.moveNext() ? _modifyNestedMap(map) : modify(map); } /// Merges [map1] and [map2], with values in [map2] taking precedence. @@ -238,6 +233,4 @@ SassMap _deepMergeImpl(SassMap map1, SassMap map2) { /// Like [new BuiltInCallable.function], but always sets the URL to `sass:map`. BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => - // TODO: no as - BuiltInCallable.function(name, arguments as String, callback, - url: "sass:map"); + BuiltInCallable.function(name, arguments, callback, url: "sass:map"); diff --git a/lib/src/functions/math.dart b/lib/src/functions/math.dart index 5970dacc4..fbfbb4302 100644 --- a/lib/src/functions/math.dart +++ b/lib/src/functions/math.dart @@ -318,6 +318,4 @@ BuiltInCallable _numberFunction(String name, num transform(num value)) { /// Like [new _function.function], but always sets the URL to `sass:math`. BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => - // TODO: no as - BuiltInCallable.function(name, arguments as String, callback, - url: "sass:math"); + BuiltInCallable.function(name, arguments, callback, url: "sass:math"); diff --git a/lib/src/functions/meta.dart b/lib/src/functions/meta.dart index 0fb263408..b959e0be7 100644 --- a/lib/src/functions/meta.dart +++ b/lib/src/functions/meta.dart @@ -63,6 +63,4 @@ final global = UnmodifiableListView([ /// Like [new BuiltInCallable.function], but always sets the URL to `sass:meta`. BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => - // TODO: no as - BuiltInCallable.function(name, arguments as String, callback, - url: "sass:meta"); + BuiltInCallable.function(name, arguments, callback, url: "sass:meta"); diff --git a/lib/src/functions/selector.dart b/lib/src/functions/selector.dart index 0473f8c1c..ed3e8ef19 100644 --- a/lib/src/functions/selector.dart +++ b/lib/src/functions/selector.dart @@ -148,6 +148,4 @@ CompoundSelector? _prependParent(CompoundSelector compound) { /// `sass:selector`. BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => - // TODO: no as - BuiltInCallable.function(name, arguments as String, callback, - url: "sass:selector"); + BuiltInCallable.function(name, arguments, callback, url: "sass:selector"); diff --git a/lib/src/functions/string.dart b/lib/src/functions/string.dart index 41dfb2723..076a1b662 100644 --- a/lib/src/functions/string.dart +++ b/lib/src/functions/string.dart @@ -172,6 +172,4 @@ int _codepointForIndex(int index, int lengthInCodepoints, /// `sass:string`. BuiltInCallable _function( String name, String arguments, Value callback(List arguments)) => - // TODO: no as - BuiltInCallable.function(name, arguments as String, callback, - url: "sass:string"); + BuiltInCallable.function(name, arguments, callback, url: "sass:string"); diff --git a/lib/src/import_cache.dart b/lib/src/import_cache.dart index 5b9ba98e1..ad88e54bc 100644 --- a/lib/src/import_cache.dart +++ b/lib/src/import_cache.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_import_cache.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: 703b475d60d087c6a7944358a9cee6d1da44a958 +// Checksum: 950db49eb9e3a85f35bc4a3d7cfe029fb60ae498 // // ignore_for_file: unused_import @@ -120,7 +120,6 @@ class ImportCache { } } - // TODO: no as return _canonicalizeCache.putIfAbsent(Tuple2(url, forImport), () { for (var importer in _importers) { var canonicalUrl = _canonicalize(importer, url, forImport); @@ -179,7 +178,6 @@ Relative canonical URLs are deprecated and will eventually be disallowed. /// Caches the result of the import and uses cached results if possible. Stylesheet? importCanonical(Importer importer, Uri canonicalUrl, [Uri? originalUrl]) { - // TODO: no as return _importCache.putIfAbsent(canonicalUrl, () { var result = importer.load(canonicalUrl); if (result == null) return null; @@ -200,13 +198,12 @@ Relative canonical URLs are deprecated and will eventually be disallowed. /// Returns [canonicalUrl] as-is if it hasn't been loaded by this cache. Uri humanize(Uri canonicalUrl) { // Display the URL with the shortest path length. - var url = minBy( + var url = minBy( _canonicalizeCache.values .whereNotNull() .where((tuple) => tuple.item2 == canonicalUrl) .map((tuple) => tuple.item3), - // TODO: no Uri - (Uri url) => url.path.length); + (url) => url.path.length); if (url == null) return canonicalUrl; // Use the canonicalized basename so that we display e.g. diff --git a/lib/src/importer/filesystem.dart b/lib/src/importer/filesystem.dart index caea86a02..36192f07a 100644 --- a/lib/src/importer/filesystem.dart +++ b/lib/src/importer/filesystem.dart @@ -8,7 +8,6 @@ import 'package:path/path.dart' as p; import '../importer.dart'; import '../io.dart' as io; import '../syntax.dart'; -import '../utils.dart'; import '../util/nullable.dart'; import 'utils.dart'; diff --git a/lib/src/importer/node/implementation.dart b/lib/src/importer/node/implementation.dart index 5f6fdea50..019395c19 100644 --- a/lib/src/importer/node/implementation.dart +++ b/lib/src/importer/node/implementation.dart @@ -58,8 +58,7 @@ class NodeImporter { /// environment variable. static Iterable _addSassPath(Iterable includePaths) sync* { yield* includePaths; - // TODO: no ! - var sassPath = getEnvironmentVariable("SASS_PATH")!; + var sassPath = getEnvironmentVariable("SASS_PATH"); if (sassPath == null) return; yield* sassPath.split(isWindows ? ';' : ':'); } @@ -179,10 +178,8 @@ class NodeImporter { if (isJSError(value)) throw value; if (value is! NodeImporterResult) return null; - // TODO: no var rename - var result = value; - var file = result.file; - var contents = result.contents; + var file = value.file; + var contents = value.contents; if (file == null) { return Tuple2(contents ?? '', url); } else if (contents != null) { diff --git a/lib/src/importer/node/interface.dart b/lib/src/importer/node/interface.dart index 65dea1d66..fb65865b9 100644 --- a/lib/src/importer/node/interface.dart +++ b/lib/src/importer/node/interface.dart @@ -8,7 +8,7 @@ class NodeImporter { NodeImporter(Object context, Iterable includePaths, Iterable importers); - Tuple2 load(String url, Uri? previous, bool forImport) => + Tuple2? load(String url, Uri? previous, bool forImport) => throw ''; Future?> loadAsync( diff --git a/lib/src/importer/utils.dart b/lib/src/importer/utils.dart index a83fe0897..6e5d51fef 100644 --- a/lib/src/importer/utils.dart +++ b/lib/src/importer/utils.dart @@ -45,17 +45,13 @@ Future inImportRuleAsync(Future callback()) async { String? resolveImportPath(String path) { var extension = p.extension(path); if (extension == '.sass' || extension == '.scss' || extension == '.css') { - return _ifInImport((() => _exactlyOne( - // TODO: no !, as - _tryPath('${p.withoutExtension(path)}.import$extension'))!) - as String Function()) ?? + return _ifInImport(() => _exactlyOne( + _tryPath('${p.withoutExtension(path)}.import$extension'))) ?? _exactlyOne(_tryPath(path)); } - return _ifInImport( - // TODO: no !, as - (() => _exactlyOne(_tryPathWithExtensions('$path.import'))!) as String - Function()) ?? + return _ifInImport( + () => _exactlyOne(_tryPathWithExtensions('$path.import'))) ?? _exactlyOne(_tryPathWithExtensions(path)) ?? _tryPathAsDirectory(path); } @@ -82,9 +78,8 @@ List _tryPath(String path) { String? _tryPathAsDirectory(String path) { if (!dirExists(path)) return null; - return _ifInImport((() => _exactlyOne( - _tryPathWithExtensions(p.join(path, 'index.import')))!) - as String Function()) ?? + return _ifInImport(() => + _exactlyOne(_tryPathWithExtensions(p.join(path, 'index.import')))) ?? _exactlyOne(_tryPathWithExtensions(p.join(path, 'index'))); } diff --git a/lib/src/io/interface.dart b/lib/src/io/interface.dart index b83e361bc..738ce656c 100644 --- a/lib/src/io/interface.dart +++ b/lib/src/io/interface.dart @@ -91,7 +91,8 @@ DateTime modificationTime(String path) => throw ''; String? getEnvironmentVariable(String name) => throw ''; /// Gets and sets the exit code that the process will use when it exits. -int exitCode; +int get exitCode => throw ''; +set exitCode(int value) => throw ''; /// Recursively watches the directory at [path] for modifications. /// diff --git a/lib/src/io/node.dart b/lib/src/io/node.dart index 2a14b5dd8..bf1689766 100644 --- a/lib/src/io/node.dart +++ b/lib/src/io/node.dart @@ -58,10 +58,8 @@ String readFile(String path) { } /// Wraps `fs.readFileSync` to throw a [FileSystemException]. -Object _readFile(String path, [String? encoding]) => - // TODO: no as - _systemErrorToFileSystemException( - (() => fs.readFileSync(path, encoding)) as Object Function()); +Object? _readFile(String path, [String? encoding]) => + _systemErrorToFileSystemException(() => fs.readFileSync(path, encoding)); void writeFile(String path, String contents) => _systemErrorToFileSystemException(() => fs.writeFileSync(path, contents)); @@ -78,8 +76,7 @@ Future readStdin() async { }); // Node defaults all buffers to 'utf8'. var sink = utf8.decoder.startChunkedConversion(innerSink); - process.stdin.on('data', allowInterop(([Object chunk]) { - assert(chunk != null); + process.stdin.on('data', allowInterop(([Object? chunk]) { sink.add(chunk as List); })); process.stdin.on('end', allowInterop(([Object? _]) { @@ -87,11 +84,10 @@ Future readStdin() async { assert(_ == null); sink.close(); })); - process.stdin.on('error', allowInterop(([Object e]) { - assert(e != null); + process.stdin.on('error', allowInterop(([Object? e]) { stderr.writeln('Failed to read from stdin'); stderr.writeln(e); - completer.completeError(e); + completer.completeError(e!); })); return completer.future; } @@ -178,7 +174,7 @@ DateTime modificationTime(String path) => DateTime.fromMillisecondsSinceEpoch(fs.statSync(path).mtime.getTime())); String? getEnvironmentVariable(String name) => - getProperty(process.env, name) as String?; + getProperty(process.env as Object, name) as String?; /// Runs callback and converts any [JsSystemError]s it throws into /// [FileSystemException]s. @@ -194,7 +190,7 @@ T _systemErrorToFileSystemException(T callback()) { final stderr = Stderr(process.stderr); -bool get hasTerminal => process.stdout.isTTY ?? false; +bool get hasTerminal => process.stdout.isTTY; bool get isWindows => process.platform == 'win32'; @@ -235,11 +231,12 @@ Future> watchDir(String path, {bool poll = false}) { var completer = Completer>(); watcher.on('ready', allowInterop(() { - controller = StreamController(onCancel: () { + // dart-lang/sdk#45348 + var stream = (controller = StreamController(onCancel: () { watcher.close(); - }); - // TODO: no ! - completer.complete(controller!.stream); + })) + .stream; + completer.complete(stream); })); return completer.future; diff --git a/lib/src/logger.dart b/lib/src/logger.dart index 8509ad68c..56b422c67 100644 --- a/lib/src/logger.dart +++ b/lib/src/logger.dart @@ -16,7 +16,7 @@ abstract class Logger { /// Creates a logger that prints warnings to standard error, with terminal /// colors if [color] is `true` (default `false`). - const factory Logger.stderr({required bool color}) = StderrLogger; + const factory Logger.stderr({bool color}) = StderrLogger; /// Emits a warning with the given [message]. /// diff --git a/lib/src/module/forwarded_view.dart b/lib/src/module/forwarded_view.dart index 79918dd96..01ed427e6 100644 --- a/lib/src/module/forwarded_view.dart +++ b/lib/src/module/forwarded_view.dart @@ -40,11 +40,10 @@ class ForwardedModuleView implements Module { static Module ifNecessary( Module inner, ForwardRule rule) { if (rule.prefix == null && - rule.shownMixinsAndFunctions == null && - rule.shownVariables == null && - rule?.hiddenMixinsAndFunctions?.isEmpty ?? - false && rule?.hiddenVariables?.isEmpty ?? - false) { + rule.shownMixinsAndFunctions == null && + rule.shownVariables == null && + (rule.hiddenMixinsAndFunctions?.isEmpty ?? false) && + (rule.hiddenVariables?.isEmpty ?? false)) { return inner; } else { return ForwardedModuleView(inner, rule); diff --git a/lib/src/module/shadowed_view.dart b/lib/src/module/shadowed_view.dart index 5aa500346..222f6dccd 100644 --- a/lib/src/module/shadowed_view.dart +++ b/lib/src/module/shadowed_view.dart @@ -9,6 +9,7 @@ import '../exception.dart'; import '../extend/extender.dart'; import '../module.dart'; import '../util/limited_map_view.dart'; +import '../util/nullable.dart'; import '../utils.dart'; import '../value.dart'; @@ -27,7 +28,7 @@ class ShadowedModuleView implements Module { _inner.transitivelyContainsExtensions; final Map variables; - final Map variableNodes; + final Map? variableNodes; final Map functions; final Map mixins; @@ -56,7 +57,8 @@ class ShadowedModuleView implements Module { ShadowedModuleView(this._inner, {Set? variables, Set? functions, Set? mixins}) : variables = _shadowedMap(_inner.variables, variables), - variableNodes = _shadowedMap(_inner.variableNodes, variables), + variableNodes = + _inner.variableNodes.andThen((map) => _shadowedMap(map, variables)), functions = _shadowedMap(_inner.functions, functions), mixins = _shadowedMap(_inner.mixins, mixins); @@ -64,9 +66,9 @@ class ShadowedModuleView implements Module { this.functions, this.mixins); /// Returns a view of [map] with all keys in [blocklist] omitted. - static Map _shadowedMap>( - M map, Set? blocklist) => - map == null || blocklist == null || !_needsBlocklist(map, blocklist) + static Map _shadowedMap( + Map map, Set? blocklist) => + blocklist == null || !_needsBlocklist(map, blocklist) ? map : LimitedMapView.blocklist(map, blocklist); diff --git a/lib/src/node.dart b/lib/src/node.dart index 65bb12708..45cbaba10 100644 --- a/lib/src/node.dart +++ b/lib/src/node.dart @@ -21,7 +21,6 @@ import 'importer/node.dart'; import 'node/exports.dart'; import 'node/function.dart'; import 'node/render_context.dart'; -import 'node/render_context_options.dart'; import 'node/render_options.dart'; import 'node/render_result.dart'; import 'node/types.dart'; @@ -29,7 +28,6 @@ import 'node/value.dart'; import 'node/utils.dart'; import 'parse/scss.dart'; import 'syntax.dart'; -import 'utils.dart'; import 'util/nullable.dart'; import 'value.dart'; import 'visitor/serialize.dart'; @@ -177,8 +175,8 @@ RenderResult _renderSync(RenderOptions options) { JsError _wrapException(Object exception) { if (exception is SassException) { return _newRenderError(exception.toString().replaceFirst("Error: ", ""), - line: exception.span?.start.line + 1, - column: exception.span?.start.column + 1, + line: exception.span.andThen((span) => span.start.line + 1), + column: exception.span.andThen((span) => span.start.column + 1), file: exception.span?.sourceUrl.andThen(p.fromUri) ?? 'stdin', status: 1); } else { @@ -193,10 +191,11 @@ JsError _wrapException(Object exception) { /// return a `List` if [asynch] is `false`. List _parseFunctions(RenderOptions options, DateTime start, {bool asynch = false}) { - if (options.functions == null) return const []; + var functions = options.functions; + if (functions == null) return const []; var result = []; - jsForEach(options.functions, (signature, callback) { + jsForEach(functions, (signature, callback) { Tuple2 tuple; try { tuple = ScssParser(signature as String).parseSignature(); @@ -305,8 +304,8 @@ RenderContext _contextWithOptions(RenderOptions options, DateTime start) { indentType: options.indentType == 'tab' ? 1 : 0, indentWidth: _parseIndentWidth(options.indentWidth) ?? 2, linefeed: _parseLineFeed(options.linefeed).text, - result: RenderResult( - stats: RenderResultStats( + result: RenderContextResult( + stats: RenderContextResultStats( start: start.millisecondsSinceEpoch, entry: options.file ?? 'data')))); context.options.context = context; @@ -349,9 +348,8 @@ RenderResult _newRenderResult( Uint8List? sourceMapBytes; if (_enableSourceMaps(options)) { var sourceMapOption = options.sourceMap; - var sourceMapPath = sourceMapOption is String - ? sourceMapOption as String - : options.outFile! + '.map'; + var sourceMapPath = + sourceMapOption is String ? sourceMapOption : options.outFile! + '.map'; var sourceMapDir = p.dirname(sourceMapPath); var sourceMap = result.sourceMap!; diff --git a/lib/src/node/render_context.dart b/lib/src/node/render_context.dart index b56e1c766..8fccdabf4 100644 --- a/lib/src/node/render_context.dart +++ b/lib/src/node/render_context.dart @@ -3,9 +3,6 @@ // https://opensource.org/licenses/MIT. import 'package:js/js.dart'; -import 'package:meta/meta.dart'; - -import 'render_context_options.dart'; @JS() @anonymous @@ -14,3 +11,49 @@ class RenderContext { external factory RenderContext({required RenderContextOptions options}); } + +@JS() +@anonymous +class RenderContextOptions { + external String? get file; + external String? get data; + external String get includePaths; + external int get precision; + external int get style; + external int get indentType; + external int get indentWidth; + external String get linefeed; + external RenderContext get context; + external set context(RenderContext value); + external RenderContextResult get result; + + external factory RenderContextOptions( + {String? file, + String? data, + required String includePaths, + required int precision, + required int style, + required int indentType, + required int indentWidth, + required String linefeed, + required RenderContextResult result}); +} + +@JS() +@anonymous +class RenderContextResult { + external RenderContextResultStats get stats; + + external factory RenderContextResult( + {required RenderContextResultStats stats}); +} + +@JS() +@anonymous +class RenderContextResultStats { + external int get start; + external String get entry; + + external factory RenderContextResultStats( + {required int start, required String entry}); +} diff --git a/lib/src/node/render_context_options.dart b/lib/src/node/render_context_options.dart deleted file mode 100644 index f197449f0..000000000 --- a/lib/src/node/render_context_options.dart +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2016 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:js/js.dart'; - -import 'render_context.dart'; -import 'render_result.dart'; - -@JS() -@anonymous -class RenderContextOptions { - external String? get file; - external String? get data; - external String? get includePaths; - external int? get precision; - external int? get style; - external int? get indentType; - external int? get indentWidth; - external String? get linefeed; - external RenderContext? get context; - external set context(RenderContext? value); - external RenderResult? get result; - - external factory RenderContextOptions( - {String? file, - String? data, - String? includePaths, - int? precision, - int? style, - int? indentType, - int? indentWidth, - String? linefeed, - RenderResult? result}); -} diff --git a/lib/src/node/render_result.dart b/lib/src/node/render_result.dart index 5fb8e5757..3e8a9ee54 100644 --- a/lib/src/node/render_result.dart +++ b/lib/src/node/render_result.dart @@ -9,27 +9,29 @@ import 'package:js/js.dart'; @JS() @anonymous class RenderResult { - external Uint8List? get css; + external Uint8List get css; external Uint8List? get map; - external RenderResultStats? get stats; + external RenderResultStats get stats; external factory RenderResult( - {Uint8List? css, Uint8List? map, RenderResultStats? stats}); + {required Uint8List css, + Uint8List? map, + required RenderResultStats stats}); } @JS() @anonymous class RenderResultStats { - external String? get entry; - external int? get start; - external int? get end; - external int? get duration; - external List? get includedFiles; + external String get entry; + external int get start; + external int get end; + external int get duration; + external List get includedFiles; external factory RenderResultStats( - {String? entry, - int? start, - int? end, - int? duration, - List? includedFiles}); + {required String entry, + required int start, + required int end, + required int duration, + required List includedFiles}); } diff --git a/lib/src/node/utils.dart b/lib/src/node/utils.dart index 21f42cc7f..a80ca8a47 100644 --- a/lib/src/node/utils.dart +++ b/lib/src/node/utils.dart @@ -19,7 +19,7 @@ void setToString(Object object, String body()) => /// Adds a `toString()` method to [klass] that forwards to Dart's `toString()`. void forwardToString(Function klass) { - setProperty(getProperty(klass, 'prototype'), 'toString', + setProperty(getProperty(klass, 'prototype') as Object, 'toString', allowInteropCaptureThis((Object thisArg) => thisArg.toString())); } @@ -62,9 +62,9 @@ Object? call3(JSFunction function, Object thisArg, Object arg1, Object arg2, external List _keys(Object? object); /// Invokes [callback] for each key/value pair in [object]. -void jsForEach(Object? object, void callback(Object key, Object? value)) { +void jsForEach(Object object, void callback(Object key, Object? value)) { for (var key in _keys(object)) { - callback(key, getProperty(object!, key)); + callback(key, getProperty(object, key)); } } @@ -76,7 +76,7 @@ Function createClass( String name, Function constructor, Map methods) { var klass = allowInteropCaptureThis(constructor); _defineProperty(klass, 'name', _PropertyDescriptor(value: name)); - var prototype = getProperty(klass, 'prototype'); + var prototype = getProperty(klass, 'prototype') as Object; methods.forEach((name, body) { setProperty(prototype, name, allowInteropCaptureThis(body)); }); @@ -106,7 +106,7 @@ external Object _create(Object prototype); /// Sets the name of `object`'s class to `name`. void setClassName(Object object, String name) { - _defineProperty(getProperty(object, "constructor"), "name", + _defineProperty(getProperty(object, "constructor") as Object, "name", _PropertyDescriptor(value: name)); } @@ -115,9 +115,10 @@ void injectSuperclass(Object object, Function constructor) { var prototype = _getPrototypeOf(object)!; var parent = _getPrototypeOf(prototype); if (parent != null) { - _setPrototypeOf(getProperty(constructor, 'prototype'), parent); + _setPrototypeOf(getProperty(constructor, 'prototype') as Object, parent); } - _setPrototypeOf(prototype, _create(getProperty(constructor, 'prototype'))); + _setPrototypeOf( + prototype, _create(getProperty(constructor, 'prototype') as Object)); } /// Returns whether [value] is truthy according to JavaScript. diff --git a/lib/src/node/value.dart b/lib/src/node/value.dart index a3136c7a6..38f9bc322 100644 --- a/lib/src/node/value.dart +++ b/lib/src/node/value.dart @@ -23,7 +23,7 @@ export 'value/string.dart'; /// Unwraps a value wrapped with [wrapValue]. /// /// If [object] is a JS error, throws it. -Value unwrapValue(Object object) { +Value unwrapValue(Object? object) { if (object != null) { if (object is Value) return object; var value = getProperty(object, 'dartValue'); diff --git a/lib/src/node/value/boolean.dart b/lib/src/node/value/boolean.dart index d4b800fd3..e547ef0e2 100644 --- a/lib/src/node/value/boolean.dart +++ b/lib/src/node/value/boolean.dart @@ -24,7 +24,7 @@ final Function booleanConstructor = () { setClassName(sassTrue, "SassBoolean"); forwardToString(constructor); setProperty( - getProperty(constructor, "prototype"), + getProperty(constructor, "prototype") as Object, "getValue", allowInteropCaptureThis( (Object thisArg) => identical(thisArg, sassTrue))); diff --git a/lib/src/node/value/color.dart b/lib/src/node/value/color.dart index 4de9ee78b..6fa7e99aa 100644 --- a/lib/src/node/value/color.dart +++ b/lib/src/node/value/color.dart @@ -17,7 +17,8 @@ class _NodeSassColor { /// Creates a new `sass.types.Color` object wrapping [value]. Object newNodeSassColor(SassColor value) => - callConstructor(colorConstructor, [null, null, null, null, value]); + callConstructor(colorConstructor, [null, null, null, null, value]) + as Object; /// The JS constructor for the `sass.types.Color` class. final Function colorConstructor = createClass('SassColor', diff --git a/lib/src/node/value/list.dart b/lib/src/node/value/list.dart index 0f2065207..4a3a7f11f 100644 --- a/lib/src/node/value/list.dart +++ b/lib/src/node/value/list.dart @@ -18,7 +18,7 @@ class _NodeSassList { /// Creates a new `sass.types.List` object wrapping [value]. Object newNodeSassList(SassList value) => - callConstructor(listConstructor, [null, null, value]); + callConstructor(listConstructor, [null, null, value]) as Object; /// The JS constructor for the `sass.types.List` class. final Function listConstructor = createClass('SassList', diff --git a/lib/src/node/value/map.dart b/lib/src/node/value/map.dart index 45bf655f3..1e8f92324 100644 --- a/lib/src/node/value/map.dart +++ b/lib/src/node/value/map.dart @@ -18,7 +18,7 @@ class _NodeSassMap { /// Creates a new `sass.types.Map` object wrapping [value]. Object newNodeSassMap(SassMap value) => - callConstructor(mapConstructor, [null, value]); + callConstructor(mapConstructor, [null, value]) as Object; /// The JS constructor for the `sass.types.Map` class. final Function mapConstructor = createClass('SassMap', @@ -37,23 +37,21 @@ final Function mapConstructor = createClass('SassMap', RangeError.checkValidIndex(index, oldMap, "index"); var newKey = unwrapValue(key); - // TODO: no var - Map newMap = {}; + var newMap = {}; var i = 0; - for (var oldKey in thisArg.dartValue.contents.keys) { + for (var oldEntry in thisArg.dartValue.contents.entries) { if (i == index) { - newMap[newKey] = oldMap[oldKey]; + newMap[newKey] = oldEntry.value; } else { - if (newKey == oldKey) { + if (newKey == oldEntry.key) { throw ArgumentError.value(key, 'key', "is already in the map"); } - newMap[oldKey] = oldMap[oldKey]; + newMap[oldEntry.key] = oldEntry.value; } i++; } - // TODO: no as - thisArg.dartValue = SassMap(newMap as Map); + thisArg.dartValue = SassMap(newMap); }, 'setValue': (_NodeSassMap thisArg, int index, Object value) { var key = thisArg.dartValue.contents.keys.elementAt(index); diff --git a/lib/src/node/value/number.dart b/lib/src/node/value/number.dart index 3e0c34fa3..918b82570 100644 --- a/lib/src/node/value/number.dart +++ b/lib/src/node/value/number.dart @@ -17,7 +17,7 @@ class _NodeSassNumber { /// Creates a new `sass.types.Number` object wrapping [value]. Object newNodeSassNumber(SassNumber value) => - callConstructor(numberConstructor, [null, null, value]); + callConstructor(numberConstructor, [null, null, value]) as Object; /// The JS constructor for the `sass.types.Number` class. final Function numberConstructor = createClass('SassNumber', diff --git a/lib/src/node/value/string.dart b/lib/src/node/value/string.dart index cd170adc6..17cd29cee 100644 --- a/lib/src/node/value/string.dart +++ b/lib/src/node/value/string.dart @@ -17,7 +17,7 @@ class _NodeSassString { /// Creates a new `sass.types.String` object wrapping [value]. Object newNodeSassString(SassString value) => - callConstructor(stringConstructor, [null, value]); + callConstructor(stringConstructor, [null, value]) as Object; /// The JS constructor for the `sass.types.String` class. final Function stringConstructor = createClass('SassString', diff --git a/lib/src/parse/css.dart b/lib/src/parse/css.dart index 14f0f03d5..2a16d2cf6 100644 --- a/lib/src/parse/css.dart +++ b/lib/src/parse/css.dart @@ -64,7 +64,6 @@ class CssParser extends ScssParser { almostAnyValue(); error("This at-rule isn't allowed in plain CSS.", scanner.spanFrom(start)); - break; case "import": return _cssImportRule(start); diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index 592ce609a..6786c8384 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -283,7 +283,6 @@ class Parser { var wroteNewline = false; loop: while (true) { - // TODO: no next! var next = scanner.peekChar(); switch (next) { case $backslash: @@ -325,7 +324,7 @@ class Parser { case $lparen: case $lbrace: case $lbracket: - buffer.writeCharCode(next!); + buffer.writeCharCode(next!); // dart-lang/sdk#45357 brackets.add(opposite(scanner.readChar())); wroteNewline = false; break; @@ -334,7 +333,7 @@ class Parser { case $rbrace: case $rbracket: if (brackets.isEmpty) break loop; - buffer.writeCharCode(next!); + buffer.writeCharCode(next!); // dart-lang/sdk#45357 scanner.expectChar(brackets.removeLast()); wroteNewline = false; break; @@ -661,8 +660,7 @@ class Parser { /// Throws an error associated with [span]. @protected - @alwaysThrows - void error(String message, FileSpan span) => + Never error(String message, FileSpan span) => throw StringScannerException(message, span, scanner.string); /// Runs callback and, if it throws a [SourceSpanFormatException], rethrows it diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index ab3ca0727..006554553 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -15,8 +15,7 @@ import 'stylesheet.dart'; /// A parser for the indented syntax. class SassParser extends StylesheetParser { int get currentIndentation => _currentIndentation; - // TODO: var - int _currentIndentation = 0; + var _currentIndentation = 0; /// The indentation level of the next source line after the scanner's /// position, or `null` if that hasn't been computed yet. @@ -175,25 +174,19 @@ class SassParser extends StylesheetParser { case $dollar: return variableDeclarationWithoutNamespace(); - break; case $slash: switch (scanner.peekChar(1)) { case $slash: return _silentComment(); - break; case $asterisk: return _loudComment(); - break; default: return child(); - break; } - break; default: return child(); - break; } } @@ -341,7 +334,6 @@ class SassParser extends StylesheetParser { switch (scanner.peekChar()) { case $semicolon: scanner.error("semicolons aren't allowed in the indented syntax."); - return; case $cr: scanner.readChar(); if (scanner.peekChar() == $lf) scanner.readChar(); @@ -392,10 +384,8 @@ class SassParser extends StylesheetParser { /// Consumes indentation whitespace and returns the indentation level of the /// next line. int _readIndentation() { - // TODO: This "!" is totally bogus - var nextIndentation = _nextIndentation!; - if (nextIndentation == null) _peekIndentation(); - var currentIndentation = _currentIndentation = nextIndentation; + var currentIndentation = + _currentIndentation = _nextIndentation ??= _peekIndentation(); scanner.state = _nextIndentationEnd!; _nextIndentation = null; _nextIndentationEnd = null; @@ -404,8 +394,8 @@ class SassParser extends StylesheetParser { /// Returns the indentation level of the next line. int _peekIndentation() { - var nextIndentation = _nextIndentation; - if (nextIndentation != null) return nextIndentation; + var cached = _nextIndentation; + if (cached != null) return cached; if (scanner.isDone) { _nextIndentation = 0; @@ -418,13 +408,10 @@ class SassParser extends StylesheetParser { scanner.error("Expected newline.", position: scanner.position); } - bool containsTab; - bool containsSpace; + var containsTab = false; + var containsSpace = false; + var nextIndentation = 0; do { - containsTab = false; - containsSpace = false; - nextIndentation = 0; - while (true) { var next = scanner.peekChar(); if (next == $space) { @@ -449,8 +436,7 @@ class SassParser extends StylesheetParser { _checkIndentationConsistency(containsTab, containsSpace); _nextIndentation = nextIndentation; - // TODO: no ! - if (nextIndentation! > 0) _spaces ??= containsSpace; + if (nextIndentation > 0) _spaces ??= containsSpace; _nextIndentationEnd = scanner.state; scanner.state = start; return nextIndentation; diff --git a/lib/src/parse/selector.dart b/lib/src/parse/selector.dart index ceb5d1d1b..78af93ca8 100644 --- a/lib/src/parse/selector.dart +++ b/lib/src/parse/selector.dart @@ -263,7 +263,6 @@ class SelectorParser extends Parser { default: scanner.error('Expected "]".', position: start.position); - throw "Unreachable"; } } diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 3441f0d16..43838302c 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -189,7 +189,6 @@ abstract class StylesheetParser extends Parser { case $rbrace: scanner.error('unmatched "}".', length: 1); - break; default: return _inStyleRule || _inUnknownAtRule || _inMixin || _inContentBlock @@ -212,14 +211,13 @@ abstract class StylesheetParser extends Parser { /// used for the declaration. @protected VariableDeclaration variableDeclarationWithoutNamespace( - [String? namespace, LineScannerState? start]) { + [String? namespace, LineScannerState? start_]) { var precedingComment = lastSilentComment; lastSilentComment = null; - start ??= scanner.state; + var start = start_ ?? scanner.state; // dart-lang/sdk#45348 var name = variableName(); - // TODO: no start! - if (namespace != null) _assertPublic(name, () => scanner.spanFrom(start!)); + if (namespace != null) _assertPublic(name, () => scanner.spanFrom(start)); if (plainCss) { error("Sass variables aren't allowed in plain CSS.", @@ -482,9 +480,10 @@ abstract class StylesheetParser extends Parser { /// Consumes a [StyleRule], optionally with a [buffer] that may contain some /// text that has already been parsed. - StyleRule _styleRule([InterpolationBuffer? buffer, LineScannerState? start]) { + StyleRule _styleRule( + [InterpolationBuffer? buffer, LineScannerState? start_]) { _isUseAllowed = false; - start ??= scanner.state; + var start = start_ ?? scanner.state; // dart-lang/sdk#45348 var interpolation = styleRuleSelector(); if (buffer != null) { @@ -504,8 +503,7 @@ abstract class StylesheetParser extends Parser { _inStyleRule = wasInStyleRule; - // TODO: no start! - return StyleRule(interpolation, children, scanner.spanFrom(start!)); + return StyleRule(interpolation, children, scanner.spanFrom(start)); }); } @@ -910,7 +908,6 @@ abstract class StylesheetParser extends Parser { case "not": case "clamp": error("Invalid function name.", scanner.spanFrom(start)); - break; } whitespace(); @@ -954,9 +951,8 @@ abstract class StylesheetParser extends Parser { return _withChildren(child, start, (children, span) { _inControlDirective = wasInControlDirective; - - // TODO: no exclusive! - return ForRule(variable, from, to, children, span, exclusive: exclusive!); + return ForRule(variable, from, to, children, span, + exclusive: exclusive!); // dart-lang/sdk#45348 }); } @@ -1204,14 +1200,16 @@ abstract class StylesheetParser extends Parser { ContentBlock? content; if (contentArguments != null || lookingAtChildren()) { - contentArguments ??= ArgumentDeclaration.empty(span: scanner.emptySpan); - var wasInContentBlock = _inContentBlock; _inContentBlock = true; - content = _withChildren(_statement, start, (children, span) { - // TODO: no ! - return ContentBlock(contentArguments!, children, span); - }); + content = _withChildren( + _statement, + start, + (children, span) => ContentBlock( + contentArguments ?? + ArgumentDeclaration.empty(span: scanner.emptySpan), + children, + span)); _inContentBlock = wasInContentBlock; } else { expectStatementSeparator(); @@ -1634,10 +1632,9 @@ relase. For details, see http://bit.ly/moz-document. @protected Expression expression( {bool bracketList = false, bool singleEquals = false, bool until()?}) { - // TODO: no ! throughout if (until != null && until()) scanner.error("Expected expression."); - late LineScannerState beforeBracket; + LineScannerState? beforeBracket; if (bracketList) { beforeBracket = scanner.state; scanner.expectChar($lbracket); @@ -1652,19 +1649,25 @@ relase. For details, see http://bit.ly/moz-document. var start = scanner.state; var wasInParentheses = _inParentheses; - List? commaExpressions; + // We use the convention below of referring to nullable variables that are + // shared across anonymous functions in this method with a trailling + // underscore. This allows us to copy them to non-underscored local + // variables to make it easier for Dart's type system to reason about their + // local nullability. + + List? commaExpressions_; - List? spaceExpressions; + List? spaceExpressions_; - // Operators whose right-hand operands are not fully parsed yet, in order of + // Operators whose right-hand operands_ are not fully parsed yet, in order of // appearance in the document. Because a low-precedence operator will cause - // parsing to finish for all preceding higher-precedence operators, this is + // parsing to finish for all preceding higher-precedence operators_, this is // naturally ordered from lowest to highest precedence. - List? operators; + List? operators_; - // The left-hand sides of [operators]. `operands[n]` is the left-hand side - // of `operators[n]`. - List? operands; + // The left-hand sides of [operators_]. `operands_[n]` is the left-hand side + // of `operators_[n]`. + List? operands_; /// Whether the single expression parsed so far may be interpreted as /// slash-separated numbers. @@ -1677,44 +1680,51 @@ relase. For details, see http://bit.ly/moz-document. /// /// foo, bar /// ^ - Expression? singleExpression = _singleExpression(); + Expression? singleExpression_ = _singleExpression(); // Resets the scanner state to the state it was at at the beginning of the // expression, except for [_inParentheses]. void resetState() { - commaExpressions = null; - spaceExpressions = null; - operators = null; - operands = null; + commaExpressions_ = null; + spaceExpressions_ = null; + operators_ = null; + operands_ = null; scanner.state = start; allowSlash = lookingAtNumber(); - singleExpression = _singleExpression(); + singleExpression_ = _singleExpression(); } void resolveOneOperation() { - if (operators == null) { - throw StateError("operators must be set for resolveOneOperation()."); + var operator = operators_!.removeLast(); + var operands = operands_!; + + var singleExpression = singleExpression_; + if (singleExpression == null) { + scanner.error("Expected expression.", + position: scanner.position - operator.operator.length, + length: operator.operator.length); } - var operator = operators!.removeLast(); if (operator != BinaryOperator.dividedBy) allowSlash = false; if (allowSlash && !_inParentheses) { - singleExpression = BinaryOperationExpression.slash( - operands!.removeLast(), singleExpression!); + singleExpression_ = BinaryOperationExpression.slash( + operands.removeLast(), singleExpression); } else { - singleExpression = BinaryOperationExpression( - operator, operands!.removeLast(), singleExpression!); + singleExpression_ = BinaryOperationExpression( + operator, operands.removeLast(), singleExpression); } } void resolveOperations() { + var operators = operators_; if (operators == null) return; - while (operators!.isNotEmpty) { + while (operators.isNotEmpty) { resolveOneOperation(); } } void addSingleExpression(Expression expression, {bool number = false}) { + var singleExpression = singleExpression_; if (singleExpression != null) { // If we discover we're parsing a list whose first element is a division // operation, and we're in parentheses, reparse outside of a paren @@ -1728,15 +1738,15 @@ relase. For details, see http://bit.ly/moz-document. } } - spaceExpressions ??= []; + var spaceExpressions = spaceExpressions_ ??= []; resolveOperations(); - spaceExpressions!.add(singleExpression!); + spaceExpressions.add(singleExpression); allowSlash = number; } else if (!number) { allowSlash = false; } - singleExpression = expression; + singleExpression_ = expression; } void addOperator(BinaryOperator operator) { @@ -1750,29 +1760,40 @@ relase. For details, see http://bit.ly/moz-document. allowSlash = allowSlash && operator == BinaryOperator.dividedBy; - operators ??= []; - operands ??= []; - while (operators!.isNotEmpty && - operators!.last.precedence >= operator.precedence) { + var operators = operators_ ??= []; + var operands = operands_ ??= []; + while (operators.isNotEmpty && + operators.last.precedence >= operator.precedence) { resolveOneOperation(); } - operators!.add(operator); + operators.add(operator); + + var singleExpression = singleExpression_; + if (singleExpression == null) { + scanner.error("Expected expression.", + position: scanner.position - operator.operator.length, + length: operator.operator.length); + } + operands.add(singleExpression); - operands!.add(singleExpression!); whitespace(); allowSlash = allowSlash && lookingAtNumber(); - singleExpression = _singleExpression(); - allowSlash = allowSlash && singleExpression is NumberExpression; + singleExpression_ = _singleExpression(); + allowSlash = allowSlash && singleExpression_ is NumberExpression; } void resolveSpaceExpressions() { resolveOperations(); + var spaceExpressions = spaceExpressions_; if (spaceExpressions != null) { - spaceExpressions!.add(singleExpression!); - singleExpression = - ListExpression(spaceExpressions!, ListSeparator.space); - spaceExpressions = null; + var singleExpression = singleExpression_; + if (singleExpression == null) scanner.error("Expected expression."); + + spaceExpressions.add(singleExpression); + singleExpression_ = + ListExpression(spaceExpressions, ListSeparator.space); + spaceExpressions_ = null; } } @@ -1854,7 +1875,7 @@ relase. For details, see http://bit.ly/moz-document. break; case $plus: - if (singleExpression == null) { + if (singleExpression_ == null) { addSingleExpression(_unaryOperation()); } else { scanner.readChar(); @@ -1866,12 +1887,12 @@ relase. For details, see http://bit.ly/moz-document. var next = scanner.peekChar(1); if ((isDigit(next) || next == $dot) && // Make sure `1-2` parses as `1 - 2`, not `1 (-2)`. - (singleExpression == null || + (singleExpression_ == null || isWhitespace(scanner.peekChar(-1)))) { addSingleExpression(_number(), number: true); } else if (_lookingAtInterpolatedIdentifier()) { addSingleExpression(identifierLike()); - } else if (singleExpression == null) { + } else if (singleExpression_ == null) { addSingleExpression(_unaryOperation()); } else { scanner.readChar(); @@ -1880,7 +1901,7 @@ relase. For details, see http://bit.ly/moz-document. break; case $slash: - if (singleExpression == null) { + if (singleExpression_ == null) { addSingleExpression(_unaryOperation()); } else { scanner.readChar(); @@ -2002,14 +2023,15 @@ relase. For details, see http://bit.ly/moz-document. } } - commaExpressions ??= []; + var commaExpressions = commaExpressions_ ??= []; + var singleExpression = singleExpression_; if (singleExpression == null) scanner.error("Expected expression."); resolveSpaceExpressions(); - commaExpressions!.add(singleExpression!); + commaExpressions.add(singleExpression); scanner.readChar(); allowSlash = true; - singleExpression = null; + singleExpression_ = null; break; default: @@ -2023,26 +2045,29 @@ relase. For details, see http://bit.ly/moz-document. } if (bracketList) scanner.expectChar($rbracket); + + var commaExpressions = commaExpressions_; + var spaceExpressions = spaceExpressions_; if (commaExpressions != null) { resolveSpaceExpressions(); _inParentheses = wasInParentheses; - if (singleExpression != null) commaExpressions!.add(singleExpression!); - return ListExpression(commaExpressions!, ListSeparator.comma, - brackets: bracketList, - span: bracketList ? scanner.spanFrom(beforeBracket) : null); + var singleExpression = singleExpression_; + if (singleExpression != null) commaExpressions.add(singleExpression); + return ListExpression(commaExpressions, ListSeparator.comma, + brackets: bracketList, span: beforeBracket.andThen(scanner.spanFrom)); } else if (bracketList && spaceExpressions != null) { resolveOperations(); return ListExpression( - spaceExpressions!..add(singleExpression!), ListSeparator.space, - brackets: true, span: scanner.spanFrom(beforeBracket)); + spaceExpressions..add(singleExpression_!), ListSeparator.space, + brackets: true, span: beforeBracket.andThen(scanner.spanFrom)); } else { resolveSpaceExpressions(); if (bracketList) { - singleExpression = ListExpression( - [singleExpression!], ListSeparator.undecided, - brackets: true, span: scanner.spanFrom(beforeBracket)); + singleExpression_ = ListExpression( + [singleExpression_!], ListSeparator.undecided, + brackets: true, span: beforeBracket.andThen(scanner.spanFrom)); } - return singleExpression!; + return singleExpression_!; } } @@ -2095,7 +2120,6 @@ relase. For details, see http://bit.ly/moz-document. } else { return identifierLike(); } - break; case $0: case $1: @@ -2108,7 +2132,6 @@ relase. For details, see http://bit.ly/moz-document. case $8: case $9: return _number(); - break; case $a: case $b: @@ -2163,12 +2186,10 @@ relase. For details, see http://bit.ly/moz-document. case $_: case $backslash: return identifierLike(); - break; default: if (first != null && first >= 0x80) return identifierLike(); scanner.error("Expected expression."); - break; } } @@ -2355,8 +2376,7 @@ relase. For details, see http://bit.ly/moz-document. /// Consumes a unary operation expression. UnaryOperationExpression _unaryOperation() { var start = scanner.state; - // TODO: no ! - var operator = _unaryOperatorFor(scanner.readChar())!; + var operator = _unaryOperatorFor(scanner.readChar()); if (operator == null) { scanner.error("Expected unary operator.", position: scanner.position - 1); } else if (plainCss && operator != UnaryOperator.divide) { @@ -3047,7 +3067,6 @@ relase. For details, see http://bit.ly/moz-document. var wroteNewline = false; loop: while (true) { - // TODO: no next!s var next = scanner.peekChar(); switch (next) { case $backslash: @@ -3102,7 +3121,7 @@ relase. For details, see http://bit.ly/moz-document. case $lparen: case $lbrace: case $lbracket: - buffer.writeCharCode(next!); + buffer.writeCharCode(next!); // dart-lang/sdk#45357 brackets.add(opposite(scanner.readChar())); wroteNewline = false; break; @@ -3111,7 +3130,7 @@ relase. For details, see http://bit.ly/moz-document. case $rbrace: case $rbracket: if (brackets.isEmpty) break loop; - buffer.writeCharCode(next!); + buffer.writeCharCode(next!); // dart-lang/sdk#45357 scanner.expectChar(brackets.removeLast()); wroteNewline = false; break; @@ -3329,8 +3348,8 @@ relase. For details, see http://bit.ly/moz-document. whitespace(); buffer.add(_expressionUntilComparison()); - // TODO: no ! if ((next == $langle || next == $rangle) && scanner.scanChar(next!)) { + // dart-lang/sdk#45356 buffer.writeCharCode($space); buffer.writeCharCode(next); if (scanner.scanChar($equal)) buffer.writeCharCode($equal); diff --git a/lib/src/stylesheet_graph.dart b/lib/src/stylesheet_graph.dart index 6db267906..3d1d178a6 100644 --- a/lib/src/stylesheet_graph.dart +++ b/lib/src/stylesheet_graph.dart @@ -3,7 +3,6 @@ // https://opensource.org/licenses/MIT. import 'package:collection/collection.dart'; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:tuple/tuple.dart'; @@ -67,10 +66,8 @@ class StylesheetGraph { /// /// Returns `null` if the import cache can't find a stylesheet at [url]. StylesheetNode? _add(Uri url, [Importer? baseImporter, Uri? baseUrl]) { - // TODO: no as - var tuple = _ignoreErrors((() => importCache.canonicalize(url, - baseImporter: baseImporter, - baseUrl: baseUrl)!) as Tuple3 Function()); + var tuple = _ignoreErrors(() => importCache.canonicalize(url, + baseImporter: baseImporter, baseUrl: baseUrl)); if (tuple == null) return null; addCanonical(tuple.item1, tuple.item2, tuple.item3); @@ -98,10 +95,8 @@ class StylesheetGraph { var node = _nodes[canonicalUrl]; if (node != null) return const {}; - // TODO: no as - var stylesheet = _ignoreErrors((() => - importCache.importCanonical(importer, canonicalUrl, originalUrl)!) - as Stylesheet Function()); + var stylesheet = _ignoreErrors( + () => importCache.importCanonical(importer, canonicalUrl, originalUrl)); if (stylesheet == null) return const {}; node = StylesheetNode._(stylesheet, importer, canonicalUrl, @@ -150,10 +145,8 @@ class StylesheetGraph { _transitiveModificationTimes.clear(); importCache.clearImport(canonicalUrl); - // TODO: no as - var stylesheet = _ignoreErrors((() => - importCache.importCanonical(node.importer, canonicalUrl)!) - as Stylesheet Function()); + var stylesheet = _ignoreErrors( + () => importCache.importCanonical(node.importer, canonicalUrl)); if (stylesheet == null) return false; node._stylesheet = stylesheet; @@ -267,11 +260,8 @@ class StylesheetGraph { StylesheetNode? _nodeFor( Uri url, Importer baseImporter, Uri baseUrl, Set active, {bool forImport = false}) { - // TODO: no as - var tuple = _ignoreErrors((() => importCache.canonicalize(url, - baseImporter: baseImporter, - baseUrl: baseUrl, - forImport: forImport)!) as Tuple3 Function()); + var tuple = _ignoreErrors(() => importCache.canonicalize(url, + baseImporter: baseImporter, baseUrl: baseUrl, forImport: forImport)); // If an import fails, let the evaluator surface that error rather than // surfacing it here. @@ -288,10 +278,8 @@ class StylesheetGraph { /// error will be produced during compilation. if (active.contains(canonicalUrl)) return null; - // TODO: no as - var stylesheet = _ignoreErrors((() => - importCache.importCanonical(importer, canonicalUrl, resolvedUrl)!) - as Stylesheet Function()); + var stylesheet = _ignoreErrors( + () => importCache.importCanonical(importer, canonicalUrl, resolvedUrl)); if (stylesheet == null) return null; active.add(canonicalUrl); diff --git a/lib/src/util/limited_map_view.dart b/lib/src/util/limited_map_view.dart index 50ad9f7ac..754e5c9f8 100644 --- a/lib/src/util/limited_map_view.dart +++ b/lib/src/util/limited_map_view.dart @@ -44,8 +44,7 @@ class LimitedMapView extends UnmodifiableMapBase { if (!blocklist.contains(key)) key }; - // TODO: no as - V? operator [](Object? key) => _keys.contains(key) ? _map[key as K] : null; + V? operator [](Object? key) => _keys.contains(key) ? _map[key] : null; bool containsKey(Object? key) => _keys.contains(key); V? remove(Object? key) => _keys.contains(key) ? _map.remove(key) : null; diff --git a/lib/src/util/nullable.dart b/lib/src/util/nullable.dart index 373baf4a3..7d1511dc8 100644 --- a/lib/src/util/nullable.dart +++ b/lib/src/util/nullable.dart @@ -29,8 +29,7 @@ extension NullableMapExtension on Map? { /// This is the equivalent of `map?.[key]`, if such a thing existed. V? andGet(Object? key) { var self = this; - // TODO: no as - return self == null ? null : self[key as K]; + return self == null ? null : self[key]; } } @@ -38,7 +37,7 @@ extension SetExtension on Set { /// Destructively removes the `null` element from this set, if it exists, and /// returns a view of it casted to a non-nullable type. Set removeNull() { - this.remove(null); - return this.cast(); + remove(null); + return cast(); } } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index d9740748b..6e56f8aa9 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -255,7 +255,7 @@ String unvendor(String name) { } /// Returns whether [string1] and [string2] are equal, ignoring ASCII case. -bool equalsIgnoreCase(String? string1, String string2) { +bool equalsIgnoreCase(String? string1, String? string2) { if (identical(string1, string2)) return true; if (string1 == null || string2 == null) return false; if (string1.length != string2.length) return false; diff --git a/lib/src/value.dart b/lib/src/value.dart index bac1df40b..cff8293ce 100644 --- a/lib/src/value.dart +++ b/lib/src/value.dart @@ -201,7 +201,7 @@ abstract class Value implements ext.Value { result.add(complex.text); } else if (complex is SassList && complex.separator == ListSeparator.space) { - var string = complex._selectorString(); + var string = complex._selectorStringOrNull(); if (string == null) return null; result.add(string); } else { diff --git a/lib/src/value/color.dart b/lib/src/value/color.dart index 7747d5c90..686b4e9ec 100644 --- a/lib/src/value/color.dart +++ b/lib/src/value/color.dart @@ -13,8 +13,6 @@ import '../visitor/interface/value.dart'; import 'external/value.dart' as ext; class SassColor extends Value implements ext.SassColor { - // TODO: lates all around - int get red { if (_red == null) _hslToRgb(); return _red!; @@ -113,8 +111,7 @@ class SassColor extends Value implements ext.SassColor { var factor = 1 - scaledWhiteness - scaledBlackness; int toRgb(num hue) { - // TODO: var - num channel = _hueToRgb(0, 1, hue) * factor + scaledWhiteness; + var channel = _hueToRgb(0, 1, hue) * factor + scaledWhiteness; return fuzzyRound(channel * 255); } diff --git a/lib/src/value/external/list.dart b/lib/src/value/external/list.dart index 97e666ab0..d97124c2f 100644 --- a/lib/src/value/external/list.dart +++ b/lib/src/value/external/list.dart @@ -18,10 +18,8 @@ abstract class SassList extends Value { /// Returns an empty list with the given [separator] and [brackets]. /// /// The [separator] defaults to [ListSeparator.undecided], and [brackets] defaults to `false`. - // TODO: No ? for brackets - const factory SassList.empty( - {ListSeparator? separator, - required bool brackets}) = internal.SassList.empty; + const factory SassList.empty({ListSeparator? separator, bool brackets}) = + internal.SassList.empty; /// Returns an empty list with the given [separator] and [brackets]. factory SassList(Iterable contents, ListSeparator separator, diff --git a/lib/src/value/external/string.dart b/lib/src/value/external/string.dart index 063399a72..eec9de362 100644 --- a/lib/src/value/external/string.dart +++ b/lib/src/value/external/string.dart @@ -46,14 +46,12 @@ abstract class SassString extends Value { /// Creates an empty string. /// /// The [quotes] argument defaults to `false`. - // TODO: no required - factory SassString.empty({required bool quotes}) = internal.SassString.empty; + factory SassString.empty({bool quotes}) = internal.SassString.empty; /// Creates a string with the given [text]. /// /// The [quotes] argument defaults to `false`. - // TODO: no ? - factory SassString(String text, {required bool quotes}) = internal.SassString; + factory SassString(String text, {bool quotes}) = internal.SassString; /// Converts [sassIndex] into a Dart-style index into [text]. /// diff --git a/lib/src/value/number.dart b/lib/src/value/number.dart index c3b242327..308f82e53 100644 --- a/lib/src/value/number.dart +++ b/lib/src/value/number.dart @@ -360,8 +360,7 @@ abstract class SassNumber extends Value implements ext.SassNumber { if (factor == null) return false; value *= factor; return true; - // TODO: no as - }, orElse: (() => throw _compatibilityException()) as String Function()?); + }, orElse: () => throw _compatibilityException()); } var oldDenominators = denominatorUnits.toList(); @@ -371,8 +370,7 @@ abstract class SassNumber extends Value implements ext.SassNumber { if (factor == null) return false; value /= factor; return true; - // TODO: no as - }, orElse: (() => throw _compatibilityException()) as String Function()?); + }, orElse: () => throw _compatibilityException()); } if (oldNumerators.isNotEmpty || oldDenominators.isNotEmpty) { diff --git a/lib/src/value/string.dart b/lib/src/value/string.dart index c7d0e964c..1a5d4ba85 100644 --- a/lib/src/value/string.dart +++ b/lib/src/value/string.dart @@ -22,14 +22,7 @@ class SassString extends Value implements ext.SassString { final bool hasQuotes; - // TODO: late - - int get sassLength { - _sassLength ??= text.runes.length; - return _sassLength!; - } - - int? _sassLength; + late final int sassLength = text.runes.length; bool get isSpecialNumber { if (hasQuotes) return false; diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 06639cf90..9a7c5f994 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -7,7 +7,6 @@ import 'dart:math' as math; import 'package:charcode/charcode.dart'; import 'package:collection/collection.dart'; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:source_span/source_span.dart'; import 'package:stack_trace/stack_trace.dart'; @@ -229,7 +228,6 @@ class _EvaluateVisitor /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - // TODO: no cast final _stack = >[]; /// Whether we're running in Node Sass-compatibility mode. @@ -428,7 +426,7 @@ class _EvaluateVisitor AsyncBuiltInCallable.mixin("load-css", r"$url, $with: null", (arguments) async { var url = Uri.parse(arguments[0].assertString("url").text); - var withMap = arguments[1].realNull?.assertMap("with")?.contents; + var withMap = arguments[1].realNull?.assertMap("with").contents; var callableNode = _callableNode!; var configuration = const Configuration.empty(); @@ -624,8 +622,7 @@ class _EvaluateVisitor bool namesInErrors = false}) async { var url = stylesheet.span?.sourceUrl; - // TODO: no ! - var alreadyLoaded = _modules[url!]; + var alreadyLoaded = _modules[url]; if (alreadyLoaded != null) { var currentConfiguration = configuration ?? _configuration; if (currentConfiguration is ExplicitConfiguration) { @@ -718,12 +715,12 @@ class _EvaluateVisitor /// Returns a copy of [_root.children] with [_outOfOrderImports] inserted /// after [_endOfImports], if necessary. List _addOutOfOrderImports() { - if (_outOfOrderImports == null) return _root.children; + var outOfOrderImports = _outOfOrderImports; + if (outOfOrderImports == null) return _root.children; return [ ..._root.children.take(_endOfImports), - // TODO: no ! - ..._outOfOrderImports!, + ...outOfOrderImports, ..._root.children.skip(_endOfImports) ]; } @@ -777,8 +774,7 @@ class _EvaluateVisitor // canonical URL). It's important that we create this in topological order, // so that by the time we're processing a module we've already filled in all // its downstream extenders and we can use them to extend that module. - // TODO: var - Map> downstreamExtenders = >{}; + var downstreamExtenders = >{}; /// Extensions that haven't yet been satisfied by some upstream module. This /// adds extensions when they're defined but not satisfied, and removes them @@ -801,10 +797,9 @@ class _EvaluateVisitor if (module.extender.isEmpty) continue; for (var upstream in module.upstream) { - if (upstream.url == null) continue; - downstreamExtenders - .putIfAbsent(upstream.url, () => []) - .add(module.extender); + var url = upstream.url; + if (url == null) continue; + downstreamExtenders.putIfAbsent(url, () => []).add(module.extender); } // Remove all extensions that are now satisfied after adding downstream @@ -820,8 +815,7 @@ class _EvaluateVisitor } /// Throws an exception indicating that [extension] is unsatisfied. - @alwaysThrows - void _throwForUnsatisfiedExtension(Extension extension) { + Never _throwForUnsatisfiedExtension(Extension extension) { throw SassException( 'The target selector was not found.\n' 'Use "@extend ${extension.target} !optional" to avoid this error.', @@ -945,8 +939,7 @@ class _EvaluateVisitor var parent = _parent; int? innermostContiguous; - var i = 0; - for (; i < nodes.length; i++) { + for (var i = 0; i < nodes.length; i++) { while (parent != nodes[i]) { innermostContiguous = null; parent = parent.parent!; @@ -956,7 +949,6 @@ class _EvaluateVisitor } if (parent != _root) return _root; - // TODO: no ! var root = nodes[innermostContiguous!]; nodes.removeRange(innermostContiguous, nodes.length); return root; @@ -1054,7 +1046,7 @@ class _EvaluateVisitor name = CssValue("$_declarationName-${name.value}", name.span); } var cssValue = await node.value.andThen( - (value) async => CssValue(await value.accept(this), value.span))!; + (value) async => CssValue(await value.accept(this), value.span)); // 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. @@ -1069,11 +1061,12 @@ class _EvaluateVisitor "Custom property values may not be empty.", cssValue.span); } - if (node.children != null) { + var children = node.children; + if (children != null) { var oldDeclarationName = _declarationName; _declarationName = name.value; await _environment.scope(() async { - for (var child in node.children) { + for (var child in children) { await child.accept(this); } }, when: node.hasDeclarations); @@ -1197,7 +1190,7 @@ class _EvaluateVisitor () async { var styleRule = _styleRule; if (styleRule == null || _inKeyframes) { - for (var child in node.children) { + for (var child in node.children!) { await child.accept(this); } } else { @@ -1206,7 +1199,7 @@ class _EvaluateVisitor // // For example, "a {@foo {b: c}}" should produce "@foo {a {b: c}}". await _withParent(styleRule.copyWithoutChildren(), () async { - for (var child in node.children) { + for (var child in node.children!) { await child.accept(this); } }, scopeWhen: false); @@ -1360,8 +1353,7 @@ class _EvaluateVisitor return await _environment.scope( () => _handleReturn( - // TODO: no ! - clause!.children, + clause!.children, // dart-lang/sdk#45348 (child) => child.accept(this)), semiGlobal: true, when: clause.hasDeclarations); @@ -1490,7 +1482,7 @@ class _EvaluateVisitor if (importCache != null) { var tuple = await importCache.import(Uri.parse(url), baseImporter: _importer, - baseUrl: baseUrl ?? _stylesheet?.span?.sourceUrl, + baseUrl: baseUrl ?? _stylesheet.span?.sourceUrl, forImport: forImport); if (tuple != null) return tuple; } else { @@ -1510,12 +1502,11 @@ class _EvaluateVisitor } catch (error) { String? message; try { - // TODO: as String! - message = error.message as String?; + message = (error as dynamic).message as String; } catch (_) { message = error.toString(); } - throw _exception(message!); + throw _exception(message); } finally { _importSpan = null; } @@ -1645,9 +1636,8 @@ class _EvaluateVisitor } var queries = await _visitMediaQueries(node.query); - var mergedQueries = _mediaQueries.andThen(((mediaQueries) => - _mergeMediaQueries(mediaQueries, queries)!) - as List Function(List)?); + var mergedQueries = _mediaQueries + .andThen((mediaQueries) => _mergeMediaQueries(mediaQueries, queries)); if (mergedQueries != null && mergedQueries.isEmpty) return null; await _withParent( @@ -1950,8 +1940,7 @@ class _EvaluateVisitor // ## Expressions - Future visitBinaryOperationExpression( - BinaryOperationExpression node) { + Future visitBinaryOperationExpression(BinaryOperationExpression node) { return _addExceptionSpanAsync(node, () async { var left = await node.left.accept(this); switch (node.operator) { @@ -2009,13 +1998,13 @@ class _EvaluateVisitor } else { return result; } - break; case BinaryOperator.modulo: var right = await node.right.accept(this); return left.modulo(right); + default: - return null; + throw ArgumentError("Unknown binary operator ${node.operator}."); } }); } @@ -2179,7 +2168,7 @@ class _EvaluateVisitor i++) { var argument = declaredArguments[i]; var value = evaluated.named.remove(argument.name) ?? - await argument.defaultValue!.accept(this); + await argument.defaultValue!.accept>(this); _environment.setLocalVariable( argument.name, value.withoutSlash(), @@ -2260,8 +2249,7 @@ class _EvaluateVisitor buffer.write(await _evaluateToCss(argument)); } - // TODO: no ! - var rest = (await arguments.rest?.accept(this))!; + var rest = await arguments.rest?.accept(this); if (rest != null) { if (!first) buffer.write(", "); buffer.write(_serialize(rest, arguments.rest!)); @@ -2296,7 +2284,7 @@ class _EvaluateVisitor i++) { var argument = declaredArguments[i]; evaluated.positional.add(evaluated.named.remove(argument.name) ?? - (await argument.defaultValue?.accept(this))!); + await argument.defaultValue!.accept(this)); } SassArgumentList? argumentList; @@ -2335,12 +2323,11 @@ class _EvaluateVisitor } catch (error) { String? message; try { - // TODO: as String! - message = error.message as String?; + message = (error as dynamic).message as String; } catch (_) { message = error.toString(); } - throw _exception(message!, nodeWithSpan.span); + throw _exception(message, nodeWithSpan.span); } _callableNode = oldCallableNode; @@ -2394,7 +2381,7 @@ class _EvaluateVisitor } var rest = await restArgs.accept(this); - var restNodeForSpan = trackSpans ? _expressionNode(restArgs) : null; + var restNodeForSpan = _expressionNode(restArgs); var separator = ListSeparator.undecided; if (rest is SassMap) { _addRestMap(named, rest, restArgs, (value) => value); @@ -2425,8 +2412,7 @@ class _EvaluateVisitor } var keywordRest = await keywordRestArgs.accept(this); - var keywordRestNodeForSpan = - trackSpans ? _expressionNode(keywordRestArgs) : null; + var keywordRestNodeForSpan = _expressionNode(keywordRestArgs); if (keywordRest is SassMap) { _addRestMap(named, keywordRest, keywordRestArgs, (value) => value); namedNodes?.addAll({ @@ -2525,8 +2511,7 @@ class _EvaluateVisitor // Don't use [performInterpolation] here because we need to get the raw text // from strings, rather than the semantic value. return SassString( - // TODO: no Object - (await mapAsync(node.text.contents, (Object value) async { + (await mapAsync(node.text.contents, (value) async { if (value is String) return value; var expression = value as Expression; var result = await expression.accept(this); @@ -2641,10 +2626,8 @@ class _EvaluateVisitor "Media rules may not be used within nested declarations.", node.span); } - // TODO: no !, no as - var mergedQueries = _mediaQueries.andThen(((mediaQueries) => - _mergeMediaQueries(mediaQueries, node.queries)!) - as List Function(List)?); + var mergedQueries = _mediaQueries.andThen( + (mediaQueries) => _mergeMediaQueries(mediaQueries, node.queries)); if (mergedQueries != null && mergedQueries.isEmpty) return null; await _withParent( @@ -2792,8 +2775,7 @@ class _EvaluateVisitor /// values passed into the interpolation. Future _performInterpolation(Interpolation interpolation, {bool warnForColor = false}) async { - // TODO: no Object - return (await mapAsync(interpolation.contents, (Object value) async { + return (await mapAsync(interpolation.contents, (value) async { if (value is String) return value; var expression = value as Expression; var result = await expression.accept(this); @@ -2871,12 +2853,10 @@ class _EvaluateVisitor Future _withParent( S node, Future callback(), {bool through(CssNode node)?, bool scopeWhen = true}) async { - // TODO: no as - _addChild(node as ModifiableCssNode, through: through); + _addChild(node, through: through); var oldParent = _parent; - // TODO: no as - _parent = node as ModifiableCssParentNode; + _parent = node; var result = await _environment.scope(callback, when: scopeWhen); _parent = oldParent; @@ -2906,8 +2886,7 @@ class _EvaluateVisitor } } - // TODO: no as - parent.addChild(node as ModifiableCssNode); + parent.addChild(node); } /// Runs [callback] with [rule] as the current style rule. diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index ea85bd8e9..75b623c95 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: c527823c670e27437320a11255efb5e86f1b712d +// Checksum: 54af55cd11a583e5fca273e00f38831af26f0111 // // ignore_for_file: unused_import @@ -16,7 +16,6 @@ import 'dart:math' as math; import 'package:charcode/charcode.dart'; import 'package:collection/collection.dart'; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:source_span/source_span.dart'; import 'package:stack_trace/stack_trace.dart'; @@ -237,7 +236,6 @@ class _EvaluateVisitor /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling /// [AstNode.span] if the span isn't required, since some nodes need to do /// real work to manufacture a source span. - // TODO: no cast final _stack = >[]; /// Whether we're running in Node Sass-compatibility mode. @@ -433,7 +431,7 @@ class _EvaluateVisitor var metaMixins = [ BuiltInCallable.mixin("load-css", r"$url, $with: null", (arguments) { var url = Uri.parse(arguments[0].assertString("url").text); - var withMap = arguments[1].realNull?.assertMap("with")?.contents; + var withMap = arguments[1].realNull?.assertMap("with").contents; var callableNode = _callableNode!; var configuration = const Configuration.empty(); @@ -629,8 +627,7 @@ class _EvaluateVisitor bool namesInErrors = false}) { var url = stylesheet.span?.sourceUrl; - // TODO: no ! - var alreadyLoaded = _modules[url!]; + var alreadyLoaded = _modules[url]; if (alreadyLoaded != null) { var currentConfiguration = configuration ?? _configuration; if (currentConfiguration is ExplicitConfiguration) { @@ -723,12 +720,12 @@ class _EvaluateVisitor /// Returns a copy of [_root.children] with [_outOfOrderImports] inserted /// after [_endOfImports], if necessary. List _addOutOfOrderImports() { - if (_outOfOrderImports == null) return _root.children; + var outOfOrderImports = _outOfOrderImports; + if (outOfOrderImports == null) return _root.children; return [ ..._root.children.take(_endOfImports), - // TODO: no ! - ..._outOfOrderImports!, + ...outOfOrderImports, ..._root.children.skip(_endOfImports) ]; } @@ -782,8 +779,7 @@ class _EvaluateVisitor // canonical URL). It's important that we create this in topological order, // so that by the time we're processing a module we've already filled in all // its downstream extenders and we can use them to extend that module. - // TODO: var - Map> downstreamExtenders = >{}; + var downstreamExtenders = >{}; /// Extensions that haven't yet been satisfied by some upstream module. This /// adds extensions when they're defined but not satisfied, and removes them @@ -806,10 +802,9 @@ class _EvaluateVisitor if (module.extender.isEmpty) continue; for (var upstream in module.upstream) { - if (upstream.url == null) continue; - downstreamExtenders - .putIfAbsent(upstream.url, () => []) - .add(module.extender); + var url = upstream.url; + if (url == null) continue; + downstreamExtenders.putIfAbsent(url, () => []).add(module.extender); } // Remove all extensions that are now satisfied after adding downstream @@ -825,8 +820,7 @@ class _EvaluateVisitor } /// Throws an exception indicating that [extension] is unsatisfied. - @alwaysThrows - void _throwForUnsatisfiedExtension(Extension extension) { + Never _throwForUnsatisfiedExtension(Extension extension) { throw SassException( 'The target selector was not found.\n' 'Use "@extend ${extension.target} !optional" to avoid this error.', @@ -949,8 +943,7 @@ class _EvaluateVisitor var parent = _parent; int? innermostContiguous; - var i = 0; - for (; i < nodes.length; i++) { + for (var i = 0; i < nodes.length; i++) { while (parent != nodes[i]) { innermostContiguous = null; parent = parent.parent!; @@ -960,7 +953,6 @@ class _EvaluateVisitor } if (parent != _root) return _root; - // TODO: no ! var root = nodes[innermostContiguous!]; nodes.removeRange(innermostContiguous, nodes.length); return root; @@ -1057,8 +1049,8 @@ class _EvaluateVisitor if (_declarationName != null) { name = CssValue("$_declarationName-${name.value}", name.span); } - var cssValue = node.value - .andThen((value) => CssValue(value.accept(this), value.span))!; + var cssValue = + node.value.andThen((value) => CssValue(value.accept(this), value.span)); // 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. @@ -1073,11 +1065,12 @@ class _EvaluateVisitor "Custom property values may not be empty.", cssValue.span); } - if (node.children != null) { + var children = node.children; + if (children != null) { var oldDeclarationName = _declarationName; _declarationName = name.value; _environment.scope(() { - for (var child in node.children) { + for (var child in children) { child.accept(this); } }, when: node.hasDeclarations); @@ -1198,7 +1191,7 @@ class _EvaluateVisitor _withParent(ModifiableCssAtRule(name, node.span, value: value), () { var styleRule = _styleRule; if (styleRule == null || _inKeyframes) { - for (var child in node.children) { + for (var child in node.children!) { child.accept(this); } } else { @@ -1207,7 +1200,7 @@ class _EvaluateVisitor // // For example, "a {@foo {b: c}}" should produce "@foo {a {b: c}}". _withParent(styleRule.copyWithoutChildren(), () { - for (var child in node.children) { + for (var child in node.children!) { child.accept(this); } }, scopeWhen: false); @@ -1361,8 +1354,7 @@ class _EvaluateVisitor return _environment.scope( () => _handleReturn( - // TODO: no ! - clause!.children, + clause!.children, // dart-lang/sdk#45348 (child) => child.accept(this)), semiGlobal: true, when: clause.hasDeclarations); @@ -1489,7 +1481,7 @@ class _EvaluateVisitor if (importCache != null) { var tuple = importCache.import(Uri.parse(url), baseImporter: _importer, - baseUrl: baseUrl ?? _stylesheet?.span?.sourceUrl, + baseUrl: baseUrl ?? _stylesheet.span?.sourceUrl, forImport: forImport); if (tuple != null) return tuple; } else { @@ -1509,12 +1501,11 @@ class _EvaluateVisitor } catch (error) { String? message; try { - // TODO: as String! - message = error.message as String?; + message = (error as dynamic).message as String; } catch (_) { message = error.toString(); } - throw _exception(message!); + throw _exception(message); } finally { _importSpan = null; } @@ -1642,9 +1633,8 @@ class _EvaluateVisitor } var queries = _visitMediaQueries(node.query); - var mergedQueries = _mediaQueries.andThen(((mediaQueries) => - _mergeMediaQueries(mediaQueries, queries)!) - as List Function(List)?); + var mergedQueries = _mediaQueries + .andThen((mediaQueries) => _mergeMediaQueries(mediaQueries, queries)); if (mergedQueries != null && mergedQueries.isEmpty) return null; _withParent(ModifiableCssMediaRule(mergedQueries ?? queries, node.span), @@ -1941,7 +1931,7 @@ class _EvaluateVisitor // ## Expressions - Value? visitBinaryOperationExpression(BinaryOperationExpression node) { + Value visitBinaryOperationExpression(BinaryOperationExpression node) { return _addExceptionSpan(node, () { var left = node.left.accept(this); switch (node.operator) { @@ -1999,13 +1989,13 @@ class _EvaluateVisitor } else { return result; } - break; case BinaryOperator.modulo: var right = node.right.accept(this); return left.modulo(right); + default: - return null; + throw ArgumentError("Unknown binary operator ${node.operator}."); } }); } @@ -2165,7 +2155,7 @@ class _EvaluateVisitor i++) { var argument = declaredArguments[i]; var value = evaluated.named.remove(argument.name) ?? - argument.defaultValue!.accept(this); + argument.defaultValue!.accept(this); _environment.setLocalVariable( argument.name, value.withoutSlash(), @@ -2244,8 +2234,7 @@ class _EvaluateVisitor buffer.write(_evaluateToCss(argument)); } - // TODO: no ! - var rest = arguments.rest?.accept(this)!; + var rest = arguments.rest?.accept(this); if (rest != null) { if (!first) buffer.write(", "); buffer.write(_serialize(rest, arguments.rest!)); @@ -2280,7 +2269,7 @@ class _EvaluateVisitor i++) { var argument = declaredArguments[i]; evaluated.positional.add(evaluated.named.remove(argument.name) ?? - argument.defaultValue?.accept(this)!); + argument.defaultValue!.accept(this)); } SassArgumentList? argumentList; @@ -2319,12 +2308,11 @@ class _EvaluateVisitor } catch (error) { String? message; try { - // TODO: as String! - message = error.message as String?; + message = (error as dynamic).message as String; } catch (_) { message = error.toString(); } - throw _exception(message!, nodeWithSpan.span); + throw _exception(message, nodeWithSpan.span); } _callableNode = oldCallableNode; @@ -2378,7 +2366,7 @@ class _EvaluateVisitor } var rest = restArgs.accept(this); - var restNodeForSpan = trackSpans ? _expressionNode(restArgs) : null; + var restNodeForSpan = _expressionNode(restArgs); var separator = ListSeparator.undecided; if (rest is SassMap) { _addRestMap(named, rest, restArgs, (value) => value); @@ -2409,8 +2397,7 @@ class _EvaluateVisitor } var keywordRest = keywordRestArgs.accept(this); - var keywordRestNodeForSpan = - trackSpans ? _expressionNode(keywordRestArgs) : null; + var keywordRestNodeForSpan = _expressionNode(keywordRestArgs); if (keywordRest is SassMap) { _addRestMap(named, keywordRest, keywordRestArgs, (value) => value); namedNodes?.addAll({ @@ -2509,8 +2496,7 @@ class _EvaluateVisitor // Don't use [performInterpolation] here because we need to get the raw text // from strings, rather than the semantic value. return SassString( - // TODO: no Object - node.text.contents.map((Object value) { + node.text.contents.map((value) { if (value is String) return value; var expression = value as Expression; var result = expression.accept(this); @@ -2624,10 +2610,8 @@ class _EvaluateVisitor "Media rules may not be used within nested declarations.", node.span); } - // TODO: no !, no as - var mergedQueries = _mediaQueries.andThen(((mediaQueries) => - _mergeMediaQueries(mediaQueries, node.queries)!) - as List Function(List)?); + var mergedQueries = _mediaQueries.andThen( + (mediaQueries) => _mergeMediaQueries(mediaQueries, node.queries)); if (mergedQueries != null && mergedQueries.isEmpty) return null; _withParent( @@ -2771,8 +2755,7 @@ class _EvaluateVisitor /// values passed into the interpolation. String _performInterpolation(Interpolation interpolation, {bool warnForColor = false}) { - // TODO: no Object - return interpolation.contents.map((Object value) { + return interpolation.contents.map((value) { if (value is String) return value; var expression = value as Expression; var result = expression.accept(this); @@ -2847,12 +2830,10 @@ class _EvaluateVisitor /// Runs [callback] in a new environment scope unless [scopeWhen] is false. T _withParent(S node, T callback(), {bool through(CssNode node)?, bool scopeWhen = true}) { - // TODO: no as - _addChild(node as ModifiableCssNode, through: through); + _addChild(node, through: through); var oldParent = _parent; - // TODO: no as - _parent = node as ModifiableCssParentNode; + _parent = node; var result = _environment.scope(callback, when: scopeWhen); _parent = oldParent; @@ -2882,8 +2863,7 @@ class _EvaluateVisitor } } - // TODO: no as - parent.addChild(node as ModifiableCssNode); + parent.addChild(node); } /// Runs [callback] with [rule] as the current style rule. diff --git a/lib/src/visitor/serialize.dart b/lib/src/visitor/serialize.dart index a48a2f8e9..d73c7ec44 100644 --- a/lib/src/visitor/serialize.dart +++ b/lib/src/visitor/serialize.dart @@ -1085,25 +1085,25 @@ class _SerializeVisitor } _writeLineFeed(); - CssNode? previous; + CssNode? previous_; _indent(() { for (var i = 0; i < children.length; i++) { var child = children[i]; if (_isInvisible(child)) continue; + var previous = previous_; // dart-lang/sdk#45348 if (previous != null) { if (_requiresSemicolon(previous)) _buffer.writeCharCode($semicolon); _writeLineFeed(); - // TODO: no ! - if (previous!.isGroupEnd) _writeLineFeed(); + if (previous.isGroupEnd) _writeLineFeed(); } - previous = child; + previous_ = child; child.accept(this); } }); - if (_requiresSemicolon(previous) && !_isCompressed) { + if (_requiresSemicolon(previous_!) && !_isCompressed) { _buffer.writeCharCode($semicolon); } _writeLineFeed(); diff --git a/test/dart_api/function_test.dart b/test/dart_api/function_test.dart index 83852579f..bebefde0d 100644 --- a/test/dart_api/function_test.dart +++ b/test/dart_api/function_test.dart @@ -77,11 +77,7 @@ void main() { test("gracefully handles a custom function throwing", () { expect(() { compileString('a {b: foo()}', - // TODO: no as - functions: [ - Callable("foo", "", - ((arguments) => throw "heck") as Value Function(List)) - ]); + functions: [Callable("foo", "", (arguments) => throw "heck")]); }, throwsA(const TypeMatcher())); }); @@ -115,18 +111,14 @@ void main() { test("supports keyword arguments", () { var css = compileString(r'a {b: foo($bar: 1)}', functions: [ - Callable( - "foo", - r"$args...", - expectAsync1((arguments) { - expect(arguments, hasLength(1)); - var list = arguments[0] as SassArgumentList; - expect(list.asList, hasLength(0)); - expect(list.keywords, contains("bar")); - expect(list.keywords["bar"]!.assertNumber().value, equals(1)); - return list.keywords["bar"]!; - // TODO: no as - } as Value Function(List))) + Callable("foo", r"$args...", expectAsync1((arguments) { + expect(arguments, hasLength(1)); + var list = arguments[0] as SassArgumentList; + expect(list.asList, hasLength(0)); + expect(list.keywords, contains("bar")); + expect(list.keywords["bar"]!.assertNumber().value, equals(1)); + return list.keywords["bar"]!; + })) ]); expect(css, equalsIgnoringWhitespace("a { b: 1; }")); diff --git a/test/dart_api/importer_test.dart b/test/dart_api/importer_test.dart index a2648b765..7ffae8f9c 100644 --- a/test/dart_api/importer_test.dart +++ b/test/dart_api/importer_test.dart @@ -13,6 +13,7 @@ import 'package:sass/sass.dart'; import 'package:sass/src/exception.dart'; import 'test_importer.dart'; +import '../utils.dart'; void main() { test("uses an importer to resolve an @import", () { @@ -126,14 +127,11 @@ void main() { test("wraps an error in canonicalize()", () { expect(() { compileString('@import "orange";', importers: [ - TestImporter( - (url) { - throw "this import is bad actually"; - } as Uri Function(Uri), - expectAsync1((_) => null, count: 0)) // TODO: no as + TestImporter((url) { + throw "this import is bad actually"; + }, expectNever1) ]); - }, throwsA(predicate((dynamic error) { - // TODO: no dynamic + }, throwsA(predicate((error) { expect(error, const TypeMatcher()); expect( error.toString(), startsWith("Error: this import is bad actually")); @@ -148,8 +146,7 @@ void main() { throw "this import is bad actually"; }) ]); - }, throwsA(predicate((dynamic error) { - // TODO: no dynamic + }, throwsA(predicate((error) { expect(error, const TypeMatcher()); expect( error.toString(), startsWith("Error: this import is bad actually")); @@ -164,8 +161,7 @@ void main() { throw FormatException("bad format somehow"); }) ]); - }, throwsA(predicate((dynamic error) { - // TODO: no dynamic + }, throwsA(predicate((error) { expect(error, const TypeMatcher()); // FormatException.toString() starts with "FormatException:", but // the error message should not. @@ -179,8 +175,7 @@ void main() { compileString('@import "orange";', importers: [ TestImporter((url) => Uri.parse("u:$url"), (url) => null) ]); - }, throwsA(predicate((dynamic error) { - // TODO: no dynamic + }, throwsA(predicate((error) { expect(error, const TypeMatcher()); expect(error.toString(), startsWith("Error: Can't find stylesheet to import")); diff --git a/test/dart_api/logger_test.dart b/test/dart_api/logger_test.dart index 842120506..b22564ff9 100644 --- a/test/dart_api/logger_test.dart +++ b/test/dart_api/logger_test.dart @@ -12,8 +12,6 @@ import 'package:sass/sass.dart'; import 'test_importer.dart'; -// TODO: no required - void main() { group("with @warn", () { test("passes the message and stack trace to the logger", () { @@ -22,10 +20,10 @@ void main() { @mixin foo {@warn heck} @include foo; ''', logger: _TestLogger.withWarn((message, - {span, required trace, deprecation}) { + {span, trace, deprecation = false}) { expect(message, equals("heck")); expect(span, isNull); - expect(trace.frames.first.member, equals('foo()')); + expect(trace!.frames.first.member, equals('foo()')); expect(deprecation, isFalse); mustBeCalled(); })); @@ -33,8 +31,8 @@ void main() { test("stringifies the argument", () { var mustBeCalled = expectAsync0(() {}); - compileString('@warn #abc', - logger: _TestLogger.withWarn((message, {span, trace, deprecation}) { + compileString('@warn #abc', logger: + _TestLogger.withWarn((message, {span, trace, deprecation = false}) { expect(message, equals("#abc")); mustBeCalled(); })); @@ -42,8 +40,8 @@ void main() { test("doesn't inspect the argument", () { var mustBeCalled = expectAsync0(() {}); - compileString('@warn null', - logger: _TestLogger.withWarn((message, {span, trace, deprecation}) { + compileString('@warn null', logger: + _TestLogger.withWarn((message, {span, trace, deprecation = false}) { expect(message, isEmpty); mustBeCalled(); })); @@ -80,10 +78,10 @@ void main() { test("with a parser warning passes the message and span", () { var mustBeCalled = expectAsync0(() {}); compileString('a {b: c && d}', logger: - _TestLogger.withWarn((message, {required span, trace, deprecation}) { + _TestLogger.withWarn((message, {span, trace, deprecation = false}) { expect(message, contains('"&&" means two copies')); - expect(span.start.line, equals(0)); + expect(span!.start.line, equals(0)); expect(span.start.column, equals(8)); expect(span.end.line, equals(0)); expect(span.end.column, equals(10)); @@ -99,16 +97,16 @@ void main() { compileString(''' @mixin foo {#{blue} {x: y}} @include foo; - ''', logger: _TestLogger.withWarn((message, - {required span, required trace, deprecation}) { + ''', logger: + _TestLogger.withWarn((message, {span, trace, deprecation = false}) { expect(message, contains("color value blue")); - expect(span.start.line, equals(0)); + expect(span!.start.line, equals(0)); expect(span.start.column, equals(22)); expect(span.end.line, equals(0)); expect(span.end.column, equals(26)); - expect(trace.frames.first.member, equals('foo()')); + expect(trace!.frames.first.member, equals('foo()')); expect(deprecation, isFalse); mustBeCalled(); })); @@ -127,15 +125,15 @@ void main() { return sassNull; })) ], logger: _TestLogger.withWarn((message, - {required span, required trace, deprecation}) { + {span, trace, deprecation = false}) { expect(message, equals("heck")); - expect(span.start.line, equals(0)); + expect(span!.start.line, equals(0)); expect(span.start.column, equals(33)); expect(span.end.line, equals(0)); expect(span.end.column, equals(38)); - expect(trace.frames.first.member, equals('bar()')); + expect(trace!.frames.first.member, equals('bar()')); expect(deprecation, isFalse); mustBeCalled(); })); @@ -152,15 +150,15 @@ void main() { return sassNull; })) ], logger: _TestLogger.withWarn((message, - {required span, required trace, deprecation}) { + {span, trace, deprecation = false}) { expect(message, equals("heck")); - expect(span.start.line, equals(0)); + expect(span!.start.line, equals(0)); expect(span.start.column, equals(33)); expect(span.end.line, equals(0)); expect(span.end.column, equals(38)); - expect(trace.frames.first.member, equals('bar()')); + expect(trace!.frames.first.member, equals('bar()')); expect(deprecation, isFalse); mustBeCalled(); })); @@ -178,15 +176,15 @@ void main() { return sassNull; })) ], logger: _TestLogger.withWarn((message, - {required span, required trace, deprecation}) { + {span, trace, deprecation = false}) { expect(message, equals("heck")); - expect(span.start.line, equals(0)); + expect(span!.start.line, equals(0)); expect(span.start.column, equals(33)); expect(span.end.line, equals(0)); expect(span.end.column, equals(38)); - expect(trace.frames.first.member, equals('bar()')); + expect(trace!.frames.first.member, equals('bar()')); expect(deprecation, isFalse); mustBeCalled(); })); @@ -200,16 +198,16 @@ void main() { warn("heck"); return ImporterResult("", indented: false); }) - ], logger: _TestLogger.withWarn((message, - {required span, required trace, deprecation}) { + ], logger: + _TestLogger.withWarn((message, {span, trace, deprecation = false}) { expect(message, equals("heck")); - expect(span.start.line, equals(0)); + expect(span!.start.line, equals(0)); expect(span.start.column, equals(8)); expect(span.end.line, equals(0)); expect(span.end.column, equals(13)); - expect(trace.frames.first.member, equals('@import')); + expect(trace!.frames.first.member, equals('@import')); expect(deprecation, isFalse); mustBeCalled(); })); @@ -222,7 +220,8 @@ void main() { warn("heck", deprecation: true); return sassNull; })) - ], logger: _TestLogger.withWarn((message, {span, trace, deprecation}) { + ], logger: + _TestLogger.withWarn((message, {span, trace, deprecation = false}) { expect(message, equals("heck")); expect(deprecation, isTrue); mustBeCalled(); @@ -237,8 +236,8 @@ void main() { /// A [Logger] whose [warn] and [debug] methods are provided by callbacks. class _TestLogger implements Logger { - final void Function(String, - {FileSpan? span, Trace? trace, required bool deprecation}) _warn; + final void Function(String, {FileSpan? span, Trace? trace, bool deprecation}) + _warn; final void Function(String, SourceSpan) _debug; _TestLogger.withWarn(this._warn) : _debug = const Logger.stderr().debug; diff --git a/test/dart_api/test_importer.dart b/test/dart_api/test_importer.dart index 5dac8e0d7..87af315df 100644 --- a/test/dart_api/test_importer.dart +++ b/test/dart_api/test_importer.dart @@ -7,12 +7,12 @@ import 'package:sass/sass.dart'; /// An [Importer] whose [canonicalize] and [load] methods are provided by /// closures. class TestImporter extends Importer { - final Uri Function(Uri url) _canonicalize; - final ImporterResult Function(Uri url) _load; + final Uri? Function(Uri url) _canonicalize; + final ImporterResult? Function(Uri url) _load; TestImporter(this._canonicalize, this._load); - Uri canonicalize(Uri url) => _canonicalize(url); + Uri? canonicalize(Uri url) => _canonicalize(url); - ImporterResult load(Uri url) => _load(url); + ImporterResult? load(Uri url) => _load(url); } diff --git a/test/dart_api/value/utils.dart b/test/dart_api/value/utils.dart index 51ed209cd..ae2435de3 100644 --- a/test/dart_api/value/utils.dart +++ b/test/dart_api/value/utils.dart @@ -26,8 +26,7 @@ final throwsSassScriptException = /// Like [equals], but asserts that the hash codes of the values are the same as /// well. -// TODO: no dynamic -Matcher equalsWithHash(Object expected) => predicate((dynamic actual) { +Matcher equalsWithHash(Object expected) => predicate((actual) { expect(actual, equals(expected)); expect(actual.hashCode, equals(expected.hashCode), reason: "Expected $actual's hash code to equal $expected's."); diff --git a/test/double_check_test.dart b/test/double_check_test.dart index fa10d1feb..5b9d38445 100644 --- a/test/double_check_test.dart +++ b/test/double_check_test.dart @@ -25,7 +25,7 @@ void main() { "Run pub run grinder to update it."; var target = File(targetPath).readAsStringSync(); - var match = checksumPattern.firstMatch(target)!; // TODO: no ! + var match = checksumPattern.firstMatch(target); if (match == null) fail(message); var source = File(sourcePath).readAsBytesSync(); diff --git a/test/hybrid.dart b/test/hybrid.dart index 004cd0a75..4311488a8 100644 --- a/test/hybrid.dart +++ b/test/hybrid.dart @@ -35,7 +35,7 @@ Future runHybridExpression(String expression, import 'package:stream_channel/stream_channel.dart'; hybridMain(StreamChannel channel, message) async { - var result = await ${expression}; + var result = await $expression; channel.sink.add(_isJsonSafe(result) ? jsonEncode(result) : 'null'); channel.sink.close(); } diff --git a/test/node_api/function_test.dart b/test/node_api/function_test.dart index 2b7cdbfd3..1ce978002 100644 --- a/test/node_api/function_test.dart +++ b/test/node_api/function_test.dart @@ -21,8 +21,6 @@ import '../hybrid.dart'; import 'api.dart'; import 'utils.dart'; -// TODO: no dynamic - void main() { setUpAll(ensureNpmPackage); useSandbox(); @@ -131,8 +129,8 @@ void main() { renderSync(RenderOptions( data: "a {b: last(1px, 2em)}", functions: jsify({ - r"last($value1, $value2)": allowInterop( - expectAsync2((dynamic value1, dynamic value2) => value2)) + r"last($value1, $value2)": + allowInterop(expectAsync2((value1, value2) => value2)) }))), equalsIgnoringWhitespace("a { b: 2em; }")); }); @@ -142,8 +140,8 @@ void main() { renderSync(RenderOptions( data: r"a {b: last($value2: 1px, $value1: 2em)}", functions: jsify({ - r"last($value1, $value2)": allowInterop( - expectAsync2((dynamic value1, dynamic value2) => value2)) + r"last($value1, $value2)": + allowInterop(expectAsync2((value1, value2) => value2)) }))), equalsIgnoringWhitespace("a { b: 1px; }")); }); @@ -153,8 +151,8 @@ void main() { renderSync(RenderOptions( data: "a {b: last((1px 2em)...)}", functions: jsify({ - r"last($value1, $value2)": allowInterop( - expectAsync2((dynamic value1, dynamic value2) => value2)) + r"last($value1, $value2)": + allowInterop(expectAsync2((value1, value2) => value2)) }))), equalsIgnoringWhitespace("a { b: 2em; }")); }); @@ -312,7 +310,7 @@ void main() { data: 'a {b: foo()}', functions: jsify({ 'foo': allowInteropCaptureThis(expectAsync1((RenderContext this_) { - expect(this_.options.result!.stats!.start, + expect(this_.options.result.stats.start, greaterThanOrEqualTo(start.millisecondsSinceEpoch)); return callConstructor(sass.types.Number, [12]); })) @@ -325,7 +323,7 @@ void main() { data: 'a {b: foo()}', functions: jsify({ 'foo': allowInteropCaptureThis(expectAsync1((RenderContext this_) { - expect(this_.options.result!.stats!.entry, equals('data')); + expect(this_.options.result.stats.entry, equals('data')); return callConstructor(sass.types.Number, [12]); })) }), @@ -338,7 +336,7 @@ void main() { file: sassPath, functions: jsify({ 'foo': allowInteropCaptureThis(expectAsync1((RenderContext this_) { - expect(this_.options.result!.stats!.entry, equals(sassPath)); + expect(this_.options.result.stats.entry, equals(sassPath)); return callConstructor(sass.types.Number, [12]); })) }), @@ -553,8 +551,7 @@ void main() { renderSync(RenderOptions( data: "a {b: call(id(get-function('str-length')), 'foo')}", functions: jsify({ - r"id($value)": - allowInterop(expectAsync1((dynamic value) => value)) + r"id($value)": allowInterop(expectAsync1((value) => value)) }))), equalsIgnoringWhitespace("a { b: 3; }")); }); diff --git a/test/node_api/importer_test.dart b/test/node_api/importer_test.dart index 1677da9fd..2a2c47f95 100644 --- a/test/node_api/importer_test.dart +++ b/test/node_api/importer_test.dart @@ -21,8 +21,6 @@ import '../hybrid.dart'; import 'api.dart'; import 'utils.dart'; -// TODO: no dynamic - void main() { setUpAll(ensureNpmPackage); useSandbox(); @@ -141,7 +139,7 @@ void main() { importer: allowInterop(expectAsync2((void _, void __) { return NodeImporterResult(contents: '', file: 'bar'); })))); - expect(result.stats!.includedFiles, equals(['bar'])); + expect(result.stats.includedFiles, equals(['bar'])); }); }); @@ -244,7 +242,7 @@ void main() { file: basePath, importer: allowInterop( (void _, void __) => NodeImporterResult(file: 'test')))); - expect(result.stats!.includedFiles, equals([basePath, sassPath])); + expect(result.stats.includedFiles, equals([basePath, sassPath])); }); test("is resolved relative to include paths", () async { @@ -329,7 +327,7 @@ void main() { test("is the exact imported text", () { renderSync(RenderOptions( data: "@import 'foo'", - importer: allowInterop(expectAsync2((dynamic url, dynamic _) { + importer: allowInterop(expectAsync2((url, _) { expect(url, equals('foo')); return NodeImporterResult(contents: ''); })))); @@ -339,7 +337,7 @@ void main() { test("doesn't remove ./", () { renderSync(RenderOptions( data: "@import './foo'", - importer: allowInterop(expectAsync2((dynamic url, dynamic _) { + importer: allowInterop(expectAsync2((url, _) { expect(url, equals('./foo')); return NodeImporterResult(contents: ''); })))); @@ -348,7 +346,7 @@ void main() { test("isn't resolved relative to the current file", () { renderSync(RenderOptions( data: "@import 'foo/bar'", - importer: allowInterop(expectAsync2((dynamic url, dynamic _) { + importer: allowInterop(expectAsync2((url, _) { if (url == 'foo/bar') { return NodeImporterResult(contents: "@import 'baz'"); } else { @@ -364,7 +362,7 @@ void main() { importer: allowInterop(expectAsync2((void _, void __) { return NodeImporterResult(contents: ''); })))); - expect(result.stats!.includedFiles, equals(['foo'])); + expect(result.stats.includedFiles, equals(['foo'])); }); }); @@ -375,7 +373,7 @@ void main() { renderSync(RenderOptions( file: importPath, - importer: allowInterop(expectAsync2((dynamic _, dynamic prev) { + importer: allowInterop(expectAsync2((_, prev) { expect(prev, equals(p.absolute(importPath))); return NodeImporterResult(contents: ''); })))); @@ -391,7 +389,7 @@ void main() { renderSync(RenderOptions( file: import1Path, - importer: allowInterop(expectAsync2((dynamic url, dynamic prev) { + importer: allowInterop(expectAsync2((url, prev) { if (url == 'foo') { return NodeImporterResult(file: 'import2'); } else { @@ -405,7 +403,7 @@ void main() { test('is "stdin" for string stylesheets', () async { renderSync(RenderOptions( data: '@import "foo"', - importer: allowInterop(expectAsync2((dynamic _, dynamic prev) { + importer: allowInterop(expectAsync2((_, prev) { expect(prev, equals('stdin')); return NodeImporterResult(contents: ''); })))); @@ -413,11 +411,11 @@ void main() { test("is the imported string for imports from importers", () async { renderSync(RenderOptions(data: '@import "foo"', importer: [ - allowInterop(expectAsync2((dynamic url, dynamic _) { + allowInterop(expectAsync2((url, _) { if (url != "foo") return null; return NodeImporterResult(contents: '@import "bar"'); }, count: 2)), - allowInterop(expectAsync2((dynamic url, dynamic prev) { + allowInterop(expectAsync2((url, prev) { expect(url, equals("bar")); expect(prev, equals("foo")); return NodeImporterResult(contents: ''); @@ -431,7 +429,7 @@ void main() { renderSync(RenderOptions( data: '@import "foo"', importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, dynamic _, dynamic __) { + expectAsync3((RenderContext this_, _, __) { var options = this_.options; expect(options.includePaths, equals(p.current)); expect(options.precision, equals(SassNumber.precision)); @@ -448,7 +446,7 @@ void main() { renderSync(RenderOptions( data: '@import "foo"', importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, dynamic _, dynamic __) { + expectAsync3((RenderContext this_, _, __) { expect(this_.options.data, equals('@import "foo"')); expect(this_.options.file, isNull); return NodeImporterResult(contents: ''); @@ -460,7 +458,7 @@ void main() { renderSync(RenderOptions( file: sassPath, importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, dynamic _, dynamic __) { + expectAsync3((RenderContext this_, _, __) { expect(this_.options.data, isNull); expect(this_.options.file, equals(sassPath)); return NodeImporterResult(contents: ''); @@ -472,7 +470,7 @@ void main() { data: '@import "foo"', includePaths: [sandbox], importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, dynamic _, dynamic __) { + expectAsync3((RenderContext this_, _, __) { expect(this_.options.includePaths, equals("${p.current}${isWindows ? ';' : ':'}$sandbox")); return NodeImporterResult(contents: ''); @@ -485,7 +483,7 @@ void main() { data: '@import "foo"', indentWidth: 5, importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, dynamic _, dynamic __) { + expectAsync3((RenderContext this_, _, __) { expect(this_.options.indentWidth, equals(5)); return NodeImporterResult(contents: ''); })))); @@ -496,7 +494,7 @@ void main() { data: '@import "foo"', indentType: 'tab', importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, dynamic _, dynamic __) { + expectAsync3((RenderContext this_, _, __) { expect(this_.options.indentType, equals(1)); return NodeImporterResult(contents: ''); })))); @@ -507,7 +505,7 @@ void main() { data: '@import "foo"', linefeed: 'cr', importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, dynamic _, dynamic __) { + expectAsync3((RenderContext this_, _, __) { expect(this_.options.linefeed, equals('\r')); return NodeImporterResult(contents: ''); })))); @@ -518,7 +516,7 @@ void main() { renderSync(RenderOptions( data: '@import "foo"', importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, dynamic _, dynamic __) { + expectAsync3((RenderContext this_, _, __) { expect(this_.options.context, same(this_)); return NodeImporterResult(contents: ''); })))); @@ -530,8 +528,8 @@ void main() { renderSync(RenderOptions( data: '@import "foo"', importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, dynamic _, dynamic __) { - expect(this_.options.result!.stats!.start, + expectAsync3((RenderContext this_, _, __) { + expect(this_.options.result.stats.start, greaterThanOrEqualTo(start.millisecondsSinceEpoch)); return NodeImporterResult(contents: ''); })))); @@ -541,8 +539,8 @@ void main() { renderSync(RenderOptions( data: '@import "foo"', importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, dynamic _, dynamic __) { - expect(this_.options.result!.stats!.entry, equals('data')); + expectAsync3((RenderContext this_, _, __) { + expect(this_.options.result.stats.entry, equals('data')); return NodeImporterResult(contents: ''); })))); }); @@ -552,8 +550,8 @@ void main() { renderSync(RenderOptions( file: sassPath, importer: allowInteropCaptureThis( - expectAsync3((RenderContext this_, dynamic _, dynamic __) { - expect(this_.options.result!.stats!.entry, equals(sassPath)); + expectAsync3((RenderContext this_, _, __) { + expect(this_.options.result.stats.entry, equals(sassPath)); return NodeImporterResult(contents: ''); })))); }); diff --git a/test/node_api/source_map_test.dart b/test/node_api/source_map_test.dart index 26fc3e310..51c009670 100644 --- a/test/node_api/source_map_test.dart +++ b/test/node_api/source_map_test.dart @@ -33,8 +33,8 @@ void main() { setUp(() { var result = sass.renderSync( RenderOptions(data: "a {b: c}", sourceMap: true, outFile: "out.css")); - css = utf8.decode(result.css!); - map = _jsonUtf8.decode(result.map) as Map; + css = utf8.decode(result.css); + map = _jsonUtf8.decode(result.map!) as Map; }); test("includes correct mappings", () { @@ -136,21 +136,21 @@ void main() { var result = sass.renderSync(RenderOptions(data: "a {b: c}", outFile: "out.css")); expect(result.map, isNull); - expect(utf8.decode(result.css!), isNot(contains("/*#"))); + expect(utf8.decode(result.css), isNot(contains("/*#"))); }); test("with sourceMap: false", () { var result = sass.renderSync(RenderOptions( data: "a {b: c}", sourceMap: false, outFile: "out.css")); expect(result.map, isNull); - expect(utf8.decode(result.css!), isNot(contains("/*#"))); + expect(utf8.decode(result.css), isNot(contains("/*#"))); }); test("without outFile", () { var result = sass.renderSync(RenderOptions(data: "a {b: c}", sourceMap: true)); expect(result.map, isNull); - expect(utf8.decode(result.css!), isNot(contains("/*#"))); + expect(utf8.decode(result.css), isNot(contains("/*#"))); }); }); @@ -158,7 +158,7 @@ void main() { test("emits a source map", () { var result = sass.renderSync( RenderOptions(data: "a {b: c}", sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map) as Map; + var map = _jsonUtf8.decode(result.map!) as Map; expect(map, containsPair("sources", ["stdin"])); }); @@ -168,7 +168,7 @@ void main() { var result = sass.renderSync(RenderOptions( file: p.join(sandbox, "test.scss"), sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map) as Map; + var map = _jsonUtf8.decode(result.map!) as Map; expect( map, containsPair( @@ -182,7 +182,7 @@ void main() { var result = sass.renderSync(RenderOptions( file: p.join(sandbox, "test"), sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map) as Map; + var map = _jsonUtf8.decode(result.map!) as Map; expect( map, containsPair( @@ -192,7 +192,7 @@ void main() { test("derives the target URL from stdin", () { var result = sass.renderSync( RenderOptions(data: "a {b: c}", sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map) as Map; + var map = _jsonUtf8.decode(result.map!) as Map; expect(map, containsPair("file", "stdin.css")); }); @@ -219,7 +219,7 @@ void main() { outFile: "out.css", omitSourceMapUrl: true)); expect(result.map, isNotNull); - expect(utf8.decode(result.css!), isNot(contains("/*#"))); + expect(utf8.decode(result.css), isNot(contains("/*#"))); }); group("with a string sourceMap", () { @@ -227,15 +227,15 @@ void main() { var result = sass.renderSync(RenderOptions( data: "a {b: c}", sourceMap: "map", outFile: "out.css")); expect(result.map, isNotNull); - expect(utf8.decode(result.css!), - endsWith("\n\n/*# sourceMappingURL=map */")); + expect( + utf8.decode(result.css), endsWith("\n\n/*# sourceMappingURL=map */")); }); test("makes the source map comment relative to the outfile", () { var result = sass.renderSync(RenderOptions( data: "a {b: c}", sourceMap: "map", outFile: "dir/out.css")); expect(result.map, isNotNull); - expect(utf8.decode(result.css!), + expect(utf8.decode(result.css), endsWith("\n\n/*# sourceMappingURL=../map */")); }); @@ -250,8 +250,8 @@ void main() { var result = sass.renderSync(RenderOptions( data: "a {b: c}", sourceMap: p.absolute("map"), outFile: "out.css")); expect(result.map, isNotNull); - expect(utf8.decode(result.css!), - endsWith("\n\n/*# sourceMappingURL=map */")); + expect( + utf8.decode(result.css), endsWith("\n\n/*# sourceMappingURL=map */")); }); test("makes the sources list relative to the map location", () async { @@ -300,8 +300,8 @@ void main() { outFile: "out.css", sourceMapEmbed: true)); - var map = embeddedSourceMap(utf8.decode(result.css!)); - expect(map, equals(_jsonUtf8.decode(result.map))); + var map = embeddedSourceMap(utf8.decode(result.css)); + expect(map, equals(_jsonUtf8.decode(result.map!))); }); group("with sourceMapRoot", () { @@ -332,4 +332,4 @@ void main() { /// Renders [options] and returns the decoded source map. Map _renderSourceMap(RenderOptions options) => - _jsonUtf8.decode(sass.renderSync(options).map) as Map; + _jsonUtf8.decode(sass.renderSync(options).map!) as Map; diff --git a/test/node_api/utils.dart b/test/node_api/utils.dart index 0ccff7174..7c6fe9597 100644 --- a/test/node_api/utils.dart +++ b/test/node_api/utils.dart @@ -54,7 +54,7 @@ Future render(RenderOptions options) { sass.render(options, allowInterop(Zone.current.bindBinaryCallbackGuarded((error, result) { expect(error, isNull); - completer.complete(utf8.decode(result.css!)); + completer.complete(utf8.decode(result.css)); }))); return completer.future; } @@ -73,7 +73,7 @@ Future renderError(RenderOptions options) { /// Returns the result of rendering via [options] as a string. String renderSync(RenderOptions options) => - utf8.decode(sass.renderSync(options).css!); + utf8.decode(sass.renderSync(options).css); /// Like [renderSync], but goes through the untyped JS API. /// @@ -81,7 +81,7 @@ String renderSync(RenderOptions options) => /// type errors. String renderSyncJS(Map options) { var result = _renderSyncJS.call(sass, jsify(options)) as RenderResult; - return utf8.decode(result.css!); + return utf8.decode(result.css); } final _renderSyncJS = diff --git a/test/node_api/value/utils.dart b/test/node_api/value/utils.dart index 6a3dc8ff2..01c079a59 100644 --- a/test/node_api/value/utils.dart +++ b/test/node_api/value/utils.dart @@ -28,6 +28,4 @@ T parseValue(String source) { /// A matcher that matches values that are JS `instanceof` [type]. Matcher isJSInstanceOf(Object type) => predicate( - // TODO: no dynamic - (dynamic value) => jsInstanceOf(value, type), - "to be an instance of $type"); + (value) => jsInstanceOf(value!, type), "to be an instance of $type"); diff --git a/test/node_api_test.dart b/test/node_api_test.dart index 6c4fd490b..3146dbbd4 100644 --- a/test/node_api_test.dart +++ b/test/node_api_test.dart @@ -302,31 +302,31 @@ a { group("the result object", () { test("includes the filename", () { var result = sass.renderSync(RenderOptions(file: sassPath)); - expect(result.stats!.entry, equals(sassPath)); + expect(result.stats.entry, equals(sassPath)); }); test("includes data without a filename", () { var result = sass.renderSync(RenderOptions(data: 'a {b: c}')); - expect(result.stats!.entry, equals('data')); + expect(result.stats.entry, equals('data')); }); test("includes timing information", () { - var stats = sass.renderSync(RenderOptions(file: sassPath)).stats!; + var stats = sass.renderSync(RenderOptions(file: sassPath)).stats; expect(stats.start, const TypeMatcher()); expect(stats.end, const TypeMatcher()); expect(stats.start, lessThanOrEqualTo(stats.end)); - expect(stats.duration, equals(stats.end! - stats.start!)); + expect(stats.duration, equals(stats.end - stats.start)); }); group("has includedFiles which", () { test("contains the root path if available", () { var result = sass.renderSync(RenderOptions(file: sassPath)); - expect(result.stats!.includedFiles, equals([sassPath])); + expect(result.stats.includedFiles, equals([sassPath])); }); test("doesn't contain the root path if it's not available", () { var result = sass.renderSync(RenderOptions(data: 'a {b: c}')); - expect(result.stats!.includedFiles, isEmpty); + expect(result.stats.includedFiles, isEmpty); }); test("contains imported paths", () async { @@ -334,7 +334,7 @@ a { await writeTextFile(importerPath, '@import "test"'); var result = sass.renderSync(RenderOptions(file: importerPath)); - expect(result.stats!.includedFiles, + expect(result.stats.includedFiles, unorderedEquals([importerPath, sassPath])); }); @@ -343,7 +343,7 @@ a { await writeTextFile(importerPath, '@import "test"; @import "test";'); var result = sass.renderSync(RenderOptions(file: importerPath)); - expect(result.stats!.includedFiles, + expect(result.stats.includedFiles, unorderedEquals([importerPath, sassPath])); }); }); diff --git a/test/source_map_test.dart b/test/source_map_test.dart index 044ce54ef..7f521d954 100644 --- a/test/source_map_test.dart +++ b/test/source_map_test.dart @@ -681,8 +681,7 @@ void main() { $map: (a: b); x {y: $map} """, sourceMap: (_) {}); - }, throwsA(predicate((dynamic untypedError) { - // TODO: no dynamic + }, throwsA(predicate((untypedError) { var error = untypedError as SourceSpanException; expect(error.span!.text, equals(r"$map")); return true; @@ -904,8 +903,7 @@ String _mapToString(SingleMapping map, String sourceText, String targetText) { var entryIter = entries.iterator..moveNext(); while (!targetScanner.isDone) { var entry = entryIter.current; - if (entry != null && - targetScanner.line == entry.target.line && + if (targetScanner.line == entry.target.line && targetScanner.column == entry.target.column) { var name = entryNames[Tuple2(entry.source.line, entry.source.column)]; targetBuffer.write("{{$name}}"); diff --git a/test/utils.dart b/test/utils.dart index dd228516e..a11f88792 100644 --- a/test/utils.dart +++ b/test/utils.dart @@ -32,5 +32,10 @@ Map embeddedSourceMap(String css) { return jsonDecode(data.contentAsString()) as Map; } +/// Returns a function with one argument that fails the test if it's ever +/// called. +Never Function(Object? arg) get expectNever1 => + expectAsync1((_) => throw '', count: 0); + // Like `p.prettyUri()`, but for a non-URL path. String prettyPath(String path) => p.prettyUri(p.toUri(path)); diff --git a/tool/grind.dart b/tool/grind.dart index ea53f7699..1162cf0f9 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -30,7 +30,7 @@ void main(List args) { pkg.jsModuleMainLibrary.value = "lib/src/node.dart"; pkg.npmPackageJson.fn = () => json.decode(File("package/package.json").readAsStringSync()) - as Map; + as Map; pkg.npmReadme.fn = () => _readAndResolveMarkdown("package/README.npm.md"); pkg.standaloneName.value = "dart-sass"; pkg.githubUser.fn = () => Platform.environment["GH_USER"]!; diff --git a/tool/grind/benchmark.dart b/tool/grind/benchmark.dart index 2cf7159a4..e0e416c2e 100644 --- a/tool/grind/benchmark.dart +++ b/tool/grind/benchmark.dart @@ -186,11 +186,9 @@ I ran five instances of each configuration and recorded the fastest time. Duration? sasscTime; if (!libsassIncompatible.contains(info[1])) { sasscTime = await _benchmark(p.join(sassc, 'bin', 'sassc'), [path]); - buffer.writeln("* sassc: ${_formatTime(sasscTime)}"); // TODO: no ! + buffer.writeln("* sassc: ${_formatTime(sasscTime)}"); } - // TODO: no as - var scriptSnapshotTime = await _benchmark(Platform.executable, ['--no-enable-asserts', p.join('build', 'sass.snapshot'), path]); buffer.writeln("* Dart Sass from a script snapshot: " @@ -246,12 +244,12 @@ Future _benchmark(String executable, List arguments) async { // chance to warm up at the OS level. await _benchmarkOnce(executable, arguments); - late Duration lowest; + Duration? lowest; for (var i = 0; i < 5; i++) { var duration = await _benchmarkOnce(executable, arguments); if (lowest == null || duration < lowest) lowest = duration; } - return lowest; + return lowest!; } Future _benchmarkOnce( @@ -263,8 +261,8 @@ Future _benchmarkOnce( fail("Process failed with exit code ${result.exitCode}\n${result.stderr}"); } - var match = RegExp(r"(\d+)m(\d+)\.(\d+)s") - .firstMatch(result.stderr as String)!; // TODO: no ! + var match = + RegExp(r"(\d+)m(\d+)\.(\d+)s").firstMatch(result.stderr as String); if (match == null) { fail("Process didn't print the expected format:\n${result.stderr}"); } diff --git a/tool/grind/utils.dart b/tool/grind/utils.dart index 6d877fae1..cd33f0cff 100644 --- a/tool/grind/utils.dart +++ b/tool/grind/utils.dart @@ -25,7 +25,7 @@ void ensureBuild() { /// Returns the environment variable named [name], or throws an exception if it /// can't be found. String environment(String name) { - var value = Platform.environment[name]!; // TODO: no ! + var value = Platform.environment[name]; if (value == null) fail("Required environment variable $name not found."); return value; } From 8fd3c1ba031f0fe2d8982476aa844a3872f568f9 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 18 Mar 2021 15:15:53 -0700 Subject: [PATCH 11/19] Rename Extender to ExtensionStore "Extender" is also commonly used to refer to the parent selector of an `@extend` rule, so this helps disambiguate. --- lib/src/async_environment.dart | 30 ++++---- lib/src/environment.dart | 30 ++++---- ...tender.dart => empty_extension_store.dart} | 18 ++--- .../{extender.dart => extension_store.dart} | 45 ++++++------ lib/src/extend/functions.dart | 6 +- lib/src/functions/selector.dart | 6 +- lib/src/module.dart | 4 +- lib/src/module/built_in.dart | 4 +- lib/src/module/forwarded_view.dart | 4 +- lib/src/module/shadowed_view.dart | 4 +- lib/src/visitor/async_evaluate.dart | 69 +++++++++--------- lib/src/visitor/clone_css.dart | 18 ++--- lib/src/visitor/evaluate.dart | 71 ++++++++++--------- 13 files changed, 162 insertions(+), 147 deletions(-) rename lib/src/extend/{empty_extender.dart => empty_extension_store.dart} (70%) rename lib/src/extend/{extender.dart => extension_store.dart} (97%) diff --git a/lib/src/async_environment.dart b/lib/src/async_environment.dart index ed13a4638..24c5a5304 100644 --- a/lib/src/async_environment.dart +++ b/lib/src/async_environment.dart @@ -14,7 +14,7 @@ import 'callable.dart'; import 'configuration.dart'; import 'configured_value.dart'; import 'exception.dart'; -import 'extend/extender.dart'; +import 'extend/extension_store.dart'; import 'module.dart'; import 'module/forwarded_view.dart'; import 'module/shadowed_view.dart'; @@ -831,15 +831,15 @@ class AsyncEnvironment { /// Returns a module that represents the top-level members defined in [this], /// that contains [css] as its CSS tree, which can be extended using - /// [extender]. - Module toModule(CssStylesheet css, Extender extender) { + /// [extensionStore]. + Module toModule(CssStylesheet css, ExtensionStore extensionStore) { assert(atRoot); - return _EnvironmentModule(this, css, extender, + return _EnvironmentModule(this, css, extensionStore, forwarded: _forwardedModules); } /// Returns a module with the same members and upstream modules as [this], but - /// an empty stylesheet and extender. + /// an empty stylesheet and extension store. /// /// This is used when resolving imports, since they need to inject forwarded /// members into the current scope. It's the only situation in which a nested @@ -849,7 +849,7 @@ class AsyncEnvironment { this, CssStylesheet(const [], SourceFile.decoded(const [], url: "").span(0)), - Extender.empty, + ExtensionStore.empty, forwarded: _forwardedModules); } @@ -924,7 +924,7 @@ class _EnvironmentModule implements Module { final Map? variableNodes; final Map functions; final Map mixins; - final Extender extender; + final ExtensionStore extensionStore; final CssStylesheet css; final bool transitivelyContainsCss; final bool transitivelyContainsExtensions; @@ -940,14 +940,14 @@ class _EnvironmentModule implements Module { /// defined at all. final Map _modulesByVariable; - factory _EnvironmentModule( - AsyncEnvironment environment, CssStylesheet css, Extender extender, + factory _EnvironmentModule(AsyncEnvironment environment, CssStylesheet css, + ExtensionStore extensionStore, {Set? forwarded}) { forwarded ??= const {}; return _EnvironmentModule._( environment, css, - extender, + extensionStore, _makeModulesByVariable(forwarded), _memberMap(environment._variables.first, forwarded.map((module) => module.variables)), @@ -962,7 +962,7 @@ class _EnvironmentModule implements Module { transitivelyContainsCss: css.children.isNotEmpty || environment._allModules .any((module) => module.transitivelyContainsCss), - transitivelyContainsExtensions: !extender.isEmpty || + transitivelyContainsExtensions: !extensionStore.isEmpty || environment._allModules .any((module) => module.transitivelyContainsExtensions)); } @@ -1007,7 +1007,7 @@ class _EnvironmentModule implements Module { _EnvironmentModule._( this._environment, this.css, - this.extender, + this.extensionStore, this._modulesByVariable, this.variables, this.variableNodes, @@ -1044,11 +1044,11 @@ class _EnvironmentModule implements Module { Module cloneCss() { if (css.children.isEmpty) return this; - var newCssAndExtender = cloneCssStylesheet(css, extender); + var newCssAndExtensionStore = cloneCssStylesheet(css, extensionStore); return _EnvironmentModule._( _environment, - newCssAndExtender.item1, - newCssAndExtender.item2, + newCssAndExtensionStore.item1, + newCssAndExtensionStore.item2, _modulesByVariable, variables, variableNodes, diff --git a/lib/src/environment.dart b/lib/src/environment.dart index e34646c70..6b8c3a079 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: 9d25f8e34c8e566be536523b873c21757b591bfc +// Checksum: f76eacd7d649c2bc26f0c2745a9e892c79dfcde3 // // ignore_for_file: unused_import @@ -21,7 +21,7 @@ import 'callable.dart'; import 'configuration.dart'; import 'configured_value.dart'; import 'exception.dart'; -import 'extend/extender.dart'; +import 'extend/extension_store.dart'; import 'module.dart'; import 'module/forwarded_view.dart'; import 'module/shadowed_view.dart'; @@ -837,15 +837,15 @@ class Environment { /// Returns a module that represents the top-level members defined in [this], /// that contains [css] as its CSS tree, which can be extended using - /// [extender]. - Module toModule(CssStylesheet css, Extender extender) { + /// [extensionStore]. + Module toModule(CssStylesheet css, ExtensionStore extensionStore) { assert(atRoot); - return _EnvironmentModule(this, css, extender, + return _EnvironmentModule(this, css, extensionStore, forwarded: _forwardedModules); } /// Returns a module with the same members and upstream modules as [this], but - /// an empty stylesheet and extender. + /// an empty stylesheet and extension store. /// /// This is used when resolving imports, since they need to inject forwarded /// members into the current scope. It's the only situation in which a nested @@ -855,7 +855,7 @@ class Environment { this, CssStylesheet(const [], SourceFile.decoded(const [], url: "").span(0)), - Extender.empty, + ExtensionStore.empty, forwarded: _forwardedModules); } @@ -931,7 +931,7 @@ class _EnvironmentModule implements Module { final Map? variableNodes; final Map functions; final Map mixins; - final Extender extender; + final ExtensionStore extensionStore; final CssStylesheet css; final bool transitivelyContainsCss; final bool transitivelyContainsExtensions; @@ -948,13 +948,13 @@ class _EnvironmentModule implements Module { final Map> _modulesByVariable; factory _EnvironmentModule( - Environment environment, CssStylesheet css, Extender extender, + Environment environment, CssStylesheet css, ExtensionStore extensionStore, {Set>? forwarded}) { forwarded ??= const {}; return _EnvironmentModule._( environment, css, - extender, + extensionStore, _makeModulesByVariable(forwarded), _memberMap(environment._variables.first, forwarded.map((module) => module.variables)), @@ -969,7 +969,7 @@ class _EnvironmentModule implements Module { transitivelyContainsCss: css.children.isNotEmpty || environment._allModules .any((module) => module.transitivelyContainsCss), - transitivelyContainsExtensions: !extender.isEmpty || + transitivelyContainsExtensions: !extensionStore.isEmpty || environment._allModules .any((module) => module.transitivelyContainsExtensions)); } @@ -1015,7 +1015,7 @@ class _EnvironmentModule implements Module { _EnvironmentModule._( this._environment, this.css, - this.extender, + this.extensionStore, this._modulesByVariable, this.variables, this.variableNodes, @@ -1052,11 +1052,11 @@ class _EnvironmentModule implements Module { Module cloneCss() { if (css.children.isEmpty) return this; - var newCssAndExtender = cloneCssStylesheet(css, extender); + var newCssAndExtensionStore = cloneCssStylesheet(css, extensionStore); return _EnvironmentModule._( _environment, - newCssAndExtender.item1, - newCssAndExtender.item2, + newCssAndExtensionStore.item1, + newCssAndExtensionStore.item2, _modulesByVariable, variables, variableNodes, diff --git a/lib/src/extend/empty_extender.dart b/lib/src/extend/empty_extension_store.dart similarity index 70% rename from lib/src/extend/empty_extender.dart rename to lib/src/extend/empty_extension_store.dart index bdef0d717..8b4aec345 100644 --- a/lib/src/extend/empty_extender.dart +++ b/lib/src/extend/empty_extension_store.dart @@ -10,15 +10,15 @@ import '../ast/css.dart'; import '../ast/css/modifiable.dart'; import '../ast/selector.dart'; import '../ast/sass.dart'; -import 'extender.dart'; +import 'extension_store.dart'; import 'extension.dart'; -class EmptyExtender implements Extender { +class EmptyExtensionStore implements ExtensionStore { bool get isEmpty => true; Set get simpleSelectors => const UnmodifiableSetView.empty(); - const EmptyExtender(); + const EmptyExtensionStore(); Iterable extensionsWhereTarget( bool callback(SimpleSelector target)) => @@ -28,22 +28,22 @@ class EmptyExtender implements Extender { SelectorList selector, FileSpan? span, [List? mediaContext]) { throw UnsupportedError( - "addSelector() can't be called for a const Extender."); + "addSelector() can't be called for a const ExtensionStore."); } void addExtension( CssValue extender, SimpleSelector target, ExtendRule extend, [List? mediaContext]) { throw UnsupportedError( - "addExtension() can't be called for a const Extender."); + "addExtension() can't be called for a const ExtensionStore."); } - void addExtensions(Iterable extenders) { + void addExtensions(Iterable extenders) { throw UnsupportedError( - "addExtensions() can't be called for a const Extender."); + "addExtensions() can't be called for a const ExtensionStore."); } - Tuple2, ModifiableCssValue>> - clone() => const Tuple2(EmptyExtender(), {}); + clone() => const Tuple2(EmptyExtensionStore(), {}); } diff --git a/lib/src/extend/extender.dart b/lib/src/extend/extension_store.dart similarity index 97% rename from lib/src/extend/extender.dart rename to lib/src/extend/extension_store.dart index 57f22daf7..f03b7814f 100644 --- a/lib/src/extend/extender.dart +++ b/lib/src/extend/extension_store.dart @@ -15,16 +15,16 @@ import '../ast/sass.dart'; import '../exception.dart'; import '../utils.dart'; import '../util/nullable.dart'; -import 'empty_extender.dart'; +import 'empty_extension_store.dart'; import 'extension.dart'; import 'merged_extension.dart'; import 'functions.dart'; import 'mode.dart'; /// Tracks selectors and extensions, and applies the latter to the former. -class Extender { - /// An [Extender] that contains no extensions and can have no extensions added. - static const empty = EmptyExtender(); +class ExtensionStore { + /// An [ExtensionStore] that contains no extensions and can have no extensions added. + static const empty = EmptyExtensionStore(); /// A map from all simple selectors in the stylesheet to the selector lists /// that contain them. @@ -108,7 +108,7 @@ class Extender { for (var simple in compound.components) simple: extenders }; - var extender = Extender._mode(mode); + var extender = ExtensionStore._mode(mode); if (!selector.isInvisible) { extender._originals.addAll(selector.components); } @@ -123,9 +123,9 @@ class Extender { /// extensions. Set get simpleSelectors => MapKeySet(_selectors); - Extender() : this._mode(ExtendMode.normal); + ExtensionStore() : this._mode(ExtendMode.normal); - Extender._mode(this._mode) + ExtensionStore._mode(this._mode) : _selectors = {}, _extensions = {}, _extensionsByExtender = {}, @@ -133,8 +133,13 @@ class Extender { _sourceSpecificity = Map.identity(), _originals = Set.identity(); - Extender._(this._selectors, this._extensions, this._extensionsByExtender, - this._mediaContexts, this._sourceSpecificity, this._originals) + ExtensionStore._( + this._selectors, + this._extensions, + this._extensionsByExtender, + this._mediaContexts, + this._sourceSpecificity, + this._originals) : _mode = ExtendMode.normal; /// Returns all mandatory extensions in this extender for whose targets @@ -400,7 +405,7 @@ class Extender { /// /// These extensions will extend all selectors already in [this], but they /// will *not* extend other extensions from [extenders]. - void addExtensions(Iterable extenders) { + void addExtensions(Iterable extensionStores) { // Extensions already in [this] whose extenders are extended by // [extensions], and thus which need to be updated. List? extensionsToExtend; @@ -410,13 +415,13 @@ class Extender { Set>? selectorsToExtend; // An extension map with the same structure as [_extensions] that only - // includes extensions from [extenders]. + // includes extensions from [extensionStores]. Map?>? newExtensions; - for (var extender in extenders) { - if (extender.isEmpty) continue; - _sourceSpecificity.addAll(extender._sourceSpecificity); - extender._extensions.forEach((target, newSources) { + for (var extensionStore in extensionStores) { + if (extensionStore.isEmpty) continue; + _sourceSpecificity.addAll(extensionStore._sourceSpecificity); + extensionStore._extensions.forEach((target, newSources) { // Private selectors can't be extended across module boundaries. if (target is PlaceholderSelector && target.isPrivate) return; @@ -437,10 +442,10 @@ class Extender { // Add [newSources] to [_extensions]. var existingSources = _extensions[target]; if (existingSources == null) { - _extensions[target] = extender._extensions[target]!; + _extensions[target] = extensionStore._extensions[target]!; if (extensionsForTarget != null || selectorsForTarget != null) { newExtensions ??= {}; - newExtensions![target] = extender._extensions[target]; + newExtensions![target] = extensionStore._extensions[target]; } } else { newSources.forEach((extender, extension) { @@ -924,8 +929,8 @@ class Extender { /// Returns a copy of [this] that extends new selectors, as well as a map from /// the selectors extended by [this] to the selectors extended by the new - /// [Extender]. - Tuple2, ModifiableCssValue>> clone() { var newSelectors = >>{}; @@ -949,7 +954,7 @@ class Extender { }); return Tuple2( - Extender._( + ExtensionStore._( newSelectors, copyMapOfMap(_extensions), copyMapOfList(_extensionsByExtender), diff --git a/lib/src/extend/functions.dart b/lib/src/extend/functions.dart index c5ac3a997..ec6123e6e 100644 --- a/lib/src/extend/functions.dart +++ b/lib/src/extend/functions.dart @@ -4,9 +4,9 @@ /// This library contains utility functions related to extending selectors. /// -/// These functions aren't private methods on [Extender] because they also need -/// to be accessible from elsewhere in the codebase. In addition, they aren't -/// instance methods on other objects because their APIs aren't a good +/// These functions aren't private methods on [ExtensionStore] because they also +/// need to be accessible from elsewhere in the codebase. In addition, they +/// aren't instance methods on other objects because their APIs aren't a good /// fit—usually because they deal with raw component lists rather than selector /// classes, to reduce allocations. diff --git a/lib/src/functions/selector.dart b/lib/src/functions/selector.dart index ed3e8ef19..8aababbeb 100644 --- a/lib/src/functions/selector.dart +++ b/lib/src/functions/selector.dart @@ -9,7 +9,7 @@ import 'package:collection/collection.dart'; import '../ast/selector.dart'; import '../callable.dart'; import '../exception.dart'; -import '../extend/extender.dart'; +import '../extend/extension_store.dart'; import '../module/built_in.dart'; import '../value.dart'; @@ -87,7 +87,7 @@ final _extend = var target = arguments[1].assertSelector(name: "extendee"); var source = arguments[2].assertSelector(name: "extender"); - return Extender.extend(selector, source, target).asSassList; + return ExtensionStore.extend(selector, source, target).asSassList; }); final _replace = @@ -96,7 +96,7 @@ final _replace = var target = arguments[1].assertSelector(name: "original"); var source = arguments[2].assertSelector(name: "replacement"); - return Extender.replace(selector, source, target).asSassList; + return ExtensionStore.replace(selector, source, target).asSassList; }); final _unify = _function("unify", r"$selector1, $selector2", (arguments) { diff --git a/lib/src/module.dart b/lib/src/module.dart index a979804c2..cbd2d32c5 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -7,7 +7,7 @@ import 'package:source_span/source_span.dart'; import 'ast/css.dart'; import 'ast/node.dart'; import 'callable.dart'; -import 'extend/extender.dart'; +import 'extend/extension_store.dart'; import 'value.dart'; /// The interface for a Sass module. @@ -50,7 +50,7 @@ abstract class Module { /// The extensions defined in this module, which is also able to update /// [css]'s style rules in-place based on downstream extensions. - Extender get extender; + ExtensionStore get extensionStore; /// The module's CSS tree. CssStylesheet get css; diff --git a/lib/src/module/built_in.dart b/lib/src/module/built_in.dart index d1589e891..ae1d2d86b 100644 --- a/lib/src/module/built_in.dart +++ b/lib/src/module/built_in.dart @@ -8,7 +8,7 @@ import '../ast/css.dart'; import '../ast/node.dart'; import '../callable.dart'; import '../exception.dart'; -import '../extend/extender.dart'; +import '../extend/extension_store.dart'; import '../module.dart'; import '../value.dart'; @@ -21,7 +21,7 @@ class BuiltInModule implements Module { List> get upstream => const []; Map get variableNodes => const {}; - Extender get extender => Extender.empty; + ExtensionStore get extensionStore => ExtensionStore.empty; CssStylesheet get css => CssStylesheet.empty(url: url); bool get transitivelyContainsCss => false; bool get transitivelyContainsExtensions => false; diff --git a/lib/src/module/forwarded_view.dart b/lib/src/module/forwarded_view.dart index 01ed427e6..14874e82a 100644 --- a/lib/src/module/forwarded_view.dart +++ b/lib/src/module/forwarded_view.dart @@ -7,7 +7,7 @@ import '../ast/node.dart'; import '../ast/sass.dart'; import '../callable.dart'; import '../exception.dart'; -import '../extend/extender.dart'; +import '../extend/extension_store.dart'; import '../module.dart'; import '../util/limited_map_view.dart'; import '../util/nullable.dart'; @@ -24,7 +24,7 @@ class ForwardedModuleView implements Module { Uri? get url => _inner.url; List> get upstream => _inner.upstream; - Extender get extender => _inner.extender; + ExtensionStore get extensionStore => _inner.extensionStore; CssStylesheet get css => _inner.css; bool get transitivelyContainsCss => _inner.transitivelyContainsCss; bool get transitivelyContainsExtensions => diff --git a/lib/src/module/shadowed_view.dart b/lib/src/module/shadowed_view.dart index 222f6dccd..f9f5516e4 100644 --- a/lib/src/module/shadowed_view.dart +++ b/lib/src/module/shadowed_view.dart @@ -6,7 +6,7 @@ import '../ast/css.dart'; import '../ast/node.dart'; import '../callable.dart'; import '../exception.dart'; -import '../extend/extender.dart'; +import '../extend/extension_store.dart'; import '../module.dart'; import '../util/limited_map_view.dart'; import '../util/nullable.dart'; @@ -21,7 +21,7 @@ class ShadowedModuleView implements Module { Uri? get url => _inner.url; List> get upstream => _inner.upstream; - Extender get extender => _inner.extender; + ExtensionStore get extensionStore => _inner.extensionStore; CssStylesheet get css => _inner.css; bool get transitivelyContainsCss => _inner.transitivelyContainsCss; bool get transitivelyContainsExtensions => diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 9a7c5f994..448a0ac21 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -24,7 +24,7 @@ import '../color_names.dart'; import '../configuration.dart'; import '../configured_value.dart'; import '../exception.dart'; -import '../extend/extender.dart'; +import '../extend/extension_store.dart'; import '../extend/extension.dart'; import '../functions.dart'; import '../functions/meta.dart' as meta; @@ -260,9 +260,9 @@ class _EvaluateVisitor /// stylesheet. late List? _outOfOrderImports; - /// The extender that tracks extensions and style rules for the current + /// The extension store that tracks extensions and style rules for the current /// module. - late Extender _extender; + late ExtensionStore _extensionStore; /// The configuration for the current module. /// @@ -651,7 +651,7 @@ class _EvaluateVisitor var environment = AsyncEnvironment(sourceMap: _sourceMap); late CssStylesheet css; - var extender = Extender(); + var extensionStore = ExtensionStore(); await _withEnvironment(environment, () async { var oldImporter = _importer; var oldStylesheet = _stylesheet; @@ -659,7 +659,7 @@ class _EvaluateVisitor var oldParent = _parent; var oldEndOfImports = _endOfImports; var oldOutOfOrderImports = _outOfOrderImports; - var oldExtender = _extender; + var oldExtensionStore = _extensionStore; var oldStyleRule = _styleRule; var oldMediaQueries = _mediaQueries; var oldDeclarationName = _declarationName; @@ -673,7 +673,7 @@ class _EvaluateVisitor _parent = root; _endOfImports = 0; _outOfOrderImports = null; - _extender = extender; + _extensionStore = extensionStore; _styleRuleIgnoringAtRoot = null; _mediaQueries = null; _declarationName = null; @@ -693,7 +693,7 @@ class _EvaluateVisitor _parent = oldParent; _endOfImports = oldEndOfImports; _outOfOrderImports = oldOutOfOrderImports; - _extender = oldExtender; + _extensionStore = oldExtensionStore; _styleRuleIgnoringAtRoot = oldStyleRule; _mediaQueries = oldMediaQueries; _declarationName = oldDeclarationName; @@ -703,7 +703,7 @@ class _EvaluateVisitor _configuration = oldConfiguration; }); - var module = environment.toModule(css, extender); + var module = environment.toModule(css, extensionStore); if (url != null) { _modules[url] = module; if (nodeWithSpan != null) _moduleNodes[url] = nodeWithSpan; @@ -734,8 +734,8 @@ class _EvaluateVisitor /// that they don't modify [root] or its dependencies. CssStylesheet _combineCss(Module root, {bool clone = false}) { if (!root.upstream.any((module) => module.transitivelyContainsCss)) { - var selectors = root.extender.simpleSelectors; - var unsatisfiedExtension = firstOrNull(root.extender + var selectors = root.extensionStore.simpleSelectors; + var unsatisfiedExtension = firstOrNull(root.extensionStore .extensionsWhereTarget((target) => !selectors.contains(target))); if (unsatisfiedExtension != null) { _throwForUnsatisfiedExtension(unsatisfiedExtension); @@ -770,11 +770,12 @@ class _EvaluateVisitor /// Extends the selectors in each module with the extensions defined in /// downstream modules. void _extendModules(List sortedModules) { - // All the extenders directly downstream of a given module (indexed by its - // canonical URL). It's important that we create this in topological order, - // so that by the time we're processing a module we've already filled in all - // its downstream extenders and we can use them to extend that module. - var downstreamExtenders = >{}; + // All the [ExtensionStore]s directly downstream of a given module (indexed + // by its canonical URL). It's important that we create this in topological + // order, so that by the time we're processing a module we've already filled + // in all its downstream [ExtensionStore]s and we can use them to extend + // that module. + var downstreamExtensionStores = >{}; /// Extensions that haven't yet been satisfied by some upstream module. This /// adds extensions when they're defined but not satisfied, and removes them @@ -782,31 +783,35 @@ class _EvaluateVisitor var unsatisfiedExtensions = Set.identity(); for (var module in sortedModules) { - // Create a snapshot of the simple selectors currently in the extender so - // that we don't consider an extension "satisfied" below because of a - // simple selector added by another (sibling) extension. - var originalSelectors = module.extender.simpleSelectors.toSet(); + // Create a snapshot of the simple selectors currently in the + // [ExtensionStore] so that we don't consider an extension "satisfied" + // below because of a simple selector added by another (sibling) + // extension. + var originalSelectors = module.extensionStore.simpleSelectors.toSet(); // Add all as-yet-unsatisfied extensions before adding downstream - // extenders, because those are all in [unsatisfiedExtensions] already. - unsatisfiedExtensions.addAll(module.extender.extensionsWhereTarget( + // [ExtensionStore]s, because those are all in [unsatisfiedExtensions] + // already. + unsatisfiedExtensions.addAll(module.extensionStore.extensionsWhereTarget( (target) => !originalSelectors.contains(target))); - var extenders = downstreamExtenders[module.url]; - if (extenders != null) module.extender.addExtensions(extenders); - if (module.extender.isEmpty) continue; + downstreamExtensionStores[module.url] + .andThen(module.extensionStore.addExtensions); + if (module.extensionStore.isEmpty) continue; for (var upstream in module.upstream) { var url = upstream.url; if (url == null) continue; - downstreamExtenders.putIfAbsent(url, () => []).add(module.extender); + downstreamExtensionStores + .putIfAbsent(url, () => []) + .add(module.extensionStore); } // Remove all extensions that are now satisfied after adding downstream - // extenders so it counts any downstream extensions that have been newly - // satisfied. - unsatisfiedExtensions.removeAll( - module.extender.extensionsWhereTarget(originalSelectors.contains)); + // [ExtensionStore]s so it counts any downstream extensions that have been + // newly satisfied. + unsatisfiedExtensions.removeAll(module.extensionStore + .extensionsWhereTarget(originalSelectors.contains)); } if (unsatisfiedExtensions.isNotEmpty) { @@ -1151,7 +1156,7 @@ class _EvaluateVisitor targetText.span); } - _extender.addExtension( + _extensionStore.addExtension( styleRule.selector, compound.components.first, node, _mediaQueries); } @@ -1751,7 +1756,7 @@ class _EvaluateVisitor _styleRuleIgnoringAtRoot?.originalSelector, implicitParent: !_atRootExcludingStyleRule)); - var selector = _extender.addSelector( + var selector = _extensionStore.addSelector( parsedSelector, node.selector.span, _mediaQueries); var rule = ModifiableCssStyleRule(selector, node.span, originalSelector: parsedSelector); @@ -2672,7 +2677,7 @@ class _EvaluateVisitor var originalSelector = node.selector.value.resolveParentSelectors( styleRule?.originalSelector, implicitParent: !_atRootExcludingStyleRule); - var selector = _extender.addSelector( + var selector = _extensionStore.addSelector( originalSelector, node.selector.span, _mediaQueries); var rule = ModifiableCssStyleRule(selector, node.span, originalSelector: originalSelector); diff --git a/lib/src/visitor/clone_css.dart b/lib/src/visitor/clone_css.dart index ae4fe1151..ba4bf7333 100644 --- a/lib/src/visitor/clone_css.dart +++ b/lib/src/visitor/clone_css.dart @@ -7,27 +7,27 @@ import 'package:tuple/tuple.dart'; import '../ast/css.dart'; import '../ast/css/modifiable.dart'; import '../ast/selector.dart'; -import '../extend/extender.dart'; +import '../extend/extension_store.dart'; import 'interface/css.dart'; /// Returns deep copies of both [stylesheet] and [extender]. /// /// The [extender] must be associated with [stylesheet]. -Tuple2 cloneCssStylesheet( - CssStylesheet stylesheet, Extender extender) { - var result = extender.clone(); - var newExtender = result.item1; +Tuple2 cloneCssStylesheet( + CssStylesheet stylesheet, ExtensionStore extensionStore) { + var result = extensionStore.clone(); + var newExtensionStore = result.item1; var oldToNewSelectors = result.item2; return Tuple2( _CloneCssVisitor(oldToNewSelectors).visitCssStylesheet(stylesheet), - newExtender); + newExtensionStore); } /// A visitor that creates a deep (and mutable) copy of a [CssStylesheet]. class _CloneCssVisitor implements CssVisitor { /// A map from selectors in the original stylesheet to selectors generated for - /// the new stylesheet using [Extender.clone]. + /// the new stylesheet using [ExtensionStore.clone]. final Map, ModifiableCssValue> _oldToNewSelectors; @@ -62,8 +62,8 @@ class _CloneCssVisitor implements CssVisitor { var newSelector = _oldToNewSelectors[node.selector]; if (newSelector == null) { throw StateError( - "The Extender and CssStylesheet passed to cloneCssStylesheet() must " - "come from the same compilation."); + "The ExtensionStore and CssStylesheet passed to cloneCssStylesheet() " + "must come from the same compilation."); } return _visitChildren( diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 75b623c95..7b1755242 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: 54af55cd11a583e5fca273e00f38831af26f0111 +// Checksum: 52761b53fe4aa8212132088b2447d54028aa9f7c // // ignore_for_file: unused_import @@ -33,7 +33,7 @@ import '../color_names.dart'; import '../configuration.dart'; import '../configured_value.dart'; import '../exception.dart'; -import '../extend/extender.dart'; +import '../extend/extension_store.dart'; import '../extend/extension.dart'; import '../functions.dart'; import '../functions/meta.dart' as meta; @@ -268,9 +268,9 @@ class _EvaluateVisitor /// stylesheet. late List? _outOfOrderImports; - /// The extender that tracks extensions and style rules for the current + /// The extension store that tracks extensions and style rules for the current /// module. - late Extender _extender; + late ExtensionStore _extensionStore; /// The configuration for the current module. /// @@ -656,7 +656,7 @@ class _EvaluateVisitor var environment = Environment(sourceMap: _sourceMap); late CssStylesheet css; - var extender = Extender(); + var extensionStore = ExtensionStore(); _withEnvironment(environment, () { var oldImporter = _importer; var oldStylesheet = _stylesheet; @@ -664,7 +664,7 @@ class _EvaluateVisitor var oldParent = _parent; var oldEndOfImports = _endOfImports; var oldOutOfOrderImports = _outOfOrderImports; - var oldExtender = _extender; + var oldExtensionStore = _extensionStore; var oldStyleRule = _styleRule; var oldMediaQueries = _mediaQueries; var oldDeclarationName = _declarationName; @@ -678,7 +678,7 @@ class _EvaluateVisitor _parent = root; _endOfImports = 0; _outOfOrderImports = null; - _extender = extender; + _extensionStore = extensionStore; _styleRuleIgnoringAtRoot = null; _mediaQueries = null; _declarationName = null; @@ -698,7 +698,7 @@ class _EvaluateVisitor _parent = oldParent; _endOfImports = oldEndOfImports; _outOfOrderImports = oldOutOfOrderImports; - _extender = oldExtender; + _extensionStore = oldExtensionStore; _styleRuleIgnoringAtRoot = oldStyleRule; _mediaQueries = oldMediaQueries; _declarationName = oldDeclarationName; @@ -708,7 +708,7 @@ class _EvaluateVisitor _configuration = oldConfiguration; }); - var module = environment.toModule(css, extender); + var module = environment.toModule(css, extensionStore); if (url != null) { _modules[url] = module; if (nodeWithSpan != null) _moduleNodes[url] = nodeWithSpan; @@ -739,8 +739,8 @@ class _EvaluateVisitor /// that they don't modify [root] or its dependencies. CssStylesheet _combineCss(Module root, {bool clone = false}) { if (!root.upstream.any((module) => module.transitivelyContainsCss)) { - var selectors = root.extender.simpleSelectors; - var unsatisfiedExtension = firstOrNull(root.extender + var selectors = root.extensionStore.simpleSelectors; + var unsatisfiedExtension = firstOrNull(root.extensionStore .extensionsWhereTarget((target) => !selectors.contains(target))); if (unsatisfiedExtension != null) { _throwForUnsatisfiedExtension(unsatisfiedExtension); @@ -775,11 +775,12 @@ class _EvaluateVisitor /// Extends the selectors in each module with the extensions defined in /// downstream modules. void _extendModules(List> sortedModules) { - // All the extenders directly downstream of a given module (indexed by its - // canonical URL). It's important that we create this in topological order, - // so that by the time we're processing a module we've already filled in all - // its downstream extenders and we can use them to extend that module. - var downstreamExtenders = >{}; + // All the [ExtensionStore]s directly downstream of a given module (indexed + // by its canonical URL). It's important that we create this in topological + // order, so that by the time we're processing a module we've already filled + // in all its downstream [ExtensionStore]s and we can use them to extend + // that module. + var downstreamExtensionStores = >{}; /// Extensions that haven't yet been satisfied by some upstream module. This /// adds extensions when they're defined but not satisfied, and removes them @@ -787,31 +788,35 @@ class _EvaluateVisitor var unsatisfiedExtensions = Set.identity(); for (var module in sortedModules) { - // Create a snapshot of the simple selectors currently in the extender so - // that we don't consider an extension "satisfied" below because of a - // simple selector added by another (sibling) extension. - var originalSelectors = module.extender.simpleSelectors.toSet(); + // Create a snapshot of the simple selectors currently in the + // [ExtensionStore] so that we don't consider an extension "satisfied" + // below because of a simple selector added by another (sibling) + // extension. + var originalSelectors = module.extensionStore.simpleSelectors.toSet(); // Add all as-yet-unsatisfied extensions before adding downstream - // extenders, because those are all in [unsatisfiedExtensions] already. - unsatisfiedExtensions.addAll(module.extender.extensionsWhereTarget( + // [ExtensionStore]s, because those are all in [unsatisfiedExtensions] + // already. + unsatisfiedExtensions.addAll(module.extensionStore.extensionsWhereTarget( (target) => !originalSelectors.contains(target))); - var extenders = downstreamExtenders[module.url]; - if (extenders != null) module.extender.addExtensions(extenders); - if (module.extender.isEmpty) continue; + downstreamExtensionStores[module.url] + .andThen(module.extensionStore.addExtensions); + if (module.extensionStore.isEmpty) continue; for (var upstream in module.upstream) { var url = upstream.url; if (url == null) continue; - downstreamExtenders.putIfAbsent(url, () => []).add(module.extender); + downstreamExtensionStores + .putIfAbsent(url, () => []) + .add(module.extensionStore); } // Remove all extensions that are now satisfied after adding downstream - // extenders so it counts any downstream extensions that have been newly - // satisfied. - unsatisfiedExtensions.removeAll( - module.extender.extensionsWhereTarget(originalSelectors.contains)); + // [ExtensionStore]s so it counts any downstream extensions that have been + // newly satisfied. + unsatisfiedExtensions.removeAll(module.extensionStore + .extensionsWhereTarget(originalSelectors.contains)); } if (unsatisfiedExtensions.isNotEmpty) { @@ -1153,7 +1158,7 @@ class _EvaluateVisitor targetText.span); } - _extender.addExtension( + _extensionStore.addExtension( styleRule.selector, compound.components.first, node, _mediaQueries); } @@ -1745,7 +1750,7 @@ class _EvaluateVisitor _styleRuleIgnoringAtRoot?.originalSelector, implicitParent: !_atRootExcludingStyleRule)); - var selector = _extender.addSelector( + var selector = _extensionStore.addSelector( parsedSelector, node.selector.span, _mediaQueries); var rule = ModifiableCssStyleRule(selector, node.span, originalSelector: parsedSelector); @@ -2655,7 +2660,7 @@ class _EvaluateVisitor var originalSelector = node.selector.value.resolveParentSelectors( styleRule?.originalSelector, implicitParent: !_atRootExcludingStyleRule); - var selector = _extender.addSelector( + var selector = _extensionStore.addSelector( originalSelector, node.selector.span, _mediaQueries); var rule = ModifiableCssStyleRule(selector, node.span, originalSelector: originalSelector); From 3ead2e2bb5f5ec1eca30bd4b7696d90623bb595d Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 18 Mar 2021 16:28:25 -0700 Subject: [PATCH 12/19] Split out an Extender class from Extension This gets rid of the weird subset of "one-off" extensions which didn't have target information available. Now instead, each method explicitly declares whether it takes/returns extensions (which do have target info) or extenders (which do not). --- lib/src/extend/extension.dart | 106 ++++++++++-------- lib/src/extend/extension_store.dart | 158 ++++++++++++++------------- lib/src/extend/merged_extension.dart | 18 +-- 3 files changed, 154 insertions(+), 128 deletions(-) diff --git a/lib/src/extend/extension.dart b/lib/src/extend/extension.dart index 31321daca..7e79afeac 100644 --- a/lib/src/extend/extension.dart +++ b/lib/src/extend/extension.dart @@ -14,34 +14,19 @@ import '../utils.dart'; /// The target of the extension is represented externally, in the map that /// contains this extender. class Extension { - /// The selector in which the `@extend` appeared. - final ComplexSelector extender; + /// The extender (such as `A` in `A {@extend B}`). + final Extender extender; /// The selector that's being extended. - /// - /// `null` for one-off extensions. - final SimpleSelector? target; + final SimpleSelector target; - /// The minimum specificity required for any selector generated from this - /// extender. - final int specificity; + /// The media query context to which this extension is restricted, or `null` + /// if it can apply within any context. + final List? mediaContext; /// Whether this extension is optional. final bool isOptional; - /// Whether this is a one-off extender representing a selector that was - /// originally in the document, rather than one defined with `@extend`. - final bool isOriginal; - - /// The media query context to which this extend is restricted, or `null` if - /// it can apply within any context. - final List? mediaContext; - - /// The span in which [extender] was defined. - /// - /// `null` for one-off extensions. - final FileSpan? extenderSpan; - /// The span for an `@extend` rule that defined this extension. /// /// If any extend rule for this is extension is mandatory, this is guaranteed @@ -51,43 +36,68 @@ class Extension { /// Creates a new extension. /// /// If [specificity] isn't passed, it defaults to `extender.maxSpecificity`. - Extension(ComplexSelector extender, this.target, this.extenderSpan, this.span, - this.mediaContext, - {int? specificity, bool optional = false}) - : extender = extender, - specificity = specificity ?? extender.maxSpecificity, - isOptional = optional, - isOriginal = false; - - /// Creates a one-off extension that's not intended to be modified over time. + Extension( + ComplexSelector extender, FileSpan? extenderSpan, this.target, this.span, + {this.mediaContext, bool optional = false}) + : extender = Extender(extender, extenderSpan), + isOptional = optional { + this.extender._extension = this; + } + + Extension withExtender(ComplexSelector newExtender) => + Extension(newExtender, extender.span, target, span, + mediaContext: mediaContext, optional: isOptional); + + String toString() => + "$extender {@extend $target${isOptional ? ' !optional' : ''}}"; +} + +/// A selector that's extending another selector, such as `A` in `A {@extend +/// B}`. +class Extender { + /// The selector in which the `@extend` appeared. + final ComplexSelector selector; + + /// The minimum specificity required for any selector generated from this + /// extender. + final int specificity; + + /// Whether this extender represents a selector that was originally in the + /// document, rather than one defined with `@extend`. + final bool isOriginal; + + /// The extension that created this [Extender]. + /// + /// Not all [Extender]s are created by extensions. Some simply represent the + /// original selectors that exist in the document. + Extension? _extension; + + /// The span in which this selector was defined. + final FileSpan? span; + + /// Creates a new extender. /// /// If [specificity] isn't passed, it defaults to `extender.maxSpecificity`. - Extension.oneOff(ComplexSelector extender, - {int? specificity, this.isOriginal = false}) - : extender = extender, - target = null, - extenderSpan = null, - specificity = specificity ?? extender.maxSpecificity, - isOptional = true, - mediaContext = null, - span = null; + Extender(this.selector, this.span, {int? specificity, bool original = false}) + : specificity = specificity ?? selector.maxSpecificity, + isOriginal = original; /// Asserts that the [mediaContext] for a selector is compatible with the /// query context for this extender. void assertCompatibleMediaContext(List? mediaContext) { - if (this.mediaContext == null) return; - if (mediaContext != null && listEquals(this.mediaContext, mediaContext)) { + var extension = _extension; + if (extension == null) return; + + var expectedMediaContext = extension.mediaContext; + if (expectedMediaContext == null) return; + if (mediaContext != null && + listEquals(expectedMediaContext, mediaContext)) { return; } throw SassException( - "You may not @extend selectors across media queries.", span); + "You may not @extend selectors across media queries.", extension.span); } - Extension withExtender(ComplexSelector newExtender) => - Extension(newExtender, target, extenderSpan, span, mediaContext, - specificity: specificity, optional: isOptional); - - String toString() => - "$extender {@extend $target${isOptional ? ' !optional' : ''}}"; + String toString() => selector.toString(); } diff --git a/lib/src/extend/extension_store.dart b/lib/src/extend/extension_store.dart index f03b7814f..2d08f3a1a 100644 --- a/lib/src/extend/extension_store.dart +++ b/lib/src/extend/extension_store.dart @@ -91,10 +91,6 @@ class ExtensionStore { /// A helper function for [extend] and [replace]. static SelectorList _extendOrReplace(SelectorList selector, SelectorList source, SelectorList targets, ExtendMode mode) { - var extenders = { - for (var complex in source.components) complex: Extension.oneOff(complex) - }; - var compoundTargets = [ for (var complex in targets.components) if (complex.components.length != 1) @@ -105,14 +101,18 @@ class ExtensionStore { var extensions = { for (var compound in compoundTargets) - for (var simple in compound.components) simple: extenders + for (var simple in compound.components) + simple: { + for (var complex in source.components) + complex: Extension(complex, null, simple, null, optional: true) + } }; var extender = ExtensionStore._mode(mode); if (!selector.isInvisible) { extender._originals.addAll(selector.components); } - selector = extender._extendList(selector, extensions, null); + selector = extender._extendList(selector, null /* listSpan */, extensions); return selector; } @@ -173,7 +173,7 @@ class ExtensionStore { /// The [mediaContext] is the media query context in which the selector was /// defined, or `null` if it was defined at the top level of the document. ModifiableCssValue addSelector( - SelectorList selector, FileSpan? span, + SelectorList selector, FileSpan? selectorSpan, [List? mediaContext]) { var originalSelector = selector; if (!originalSelector.isInvisible) { @@ -184,7 +184,8 @@ class ExtensionStore { if (_extensions.isNotEmpty) { try { - selector = _extendList(originalSelector, _extensions, mediaContext); + selector = _extendList( + originalSelector, selectorSpan, _extensions, mediaContext); } on SassException catch (error) { var span = error.span; if (span == null) rethrow; @@ -196,7 +197,7 @@ class ExtensionStore { } } - var modifiableSelector = ModifiableCssValue(selector, span); + var modifiableSelector = ModifiableCssValue(selector, selectorSpan); if (mediaContext != null) _mediaContexts[modifiableSelector] = mediaContext; _registerSelector(selector, modifiableSelector); @@ -242,25 +243,24 @@ class ExtensionStore { Map? newExtensions; var sources = _extensions.putIfAbsent(target, () => {}); for (var complex in extender.value.components) { - var state = Extension( - complex, target, extender.span, extend.span, mediaContext, - optional: extend.isOptional); + var extension = Extension(complex, extender.span, target, extend.span, + mediaContext: mediaContext, optional: extend.isOptional); - var existingState = sources[complex]; - if (existingState != null) { + var existingExtension = sources[complex]; + if (existingExtension != null) { // If there's already an extend from [extender] to [target], we don't need // to re-run the extension. We may need to mark the extension as // mandatory, though. - sources[complex] = MergedExtension.merge(existingState, state); + sources[complex] = MergedExtension.merge(existingExtension, extension); continue; } - sources[complex] = state; + sources[complex] = extension; for (var component in complex.components) { if (component is CompoundSelector) { for (var simple in component.components) { - _extensionsByExtender.putIfAbsent(simple, () => []).add(state); + _extensionsByExtender.putIfAbsent(simple, () => []).add(extension); // Only source specificity for the original selector is relevant. // Selectors generated by `@extend` don't get new specificity. _sourceSpecificity.putIfAbsent( @@ -271,7 +271,7 @@ class ExtensionStore { if (selectors != null || existingExtensions != null) { newExtensions ??= {}; - newExtensions[complex] = state; + newExtensions[complex] = extension; } } @@ -312,16 +312,15 @@ class ExtensionStore { Map>? additionalExtensions; for (var extension in extensions.toList()) { - var sources = _extensions[extension.target!]!; + var sources = _extensions[extension.target]!; - // [_extendExistingSelectors] would have thrown already. List? selectors; try { - selectors = _extendComplex( - extension.extender, newExtensions, extension.mediaContext); + selectors = _extendComplex(extension.extender.selector, + extension.extender.span, newExtensions, extension.mediaContext); if (selectors == null) continue; } on SassException catch (error) { - var extenderSpan = extension.extenderSpan; + var extenderSpan = extension.extender.span; if (extenderSpan == null) rethrow; throw SassException( @@ -330,7 +329,7 @@ class ExtensionStore { error.span); } - var containsExtension = selectors.first == extension.extender; + var containsExtension = selectors.first == extension.extender.selector; var first = false; for (var complex in selectors) { // If the output contains the original complex selector, there's no @@ -361,7 +360,7 @@ class ExtensionStore { if (newExtensions!.containsKey(extension.target)) { additionalExtensions ??= {}; var additionalSources = - additionalExtensions.putIfAbsent(extension.target!, () => {}); + additionalExtensions.putIfAbsent(extension.target, () => {}); additionalSources[complex] = withExtender; } } @@ -382,8 +381,8 @@ class ExtensionStore { for (var selector in selectors) { var oldValue = selector.value; try { - selector.value = _extendList( - selector.value, newExtensions, _mediaContexts[selector]); + selector.value = _extendList(selector.value, selector.span, + newExtensions, _mediaContexts[selector]); } on SassException catch (error) { if (selector.span == null) rethrow; @@ -482,16 +481,16 @@ class ExtensionStore { } /// Extends [list] using [extensions]. - SelectorList _extendList( - SelectorList list, + SelectorList _extendList(SelectorList list, FileSpan? listSpan, Map?>? extensions, - List? mediaQueryContext) { + [List? mediaQueryContext]) { // This could be written more simply using [List.map], but we want to avoid // any allocations in the common case where no extends apply. List? extended; for (var i = 0; i < list.components.length; i++) { var complex = list.components[i]; - var result = _extendComplex(complex, extensions, mediaQueryContext); + var result = + _extendComplex(complex, listSpan, extensions, mediaQueryContext); if (result == null) { if (extended != null) extended.add(complex); } else { @@ -508,6 +507,7 @@ class ExtensionStore { /// [SelectorList]. List? _extendComplex( ComplexSelector complex, + FileSpan? complexSpan, Map?>? extensions, List? mediaQueryContext) { // The complex selectors that each compound selector in [complex.components] @@ -532,7 +532,8 @@ class ExtensionStore { for (var i = 0; i < complex.components.length; i++) { var component = complex.components[i]; if (component is CompoundSelector) { - var extended = _extendCompound(component, extensions, mediaQueryContext, + var extended = _extendCompound( + component, complexSpan, extensions, mediaQueryContext, inOriginal: isOriginal); if (extended == null) { extendedNotExpanded?.add([ @@ -583,6 +584,7 @@ class ExtensionStore { /// complex selector, meaning that [compound] should not be trimmed out. List? _extendCompound( CompoundSelector compound, + FileSpan? compoundSpan, Map?>? extensions, List? mediaQueryContext, {bool? inOriginal}) { @@ -593,18 +595,20 @@ class ExtensionStore { : {}; // The complex selectors produced from each component of [compound]. - List>? options; + List>? options; for (var i = 0; i < compound.components.length; i++) { var simple = compound.components[i]; - var extended = - _extendSimple(simple, extensions, mediaQueryContext, targetsUsed); + var extended = _extendSimple( + simple, compoundSpan, extensions, mediaQueryContext, targetsUsed); if (extended == null) { - options?.add([_extensionForSimple(simple)]); + options?.add([_extenderForSimple(simple, compoundSpan)]); } else { if (options == null) { options = []; if (i != 0) { - options.add([_extensionForCompound(compound.components.take(i))]); + options.add([ + _extenderForCompound(compound.components.take(i), compoundSpan) + ]); } } @@ -622,9 +626,9 @@ class ExtensionStore { // Optimize for the simple case of a single simple selector that doesn't // need any unification. if (options.length == 1) { - return options.first.map((state) { - state.assertCompatibleMediaContext(mediaQueryContext); - return state.extender; + return options.first.map((extender) { + extender.assertCompatibleMediaContext(mediaQueryContext); + return extender.selector; }).toList(); } @@ -662,9 +666,9 @@ class ExtensionStore { first = false; complexes = [ [ - CompoundSelector(path.expand((state) { - assert(state.extender.components.length == 1); - return (state.extender.components.last as CompoundSelector) + CompoundSelector(path.expand((extender) { + assert(extender.selector.components.length == 1); + return (extender.selector.components.last as CompoundSelector) .components; })) ] @@ -672,14 +676,14 @@ class ExtensionStore { } else { var toUnify = QueueList>(); List? originals; - for (var state in path) { - if (state.isOriginal) { + for (var extender in path) { + if (extender.isOriginal) { originals ??= []; originals.addAll( - (state.extender.components.last as CompoundSelector) + (extender.selector.components.last as CompoundSelector) .components); } else { - toUnify.add(state.extender.components); + toUnify.add(extender.selector.components); } } @@ -692,9 +696,9 @@ class ExtensionStore { } var lineBreak = false; - for (var state in path) { - state.assertCompatibleMediaContext(mediaQueryContext); - lineBreak = lineBreak || state.extender.lineBreak; + for (var extender in path) { + extender.assertCompatibleMediaContext(mediaQueryContext); + lineBreak = lineBreak || extender.selector.lineBreak; } return complexes @@ -719,57 +723,65 @@ class ExtensionStore { isOriginal); } - Iterable>? _extendSimple( + Iterable>? _extendSimple( SimpleSelector simple, + FileSpan? simpleSpan, Map?>? extensions, List? mediaQueryContext, Set? targetsUsed) { // Extends [simple] without extending the contents of any selector pseudos // it contains. - List? withoutPseudo(SimpleSelector simple) { - var extenders = extensions![simple]; - if (extenders == null) return null; + List? withoutPseudo(SimpleSelector simple) { + var extensionsForSimple = extensions![simple]; + if (extensionsForSimple == null) return null; targetsUsed?.add(simple); - if (_mode == ExtendMode.replace) return extenders.values.toList(); - return [_extensionForSimple(simple), ...extenders.values]; + return [ + if (_mode != ExtendMode.replace) _extenderForSimple(simple, simpleSpan), + for (var extension in extensionsForSimple.values) extension.extender + ]; } if (simple is PseudoSelector && simple.selector != null) { - var extended = _extendPseudo(simple, extensions, mediaQueryContext); + var extended = + _extendPseudo(simple, simpleSpan, extensions, mediaQueryContext); if (extended != null) { - return extended.map( - (pseudo) => withoutPseudo(pseudo) ?? [_extensionForSimple(pseudo)]); + return extended.map((pseudo) => + withoutPseudo(pseudo) ?? [_extenderForSimple(pseudo, simpleSpan)]); } } return withoutPseudo(simple).andThen((result) => [result]); } - /// Returns a one-off [Extension] whose extender is composed solely of a - /// compound selector containing [simples]. - Extension _extensionForCompound(Iterable simples) { + /// Returns an [Extender] composed solely of a compound selector containing + /// [simples]. + Extender _extenderForCompound( + Iterable simples, FileSpan? span) { var compound = CompoundSelector(simples); - return Extension.oneOff(ComplexSelector([compound]), - specificity: _sourceSpecificityFor(compound), isOriginal: true); + return Extender(ComplexSelector([compound]), span, + specificity: _sourceSpecificityFor(compound), original: true); } - /// Returns a one-off [Extension] whose extender is composed solely of - /// [simple]. - Extension _extensionForSimple(SimpleSelector simple) => Extension.oneOff( - ComplexSelector([ - CompoundSelector([simple]) - ]), - specificity: _sourceSpecificity[simple] ?? 0, - isOriginal: true); + /// Returns an [Extender] composed solely of [simple]. + Extender _extenderForSimple(SimpleSelector simple, FileSpan? span) => + Extender( + ComplexSelector([ + CompoundSelector([simple]) + ]), + span, + specificity: _sourceSpecificity[simple] ?? 0, + original: true); /// Extends [pseudo] using [extensions], and returns a list of resulting /// pseudo selectors. List? _extendPseudo( PseudoSelector pseudo, + FileSpan? pseudoSpan, Map?>? extensions, List? mediaQueryContext) { - var extended = _extendList(pseudo.selector!, extensions, mediaQueryContext); + var extended = _extendList( + pseudo.selector!, pseudoSpan, extensions, mediaQueryContext); if (identical(extended, pseudo.selector)) return null; // For `:not()`, we usually want to get rid of any complex selectors because diff --git a/lib/src/extend/merged_extension.dart b/lib/src/extend/merged_extension.dart index f033e71cf..3966124a0 100644 --- a/lib/src/extend/merged_extension.dart +++ b/lib/src/extend/merged_extension.dart @@ -26,7 +26,8 @@ class MergedExtension extends Extension { /// Throws an [ArgumentError] if [left] and [right] don't have the same /// extender and target. static Extension merge(Extension left, Extension right) { - if (left.extender != right.extender || left.target != right.target) { + if (left.extender.selector != right.extender.selector || + left.target != right.target) { throw ArgumentError("$left and $right aren't the same extension."); } @@ -49,20 +50,23 @@ class MergedExtension extends Extension { } MergedExtension._(this.left, this.right) - : super(left.extender, left.target, left.extenderSpan, left.span, - left.mediaContext ?? right.mediaContext, - specificity: left.specificity, optional: true); + : super( + left.extender.selector, left.extender.span, left.target, left.span, + mediaContext: left.mediaContext ?? right.mediaContext, + optional: true); - /// Returns all leaf-node [Extension]s in the tree or [MergedExtension]s. + /// Returns all leaf-node [Extension]s in the tree of [MergedExtension]s. Iterable unmerge() sync* { + var left = this.left; if (left is MergedExtension) { - yield* (left as MergedExtension).unmerge(); + yield* left.unmerge(); } else { yield left; } + var right = this.right; if (right is MergedExtension) { - yield* (right as MergedExtension).unmerge(); + yield* right.unmerge(); } else { yield right; } From de013bf8acb734b54d1cfa649e0eb37186c78042 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 18 Mar 2021 19:45:49 -0700 Subject: [PATCH 13/19] Make spans universally non-nullable --- lib/src/ast/css/declaration.dart | 2 +- lib/src/ast/css/modifiable/at_rule.dart | 2 +- lib/src/ast/css/modifiable/comment.dart | 2 +- lib/src/ast/css/modifiable/declaration.dart | 4 +- lib/src/ast/css/modifiable/import.dart | 2 +- .../ast/css/modifiable/keyframe_block.dart | 2 +- lib/src/ast/css/modifiable/media_rule.dart | 2 +- lib/src/ast/css/modifiable/style_rule.dart | 2 +- lib/src/ast/css/modifiable/stylesheet.dart | 2 +- lib/src/ast/css/modifiable/supports_rule.dart | 2 +- lib/src/ast/css/modifiable/value.dart | 2 +- lib/src/ast/css/stylesheet.dart | 2 +- lib/src/ast/css/value.dart | 2 +- lib/src/ast/node.dart | 2 +- lib/src/ast/sass/argument.dart | 11 +- lib/src/ast/sass/argument_declaration.dart | 20 +-- lib/src/ast/sass/argument_invocation.dart | 2 +- .../ast/sass/expression/binary_operation.dart | 5 +- lib/src/ast/sass/expression/boolean.dart | 2 +- lib/src/ast/sass/expression/color.dart | 2 +- lib/src/ast/sass/expression/function.dart | 2 +- lib/src/ast/sass/expression/if.dart | 2 +- lib/src/ast/sass/expression/list.dart | 12 +- lib/src/ast/sass/expression/null.dart | 2 +- lib/src/ast/sass/expression/string.dart | 94 +++++----- .../ast/sass/expression/unary_operation.dart | 2 +- lib/src/ast/sass/expression/value.dart | 4 +- lib/src/ast/sass/interpolation.dart | 2 +- lib/src/ast/sass/statement/include_rule.dart | 2 +- lib/src/ast/sass/statement/loud_comment.dart | 2 +- lib/src/ast/sass/statement/stylesheet.dart | 2 +- .../supports_condition/interpolation.dart | 2 +- lib/src/async_compile.dart | 2 +- lib/src/async_environment.dart | 4 +- lib/src/compile.dart | 4 +- lib/src/configuration.dart | 12 +- lib/src/configured_value.dart | 13 +- lib/src/environment.dart | 6 +- lib/src/exception.dart | 20 +-- lib/src/executable/repl.dart | 10 +- lib/src/extend/empty_extension_store.dart | 2 +- lib/src/extend/extender.dart | 60 +++++++ lib/src/extend/extension.dart | 6 +- lib/src/extend/extension_store.dart | 69 ++++--- lib/src/extend/merged_extension.dart | 2 +- lib/src/functions.dart | 21 +++ lib/src/functions/selector.dart | 7 +- lib/src/interpolation_buffer.dart | 2 +- lib/src/node.dart | 6 +- lib/src/parse/parser.dart | 2 +- lib/src/parse/stylesheet.dart | 28 +-- lib/src/stylesheet_graph.dart | 2 +- lib/src/util/no_source_map_buffer.dart | 2 +- lib/src/util/source_map_buffer.dart | 6 +- lib/src/utils.dart | 27 +-- lib/src/visitor/async_evaluate.dart | 165 ++++++++--------- lib/src/visitor/evaluate.dart | 169 +++++++++--------- lib/src/visitor/serialize.dart | 12 +- 58 files changed, 459 insertions(+), 400 deletions(-) create mode 100644 lib/src/extend/extender.dart diff --git a/lib/src/ast/css/declaration.dart b/lib/src/ast/css/declaration.dart index 1e03237c8..1bd7279fc 100644 --- a/lib/src/ast/css/declaration.dart +++ b/lib/src/ast/css/declaration.dart @@ -22,7 +22,7 @@ abstract class CssDeclaration extends CssNode { /// When the declaration's expression is just a variable, this is the span /// where that variable was declared whereas [value.span] is the span where /// the variable was used. Otherwise, this is identical to [value.span]. - FileSpan? get valueSpanForMap; + FileSpan get valueSpanForMap; /// Returns whether this is a CSS Custom Property declaration. bool get isCustomProperty; diff --git a/lib/src/ast/css/modifiable/at_rule.dart b/lib/src/ast/css/modifiable/at_rule.dart index ae70ff7a6..e616de5c2 100644 --- a/lib/src/ast/css/modifiable/at_rule.dart +++ b/lib/src/ast/css/modifiable/at_rule.dart @@ -14,7 +14,7 @@ class ModifiableCssAtRule extends ModifiableCssParentNode implements CssAtRule { final CssValue name; final CssValue? value; final bool isChildless; - final FileSpan? span; + final FileSpan span; ModifiableCssAtRule(this.name, this.span, {bool childless = false, this.value}) diff --git a/lib/src/ast/css/modifiable/comment.dart b/lib/src/ast/css/modifiable/comment.dart index 80aff487d..40e7838c7 100644 --- a/lib/src/ast/css/modifiable/comment.dart +++ b/lib/src/ast/css/modifiable/comment.dart @@ -12,7 +12,7 @@ import 'node.dart'; /// A modifiable version of [CssComment] for use in the evaluation step. class ModifiableCssComment extends ModifiableCssNode implements CssComment { final String text; - final FileSpan? span; + final FileSpan span; bool get isPreserved => text.codeUnitAt(2) == $exclamation; diff --git a/lib/src/ast/css/modifiable/declaration.dart b/lib/src/ast/css/modifiable/declaration.dart index 989f99ed3..c6564fb07 100644 --- a/lib/src/ast/css/modifiable/declaration.dart +++ b/lib/src/ast/css/modifiable/declaration.dart @@ -16,8 +16,8 @@ class ModifiableCssDeclaration extends ModifiableCssNode final CssValue name; final CssValue value; final bool parsedAsCustomProperty; - final FileSpan? valueSpanForMap; - final FileSpan? span; + final FileSpan valueSpanForMap; + final FileSpan span; bool get isCustomProperty => name.value.startsWith('--'); diff --git a/lib/src/ast/css/modifiable/import.dart b/lib/src/ast/css/modifiable/import.dart index c52da8d2d..9d705b72e 100644 --- a/lib/src/ast/css/modifiable/import.dart +++ b/lib/src/ast/css/modifiable/import.dart @@ -23,7 +23,7 @@ class ModifiableCssImport extends ModifiableCssNode implements CssImport { /// The media query attached to this import. final List? media; - final FileSpan? span; + final FileSpan span; ModifiableCssImport(this.url, this.span, {this.supports, Iterable? media}) diff --git a/lib/src/ast/css/modifiable/keyframe_block.dart b/lib/src/ast/css/modifiable/keyframe_block.dart index 089a25014..69233f355 100644 --- a/lib/src/ast/css/modifiable/keyframe_block.dart +++ b/lib/src/ast/css/modifiable/keyframe_block.dart @@ -13,7 +13,7 @@ import 'node.dart'; class ModifiableCssKeyframeBlock extends ModifiableCssParentNode implements CssKeyframeBlock { final CssValue> selector; - final FileSpan? span; + final FileSpan span; ModifiableCssKeyframeBlock(this.selector, this.span); diff --git a/lib/src/ast/css/modifiable/media_rule.dart b/lib/src/ast/css/modifiable/media_rule.dart index 710ee9fd2..f1dcf25e4 100644 --- a/lib/src/ast/css/modifiable/media_rule.dart +++ b/lib/src/ast/css/modifiable/media_rule.dart @@ -13,7 +13,7 @@ import 'node.dart'; class ModifiableCssMediaRule extends ModifiableCssParentNode implements CssMediaRule { final List queries; - final FileSpan? span; + final FileSpan span; ModifiableCssMediaRule(Iterable queries, this.span) : queries = List.unmodifiable(queries) { diff --git a/lib/src/ast/css/modifiable/style_rule.dart b/lib/src/ast/css/modifiable/style_rule.dart index 38c5b66e3..1e9d7e869 100644 --- a/lib/src/ast/css/modifiable/style_rule.dart +++ b/lib/src/ast/css/modifiable/style_rule.dart @@ -15,7 +15,7 @@ class ModifiableCssStyleRule extends ModifiableCssParentNode implements CssStyleRule { final ModifiableCssValue selector; final SelectorList originalSelector; - final FileSpan? span; + final FileSpan span; /// Creates a new [ModifiableCssStyleRule]. /// diff --git a/lib/src/ast/css/modifiable/stylesheet.dart b/lib/src/ast/css/modifiable/stylesheet.dart index 87a332ace..46610a948 100644 --- a/lib/src/ast/css/modifiable/stylesheet.dart +++ b/lib/src/ast/css/modifiable/stylesheet.dart @@ -11,7 +11,7 @@ import 'node.dart'; /// A modifiable version of [CssStylesheet] for use in the evaluation step. class ModifiableCssStylesheet extends ModifiableCssParentNode implements CssStylesheet { - final FileSpan? span; + final FileSpan span; ModifiableCssStylesheet(this.span); diff --git a/lib/src/ast/css/modifiable/supports_rule.dart b/lib/src/ast/css/modifiable/supports_rule.dart index 51611679c..ef921b511 100644 --- a/lib/src/ast/css/modifiable/supports_rule.dart +++ b/lib/src/ast/css/modifiable/supports_rule.dart @@ -13,7 +13,7 @@ import 'node.dart'; class ModifiableCssSupportsRule extends ModifiableCssParentNode implements CssSupportsRule { final CssValue condition; - final FileSpan? span; + final FileSpan span; ModifiableCssSupportsRule(this.condition, this.span); diff --git a/lib/src/ast/css/modifiable/value.dart b/lib/src/ast/css/modifiable/value.dart index f345feac6..2f29676be 100644 --- a/lib/src/ast/css/modifiable/value.dart +++ b/lib/src/ast/css/modifiable/value.dart @@ -9,7 +9,7 @@ import '../value.dart'; /// A modifiable version of [CssValue] for use in the evaluation step. class ModifiableCssValue implements CssValue { T value; - final FileSpan? span; + final FileSpan span; ModifiableCssValue(this.value, this.span); diff --git a/lib/src/ast/css/stylesheet.dart b/lib/src/ast/css/stylesheet.dart index 8c43bc6d7..08a671cde 100644 --- a/lib/src/ast/css/stylesheet.dart +++ b/lib/src/ast/css/stylesheet.dart @@ -14,7 +14,7 @@ import 'node.dart'; /// This is the root plain CSS node. It contains top-level statements. class CssStylesheet extends CssParentNode { final List children; - final FileSpan? span; + final FileSpan span; bool get isGroupEnd => false; bool get isChildless => false; diff --git a/lib/src/ast/css/value.dart b/lib/src/ast/css/value.dart index 2898db9b2..ce8ee2689 100644 --- a/lib/src/ast/css/value.dart +++ b/lib/src/ast/css/value.dart @@ -15,7 +15,7 @@ class CssValue implements AstNode { final T value; /// The span associated with the value. - final FileSpan? span; + final FileSpan span; CssValue(this.value, this.span); diff --git a/lib/src/ast/node.dart b/lib/src/ast/node.dart index 1ebab4db4..1d9a9fd3d 100644 --- a/lib/src/ast/node.dart +++ b/lib/src/ast/node.dart @@ -10,7 +10,7 @@ abstract class AstNode { /// /// This indicates where in the source Sass or SCSS stylesheet the node was /// defined. - FileSpan? get span; + FileSpan get span; /// Returns an [AstNode] that doesn't have any data and whose span is /// generated by [callback]. diff --git a/lib/src/ast/sass/argument.dart b/lib/src/ast/sass/argument.dart index e8391e3e4..6e725496a 100644 --- a/lib/src/ast/sass/argument.dart +++ b/lib/src/ast/sass/argument.dart @@ -16,20 +16,17 @@ class Argument implements SassNode { /// The default value of this argument, or `null` if none was declared. final Expression? defaultValue; - final FileSpan? span; + final FileSpan span; /// The variable name as written in the document, without underscores /// converted to hyphens and including the leading `$`. /// /// This isn't particularly efficient, and should only be used for error /// messages. - String get originalName { - var span = this.span; - if (span == null) return '\$$name'; - return defaultValue == null ? span.text : declarationName(span); - } + String get originalName => + defaultValue == null ? span.text : declarationName(span); - Argument(this.name, {this.defaultValue, this.span}); + Argument(this.name, {this.defaultValue, required this.span}); String toString() => defaultValue == null ? name : "$name: $defaultValue"; } diff --git a/lib/src/ast/sass/argument_declaration.dart b/lib/src/ast/sass/argument_declaration.dart index 1af14a069..a3cf142eb 100644 --- a/lib/src/ast/sass/argument_declaration.dart +++ b/lib/src/ast/sass/argument_declaration.dart @@ -21,14 +21,11 @@ class ArgumentDeclaration implements SassNode { /// declared. final String? restArgument; - final FileSpan? span; + final FileSpan span; /// Returns [span] expanded to include an identifier immediately before the /// declaration, if possible. - FileSpan? get spanWithName { - var span = this.span; - if (span == null) return null; - + FileSpan get spanWithName { var text = span.file.getText(0); // Move backwards through and whitspace between the name and the arguments. @@ -60,9 +57,6 @@ class ArgumentDeclaration implements SassNode { String? get originalRestArgument { if (restArgument == null) return null; - var span = this.span; - if (span == null) return '\$$restArgument'; - var text = span.text; var fromDollar = text.substring(text.lastIndexOf("\$")); return fromDollar.substring(0, text.indexOf(".")); @@ -72,11 +66,11 @@ class ArgumentDeclaration implements SassNode { bool get isEmpty => arguments.isEmpty && restArgument == null; ArgumentDeclaration(Iterable arguments, - {this.restArgument, this.span}) + {this.restArgument, required this.span}) : arguments = List.unmodifiable(arguments); /// Creates a declaration that declares no arguments. - ArgumentDeclaration.empty({this.span}) + ArgumentDeclaration.empty({required this.span}) : arguments = const [], restArgument = null; @@ -108,7 +102,7 @@ class ArgumentDeclaration implements SassNode { throw MultiSpanSassScriptException( "Missing argument ${_originalArgumentName(argument.name)}.", "invocation", - {spanWithName!: "declaration"}); + {spanWithName: "declaration"}); } } @@ -122,7 +116,7 @@ class ArgumentDeclaration implements SassNode { "$positional ${pluralize('was', positional, plural: 'were')} " "passed.", "invocation", - {spanWithName!: "declaration"}); + {spanWithName: "declaration"}); } if (namedUsed < names.length) { @@ -132,7 +126,7 @@ class ArgumentDeclaration implements SassNode { "No ${pluralize('argument', unknownNames.length)} named " "${toSentence(unknownNames.map((name) => "\$$name"), 'or')}.", "invocation", - {spanWithName!: "declaration"}); + {spanWithName: "declaration"}); } } diff --git a/lib/src/ast/sass/argument_invocation.dart b/lib/src/ast/sass/argument_invocation.dart index 144547c7d..3fb5d7fb9 100644 --- a/lib/src/ast/sass/argument_invocation.dart +++ b/lib/src/ast/sass/argument_invocation.dart @@ -21,7 +21,7 @@ class ArgumentInvocation implements SassNode { /// The second rest argument, which is expected to only contain a keyword map. final Expression? keywordRest; - final FileSpan? span; + final FileSpan span; /// Returns whether this invocation passes no arguments. bool get isEmpty => positional.isEmpty && named.isEmpty && rest == null; diff --git a/lib/src/ast/sass/expression/binary_operation.dart b/lib/src/ast/sass/expression/binary_operation.dart index 282d6007e..864f30d70 100644 --- a/lib/src/ast/sass/expression/binary_operation.dart +++ b/lib/src/ast/sass/expression/binary_operation.dart @@ -5,7 +5,6 @@ import 'package:source_span/source_span.dart'; import 'package:charcode/charcode.dart'; -import '../../../utils.dart'; import '../../../visitor/interface/expression.dart'; import '../expression.dart'; @@ -24,7 +23,7 @@ class BinaryOperationExpression implements Expression { /// interpreted as slash-separated numbers. final bool allowsSlash; - FileSpan? get span { + FileSpan get span { // Avoid creating a bunch of intermediate spans for multiple binary // expressions in a row by moving to the left- and right-most expressions. var left = this.left; @@ -36,7 +35,7 @@ class BinaryOperationExpression implements Expression { while (right is BinaryOperationExpression) { right = right.right; } - return spanForList([left, right]); + return left.span.expand(right.span); } BinaryOperationExpression(this.operator, this.left, this.right) diff --git a/lib/src/ast/sass/expression/boolean.dart b/lib/src/ast/sass/expression/boolean.dart index f822eea40..7a21c45ed 100644 --- a/lib/src/ast/sass/expression/boolean.dart +++ b/lib/src/ast/sass/expression/boolean.dart @@ -12,7 +12,7 @@ class BooleanExpression implements Expression { /// The value of this expression. final bool value; - final FileSpan? span; + final FileSpan span; BooleanExpression(this.value, this.span); diff --git a/lib/src/ast/sass/expression/color.dart b/lib/src/ast/sass/expression/color.dart index ee46fceda..f0299a398 100644 --- a/lib/src/ast/sass/expression/color.dart +++ b/lib/src/ast/sass/expression/color.dart @@ -13,7 +13,7 @@ class ColorExpression implements Expression { /// The value of this color. final SassColor value; - FileSpan? get span => value.originalSpan; + FileSpan get span => value.originalSpan!; ColorExpression(this.value); diff --git a/lib/src/ast/sass/expression/function.dart b/lib/src/ast/sass/expression/function.dart index 8afcc48dc..ca9d3c5a7 100644 --- a/lib/src/ast/sass/expression/function.dart +++ b/lib/src/ast/sass/expression/function.dart @@ -30,7 +30,7 @@ class FunctionExpression implements Expression, CallableInvocation { /// The arguments to pass to the function. final ArgumentInvocation arguments; - final FileSpan? span; + final FileSpan span; FunctionExpression(this.name, this.arguments, this.span, {this.namespace}); diff --git a/lib/src/ast/sass/expression/if.dart b/lib/src/ast/sass/expression/if.dart index 74ed82333..99f1898c1 100644 --- a/lib/src/ast/sass/expression/if.dart +++ b/lib/src/ast/sass/expression/if.dart @@ -20,7 +20,7 @@ class IfExpression implements Expression, CallableInvocation { /// The arguments passed to `if()`. final ArgumentInvocation arguments; - final FileSpan? span; + final FileSpan span; IfExpression(this.arguments, this.span); diff --git a/lib/src/ast/sass/expression/list.dart b/lib/src/ast/sass/expression/list.dart index 26fa5a544..661e1bbf5 100644 --- a/lib/src/ast/sass/expression/list.dart +++ b/lib/src/ast/sass/expression/list.dart @@ -5,7 +5,6 @@ import 'package:charcode/charcode.dart'; import 'package:source_span/source_span.dart'; -import '../../../utils.dart'; import '../../../value.dart'; import '../../../visitor/interface/expression.dart'; import '../expression.dart'; @@ -22,16 +21,15 @@ class ListExpression implements Expression { /// Whether the list has square brackets or not. final bool hasBrackets; - final FileSpan? span; + final FileSpan span; ListExpression(Iterable contents, ListSeparator separator, - {bool brackets = false, FileSpan? span}) + {bool brackets = false, required FileSpan span}) : this._(List.unmodifiable(contents), separator, brackets, span); - ListExpression._(List contents, this.separator, this.hasBrackets, - FileSpan? span) - : contents = contents, - span = span ?? spanForList(contents); + ListExpression._( + List contents, this.separator, this.hasBrackets, this.span) + : contents = contents; T accept(ExpressionVisitor visitor) => visitor.visitListExpression(this); diff --git a/lib/src/ast/sass/expression/null.dart b/lib/src/ast/sass/expression/null.dart index d12d96264..db2140be9 100644 --- a/lib/src/ast/sass/expression/null.dart +++ b/lib/src/ast/sass/expression/null.dart @@ -9,7 +9,7 @@ import '../expression.dart'; /// A null literal. class NullExpression implements Expression { - final FileSpan? span; + final FileSpan span; NullExpression(this.span); diff --git a/lib/src/ast/sass/expression/string.dart b/lib/src/ast/sass/expression/string.dart index 6f38d198f..b2eb7a542 100644 --- a/lib/src/ast/sass/expression/string.dart +++ b/lib/src/ast/sass/expression/string.dart @@ -22,18 +22,22 @@ class StringExpression implements Expression { /// Whether [this] has quotes. final bool hasQuotes; - FileSpan? get span => text.span; + FileSpan get span => text.span; /// Returns Sass source for a quoted string that, when evaluated, will have /// [text] as its contents. - static String quoteText(String text) => - StringExpression.plain(text, null, quotes: true) - .asInterpolation(static: true) - .asPlain!; + static String quoteText(String text) { + var quote = _bestQuote([text]); + var buffer = StringBuffer(); + buffer.writeCharCode(quote); + _quoteInnerText(text, quote, buffer, static: true); + buffer.writeCharCode(quote); + return buffer.toString(); + } StringExpression(this.text, {bool quotes = false}) : hasQuotes = quotes; - StringExpression.plain(String text, FileSpan? span, {bool quotes = false}) + StringExpression.plain(String text, FileSpan span, {bool quotes = false}) : text = Interpolation([text], span), hasQuotes = quotes; @@ -51,7 +55,7 @@ class StringExpression implements Expression { Interpolation asInterpolation({bool static = false, int? quote}) { if (!hasQuotes) return text; - quote ??= _bestQuote(); + quote ??= _bestQuote(text.contents.whereType()); var buffer = InterpolationBuffer(); buffer.writeCharCode(quote); for (var value in text.contents) { @@ -59,30 +63,7 @@ class StringExpression implements Expression { if (value is Expression) { buffer.add(value); } else if (value is String) { - for (var i = 0; i < value.length; i++) { - var codeUnit = value.codeUnitAt(i); - - if (isNewline(codeUnit)) { - buffer.writeCharCode($backslash); - buffer.writeCharCode($a); - if (i != value.length - 1) { - var next = value.codeUnitAt(i + 1); - if (isWhitespace(next) || isHex(next)) { - buffer.writeCharCode($space); - } - } - } else { - if (codeUnit == quote || - codeUnit == $backslash || - (static && - codeUnit == $hash && - i < value.length - 1 && - value.codeUnitAt(i + 1) == $lbrace)) { - buffer.writeCharCode($backslash); - } - buffer.writeCharCode(codeUnit); - } - } + _quoteInnerText(value, quote, buffer, static: static); } } buffer.writeCharCode(quote); @@ -90,17 +71,48 @@ class StringExpression implements Expression { return buffer.interpolation(text.span); } - /// Returns the code unit for the best quote to use when converting this - /// string to Sass source. - int _bestQuote() { - var containsDoubleQuote = false; - for (var value in text.contents) { - if (value is String) { - for (var i = 0; i < value.length; i++) { - var codeUnit = value.codeUnitAt(i); - if (codeUnit == $single_quote) return $double_quote; - if (codeUnit == $double_quote) containsDoubleQuote = true; + /// Writes to [buffer] the contents of a string (without quotes) that evalutes + /// to [text] according to Sass's parsing logic. + /// + /// This always adds an escape sequence before [quote]. If [static] is true, + /// it also escapes any `#{` sequences in the string. + static void _quoteInnerText(String text, int quote, StringSink buffer, + {bool static = false}) { + for (var i = 0; i < text.length; i++) { + var codeUnit = text.codeUnitAt(i); + + if (isNewline(codeUnit)) { + buffer.writeCharCode($backslash); + buffer.writeCharCode($a); + if (i != text.length - 1) { + var next = text.codeUnitAt(i + 1); + if (isWhitespace(next) || isHex(next)) { + buffer.writeCharCode($space); + } } + } else { + if (codeUnit == quote || + codeUnit == $backslash || + (static && + codeUnit == $hash && + i < text.length - 1 && + text.codeUnitAt(i + 1) == $lbrace)) { + buffer.writeCharCode($backslash); + } + buffer.writeCharCode(codeUnit); + } + } + } + + /// Returns the code unit for the best quote to use when converting [strings] + /// to Sass source. + static int _bestQuote(Iterable strings) { + var containsDoubleQuote = false; + for (var value in strings) { + for (var i = 0; i < value.length; i++) { + var codeUnit = value.codeUnitAt(i); + if (codeUnit == $single_quote) return $double_quote; + if (codeUnit == $double_quote) containsDoubleQuote = true; } } return containsDoubleQuote ? $single_quote : $double_quote; diff --git a/lib/src/ast/sass/expression/unary_operation.dart b/lib/src/ast/sass/expression/unary_operation.dart index f9070e54c..c3ac12642 100644 --- a/lib/src/ast/sass/expression/unary_operation.dart +++ b/lib/src/ast/sass/expression/unary_operation.dart @@ -16,7 +16,7 @@ class UnaryOperationExpression implements Expression { /// The operand. final Expression operand; - final FileSpan? span; + final FileSpan span; UnaryOperationExpression(this.operator, this.operand, this.span); diff --git a/lib/src/ast/sass/expression/value.dart b/lib/src/ast/sass/expression/value.dart index 8aac8454b..e188d0d98 100644 --- a/lib/src/ast/sass/expression/value.dart +++ b/lib/src/ast/sass/expression/value.dart @@ -16,9 +16,9 @@ class ValueExpression implements Expression { /// The embedded value. final Value value; - final FileSpan? span; + final FileSpan span; - ValueExpression(this.value, [this.span]); + ValueExpression(this.value, this.span); T accept(ExpressionVisitor visitor) => visitor.visitValueExpression(this); diff --git a/lib/src/ast/sass/interpolation.dart b/lib/src/ast/sass/interpolation.dart index 83f568886..5df0b7c14 100644 --- a/lib/src/ast/sass/interpolation.dart +++ b/lib/src/ast/sass/interpolation.dart @@ -15,7 +15,7 @@ class Interpolation implements SassNode { /// [String]s. final List contents; - final FileSpan? span; + final FileSpan span; /// If this contains no interpolated expressions, returns its text contents. /// diff --git a/lib/src/ast/sass/statement/include_rule.dart b/lib/src/ast/sass/statement/include_rule.dart index a0b8fde7b..ad8a25ee2 100644 --- a/lib/src/ast/sass/statement/include_rule.dart +++ b/lib/src/ast/sass/statement/include_rule.dart @@ -33,7 +33,7 @@ class IncludeRule implements Statement, CallableInvocation { /// Returns this include's span, without its content block (if it has one). FileSpan get spanWithoutContent => content == null ? span - : span.file.span(span.start.offset, arguments.span!.end.offset).trim(); + : span.file.span(span.start.offset, arguments.span.end.offset).trim(); IncludeRule(this.name, this.arguments, this.span, {this.namespace, this.content}); diff --git a/lib/src/ast/sass/statement/loud_comment.dart b/lib/src/ast/sass/statement/loud_comment.dart index 4ee009857..63440895b 100644 --- a/lib/src/ast/sass/statement/loud_comment.dart +++ b/lib/src/ast/sass/statement/loud_comment.dart @@ -13,7 +13,7 @@ class LoudComment implements Statement { /// The interpolated text of this comment, including comment characters. final Interpolation text; - FileSpan? get span => text.span; + FileSpan get span => text.span; LoudComment(this.text); diff --git a/lib/src/ast/sass/statement/stylesheet.dart b/lib/src/ast/sass/statement/stylesheet.dart index 22a8c7c80..b65214411 100644 --- a/lib/src/ast/sass/statement/stylesheet.dart +++ b/lib/src/ast/sass/statement/stylesheet.dart @@ -24,7 +24,7 @@ import 'variable_declaration.dart'; /// /// This is the root Sass node. It contains top-level statements. class Stylesheet extends ParentStatement> { - final FileSpan? span; + final FileSpan span; /// Whether this was parsed from a plain CSS stylesheet. final bool plainCss; diff --git a/lib/src/ast/sass/supports_condition/interpolation.dart b/lib/src/ast/sass/supports_condition/interpolation.dart index 4abf86e9e..3deccfa6a 100644 --- a/lib/src/ast/sass/supports_condition/interpolation.dart +++ b/lib/src/ast/sass/supports_condition/interpolation.dart @@ -12,7 +12,7 @@ class SupportsInterpolation implements SupportsCondition { /// The expression in the interpolation. final Expression expression; - final FileSpan? span; + final FileSpan span; SupportsInterpolation(this.expression, this.span); diff --git a/lib/src/async_compile.dart b/lib/src/async_compile.dart index 812f0aa6d..2e90fa50b 100644 --- a/lib/src/async_compile.dart +++ b/lib/src/async_compile.dart @@ -142,7 +142,7 @@ Future _compileStylesheet( mapInPlace( resultSourceMap.urls, (url) => url == '' - ? Uri.dataFromString(stylesheet.span!.file.getText(0), + ? Uri.dataFromString(stylesheet.span.file.getText(0), encoding: utf8) .toString() : importCache.sourceMapUrl(Uri.parse(url)).toString()); diff --git a/lib/src/async_environment.dart b/lib/src/async_environment.dart index 24c5a5304..6bc5c2968 100644 --- a/lib/src/async_environment.dart +++ b/lib/src/async_environment.dart @@ -823,7 +823,7 @@ class AsyncEnvironment { // Implicit configurations are never invalid, making [configurationSpan] // unnecessary, so we pass null here to avoid having to compute it. configuration[entry.key] = - ConfiguredValue(entry.value, null, nodes[entry.key]); + ConfiguredValue.implicit(entry.value, nodes[entry.key]); } } return Configuration.implicit(configuration); @@ -917,7 +917,7 @@ class AsyncEnvironment { /// A module that represents the top-level members defined in an [Environment]. class _EnvironmentModule implements Module { - Uri? get url => css.span?.sourceUrl; + Uri? get url => css.span.sourceUrl; final List upstream; final Map variables; diff --git a/lib/src/compile.dart b/lib/src/compile.dart index 7888370b9..ed47a446c 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: 399932f6eea5cbf7a1c7851ca9e1b5455e14564c +// Checksum: dcb7cfbedf1e1189808c0056debf6a68bd387dab // // ignore_for_file: unused_import @@ -152,7 +152,7 @@ CompileResult _compileStylesheet( mapInPlace( resultSourceMap.urls, (url) => url == '' - ? Uri.dataFromString(stylesheet.span!.file.getText(0), + ? Uri.dataFromString(stylesheet.span.file.getText(0), encoding: utf8) .toString() : importCache.sourceMapUrl(Uri.parse(url)).toString()); diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index 3b8e96700..9081ef802 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -26,11 +26,6 @@ class Configuration { Map get values => UnmodifiableMapView(_values); final Map _values; - /// Creates an explicit configuration with the given [values]. - factory Configuration( - Map values, AstNode nodeWithSpan) = - ExplicitConfiguration._; - /// Creates an implicit configuration with the given [values]. Configuration.implicit(this._values); @@ -78,8 +73,6 @@ class Configuration { /// A [Configuratoin] that was created with an explicit `with` clause of a /// `@use` rule. /// -/// This is as opposed to *implicit* configurations, which are -/// /// Both types of configuration pass through `@forward` rules, but explicit /// configurations will cause an error if attempting to use them on a module /// that has already been loaded, while implicit configurations will be @@ -88,11 +81,10 @@ class ExplicitConfiguration extends Configuration { /// The node whose span indicates where the configuration was declared. final AstNode nodeWithSpan; - ExplicitConfiguration._( - Map values, this.nodeWithSpan) + ExplicitConfiguration(Map values, this.nodeWithSpan) : super.implicit(values); /// Returns a copy of [this] with the given [values] map. Configuration _withValues(Map values) => - Configuration(values, nodeWithSpan); + ExplicitConfiguration(values, nodeWithSpan); } diff --git a/lib/src/configured_value.dart b/lib/src/configured_value.dart index a1e35f5bc..01d46b3c4 100644 --- a/lib/src/configured_value.dart +++ b/lib/src/configured_value.dart @@ -12,7 +12,8 @@ class ConfiguredValue { /// The value of the variable. final Value value; - /// The span where the variable's configuration was written. + /// The span where the variable's configuration was written, or `null` if this + /// value was configured implicitly. final FileSpan? configurationSpan; /// The [AstNode] where the variable's value originated. @@ -20,5 +21,13 @@ class ConfiguredValue { /// This is used to generate source maps. final AstNode? assignmentNode; - ConfiguredValue(this.value, this.configurationSpan, this.assignmentNode); + /// Creates a variable value that's been configured explicitly with a `with` + /// clause. + ConfiguredValue.explicit( + this.value, this.configurationSpan, this.assignmentNode); + + /// Creates a variable value that's implicitly configured by setting a + /// variable prior to an `@import` of a file that contains a `@forward`. + ConfiguredValue.implicit(this.value, this.assignmentNode) + : configurationSpan = null; } diff --git a/lib/src/environment.dart b/lib/src/environment.dart index 6b8c3a079..5f3cf99a3 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: f76eacd7d649c2bc26f0c2745a9e892c79dfcde3 +// Checksum: 321e236c574d6623660d9a1ce6d9b7507442a318 // // ignore_for_file: unused_import @@ -829,7 +829,7 @@ class Environment { // Implicit configurations are never invalid, making [configurationSpan] // unnecessary, so we pass null here to avoid having to compute it. configuration[entry.key] = - ConfiguredValue(entry.value, null, nodes[entry.key]); + ConfiguredValue.implicit(entry.value, nodes[entry.key]); } } return Configuration.implicit(configuration); @@ -924,7 +924,7 @@ class Environment { /// A module that represents the top-level members defined in an [Environment]. class _EnvironmentModule implements Module { - Uri? get url => css.span?.sourceUrl; + Uri? get url => css.span.sourceUrl; final List> upstream; final Map variables; diff --git a/lib/src/exception.dart b/lib/src/exception.dart index 2c4a6830b..d034f4d35 100644 --- a/lib/src/exception.dart +++ b/lib/src/exception.dart @@ -18,13 +18,13 @@ class SassException extends SourceSpanException { /// This includes [span]. Trace get trace => Trace([frameForSpan(span, "root stylesheet")]); - FileSpan? get span => super.span as FileSpan?; + FileSpan get span => super.span as FileSpan; - SassException(String message, FileSpan? span) : super(message, span); + SassException(String message, FileSpan span) : super(message, span); String toString({Object? color}) { var buffer = StringBuffer("Error: $message"); - span?.highlight(color: color).andThen(buffer.write); + span.highlight(color: color).andThen(buffer.write); for (var frame in trace.toString().split("\n")) { if (frame.isEmpty) continue; @@ -83,7 +83,7 @@ class MultiSpanSassException extends SassException final String primaryLabel; final Map secondarySpans; - MultiSpanSassException(String message, FileSpan? span, this.primaryLabel, + MultiSpanSassException(String message, FileSpan span, this.primaryLabel, Map secondarySpans) : secondarySpans = Map.unmodifiable(secondarySpans), super(message, span); @@ -101,7 +101,7 @@ class MultiSpanSassException extends SassException var buffer = StringBuffer("Error: $message"); span - ?.highlightMultiple(primaryLabel, secondarySpans, + .highlightMultiple(primaryLabel, secondarySpans, color: useColor, primaryColor: primaryColor, secondaryColor: secondaryColor) @@ -120,7 +120,7 @@ class MultiSpanSassException extends SassException class SassRuntimeException extends SassException { final Trace trace; - SassRuntimeException(String message, FileSpan? span, this.trace) + SassRuntimeException(String message, FileSpan span, this.trace) : super(message, span); } @@ -129,7 +129,7 @@ class MultiSpanSassRuntimeException extends MultiSpanSassException implements SassRuntimeException { final Trace trace; - MultiSpanSassRuntimeException(String message, FileSpan? span, + MultiSpanSassRuntimeException(String message, FileSpan span, String primaryLabel, Map secondarySpans, this.trace) : super(message, span, primaryLabel, secondarySpans); } @@ -137,11 +137,11 @@ class MultiSpanSassRuntimeException extends MultiSpanSassException /// An exception thrown when Sass parsing has failed. class SassFormatException extends SassException implements SourceSpanFormatException { - String? get source => span?.file.getText(0); + String get source => span.file.getText(0); - int? get offset => span?.start.offset; + int get offset => span.start.offset; - SassFormatException(String message, FileSpan? span) : super(message, span); + SassFormatException(String message, FileSpan span) : super(message, span); } /// An exception thrown by SassScript. diff --git a/lib/src/executable/repl.dart b/lib/src/executable/repl.dart index 7f0a732bd..eb3e4fca4 100644 --- a/lib/src/executable/repl.dart +++ b/lib/src/executable/repl.dart @@ -52,7 +52,7 @@ void _logError(SassException error, StackTrace stackTrace, String line, Repl repl, ExecutableOptions options, TrackingLogger logger) { // If the error doesn't come from the repl line, or if something was logged // after the user's input, just print the error normally. - if (error.span!.sourceUrl != null || + if (error.span.sourceUrl != null || (!options.quiet && (logger.emittedDebug || logger.emittedWarning))) { print(error.toString(color: options.color)); return; @@ -62,17 +62,17 @@ void _logError(SassException error, StackTrace stackTrace, String line, var buffer = StringBuffer(); if (options.color) buffer.write("\u001b[31m"); - var spacesBeforeError = repl.prompt.length + error.span!.start.column; - if (options.color && error.span!.start.column < line.length) { + var spacesBeforeError = repl.prompt.length + error.span.start.column; + if (options.color && error.span.start.column < line.length) { // Position the cursor at the beginning of the error text. buffer.write("\u001b[1F\u001b[${spacesBeforeError}C"); // Rewrite the bad input, this time in red text. - buffer.writeln(error.span!.text); + buffer.writeln(error.span.text); } // Write arrows underneath the error text. buffer.write(" " * spacesBeforeError); - buffer.writeln("^" * math.max(1, error.span!.length)); + buffer.writeln("^" * math.max(1, error.span.length)); if (options.color) buffer.write("\u001b[0m"); buffer.writeln("Error: ${error.message}"); diff --git a/lib/src/extend/empty_extension_store.dart b/lib/src/extend/empty_extension_store.dart index 8b4aec345..4bb9e7a29 100644 --- a/lib/src/extend/empty_extension_store.dart +++ b/lib/src/extend/empty_extension_store.dart @@ -25,7 +25,7 @@ class EmptyExtensionStore implements ExtensionStore { const []; ModifiableCssValue addSelector( - SelectorList selector, FileSpan? span, + SelectorList selector, FileSpan span, [List? mediaContext]) { throw UnsupportedError( "addSelector() can't be called for a const ExtensionStore."); diff --git a/lib/src/extend/extender.dart b/lib/src/extend/extender.dart new file mode 100644 index 000000000..5f2b48ae7 --- /dev/null +++ b/lib/src/extend/extender.dart @@ -0,0 +1,60 @@ +// Copyright 2021 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:source_span/source_span.dart'; + +import '../ast/css.dart'; +import '../ast/selector.dart'; +import '../exception.dart'; +import '../utils.dart'; + +/// A selector that's extending another selector, such as `A` in `A {@extend +/// B}`. +class Extender { + /// The selector in which the `@extend` appeared. + final ComplexSelector selector; + + /// The minimum specificity required for any selector generated from this + /// extender. + final int specificity; + + /// Whether this extender represents a selector that was originally in the + /// document, rather than one defined with `@extend`. + final bool isOriginal; + + /// The media query context to which this extension is restricted, or `null` + /// if it can apply within any context. + final List? mediaContext; + + /// The span in which this selector was defined. + final FileSpan span; + + /// Creates a new extender. + /// + /// If [specificity] isn't passed, it defaults to `extender.maxSpecificity`. + Extender(this.selector, this.span, + {this.mediaContext, int? specificity, bool original = false}) + : specificity = specificity ?? selector.maxSpecificity, + isOriginal = original; + + /// Asserts that the [mediaContext] for a selector is compatible with the + /// query context for this extender. + void assertCompatibleMediaContext(List? mediaContext) { + if (this.mediaContext == null) return; + if (mediaContext != null && listEquals(this.mediaContext, mediaContext)) { + return; + } + + throw SassException( + "You may not @extend selectors across media queries.", span); + } + + Extender withSelector(ComplexSelector newSelector) => + Extender(newSelector, span, + mediaContext: mediaContext, + specificity: specificity, + original: isOriginal); + + String toString() => selector.toString(); +} diff --git a/lib/src/extend/extension.dart b/lib/src/extend/extension.dart index 7e79afeac..0c3afe61a 100644 --- a/lib/src/extend/extension.dart +++ b/lib/src/extend/extension.dart @@ -31,13 +31,13 @@ class Extension { /// /// If any extend rule for this is extension is mandatory, this is guaranteed /// to be a span for a mandatory rule. - final FileSpan? span; + final FileSpan span; /// Creates a new extension. /// /// If [specificity] isn't passed, it defaults to `extender.maxSpecificity`. Extension( - ComplexSelector extender, FileSpan? extenderSpan, this.target, this.span, + ComplexSelector extender, FileSpan extenderSpan, this.target, this.span, {this.mediaContext, bool optional = false}) : extender = Extender(extender, extenderSpan), isOptional = optional { @@ -73,7 +73,7 @@ class Extender { Extension? _extension; /// The span in which this selector was defined. - final FileSpan? span; + final FileSpan span; /// Creates a new extender. /// diff --git a/lib/src/extend/extension_store.dart b/lib/src/extend/extension_store.dart index 2d08f3a1a..5f283d3fd 100644 --- a/lib/src/extend/extension_store.dart +++ b/lib/src/extend/extension_store.dart @@ -79,18 +79,22 @@ class ExtensionStore { /// This works as though `source {@extend target}` were written in the /// stylesheet, with the exception that [target] can contain compound /// selectors which must be extended as a unit. - static SelectorList extend( - SelectorList selector, SelectorList source, SelectorList targets) => - _extendOrReplace(selector, source, targets, ExtendMode.allTargets); + static SelectorList extend(SelectorList selector, SelectorList source, + SelectorList targets, FileSpan span) => + _extendOrReplace(selector, source, targets, ExtendMode.allTargets, span); /// Returns a copy of [selector] with [targets] replaced by [source]. - static SelectorList replace( - SelectorList selector, SelectorList source, SelectorList targets) => - _extendOrReplace(selector, source, targets, ExtendMode.replace); + static SelectorList replace(SelectorList selector, SelectorList source, + SelectorList targets, FileSpan span) => + _extendOrReplace(selector, source, targets, ExtendMode.replace, span); /// A helper function for [extend] and [replace]. - static SelectorList _extendOrReplace(SelectorList selector, - SelectorList source, SelectorList targets, ExtendMode mode) { + static SelectorList _extendOrReplace( + SelectorList selector, + SelectorList source, + SelectorList targets, + ExtendMode mode, + FileSpan span) { var compoundTargets = [ for (var complex in targets.components) if (complex.components.length != 1) @@ -104,7 +108,7 @@ class ExtensionStore { for (var simple in compound.components) simple: { for (var complex in source.components) - complex: Extension(complex, null, simple, null, optional: true) + complex: Extension(complex, span, simple, span, optional: true) } }; @@ -112,7 +116,7 @@ class ExtensionStore { if (!selector.isInvisible) { extender._originals.addAll(selector.components); } - selector = extender._extendList(selector, null /* listSpan */, extensions); + selector = extender._extendList(selector, span, extensions); return selector; } @@ -173,7 +177,7 @@ class ExtensionStore { /// The [mediaContext] is the media query context in which the selector was /// defined, or `null` if it was defined at the top level of the document. ModifiableCssValue addSelector( - SelectorList selector, FileSpan? selectorSpan, + SelectorList selector, FileSpan selectorSpan, [List? mediaContext]) { var originalSelector = selector; if (!originalSelector.isInvisible) { @@ -187,13 +191,10 @@ class ExtensionStore { selector = _extendList( originalSelector, selectorSpan, _extensions, mediaContext); } on SassException catch (error) { - var span = error.span; - if (span == null) rethrow; - throw SassException( - "From ${span.message('')}\n" + "From ${error.span.message('')}\n" "${error.message}", - span); + error.span); } } @@ -320,11 +321,8 @@ class ExtensionStore { extension.extender.span, newExtensions, extension.mediaContext); if (selectors == null) continue; } on SassException catch (error) { - var extenderSpan = extension.extender.span; - if (extenderSpan == null) rethrow; - throw SassException( - "From ${extenderSpan.message('')}\n" + "From ${extension.extender.span.message('')}\n" "${error.message}", error.span); } @@ -384,11 +382,9 @@ class ExtensionStore { selector.value = _extendList(selector.value, selector.span, newExtensions, _mediaContexts[selector]); } on SassException catch (error) { - if (selector.span == null) rethrow; - // TODO(nweiz): Make this a MultiSpanSassException. throw SassException( - "From ${selector.span!.message('')}\n" + "From ${selector.span.message('')}\n" "${error.message}", error.span); } @@ -481,7 +477,7 @@ class ExtensionStore { } /// Extends [list] using [extensions]. - SelectorList _extendList(SelectorList list, FileSpan? listSpan, + SelectorList _extendList(SelectorList list, FileSpan listSpan, Map?>? extensions, [List? mediaQueryContext]) { // This could be written more simply using [List.map], but we want to avoid @@ -507,7 +503,7 @@ class ExtensionStore { /// [SelectorList]. List? _extendComplex( ComplexSelector complex, - FileSpan? complexSpan, + FileSpan complexSpan, Map?>? extensions, List? mediaQueryContext) { // The complex selectors that each compound selector in [complex.components] @@ -584,7 +580,7 @@ class ExtensionStore { /// complex selector, meaning that [compound] should not be trimmed out. List? _extendCompound( CompoundSelector compound, - FileSpan? compoundSpan, + FileSpan compoundSpan, Map?>? extensions, List? mediaQueryContext, {bool? inOriginal}) { @@ -725,7 +721,7 @@ class ExtensionStore { Iterable>? _extendSimple( SimpleSelector simple, - FileSpan? simpleSpan, + FileSpan simpleSpan, Map?>? extensions, List? mediaQueryContext, Set? targetsUsed) { @@ -757,27 +753,26 @@ class ExtensionStore { /// Returns an [Extender] composed solely of a compound selector containing /// [simples]. Extender _extenderForCompound( - Iterable simples, FileSpan? span) { + Iterable simples, FileSpan span) { var compound = CompoundSelector(simples); return Extender(ComplexSelector([compound]), span, specificity: _sourceSpecificityFor(compound), original: true); } /// Returns an [Extender] composed solely of [simple]. - Extender _extenderForSimple(SimpleSelector simple, FileSpan? span) => - Extender( - ComplexSelector([ - CompoundSelector([simple]) - ]), - span, - specificity: _sourceSpecificity[simple] ?? 0, - original: true); + Extender _extenderForSimple(SimpleSelector simple, FileSpan span) => Extender( + ComplexSelector([ + CompoundSelector([simple]) + ]), + span, + specificity: _sourceSpecificity[simple] ?? 0, + original: true); /// Extends [pseudo] using [extensions], and returns a list of resulting /// pseudo selectors. List? _extendPseudo( PseudoSelector pseudo, - FileSpan? pseudoSpan, + FileSpan pseudoSpan, Map?>? extensions, List? mediaQueryContext) { var extended = _extendList( diff --git a/lib/src/extend/merged_extension.dart b/lib/src/extend/merged_extension.dart index 3966124a0..ddffad5e1 100644 --- a/lib/src/extend/merged_extension.dart +++ b/lib/src/extend/merged_extension.dart @@ -35,7 +35,7 @@ class MergedExtension extends Extension { right.mediaContext != null && !listEquals(left.mediaContext, right.mediaContext)) { throw SassException( - "From ${left.span!.message('')}\n" + "From ${left.span.message('')}\n" "You may not @extend the same selector from within different media " "queries.", right.span); diff --git a/lib/src/functions.dart b/lib/src/functions.dart index d29de59a7..f83695b17 100644 --- a/lib/src/functions.dart +++ b/lib/src/functions.dart @@ -3,9 +3,12 @@ // https://opensource.org/licenses/MIT. import 'dart:collection'; +import 'dart:async'; import 'package:collection/collection.dart'; +import 'package:source_span/source_span.dart'; +import 'ast/node.dart'; import 'callable.dart'; import 'functions/color.dart' as color; import 'functions/list.dart' as list; @@ -46,3 +49,21 @@ final coreModules = UnmodifiableListView([ selector.module, string.module ]); + +/// Returns the span for the currently executing callable. +/// +/// For normal exception reporting, this should be avoided in favor of throwing +/// [SassScriptException]s. It should only be used when calling APIs that +/// require spans. +FileSpan get currentCallableSpan { + var node = Zone.current[#_currentCallableNode]; + if (node is AstNode) return node.span; + + throw StateError("currentCallableSpan may only be called within an " + "active Sass callable."); +} + +/// Runs [callback] in a zone with [callableNode]'s span available from +/// [currentCallableSpan]. +T withCurrentCallableNode(AstNode callableNode, T callback()) => + runZoned(callback, zoneValues: {#_currentCallableNode: callableNode}); diff --git a/lib/src/functions/selector.dart b/lib/src/functions/selector.dart index 8aababbeb..83eb77e8f 100644 --- a/lib/src/functions/selector.dart +++ b/lib/src/functions/selector.dart @@ -10,6 +10,7 @@ import '../ast/selector.dart'; import '../callable.dart'; import '../exception.dart'; import '../extend/extension_store.dart'; +import '../functions.dart'; import '../module/built_in.dart'; import '../value.dart'; @@ -87,7 +88,8 @@ final _extend = var target = arguments[1].assertSelector(name: "extendee"); var source = arguments[2].assertSelector(name: "extender"); - return ExtensionStore.extend(selector, source, target).asSassList; + return ExtensionStore.extend(selector, source, target, currentCallableSpan) + .asSassList; }); final _replace = @@ -96,7 +98,8 @@ final _replace = var target = arguments[1].assertSelector(name: "original"); var source = arguments[2].assertSelector(name: "replacement"); - return ExtensionStore.replace(selector, source, target).asSassList; + return ExtensionStore.replace(selector, source, target, currentCallableSpan) + .asSassList; }); final _unify = _function("unify", r"$selector1, $selector2", (arguments) { diff --git a/lib/src/interpolation_buffer.dart b/lib/src/interpolation_buffer.dart index 9fe3d890a..68cd37701 100644 --- a/lib/src/interpolation_buffer.dart +++ b/lib/src/interpolation_buffer.dart @@ -69,7 +69,7 @@ class InterpolationBuffer implements StringSink { /// Creates an [Interpolation] with the given [span] from the contents of this /// buffer. - Interpolation interpolation(FileSpan? span) { + Interpolation interpolation(FileSpan span) { return Interpolation( [..._contents, if (_text.isNotEmpty) _text.toString()], span); } diff --git a/lib/src/node.dart b/lib/src/node.dart index 45cbaba10..d4b13d88f 100644 --- a/lib/src/node.dart +++ b/lib/src/node.dart @@ -175,9 +175,9 @@ RenderResult _renderSync(RenderOptions options) { JsError _wrapException(Object exception) { if (exception is SassException) { return _newRenderError(exception.toString().replaceFirst("Error: ", ""), - line: exception.span.andThen((span) => span.start.line + 1), - column: exception.span.andThen((span) => span.start.column + 1), - file: exception.span?.sourceUrl.andThen(p.fromUri) ?? 'stdin', + line: exception.span.start.line + 1, + column: exception.span.start.column + 1, + file: exception.span.sourceUrl.andThen(p.fromUri) ?? 'stdin', status: 1); } else { return JsError(exception.toString()); diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index 6786c8384..621270825 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -656,7 +656,7 @@ class Parser { /// Prints a warning to standard error, associated with [span]. @protected - void warn(String message, FileSpan? span) => logger.warn(message, span: span); + void warn(String message, FileSpan span) => logger.warn(message, span: span); /// Throws an error associated with [span]. @protected diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 43838302c..1b04aee8c 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -717,7 +717,7 @@ abstract class StylesheetParser extends Parser { error( "@function rules may not contain " "${statement is StyleRule ? "style rules" : "declarations"}.", - statement.span!); + statement.span); } } @@ -1216,7 +1216,7 @@ abstract class StylesheetParser extends Parser { } var span = - scanner.spanFrom(start, start).expand((content ?? arguments).span!); + scanner.spanFrom(start, start).expand((content ?? arguments).span); return IncludeRule(name, arguments, span, namespace: namespace, content: content); } @@ -1554,7 +1554,7 @@ relase. For details, see http://bit.ly/moz-document. arguments.add(Argument(name, span: scanner.spanFrom(variableStart), defaultValue: defaultValue)); if (!named.add(name)) { - error("Duplicate argument.", arguments.last.span!); + error("Duplicate argument.", arguments.last.span); } if (!scanner.scanChar($comma)) break; @@ -1603,7 +1603,7 @@ relase. For details, see http://bit.ly/moz-document. } } else if (named.isNotEmpty) { error("Positional arguments must come before keyword arguments.", - expression.span!); + expression.span); } else { positional.add(expression); } @@ -1791,8 +1791,9 @@ relase. For details, see http://bit.ly/moz-document. if (singleExpression == null) scanner.error("Expected expression."); spaceExpressions.add(singleExpression); - singleExpression_ = - ListExpression(spaceExpressions, ListSeparator.space); + singleExpression_ = ListExpression( + spaceExpressions, ListSeparator.space, + span: spaceExpressions.first.span.expand(singleExpression.span)); spaceExpressions_ = null; } } @@ -2054,18 +2055,18 @@ relase. For details, see http://bit.ly/moz-document. var singleExpression = singleExpression_; if (singleExpression != null) commaExpressions.add(singleExpression); return ListExpression(commaExpressions, ListSeparator.comma, - brackets: bracketList, span: beforeBracket.andThen(scanner.spanFrom)); + brackets: bracketList, span: scanner.spanFrom(beforeBracket!)); } else if (bracketList && spaceExpressions != null) { resolveOperations(); return ListExpression( spaceExpressions..add(singleExpression_!), ListSeparator.space, - brackets: true, span: beforeBracket.andThen(scanner.spanFrom)); + brackets: true, span: scanner.spanFrom(beforeBracket!)); } else { resolveSpaceExpressions(); if (bracketList) { singleExpression_ = ListExpression( [singleExpression_!], ListSeparator.undecided, - brackets: true, span: beforeBracket.andThen(scanner.spanFrom)); + brackets: true, span: scanner.spanFrom(beforeBracket!)); } return singleExpression_!; } @@ -2603,7 +2604,8 @@ relase. For details, see http://bit.ly/moz-document. if (plain != null) { if (plain == "if") { var invocation = _argumentInvocation(); - return IfExpression(invocation, spanForList([identifier, invocation])); + return IfExpression( + invocation, identifier.span.expand(invocation.span)); } else if (plain == "not") { whitespace(); return UnaryOperationExpression( @@ -2639,7 +2641,7 @@ relase. For details, see http://bit.ly/moz-document. scanner.readChar(); if (plain == null) { - error("Interpolation isn't allowed in namespaces.", identifier.span!); + error("Interpolation isn't allowed in namespaces.", identifier.span); } if (scanner.peekChar() == $dollar) { @@ -3416,7 +3418,7 @@ relase. For details, see http://bit.ly/moz-document. if (_lookingAtInterpolatedIdentifier()) { var identifier = interpolatedIdentifier(); if (identifier.asPlain?.toLowerCase() == "not") { - error('"not" is not a valid identifier here.', identifier.span!); + error('"not" is not a valid identifier here.', identifier.span); } if (scanner.scanChar($lparen)) { @@ -3426,7 +3428,7 @@ relase. For details, see http://bit.ly/moz-document. return SupportsFunction(identifier, arguments, scanner.spanFrom(start)); } else if (identifier.contents.length != 1 || identifier.contents.first is! Expression) { - error("Expected @supports condition.", identifier.span!); + error("Expected @supports condition.", identifier.span); } else { return SupportsInterpolation( identifier.contents.first as Expression, scanner.spanFrom(start)); diff --git a/lib/src/stylesheet_graph.dart b/lib/src/stylesheet_graph.dart index 3d1d178a6..9d9e7ca03 100644 --- a/lib/src/stylesheet_graph.dart +++ b/lib/src/stylesheet_graph.dart @@ -399,5 +399,5 @@ class StylesheetNode { } String toString() => - stylesheet.span?.sourceUrl.andThen(p.prettyUri) ?? ''; + stylesheet.span.sourceUrl.andThen(p.prettyUri) ?? ''; } diff --git a/lib/src/util/no_source_map_buffer.dart b/lib/src/util/no_source_map_buffer.dart index c69100883..0e2bd257d 100644 --- a/lib/src/util/no_source_map_buffer.dart +++ b/lib/src/util/no_source_map_buffer.dart @@ -17,7 +17,7 @@ class NoSourceMapBuffer implements SourceMapBuffer { int get length => _buffer.length; Map get sourceFiles => const {}; - T forSpan(SourceSpan? span, T callback()) => callback(); + T forSpan(SourceSpan span, T callback()) => callback(); void write(Object? object) => _buffer.write(object); void writeAll(Iterable objects, [String separator = ""]) => _buffer.writeAll(objects, separator); diff --git a/lib/src/util/source_map_buffer.dart b/lib/src/util/source_map_buffer.dart index fae869156..282042351 100644 --- a/lib/src/util/source_map_buffer.dart +++ b/lib/src/util/source_map_buffer.dart @@ -49,11 +49,7 @@ class SourceMapBuffer implements StringBuffer { /// Specifically, this associates the point at the beginning of the written /// text with [span.start] and the point at the end of the written text with /// [span.end]. - /// - /// Just calls `callback()` if [span] is `null`. - T forSpan(FileSpan? span, T callback()) { - if (span == null) return callback(); - + T forSpan(FileSpan span, T callback()) { var wasInSpan = _inSpan; _inSpan = true; _addEntry(span.start, _targetLocation); diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 6e56f8aa9..6bd511c44 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -10,9 +10,7 @@ import 'package:source_span/source_span.dart'; import 'package:stack_trace/stack_trace.dart'; import 'package:term_glyph/term_glyph.dart' as glyph; -import 'ast/node.dart'; import 'util/character.dart'; -import 'util/nullable.dart'; /// The URL used in stack traces when no source URL is available. final _noSourceUrl = Uri.parse("-"); @@ -206,29 +204,12 @@ int mapHash(Map map) => /// /// By default, the frame's URL is set to `span.sourceUrl`. However, if [url] is /// passed, it's used instead. -Frame frameForSpan(SourceSpan? span, String member, {Uri? url}) => Frame( - url ?? span?.sourceUrl ?? _noSourceUrl, - span.andThen((span) => span.start.line + 1) ?? 1, - span.andThen((span) => span.start.column + 1) ?? 1, +Frame frameForSpan(SourceSpan span, String member, {Uri? url}) => Frame( + url ?? span.sourceUrl ?? _noSourceUrl, + span.start.line + 1, + span.start.column + 1, member); -/// Returns a source span that covers the spans of both the first and last nodes -/// in [nodes]. -/// -/// If [nodes] is empty, or if either the first or last node has a `null` span, -/// returns `null`. -FileSpan? spanForList(List nodes) { - if (nodes.isEmpty) return null; - - var left = nodes.first.span; - if (left == null) return null; - - var right = nodes.last.span; - if (right == null) return null; - - return left.expand(right); -} - /// Returns the variable name (including the leading `$`) from a [span] that /// covers a variable declaration, which includes the variable name as well as /// the colon and expression following it. diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 448a0ac21..8bdefdfe5 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -440,14 +440,14 @@ class _EvaluateVisitor throw "The variable \$$name was configured twice."; } - values[name] = ConfiguredValue(value, span, callableNode); + values[name] = ConfiguredValue.explicit(value, span, callableNode); }); - configuration = Configuration(values, callableNode); + configuration = ExplicitConfiguration(values, callableNode); } await _loadModule(url, "load-css()", callableNode, (module) => _combineCss(module, clone: true).accept(this), - baseUrl: callableNode.span?.sourceUrl, + baseUrl: callableNode.span.sourceUrl, configuration: configuration, namesInErrors: true); _assertConfigurationIsEmpty(configuration, nameInError: true); @@ -470,8 +470,8 @@ class _EvaluateVisitor } Future run(AsyncImporter? importer, Stylesheet node) async { - return _withWarnCallback(() async { - var url = node.span?.sourceUrl; + return _withWarnCallback(node, () async { + var url = node.span.sourceUrl; if (url != null) { _activeModules[url] = null; if (_asNodeSass) { @@ -490,18 +490,25 @@ class _EvaluateVisitor } Future runExpression(AsyncImporter? importer, Expression expression) => - _withWarnCallback(() => _withFakeStylesheet( - importer, expression, () => expression.accept(this))); + _withWarnCallback( + expression, + () => _withFakeStylesheet( + importer, expression, () => expression.accept(this))); Future runStatement(AsyncImporter? importer, Statement statement) => - _withWarnCallback(() => _withFakeStylesheet( - importer, statement, () => statement.accept(this))); + _withWarnCallback( + statement, + () => _withFakeStylesheet( + importer, statement, () => statement.accept(this))); /// Runs [callback] with a definition for the top-level `warn` function. - T _withWarnCallback(T callback()) { + /// + /// If no other span can be found to report a warning, falls back on + /// [nodeWithSpan]'s. + T _withWarnCallback(AstNode nodeWithSpan, T callback()) { return withWarnCallback( (message, deprecation) => _warn( - message, _importSpan ?? _callableNode?.span, + message, _importSpan ?? _callableNode?.span ?? nodeWithSpan.span, deprecation: deprecation), callback); } @@ -564,18 +571,16 @@ class _EvaluateVisitor var importer = result.item1; var stylesheet = result.item2; - var canonicalUrl = stylesheet.span?.sourceUrl; + var canonicalUrl = stylesheet.span.sourceUrl; if (canonicalUrl != null && _activeModules.containsKey(canonicalUrl)) { var message = namesInErrors ? "Module loop: ${p.prettyUri(canonicalUrl)} is already being " "loaded." : "Module loop: this module is already being loaded."; - throw _activeModules[canonicalUrl].andThen((previousLoad) { - var span = previousLoad.span; - return _multiSpanException(message, "new load", - {if (span != null) span: "original load"}); - }) ?? + throw _activeModules[canonicalUrl].andThen((previousLoad) => + _multiSpanException(message, "new load", + {previousLoad.span: "original load"})) ?? _exception(message); } if (canonicalUrl != null) _activeModules[canonicalUrl] = nodeWithSpan; @@ -620,7 +625,7 @@ class _EvaluateVisitor {Configuration? configuration, AstNode? nodeWithSpan, bool namesInErrors = false}) async { - var url = stylesheet.span?.sourceUrl; + var url = stylesheet.span.sourceUrl; var alreadyLoaded = _modules[url]; if (alreadyLoaded != null) { @@ -1272,8 +1277,7 @@ class _EvaluateVisitor if (!variable.isGuarded) variable.name }); - _assertConfigurationIsEmpty(newConfiguration, - only: {for (var variable in node.configuration) variable.name}); + _assertConfigurationIsEmpty(newConfiguration); } else { _configuration = adjustedConfiguration; await _loadModule(node.url, "@forward", node, (module) { @@ -1285,7 +1289,7 @@ class _EvaluateVisitor return null; } - /// Updates [configuration] to include [node]'s configuration and return the + /// Updates [configuration] to include [node]'s configuration and returns the /// result. Future _addForwardConfiguration( Configuration configuration, ForwardRule node) async { @@ -1299,13 +1303,17 @@ class _EvaluateVisitor } } - newValues[variable.name] = ConfiguredValue( + newValues[variable.name] = ConfiguredValue.explicit( (await variable.expression.accept(this)).withoutSlash(), variable.span, _expressionNode(variable.expression)); } - return Configuration(newValues, node); + if (configuration is ExplicitConfiguration || configuration.isEmpty) { + return ExplicitConfiguration(newValues, node); + } else { + return Configuration.implicit(newValues); + } } /// Remove configured values from [upstream] that have been removed from @@ -1328,17 +1336,19 @@ class _EvaluateVisitor /// variable in the error message. This should only be `true` if the name /// won't be obvious from the source span. void _assertConfigurationIsEmpty(Configuration configuration, - {Set? only, bool nameInError = false}) { - configuration.values.forEach((name, value) { - if (only != null && !only.contains(name)) return; + {bool nameInError = false}) { + // By definition, implicit configurations are allowed to only use a subset + // of their values. + if (configuration is! ExplicitConfiguration) return; - throw _exception( - nameInError - ? "\$$name was not declared with !default in the @used module." - : "This variable was not declared with !default in the @used " - "module.", - value.configurationSpan); - }); + var entry = configuration.values.entries.first; + throw _exception( + nameInError + ? "\$${entry.key} was not declared with !default in the @used " + "module." + : "This variable was not declared with !default in the @used " + "module.", + entry.value.configurationSpan); } Future visitFunctionRule(FunctionRule node) async { @@ -1383,14 +1393,12 @@ class _EvaluateVisitor var importer = result.item1; var stylesheet = result.item2; - var url = stylesheet.span?.sourceUrl; + var url = stylesheet.span.sourceUrl; if (url != null) { if (_activeModules.containsKey(url)) { - throw _activeModules[url].andThen((previousLoad) { - var span = previousLoad.span; - return _multiSpanException("This file is already being loaded.", - "new load", {if (span != null) span: "original load"}); - }) ?? + throw _activeModules[url].andThen((previousLoad) => + _multiSpanException("This file is already being loaded.", + "new load", {previousLoad.span: "original load"})) ?? _exception("This file is already being loaded."); } _activeModules[url] = import; @@ -1477,7 +1485,7 @@ class _EvaluateVisitor /// This first tries loading [url] relative to [baseUrl], which defaults to /// `_stylesheet.span.sourceUrl`. Future> _loadStylesheet( - String url, FileSpan? span, + String url, FileSpan span, {Uri? baseUrl, bool forImport = false}) async { try { assert(_importSpan == null); @@ -1487,7 +1495,7 @@ class _EvaluateVisitor if (importCache != null) { var tuple = await importCache.import(Uri.parse(url), baseImporter: _importer, - baseUrl: baseUrl ?? _stylesheet.span?.sourceUrl, + baseUrl: baseUrl ?? _stylesheet.span.sourceUrl, forImport: forImport); if (tuple != null) return tuple; } else { @@ -1523,7 +1531,7 @@ class _EvaluateVisitor Future _importLikeNode( String originalUrl, bool forImport) async { var result = await _nodeImporter! - .loadAsync(originalUrl, _stylesheet.span?.sourceUrl, forImport); + .loadAsync(originalUrl, _stylesheet.span.sourceUrl, forImport); if (result == null) return null; var contents = result.item1; @@ -1580,12 +1588,11 @@ class _EvaluateVisitor } else if (mixin is UserDefinedCallable) { if (node.content != null && !(mixin.declaration as MixinRule).hasContent) { - var declarationSpan = mixin.declaration.arguments.spanWithName; throw MultiSpanSassRuntimeException( "Mixin doesn't accept a content block.", node.spanWithoutContent, "invocation", - {if (declarationSpan != null) declarationSpan: "declaration"}, + {mixin.declaration.arguments.spanWithName: "declaration"}, _stackTrace(node.spanWithoutContent)); } @@ -1907,9 +1914,9 @@ class _EvaluateVisitor Future visitUseRule(UseRule node) async { var configuration = node.configuration.isEmpty ? const Configuration.empty() - : Configuration({ + : ExplicitConfiguration({ for (var variable in node.configuration) - variable.name: ConfiguredValue( + variable.name: ConfiguredValue.explicit( (await variable.expression.accept(this)).withoutSlash(), variable.span, _expressionNode(variable.expression)) @@ -2206,12 +2213,11 @@ class _EvaluateVisitor var argumentWord = pluralize('argument', evaluated.named.keys.length); var argumentNames = toSentence(evaluated.named.keys.map((name) => "\$$name"), 'or'); - var declarationSpan = callable.declaration.arguments.spanWithName; throw MultiSpanSassRuntimeException( "No $argumentWord named $argumentNames.", nodeWithSpan.span, "invocation", - {if (declarationSpan != null) declarationSpan: "declaration"}, + {callable.declaration.arguments.spanWithName: "declaration"}, _stackTrace(nodeWithSpan.span)); }); }); @@ -2312,7 +2318,8 @@ class _EvaluateVisitor Value result; try { - result = await callback(evaluated.positional); + result = await withCurrentCallableNode( + nodeWithSpan, () => callback(evaluated.positional)); } on SassRuntimeException { rethrow; } on MultiSpanSassScriptException catch (error) { @@ -2340,13 +2347,12 @@ class _EvaluateVisitor if (evaluated.named.isEmpty) return result; if (argumentList.wereKeywordsAccessed) return result; - var declarationSpan = overload.spanWithName; throw MultiSpanSassRuntimeException( "No ${pluralize('argument', evaluated.named.keys.length)} named " "${toSentence(evaluated.named.keys.map((name) => "\$$name"), 'or')}.", nodeWithSpan.span, "invocation", - {if (declarationSpan != null) declarationSpan: "declaration"}, + {overload.spanWithName: "declaration"}, _stackTrace(nodeWithSpan.span)); } @@ -2440,35 +2446,39 @@ class _EvaluateVisitor /// for macros such as `if()`. Future, Map>> _evaluateMacroArguments(CallableInvocation invocation) async { - var restArgs = invocation.arguments.rest; - if (restArgs == null) { + var restArgs_ = invocation.arguments.rest; + if (restArgs_ == null) { return Tuple2( invocation.arguments.positional, invocation.arguments.named); } + var restArgs = restArgs_; // dart-lang/sdk#45348 var positional = invocation.arguments.positional.toList(); var named = Map.of(invocation.arguments.named); var rest = await restArgs.accept(this); if (rest is SassMap) { - _addRestMap(named, rest, invocation, (value) => ValueExpression(value)); + _addRestMap(named, rest, invocation, + (value) => ValueExpression(value, restArgs.span)); } else if (rest is SassList) { - positional.addAll(rest.asList.map((value) => ValueExpression(value))); + positional.addAll( + rest.asList.map((value) => ValueExpression(value, restArgs.span))); if (rest is SassArgumentList) { rest.keywords.forEach((key, value) { - named[key] = ValueExpression(value); + named[key] = ValueExpression(value, restArgs.span); }); } } else { - positional.add(ValueExpression(rest)); + positional.add(ValueExpression(rest, restArgs.span)); } - var keywordRestArgs = invocation.arguments.keywordRest; - if (keywordRestArgs == null) return Tuple2(positional, named); + var keywordRestArgs_ = invocation.arguments.keywordRest; + if (keywordRestArgs_ == null) return Tuple2(positional, named); + var keywordRestArgs = keywordRestArgs_; // dart-lang/sdk#45348 var keywordRest = await keywordRestArgs.accept(this); if (keywordRest is SassMap) { - _addRestMap( - named, keywordRest, invocation, (value) => ValueExpression(value)); + _addRestMap(named, keywordRest, invocation, + (value) => ValueExpression(value, keywordRestArgs.span)); return Tuple2(positional, named); } else { throw _exception( @@ -2790,7 +2800,8 @@ class _EvaluateVisitor namesByColor.containsKey(result)) { var alternative = BinaryOperationExpression( BinaryOperator.plus, - StringExpression(Interpolation([""], null), quotes: true), + StringExpression(Interpolation([""], interpolation.span), + quotes: true), expression); _warn( "You probably don't mean to use the color value " @@ -2935,9 +2946,8 @@ class _EvaluateVisitor /// Creates a new stack frame with location information from [member] and /// [span]. - Frame _stackFrame(String member, FileSpan? span) => frameForSpan(span, member, - url: - span?.sourceUrl.andThen((url) => _importCache?.humanize(url) ?? url)); + Frame _stackFrame(String member, FileSpan span) => frameForSpan(span, member, + url: span.sourceUrl.andThen((url) => _importCache?.humanize(url) ?? url)); /// Returns a stack trace at the current point. /// @@ -2951,7 +2961,7 @@ class _EvaluateVisitor } /// Emits a warning with the given [message] about the given [span]. - void _warn(String message, FileSpan? span, {bool deprecation = false}) => + void _warn(String message, FileSpan span, {bool deprecation = false}) => _logger.warn(message, span: span, trace: _stackTrace(span), deprecation: deprecation); @@ -2986,20 +2996,15 @@ class _EvaluateVisitor try { return callback(); } on SassFormatException catch (error) { - var errorSpan = error.span; - if (errorSpan == null) rethrow; - - var nodeSpan = nodeWithSpan.span; - if (nodeSpan == null) rethrow; - - var errorText = errorSpan.file.getText(0); - var syntheticFile = nodeSpan.file + var errorText = error.span.file.getText(0); + var span = nodeWithSpan.span; + var syntheticFile = span.file .getText(0) - .replaceRange(nodeSpan.start.offset, nodeSpan.end.offset, errorText); + .replaceRange(span.start.offset, span.end.offset, errorText); var syntheticSpan = - SourceFile.fromString(syntheticFile, url: nodeSpan.file.url).span( - nodeSpan.start.offset + errorSpan.start.offset, - nodeSpan.start.offset + errorSpan.end.offset); + SourceFile.fromString(syntheticFile, url: span.file.url).span( + span.start.offset + error.span.start.offset, + span.start.offset + error.span.end.offset); throw _exception(error.message, syntheticSpan); } } @@ -3049,9 +3054,7 @@ class _EvaluateVisitor try { return await callback(); } on SassRuntimeException catch (error) { - var span = error.span; - if (span == null) rethrow; - if (span.text.startsWith("@error")) rethrow; + if (error.span.text.startsWith("@error")) rethrow; throw SassRuntimeException( error.message, nodeWithSpan.span, _stackTrace()); } diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 7b1755242..e2a189fa9 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: 52761b53fe4aa8212132088b2447d54028aa9f7c +// Checksum: 6c0d909812330091658fa30b673c5f529917e30b // // ignore_for_file: unused_import @@ -445,14 +445,14 @@ class _EvaluateVisitor throw "The variable \$$name was configured twice."; } - values[name] = ConfiguredValue(value, span, callableNode); + values[name] = ConfiguredValue.explicit(value, span, callableNode); }); - configuration = Configuration(values, callableNode); + configuration = ExplicitConfiguration(values, callableNode); } _loadModule(url, "load-css()", callableNode, (module) => _combineCss(module, clone: true).accept(this), - baseUrl: callableNode.span?.sourceUrl, + baseUrl: callableNode.span.sourceUrl, configuration: configuration, namesInErrors: true); _assertConfigurationIsEmpty(configuration, nameInError: true); @@ -475,8 +475,8 @@ class _EvaluateVisitor } EvaluateResult run(Importer? importer, Stylesheet node) { - return _withWarnCallback(() { - var url = node.span?.sourceUrl; + return _withWarnCallback(node, () { + var url = node.span.sourceUrl; if (url != null) { _activeModules[url] = null; if (_asNodeSass) { @@ -495,18 +495,25 @@ class _EvaluateVisitor } Value runExpression(Importer? importer, Expression expression) => - _withWarnCallback(() => _withFakeStylesheet( - importer, expression, () => expression.accept(this))); + _withWarnCallback( + expression, + () => _withFakeStylesheet( + importer, expression, () => expression.accept(this))); void runStatement(Importer? importer, Statement statement) => - _withWarnCallback(() => _withFakeStylesheet( - importer, statement, () => statement.accept(this))); + _withWarnCallback( + statement, + () => _withFakeStylesheet( + importer, statement, () => statement.accept(this))); /// Runs [callback] with a definition for the top-level `warn` function. - T _withWarnCallback(T callback()) { + /// + /// If no other span can be found to report a warning, falls back on + /// [nodeWithSpan]'s. + T _withWarnCallback(AstNode nodeWithSpan, T callback()) { return withWarnCallback( (message, deprecation) => _warn( - message, _importSpan ?? _callableNode?.span, + message, _importSpan ?? _callableNode?.span ?? nodeWithSpan.span, deprecation: deprecation), callback); } @@ -569,18 +576,16 @@ class _EvaluateVisitor var importer = result.item1; var stylesheet = result.item2; - var canonicalUrl = stylesheet.span?.sourceUrl; + var canonicalUrl = stylesheet.span.sourceUrl; if (canonicalUrl != null && _activeModules.containsKey(canonicalUrl)) { var message = namesInErrors ? "Module loop: ${p.prettyUri(canonicalUrl)} is already being " "loaded." : "Module loop: this module is already being loaded."; - throw _activeModules[canonicalUrl].andThen((previousLoad) { - var span = previousLoad.span; - return _multiSpanException(message, "new load", - {if (span != null) span: "original load"}); - }) ?? + throw _activeModules[canonicalUrl].andThen((previousLoad) => + _multiSpanException(message, "new load", + {previousLoad.span: "original load"})) ?? _exception(message); } if (canonicalUrl != null) _activeModules[canonicalUrl] = nodeWithSpan; @@ -625,7 +630,7 @@ class _EvaluateVisitor {Configuration? configuration, AstNode? nodeWithSpan, bool namesInErrors = false}) { - var url = stylesheet.span?.sourceUrl; + var url = stylesheet.span.sourceUrl; var alreadyLoaded = _modules[url]; if (alreadyLoaded != null) { @@ -1273,8 +1278,7 @@ class _EvaluateVisitor if (!variable.isGuarded) variable.name }); - _assertConfigurationIsEmpty(newConfiguration, - only: {for (var variable in node.configuration) variable.name}); + _assertConfigurationIsEmpty(newConfiguration); } else { _configuration = adjustedConfiguration; _loadModule(node.url, "@forward", node, (module) { @@ -1286,7 +1290,7 @@ class _EvaluateVisitor return null; } - /// Updates [configuration] to include [node]'s configuration and return the + /// Updates [configuration] to include [node]'s configuration and returns the /// result. Configuration _addForwardConfiguration( Configuration configuration, ForwardRule node) { @@ -1300,13 +1304,17 @@ class _EvaluateVisitor } } - newValues[variable.name] = ConfiguredValue( + newValues[variable.name] = ConfiguredValue.explicit( variable.expression.accept(this).withoutSlash(), variable.span, _expressionNode(variable.expression)); } - return Configuration(newValues, node); + if (configuration is ExplicitConfiguration || configuration.isEmpty) { + return ExplicitConfiguration(newValues, node); + } else { + return Configuration.implicit(newValues); + } } /// Remove configured values from [upstream] that have been removed from @@ -1329,17 +1337,19 @@ class _EvaluateVisitor /// variable in the error message. This should only be `true` if the name /// won't be obvious from the source span. void _assertConfigurationIsEmpty(Configuration configuration, - {Set? only, bool nameInError = false}) { - configuration.values.forEach((name, value) { - if (only != null && !only.contains(name)) return; + {bool nameInError = false}) { + // By definition, implicit configurations are allowed to only use a subset + // of their values. + if (configuration is! ExplicitConfiguration) return; - throw _exception( - nameInError - ? "\$$name was not declared with !default in the @used module." - : "This variable was not declared with !default in the @used " - "module.", - value.configurationSpan); - }); + var entry = configuration.values.entries.first; + throw _exception( + nameInError + ? "\$${entry.key} was not declared with !default in the @used " + "module." + : "This variable was not declared with !default in the @used " + "module.", + entry.value.configurationSpan); } Value? visitFunctionRule(FunctionRule node) { @@ -1383,14 +1393,12 @@ class _EvaluateVisitor var importer = result.item1; var stylesheet = result.item2; - var url = stylesheet.span?.sourceUrl; + var url = stylesheet.span.sourceUrl; if (url != null) { if (_activeModules.containsKey(url)) { - throw _activeModules[url].andThen((previousLoad) { - var span = previousLoad.span; - return _multiSpanException("This file is already being loaded.", - "new load", {if (span != null) span: "original load"}); - }) ?? + throw _activeModules[url].andThen((previousLoad) => + _multiSpanException("This file is already being loaded.", + "new load", {previousLoad.span: "original load"})) ?? _exception("This file is already being loaded."); } _activeModules[url] = import; @@ -1476,7 +1484,7 @@ class _EvaluateVisitor /// /// This first tries loading [url] relative to [baseUrl], which defaults to /// `_stylesheet.span.sourceUrl`. - Tuple2 _loadStylesheet(String url, FileSpan? span, + Tuple2 _loadStylesheet(String url, FileSpan span, {Uri? baseUrl, bool forImport = false}) { try { assert(_importSpan == null); @@ -1486,7 +1494,7 @@ class _EvaluateVisitor if (importCache != null) { var tuple = importCache.import(Uri.parse(url), baseImporter: _importer, - baseUrl: baseUrl ?? _stylesheet.span?.sourceUrl, + baseUrl: baseUrl ?? _stylesheet.span.sourceUrl, forImport: forImport); if (tuple != null) return tuple; } else { @@ -1520,8 +1528,8 @@ class _EvaluateVisitor /// /// Returns the [Stylesheet], or `null` if the import failed. Stylesheet? _importLikeNode(String originalUrl, bool forImport) { - var result = _nodeImporter! - .load(originalUrl, _stylesheet.span?.sourceUrl, forImport); + var result = + _nodeImporter!.load(originalUrl, _stylesheet.span.sourceUrl, forImport); if (result == null) return null; var contents = result.item1; @@ -1578,12 +1586,11 @@ class _EvaluateVisitor } else if (mixin is UserDefinedCallable) { if (node.content != null && !(mixin.declaration as MixinRule).hasContent) { - var declarationSpan = mixin.declaration.arguments.spanWithName; throw MultiSpanSassRuntimeException( "Mixin doesn't accept a content block.", node.spanWithoutContent, "invocation", - {if (declarationSpan != null) declarationSpan: "declaration"}, + {mixin.declaration.arguments.spanWithName: "declaration"}, _stackTrace(node.spanWithoutContent)); } @@ -1899,9 +1906,9 @@ class _EvaluateVisitor Value? visitUseRule(UseRule node) { var configuration = node.configuration.isEmpty ? const Configuration.empty() - : Configuration({ + : ExplicitConfiguration({ for (var variable in node.configuration) - variable.name: ConfiguredValue( + variable.name: ConfiguredValue.explicit( variable.expression.accept(this).withoutSlash(), variable.span, _expressionNode(variable.expression)) @@ -2193,12 +2200,11 @@ class _EvaluateVisitor var argumentWord = pluralize('argument', evaluated.named.keys.length); var argumentNames = toSentence(evaluated.named.keys.map((name) => "\$$name"), 'or'); - var declarationSpan = callable.declaration.arguments.spanWithName; throw MultiSpanSassRuntimeException( "No $argumentWord named $argumentNames.", nodeWithSpan.span, "invocation", - {if (declarationSpan != null) declarationSpan: "declaration"}, + {callable.declaration.arguments.spanWithName: "declaration"}, _stackTrace(nodeWithSpan.span)); }); }); @@ -2297,7 +2303,8 @@ class _EvaluateVisitor Value result; try { - result = callback(evaluated.positional); + result = withCurrentCallableNode( + nodeWithSpan, () => callback(evaluated.positional)); } on SassRuntimeException { rethrow; } on MultiSpanSassScriptException catch (error) { @@ -2325,13 +2332,12 @@ class _EvaluateVisitor if (evaluated.named.isEmpty) return result; if (argumentList.wereKeywordsAccessed) return result; - var declarationSpan = overload.spanWithName; throw MultiSpanSassRuntimeException( "No ${pluralize('argument', evaluated.named.keys.length)} named " "${toSentence(evaluated.named.keys.map((name) => "\$$name"), 'or')}.", nodeWithSpan.span, "invocation", - {if (declarationSpan != null) declarationSpan: "declaration"}, + {overload.spanWithName: "declaration"}, _stackTrace(nodeWithSpan.span)); } @@ -2425,35 +2431,39 @@ class _EvaluateVisitor /// for macros such as `if()`. Tuple2, Map> _evaluateMacroArguments( CallableInvocation invocation) { - var restArgs = invocation.arguments.rest; - if (restArgs == null) { + var restArgs_ = invocation.arguments.rest; + if (restArgs_ == null) { return Tuple2( invocation.arguments.positional, invocation.arguments.named); } + var restArgs = restArgs_; // dart-lang/sdk#45348 var positional = invocation.arguments.positional.toList(); var named = Map.of(invocation.arguments.named); var rest = restArgs.accept(this); if (rest is SassMap) { - _addRestMap(named, rest, invocation, (value) => ValueExpression(value)); + _addRestMap(named, rest, invocation, + (value) => ValueExpression(value, restArgs.span)); } else if (rest is SassList) { - positional.addAll(rest.asList.map((value) => ValueExpression(value))); + positional.addAll( + rest.asList.map((value) => ValueExpression(value, restArgs.span))); if (rest is SassArgumentList) { rest.keywords.forEach((key, value) { - named[key] = ValueExpression(value); + named[key] = ValueExpression(value, restArgs.span); }); } } else { - positional.add(ValueExpression(rest)); + positional.add(ValueExpression(rest, restArgs.span)); } - var keywordRestArgs = invocation.arguments.keywordRest; - if (keywordRestArgs == null) return Tuple2(positional, named); + var keywordRestArgs_ = invocation.arguments.keywordRest; + if (keywordRestArgs_ == null) return Tuple2(positional, named); + var keywordRestArgs = keywordRestArgs_; // dart-lang/sdk#45348 var keywordRest = keywordRestArgs.accept(this); if (keywordRest is SassMap) { - _addRestMap( - named, keywordRest, invocation, (value) => ValueExpression(value)); + _addRestMap(named, keywordRest, invocation, + (value) => ValueExpression(value, keywordRestArgs.span)); return Tuple2(positional, named); } else { throw _exception( @@ -2770,7 +2780,8 @@ class _EvaluateVisitor namesByColor.containsKey(result)) { var alternative = BinaryOperationExpression( BinaryOperator.plus, - StringExpression(Interpolation([""], null), quotes: true), + StringExpression(Interpolation([""], interpolation.span), + quotes: true), expression); _warn( "You probably don't mean to use the color value " @@ -2909,9 +2920,8 @@ class _EvaluateVisitor /// Creates a new stack frame with location information from [member] and /// [span]. - Frame _stackFrame(String member, FileSpan? span) => frameForSpan(span, member, - url: - span?.sourceUrl.andThen((url) => _importCache?.humanize(url) ?? url)); + Frame _stackFrame(String member, FileSpan span) => frameForSpan(span, member, + url: span.sourceUrl.andThen((url) => _importCache?.humanize(url) ?? url)); /// Returns a stack trace at the current point. /// @@ -2925,7 +2935,7 @@ class _EvaluateVisitor } /// Emits a warning with the given [message] about the given [span]. - void _warn(String message, FileSpan? span, {bool deprecation = false}) => + void _warn(String message, FileSpan span, {bool deprecation = false}) => _logger.warn(message, span: span, trace: _stackTrace(span), deprecation: deprecation); @@ -2960,20 +2970,15 @@ class _EvaluateVisitor try { return callback(); } on SassFormatException catch (error) { - var errorSpan = error.span; - if (errorSpan == null) rethrow; - - var nodeSpan = nodeWithSpan.span; - if (nodeSpan == null) rethrow; - - var errorText = errorSpan.file.getText(0); - var syntheticFile = nodeSpan.file + var errorText = error.span.file.getText(0); + var span = nodeWithSpan.span; + var syntheticFile = span.file .getText(0) - .replaceRange(nodeSpan.start.offset, nodeSpan.end.offset, errorText); + .replaceRange(span.start.offset, span.end.offset, errorText); var syntheticSpan = - SourceFile.fromString(syntheticFile, url: nodeSpan.file.url).span( - nodeSpan.start.offset + errorSpan.start.offset, - nodeSpan.start.offset + errorSpan.end.offset); + SourceFile.fromString(syntheticFile, url: span.file.url).span( + span.start.offset + error.span.start.offset, + span.start.offset + error.span.end.offset); throw _exception(error.message, syntheticSpan); } } @@ -3006,9 +3011,7 @@ class _EvaluateVisitor try { return callback(); } on SassRuntimeException catch (error) { - var span = error.span; - if (span == null) rethrow; - if (span.text.startsWith("@error")) rethrow; + if (error.span.text.startsWith("@error")) rethrow; throw SassRuntimeException( error.message, nodeWithSpan.span, _stackTrace()); } diff --git a/lib/src/visitor/serialize.dart b/lib/src/visitor/serialize.dart index d73c7ec44..6dc757715 100644 --- a/lib/src/visitor/serialize.dart +++ b/lib/src/visitor/serialize.dart @@ -188,10 +188,7 @@ class _SerializeVisitor return; } - var span = node.span; - if (span != null) { - minimumIndentation = math.min(minimumIndentation, span.start.column); - } + minimumIndentation = math.min(minimumIndentation, node.span.start.column); _writeIndentation(); _writeWithIndent(node.text, minimumIndentation); @@ -392,11 +389,8 @@ class _SerializeVisitor return; } - var span = node.value.span; - if (span != null) { - minimumIndentation = math.min(minimumIndentation, span.start.column); - } - + minimumIndentation = + math.min(minimumIndentation, node.value.span.start.column); _writeWithIndent(value, minimumIndentation); } From 654744f5f475be275d2c236bee69a3846d15b356 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 18 Mar 2021 21:31:10 -0700 Subject: [PATCH 14/19] Fix bogus null assertions --- lib/src/ast/sass/argument_declaration.dart | 19 +- .../ast/sass/expression/binary_operation.dart | 2 +- lib/src/ast/sass/expression/color.dart | 4 +- lib/src/ast/sass/statement/at_root_rule.dart | 2 +- lib/src/ast/sass/statement/at_rule.dart | 2 +- lib/src/ast/sass/statement/content_rule.dart | 2 +- lib/src/ast/sass/statement/debug_rule.dart | 2 +- lib/src/ast/sass/statement/declaration.dart | 2 +- lib/src/ast/sass/statement/each_rule.dart | 2 +- lib/src/ast/sass/statement/extend_rule.dart | 2 +- lib/src/ast/sass/statement/for_rule.dart | 2 +- lib/src/ast/sass/statement/forward_rule.dart | 2 +- lib/src/ast/sass/statement/function_rule.dart | 2 +- lib/src/ast/sass/statement/if_rule.dart | 2 +- lib/src/ast/sass/statement/import_rule.dart | 2 +- lib/src/ast/sass/statement/include_rule.dart | 2 +- lib/src/ast/sass/statement/loud_comment.dart | 2 +- lib/src/ast/sass/statement/media_rule.dart | 2 +- lib/src/ast/sass/statement/mixin_rule.dart | 2 +- .../ast/sass/statement/silent_comment.dart | 2 +- lib/src/ast/sass/statement/style_rule.dart | 2 +- lib/src/ast/sass/statement/stylesheet.dart | 2 +- lib/src/ast/sass/statement/supports_rule.dart | 2 +- lib/src/ast/sass/statement/use_rule.dart | 2 +- .../sass/statement/variable_declaration.dart | 2 +- lib/src/ast/sass/statement/warn_rule.dart | 2 +- lib/src/ast/sass/statement/while_rule.dart | 2 +- lib/src/async_environment.dart | 2 +- lib/src/environment.dart | 4 +- lib/src/executable/compile_stylesheet.dart | 1 + lib/src/executable/watch.dart | 1 + lib/src/extend/extension_store.dart | 199 +++++++++--------- lib/src/extend/functions.dart | 36 ++-- lib/src/parse/css.dart | 2 +- lib/src/parse/stylesheet.dart | 5 +- lib/src/utils.dart | 24 +-- lib/src/value/number/single_unit.dart | 5 +- lib/src/visitor/async_evaluate.dart | 64 ++++-- lib/src/visitor/evaluate.dart | 66 ++++-- lib/src/visitor/interface/expression.dart | 2 +- lib/src/visitor/interface/statement.dart | 48 ++--- 41 files changed, 295 insertions(+), 237 deletions(-) diff --git a/lib/src/ast/sass/argument_declaration.dart b/lib/src/ast/sass/argument_declaration.dart index a3cf142eb..9b7f71584 100644 --- a/lib/src/ast/sass/argument_declaration.dart +++ b/lib/src/ast/sass/argument_declaration.dart @@ -49,19 +49,6 @@ class ArgumentDeclaration implements SassNode { return span.file.span(i + 1, span.end.offset).trim(); } - /// The name of the rest argument as written in the document, without - /// underscores converted to hyphens and including the leading `$`. - /// - /// This isn't particularly efficient, and should only be used for error - /// messages. - String? get originalRestArgument { - if (restArgument == null) return null; - - var text = span.text; - var fromDollar = text.substring(text.lastIndexOf("\$")); - return fromDollar.substring(0, text.indexOf(".")); - } - /// Returns whether this declaration takes no arguments. bool get isEmpty => arguments.isEmpty && restArgument == null; @@ -133,7 +120,11 @@ class ArgumentDeclaration implements SassNode { /// Returns the argument named [name] with a leading `$` and its original /// underscores (which are otherwise converted to hyphens). String _originalArgumentName(String name) { - if (name == restArgument) return originalRestArgument!; + if (name == restArgument) { + var text = span.text; + var fromDollar = text.substring(text.lastIndexOf("\$")); + return fromDollar.substring(0, text.indexOf(".")); + } for (var argument in arguments) { if (argument.name == name) return argument.originalName; diff --git a/lib/src/ast/sass/expression/binary_operation.dart b/lib/src/ast/sass/expression/binary_operation.dart index 864f30d70..5d7e566c6 100644 --- a/lib/src/ast/sass/expression/binary_operation.dart +++ b/lib/src/ast/sass/expression/binary_operation.dart @@ -48,7 +48,7 @@ class BinaryOperationExpression implements Expression { allowsSlash = true; T accept(ExpressionVisitor visitor) => - visitor.visitBinaryOperationExpression(this)!; + visitor.visitBinaryOperationExpression(this); String toString() { var buffer = StringBuffer(); diff --git a/lib/src/ast/sass/expression/color.dart b/lib/src/ast/sass/expression/color.dart index f0299a398..359aef859 100644 --- a/lib/src/ast/sass/expression/color.dart +++ b/lib/src/ast/sass/expression/color.dart @@ -13,9 +13,9 @@ class ColorExpression implements Expression { /// The value of this color. final SassColor value; - FileSpan get span => value.originalSpan!; + final FileSpan span; - ColorExpression(this.value); + ColorExpression(this.value) : span = value.originalSpan!; T accept(ExpressionVisitor visitor) => visitor.visitColorExpression(this); diff --git a/lib/src/ast/sass/statement/at_root_rule.dart b/lib/src/ast/sass/statement/at_root_rule.dart index b3216f23c..ec3e6f069 100644 --- a/lib/src/ast/sass/statement/at_root_rule.dart +++ b/lib/src/ast/sass/statement/at_root_rule.dart @@ -22,7 +22,7 @@ class AtRootRule extends ParentStatement> { AtRootRule(Iterable children, this.span, {this.query}) : super(List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitAtRootRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitAtRootRule(this); String toString() { var buffer = StringBuffer("@at-root "); diff --git a/lib/src/ast/sass/statement/at_rule.dart b/lib/src/ast/sass/statement/at_rule.dart index ff273e3dc..91075e1e3 100644 --- a/lib/src/ast/sass/statement/at_rule.dart +++ b/lib/src/ast/sass/statement/at_rule.dart @@ -22,7 +22,7 @@ class AtRule extends ParentStatement { AtRule(this.name, this.span, {this.value, Iterable? children}) : super(children == null ? null : List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitAtRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitAtRule(this); String toString() { var buffer = StringBuffer("@$name"); diff --git a/lib/src/ast/sass/statement/content_rule.dart b/lib/src/ast/sass/statement/content_rule.dart index d9348f83c..ab9bbd738 100644 --- a/lib/src/ast/sass/statement/content_rule.dart +++ b/lib/src/ast/sass/statement/content_rule.dart @@ -19,7 +19,7 @@ class ContentRule implements Statement { ContentRule(this.arguments, this.span); - T accept(StatementVisitor visitor) => visitor.visitContentRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitContentRule(this); String toString() => arguments.isEmpty ? "@content;" : "@content($arguments);"; diff --git a/lib/src/ast/sass/statement/debug_rule.dart b/lib/src/ast/sass/statement/debug_rule.dart index 75f24f8b4..16c9abad0 100644 --- a/lib/src/ast/sass/statement/debug_rule.dart +++ b/lib/src/ast/sass/statement/debug_rule.dart @@ -19,7 +19,7 @@ class DebugRule implements Statement { DebugRule(this.expression, this.span); - T accept(StatementVisitor visitor) => visitor.visitDebugRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitDebugRule(this); String toString() => "@debug $expression;"; } diff --git a/lib/src/ast/sass/statement/declaration.dart b/lib/src/ast/sass/statement/declaration.dart index 04398536c..f7a889046 100644 --- a/lib/src/ast/sass/statement/declaration.dart +++ b/lib/src/ast/sass/statement/declaration.dart @@ -55,5 +55,5 @@ class Declaration extends ParentStatement { } } - T accept(StatementVisitor visitor) => visitor.visitDeclaration(this)!; + T accept(StatementVisitor visitor) => visitor.visitDeclaration(this); } diff --git a/lib/src/ast/sass/statement/each_rule.dart b/lib/src/ast/sass/statement/each_rule.dart index 16d34a1ba..4efa30076 100644 --- a/lib/src/ast/sass/statement/each_rule.dart +++ b/lib/src/ast/sass/statement/each_rule.dart @@ -26,7 +26,7 @@ class EachRule extends ParentStatement> { : variables = List.unmodifiable(variables), super(List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitEachRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitEachRule(this); String toString() => "@each ${variables.map((variable) => '\$' + variable).join(', ')} in " diff --git a/lib/src/ast/sass/statement/extend_rule.dart b/lib/src/ast/sass/statement/extend_rule.dart index 4f035f2ce..75cc5c1f2 100644 --- a/lib/src/ast/sass/statement/extend_rule.dart +++ b/lib/src/ast/sass/statement/extend_rule.dart @@ -26,7 +26,7 @@ class ExtendRule implements Statement { ExtendRule(this.selector, this.span, {bool optional = false}) : isOptional = optional; - T accept(StatementVisitor visitor) => visitor.visitExtendRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitExtendRule(this); String toString() => "@extend $selector"; } diff --git a/lib/src/ast/sass/statement/for_rule.dart b/lib/src/ast/sass/statement/for_rule.dart index 0ae61b4ae..bde7721b2 100644 --- a/lib/src/ast/sass/statement/for_rule.dart +++ b/lib/src/ast/sass/statement/for_rule.dart @@ -33,7 +33,7 @@ class ForRule extends ParentStatement> { : isExclusive = exclusive, super(List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitForRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitForRule(this); String toString() => "@for \$$variable from $from ${isExclusive ? 'to' : 'through'} $to " diff --git a/lib/src/ast/sass/statement/forward_rule.dart b/lib/src/ast/sass/statement/forward_rule.dart index 04c44eaa1..53ca23f31 100644 --- a/lib/src/ast/sass/statement/forward_rule.dart +++ b/lib/src/ast/sass/statement/forward_rule.dart @@ -106,7 +106,7 @@ class ForwardRule implements Statement { configuration = configuration == null ? const [] : List.unmodifiable(configuration); - T accept(StatementVisitor visitor) => visitor.visitForwardRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitForwardRule(this); String toString() { var buffer = diff --git a/lib/src/ast/sass/statement/function_rule.dart b/lib/src/ast/sass/statement/function_rule.dart index 1fd7f2b53..136194ab1 100644 --- a/lib/src/ast/sass/statement/function_rule.dart +++ b/lib/src/ast/sass/statement/function_rule.dart @@ -19,7 +19,7 @@ class FunctionRule extends CallableDeclaration { {SilentComment? comment}) : super(name, arguments, children, span, comment: comment); - T accept(StatementVisitor visitor) => visitor.visitFunctionRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitFunctionRule(this); String toString() => "@function $name($arguments) {${children.join(' ')}}"; } diff --git a/lib/src/ast/sass/statement/if_rule.dart b/lib/src/ast/sass/statement/if_rule.dart index c8570ebaa..d69cd637e 100644 --- a/lib/src/ast/sass/statement/if_rule.dart +++ b/lib/src/ast/sass/statement/if_rule.dart @@ -34,7 +34,7 @@ class IfRule implements Statement { IfRule(Iterable clauses, this.span, {this.lastClause}) : clauses = List.unmodifiable(clauses); - T accept(StatementVisitor visitor) => visitor.visitIfRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitIfRule(this); String toString() { var first = true; diff --git a/lib/src/ast/sass/statement/import_rule.dart b/lib/src/ast/sass/statement/import_rule.dart index b51b4f54c..fdf1dbaa9 100644 --- a/lib/src/ast/sass/statement/import_rule.dart +++ b/lib/src/ast/sass/statement/import_rule.dart @@ -18,7 +18,7 @@ class ImportRule implements Statement { ImportRule(Iterable imports, this.span) : imports = List.unmodifiable(imports); - T accept(StatementVisitor visitor) => visitor.visitImportRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitImportRule(this); String toString() => "@import ${imports.join(', ')};"; } diff --git a/lib/src/ast/sass/statement/include_rule.dart b/lib/src/ast/sass/statement/include_rule.dart index ad8a25ee2..1a51e6cd0 100644 --- a/lib/src/ast/sass/statement/include_rule.dart +++ b/lib/src/ast/sass/statement/include_rule.dart @@ -38,7 +38,7 @@ class IncludeRule implements Statement, CallableInvocation { IncludeRule(this.name, this.arguments, this.span, {this.namespace, this.content}); - T accept(StatementVisitor visitor) => visitor.visitIncludeRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitIncludeRule(this); String toString() { var buffer = StringBuffer("@include "); diff --git a/lib/src/ast/sass/statement/loud_comment.dart b/lib/src/ast/sass/statement/loud_comment.dart index 63440895b..41e824645 100644 --- a/lib/src/ast/sass/statement/loud_comment.dart +++ b/lib/src/ast/sass/statement/loud_comment.dart @@ -17,7 +17,7 @@ class LoudComment implements Statement { LoudComment(this.text); - T accept(StatementVisitor visitor) => visitor.visitLoudComment(this)!; + T accept(StatementVisitor visitor) => visitor.visitLoudComment(this); String toString() => text.toString(); } diff --git a/lib/src/ast/sass/statement/media_rule.dart b/lib/src/ast/sass/statement/media_rule.dart index 70211ff83..04e4911f4 100644 --- a/lib/src/ast/sass/statement/media_rule.dart +++ b/lib/src/ast/sass/statement/media_rule.dart @@ -21,7 +21,7 @@ class MediaRule extends ParentStatement> { MediaRule(this.query, Iterable children, this.span) : super(List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitMediaRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitMediaRule(this); String toString() => "@media $query {${children.join(" ")}}"; } diff --git a/lib/src/ast/sass/statement/mixin_rule.dart b/lib/src/ast/sass/statement/mixin_rule.dart index cb6fa1e8f..1b3ceb2c3 100644 --- a/lib/src/ast/sass/statement/mixin_rule.dart +++ b/lib/src/ast/sass/statement/mixin_rule.dart @@ -27,7 +27,7 @@ class MixinRule extends CallableDeclaration { {this.hasContent = false, SilentComment? comment}) : super(name, arguments, children, span, comment: comment); - T accept(StatementVisitor visitor) => visitor.visitMixinRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitMixinRule(this); String toString() { var buffer = StringBuffer("@mixin $name"); diff --git a/lib/src/ast/sass/statement/silent_comment.dart b/lib/src/ast/sass/statement/silent_comment.dart index cf1034906..c716ae3c4 100644 --- a/lib/src/ast/sass/statement/silent_comment.dart +++ b/lib/src/ast/sass/statement/silent_comment.dart @@ -35,7 +35,7 @@ class SilentComment implements Statement { SilentComment(this.text, this.span); - T accept(StatementVisitor visitor) => visitor.visitSilentComment(this)!; + T accept(StatementVisitor visitor) => visitor.visitSilentComment(this); String toString() => text; } diff --git a/lib/src/ast/sass/statement/style_rule.dart b/lib/src/ast/sass/statement/style_rule.dart index 8e55ce62a..f6e4f9734 100644 --- a/lib/src/ast/sass/statement/style_rule.dart +++ b/lib/src/ast/sass/statement/style_rule.dart @@ -23,7 +23,7 @@ class StyleRule extends ParentStatement> { StyleRule(this.selector, Iterable children, this.span) : super(List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitStyleRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitStyleRule(this); String toString() => "$selector {${children.join(" ")}}"; } diff --git a/lib/src/ast/sass/statement/stylesheet.dart b/lib/src/ast/sass/statement/stylesheet.dart index b65214411..e7c440fe2 100644 --- a/lib/src/ast/sass/statement/stylesheet.dart +++ b/lib/src/ast/sass/statement/stylesheet.dart @@ -97,7 +97,7 @@ class Stylesheet extends ParentStatement> { factory Stylesheet.parseCss(String contents, {Object? url, Logger? logger}) => CssParser(contents, url: url, logger: logger).parse(); - T accept(StatementVisitor visitor) => visitor.visitStylesheet(this)!; + T accept(StatementVisitor visitor) => visitor.visitStylesheet(this); String toString() => children.join(" "); } diff --git a/lib/src/ast/sass/statement/supports_rule.dart b/lib/src/ast/sass/statement/supports_rule.dart index cabc4cf59..8898a8f1f 100644 --- a/lib/src/ast/sass/statement/supports_rule.dart +++ b/lib/src/ast/sass/statement/supports_rule.dart @@ -19,7 +19,7 @@ class SupportsRule extends ParentStatement> { SupportsRule(this.condition, Iterable children, this.span) : super(List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitSupportsRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitSupportsRule(this); String toString() => "@supports $condition {${children.join(' ')}}"; } diff --git a/lib/src/ast/sass/statement/use_rule.dart b/lib/src/ast/sass/statement/use_rule.dart index d37bbcc46..ffa13a839 100644 --- a/lib/src/ast/sass/statement/use_rule.dart +++ b/lib/src/ast/sass/statement/use_rule.dart @@ -48,7 +48,7 @@ class UseRule implements Statement { factory UseRule.parse(String contents, {Object? url, Logger? logger}) => ScssParser(contents, url: url, logger: logger).parseUseRule(); - T accept(StatementVisitor visitor) => visitor.visitUseRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitUseRule(this); String toString() { var buffer = diff --git a/lib/src/ast/sass/statement/variable_declaration.dart b/lib/src/ast/sass/statement/variable_declaration.dart index 8aa9f494b..4f401a2a5 100644 --- a/lib/src/ast/sass/statement/variable_declaration.dart +++ b/lib/src/ast/sass/statement/variable_declaration.dart @@ -72,7 +72,7 @@ class VariableDeclaration implements Statement { ScssParser(contents, url: url, logger: logger).parseVariableDeclaration(); T accept(StatementVisitor visitor) => - visitor.visitVariableDeclaration(this)!; + visitor.visitVariableDeclaration(this); String toString() { var buffer = StringBuffer("\$"); diff --git a/lib/src/ast/sass/statement/warn_rule.dart b/lib/src/ast/sass/statement/warn_rule.dart index 06f389f5b..2ed5f13c1 100644 --- a/lib/src/ast/sass/statement/warn_rule.dart +++ b/lib/src/ast/sass/statement/warn_rule.dart @@ -19,7 +19,7 @@ class WarnRule implements Statement { WarnRule(this.expression, this.span); - T accept(StatementVisitor visitor) => visitor.visitWarnRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitWarnRule(this); String toString() => "@warn $expression;"; } diff --git a/lib/src/ast/sass/statement/while_rule.dart b/lib/src/ast/sass/statement/while_rule.dart index 5aea12ddb..7aa1b1bd7 100644 --- a/lib/src/ast/sass/statement/while_rule.dart +++ b/lib/src/ast/sass/statement/while_rule.dart @@ -22,7 +22,7 @@ class WhileRule extends ParentStatement> { WhileRule(this.condition, Iterable children, this.span) : super(List.unmodifiable(children)); - T accept(StatementVisitor visitor) => visitor.visitWhileRule(this)!; + T accept(StatementVisitor visitor) => visitor.visitWhileRule(this); String toString() => "@while $condition {${children.join(" ")}}"; } diff --git a/lib/src/async_environment.dart b/lib/src/async_environment.dart index 6bc5c2968..3f03344ea 100644 --- a/lib/src/async_environment.dart +++ b/lib/src/async_environment.dart @@ -953,7 +953,7 @@ class _EnvironmentModule implements Module { forwarded.map((module) => module.variables)), environment._variableNodes.andThen((nodes) => _memberMap( nodes.first, - // `forwarded!` due to dart-lang/sdk#45348 + // dart-lang/sdk#45348 forwarded!.map((module) => module.variableNodes!))), _memberMap(environment._functions.first, forwarded.map((module) => module.functions)), diff --git a/lib/src/environment.dart b/lib/src/environment.dart index 5f3cf99a3..b120e0511 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: 321e236c574d6623660d9a1ce6d9b7507442a318 +// Checksum: 1ef0f32701764f4b160ef045ca162b38653e0504 // // ignore_for_file: unused_import @@ -960,7 +960,7 @@ class _EnvironmentModule implements Module { forwarded.map((module) => module.variables)), environment._variableNodes.andThen((nodes) => _memberMap( nodes.first, - // `forwarded!` due to dart-lang/sdk#45348 + // dart-lang/sdk#45348 forwarded!.map((module) => module.variableNodes!))), _memberMap(environment._functions.first, forwarded.map((module) => module.functions)), diff --git a/lib/src/executable/compile_stylesheet.dart b/lib/src/executable/compile_stylesheet.dart index ed46039d6..1714390c2 100644 --- a/lib/src/executable/compile_stylesheet.dart +++ b/lib/src/executable/compile_stylesheet.dart @@ -121,6 +121,7 @@ Future compileStylesheet(ExecutableOptions options, StylesheetGraph graph, if (options.color) buffer.write('\u001b[32m'); var sourceName = source == null ? 'stdin' : p.prettyUri(p.toUri(source)); + // `destination` is guaranteed to be non-null in update and watch mode. var destinationName = p.prettyUri(p.toUri(destination!)); buffer.write('Compiled $sourceName to $destinationName.'); if (options.color) buffer.write('\u001b[0m'); diff --git a/lib/src/executable/watch.dart b/lib/src/executable/watch.dart index bcd3da179..2a158145d 100644 --- a/lib/src/executable/watch.dart +++ b/lib/src/executable/watch.dart @@ -211,6 +211,7 @@ class _Watcher { return [ for (var entry in typeForPath.entries) + // PathMap always has nullable keys WatchEvent(entry.value, entry.key!) ]; }); diff --git a/lib/src/extend/extension_store.dart b/lib/src/extend/extension_store.dart index 5f283d3fd..f0a87cb7c 100644 --- a/lib/src/extend/extension_store.dart +++ b/lib/src/extend/extension_store.dart @@ -309,7 +309,7 @@ class ExtensionStore { /// Returns `null` if there are no extensions to add. Map>? _extendExistingExtensions(List extensions, - Map>? newExtensions) { + Map> newExtensions) { Map>? additionalExtensions; for (var extension in extensions.toList()) { @@ -355,7 +355,7 @@ class ExtensionStore { } } - if (newExtensions!.containsKey(extension.target)) { + if (newExtensions.containsKey(extension.target)) { additionalExtensions ??= {}; var additionalSources = additionalExtensions.putIfAbsent(extension.target, () => {}); @@ -375,7 +375,7 @@ class ExtensionStore { /// Extend [extensions] using [newExtensions]. void _extendExistingSelectors(Set> selectors, - Map?>? newExtensions) { + Map> newExtensions) { for (var selector in selectors) { var oldValue = selector.value; try { @@ -411,7 +411,7 @@ class ExtensionStore { // An extension map with the same structure as [_extensions] that only // includes extensions from [extensionStores]. - Map?>? newExtensions; + Map>? newExtensions; for (var extensionStore in extensionStores) { if (extensionStore.isEmpty) continue; @@ -423,24 +423,21 @@ class ExtensionStore { // Find existing extensions to extend. var extensionsForTarget = _extensionsByExtender[target]; if (extensionsForTarget != null) { - extensionsToExtend ??= []; - extensionsToExtend!.addAll(extensionsForTarget); + (extensionsToExtend ??= []).addAll(extensionsForTarget); } // Find existing selectors to extend. var selectorsForTarget = _selectors[target]; if (selectorsForTarget != null) { - selectorsToExtend ??= {}; - selectorsToExtend!.addAll(selectorsForTarget); + (selectorsToExtend ??= {}).addAll(selectorsForTarget); } // Add [newSources] to [_extensions]. var existingSources = _extensions[target]; if (existingSources == null) { - _extensions[target] = extensionStore._extensions[target]!; + _extensions[target] = newSources; if (extensionsForTarget != null || selectorsForTarget != null) { - newExtensions ??= {}; - newExtensions![target] = extensionStore._extensions[target]; + (newExtensions ??= {})[target] = newSources; } } else { newSources.forEach((extender, extension) { @@ -450,9 +447,8 @@ class ExtensionStore { existingSources[extender] = extension; if (extensionsForTarget != null || selectorsForTarget != null) { - newExtensions ??= {}; - newExtensions! - .putIfAbsent(target, () => {})! + (newExtensions ??= {}) + .putIfAbsent(target, () => {}) .putIfAbsent(extender, () => extension); } }); @@ -460,25 +456,21 @@ class ExtensionStore { }); } - if (newExtensions == null) return; - - if (extensionsToExtend != null) { + // We can't just naively check for `null` here due to dart-lang/sdk#45348. + newExtensions.andThen((newExtensions) { // We can ignore the return value here because it's only useful for extend // loops, which can't exist across module boundaries. - _extendExistingExtensions( - extensionsToExtend!, - newExtensions - as Map>?); - } + extensionsToExtend.andThen((extensionsToExtend) => + _extendExistingExtensions(extensionsToExtend, newExtensions)); - if (selectorsToExtend != null) { - _extendExistingSelectors(selectorsToExtend!, newExtensions); - } + selectorsToExtend.andThen((selectorsToExtend) => + _extendExistingSelectors(selectorsToExtend, newExtensions)); + }); } /// Extends [list] using [extensions]. SelectorList _extendList(SelectorList list, FileSpan listSpan, - Map?>? extensions, + Map> extensions, [List? mediaQueryContext]) { // This could be written more simply using [List.map], but we want to avoid // any allocations in the common case where no extends apply. @@ -504,7 +496,7 @@ class ExtensionStore { List? _extendComplex( ComplexSelector complex, FileSpan complexSpan, - Map?>? extensions, + Map> extensions, List? mediaQueryContext) { // The complex selectors that each compound selector in [complex.components] // can expand to. @@ -581,12 +573,12 @@ class ExtensionStore { List? _extendCompound( CompoundSelector compound, FileSpan compoundSpan, - Map?>? extensions, + Map> extensions, List? mediaQueryContext, - {bool? inOriginal}) { + {required bool inOriginal}) { // If there's more than one target and they all need to match, we track // which targets are actually extended. - var targetsUsed = _mode == ExtendMode.normal || extensions!.length < 2 + var targetsUsed = _mode == ExtendMode.normal || extensions.length < 2 ? null : {}; @@ -615,7 +607,7 @@ class ExtensionStore { // If [_mode] isn't [ExtendMode.normal] and we didn't use all the targets in // [extensions], extension fails for [compound]. - if (targetsUsed != null && targetsUsed.length != extensions!.length) { + if (targetsUsed != null && targetsUsed.length != extensions.length) { return null; } @@ -652,83 +644,92 @@ class ExtensionStore { // [.w .x.b], // [.w .y .x.z, .y .w .x.z] // ] + // + // And finally flatten them to get: + // + // [ + // .a.b, + // .y .a.z, + // .w .x.b, + // .w .y .x.z, + // .y .w .x.z + // ] var first = _mode != ExtendMode.replace; - var unifiedPaths = paths(options).map((path) { - List>? complexes; - if (first) { - // The first path is always the original selector. We can't just - // return [compound] directly because pseudo selectors may be - // modified, but we don't have to do any unification. - first = false; - complexes = [ - [ - CompoundSelector(path.expand((extender) { - assert(extender.selector.components.length == 1); - return (extender.selector.components.last as CompoundSelector) - .components; - })) - ] - ]; - } else { - var toUnify = QueueList>(); - List? originals; - for (var extender in path) { - if (extender.isOriginal) { - originals ??= []; - originals.addAll( - (extender.selector.components.last as CompoundSelector) - .components); + var result = paths(options) + .map((path) { + List>? complexes; + if (first) { + // The first path is always the original selector. We can't just + // return [compound] directly because pseudo selectors may be + // modified, but we don't have to do any unification. + first = false; + complexes = [ + [ + CompoundSelector(path.expand((extender) { + assert(extender.selector.components.length == 1); + return (extender.selector.components.last as CompoundSelector) + .components; + })) + ] + ]; } else { - toUnify.add(extender.selector.components); - } - } + var toUnify = QueueList>(); + List? originals; + for (var extender in path) { + if (extender.isOriginal) { + originals ??= []; + originals.addAll( + (extender.selector.components.last as CompoundSelector) + .components); + } else { + toUnify.add(extender.selector.components); + } + } - if (originals != null) { - toUnify.addFirst([CompoundSelector(originals)]); - } + if (originals != null) { + toUnify.addFirst([CompoundSelector(originals)]); + } - complexes = unifyComplex(toUnify); - if (complexes == null) return null; - } + complexes = unifyComplex(toUnify); + if (complexes == null) return null; + } - var lineBreak = false; - for (var extender in path) { - extender.assertCompatibleMediaContext(mediaQueryContext); - lineBreak = lineBreak || extender.selector.lineBreak; - } + var lineBreak = false; + for (var extender in path) { + extender.assertCompatibleMediaContext(mediaQueryContext); + lineBreak = lineBreak || extender.selector.lineBreak; + } - return complexes - .map( - (components) => ComplexSelector(components, lineBreak: lineBreak)) - .toList(); - }); + return complexes + .map((components) => + ComplexSelector(components, lineBreak: lineBreak)) + .toList(); + }) + .whereNotNull() + .expand((l) => l) + .toList(); // If we're preserving the original selector, mark the first unification as // such so [_trim] doesn't get rid of it. var isOriginal = (ComplexSelector _) => false; - if (inOriginal! && _mode != ExtendMode.replace) { - var original = unifiedPaths.first!.first; + if (inOriginal && _mode != ExtendMode.replace) { + var original = result.first; isOriginal = (complex) => complex == original; } - return _trim( - unifiedPaths - .where((complexes) => complexes != null) - .expand((l) => l!) - .toList(), - isOriginal); + return _trim(result, isOriginal); } Iterable>? _extendSimple( SimpleSelector simple, FileSpan simpleSpan, - Map?>? extensions, + Map> extensions, List? mediaQueryContext, Set? targetsUsed) { // Extends [simple] without extending the contents of any selector pseudos // it contains. List? withoutPseudo(SimpleSelector simple) { - var extensionsForSimple = extensions![simple]; + var extensionsForSimple = extensions[simple]; if (extensionsForSimple == null) return null; targetsUsed?.add(simple); @@ -770,14 +771,21 @@ class ExtensionStore { /// Extends [pseudo] using [extensions], and returns a list of resulting /// pseudo selectors. + /// + /// This requires that [pseudo] have a selector argument. List? _extendPseudo( PseudoSelector pseudo, FileSpan pseudoSpan, - Map?>? extensions, + Map> extensions, List? mediaQueryContext) { - var extended = _extendList( - pseudo.selector!, pseudoSpan, extensions, mediaQueryContext); - if (identical(extended, pseudo.selector)) return null; + var selector = pseudo.selector; + if (selector == null) { + throw ArgumentError("Selector $pseudo must have a selector argument."); + } + + var extended = + _extendList(selector, pseudoSpan, extensions, mediaQueryContext); + if (identical(extended, selector)) return null; // For `:not()`, we usually want to get rid of any complex selectors because // that will cause the selector to fail to parse on all browsers at time of @@ -786,8 +794,7 @@ class ExtensionStore { // either way we aren't breaking anything that isn't already broken. Iterable complexes = extended.components; if (pseudo.normalizedName == "not" && - !pseudo.selector!.components - .any((complex) => complex.components.length > 1) && + !selector.components.any((complex) => complex.components.length > 1) && extended.components.any((complex) => complex.components.length == 1)) { complexes = extended.components .where((complex) => complex.components.length <= 1); @@ -800,7 +807,8 @@ class ExtensionStore { if (compound.components.length != 1) return [complex]; if (compound.components.first is! PseudoSelector) return [complex]; var innerPseudo = compound.components.first as PseudoSelector; - if (innerPseudo.selector == null) return [complex]; + var innerSelector = innerPseudo.selector; + if (innerSelector == null) return [complex]; switch (pseudo.normalizedName) { case 'not': @@ -811,7 +819,7 @@ class ExtensionStore { // supporting it properly would make this code and the code calling it // a lot more complicated, so it's not supported for now. if (innerPseudo.normalizedName != 'matches') return []; - return innerPseudo.selector!.components; + return innerSelector.components; case 'matches': case 'any': @@ -823,7 +831,7 @@ class ExtensionStore { // more complex cases that likely aren't worth the pain. if (innerPseudo.name != pseudo.name) return []; if (innerPseudo.argument != pseudo.argument) return []; - return innerPseudo.selector!.components; + return innerSelector.components; case 'has': case 'host': @@ -842,8 +850,7 @@ class ExtensionStore { // Older browsers support `:not`, but only with a single complex selector. // In order to support those browsers, we break up the contents of a `:not` // unless it originally contained a selector list. - if (pseudo.normalizedName == 'not' && - pseudo.selector!.components.length == 1) { + if (pseudo.normalizedName == 'not' && selector.components.length == 1) { var result = complexes .map((complex) => pseudo.withSelector(SelectorList([complex]))) .toList(); diff --git a/lib/src/extend/functions.dart b/lib/src/extend/functions.dart index ec6123e6e..410aa8739 100644 --- a/lib/src/extend/functions.dart +++ b/lib/src/extend/functions.dart @@ -36,23 +36,23 @@ List>? unifyComplex( List? unifiedBase; for (var complex in complexes) { var base = complex.last; - if (base is CompoundSelector) { - if (unifiedBase == null) { - unifiedBase = base.components; - } else { - for (var simple in base.components) { - unifiedBase = simple.unify(unifiedBase!); - if (unifiedBase == null) return null; - } - } + if (base is! CompoundSelector) return null; + + assert(base.components.isNotEmpty); + if (unifiedBase == null) { + unifiedBase = base.components; } else { - return null; + for (var simple in base.components) { + unifiedBase = simple.unify(unifiedBase!); // dart-lang/sdk#45348 + if (unifiedBase == null) return null; + } } } var complexesWithoutBases = complexes .map((complex) => complex.sublist(0, complex.length - 1)) .toList(); + // By the time we make it here, [unifiedBase] must be non-null. complexesWithoutBases.last.add(CompoundSelector(unifiedBase!)); return weave(complexesWithoutBases); } @@ -63,13 +63,14 @@ List>? unifyComplex( /// If no such selector can be produced, returns `null`. CompoundSelector? unifyCompound( List compound1, List compound2) { - List? result = compound2; + var result = compound2; for (var simple in compound1) { - result = simple.unify(result!); - if (result == null) return null; + var unified = simple.unify(result); + if (unified == null) return null; + result = unified; } - return CompoundSelector(result!); + return CompoundSelector(result); } /// Returns a [SimpleSelector] that matches only elements that are matched by @@ -726,7 +727,12 @@ bool _simpleIsSuperselectorOfCompound( bool _selectorPseudoIsSuperselector( PseudoSelector pseudo1, CompoundSelector compound2, {Iterable? parents}) { - var selector1 = pseudo1.selector!; + var selector1_ = pseudo1.selector; + if (selector1_ == null) { + throw ArgumentError("Selector $pseudo1 must have a selector argument."); + } + var selector1 = selector1_; // dart-lang/sdk#45348 + switch (pseudo1.normalizedName) { case 'matches': case 'any': diff --git a/lib/src/parse/css.dart b/lib/src/parse/css.dart index 2a16d2cf6..e0295cc29 100644 --- a/lib/src/parse/css.dart +++ b/lib/src/parse/css.dart @@ -105,7 +105,7 @@ class CssParser extends ScssParser { Expression identifierLike() { var start = scanner.state; var identifier = interpolatedIdentifier(); - var plain = identifier.asPlain!; + var plain = identifier.asPlain!; // CSS doesn't allow non-plain identifiers var specialFunction = trySpecialFunction(plain.toLowerCase(), start); if (specialFunction != null) return specialFunction; diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 1b04aee8c..1c6a0b8cc 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -3350,8 +3350,9 @@ relase. For details, see http://bit.ly/moz-document. whitespace(); buffer.add(_expressionUntilComparison()); - if ((next == $langle || next == $rangle) && scanner.scanChar(next!)) { - // dart-lang/sdk#45356 + if ((next == $langle || next == $rangle) && + // dart-lang/sdk#45356 + scanner.scanChar(next!)) { buffer.writeCharCode($space); buffer.writeCharCode(next); if (scanner.scanChar($equal)) buffer.writeCharCode($equal); diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 6bd511c44..3781fe1ff 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -312,25 +312,17 @@ List longestCommonSubsequence(List list1, List list2, return backtrack(list1.length - 1, list2.length - 1); } -/// Removes and returns the first value in [list] that matches [test]. +/// Removes the first value in [list] that matches [test]. /// -/// By default, throws a [StateError] if no value matches. If [orElse] is -/// passed, its return value is used instead. -T removeFirstWhere(List list, bool test(T value), {T orElse()?}) { - T? toRemove; - for (var element in list) { - if (!test(element)) continue; - toRemove = element; - break; +/// If [orElse] is passed, calls it if no value matches. +void removeFirstWhere(List list, bool test(T value), {void orElse()?}) { + for (var i = 0; i < list.length; i++) { + if (!test(list[i])) continue; + list.removeAt(i); + return; } - if (toRemove == null) { - if (orElse != null) return orElse(); - throw StateError("No such element."); - } else { - list.remove(toRemove); - return toRemove; - } + if (orElse != null) orElse(); } /// Like [Map.addAll], but for two-layer maps. diff --git a/lib/src/value/number/single_unit.dart b/lib/src/value/number/single_unit.dart index 7bc08760a..e58517627 100644 --- a/lib/src/value/number/single_unit.dart +++ b/lib/src/value/number/single_unit.dart @@ -98,14 +98,13 @@ class SingleUnitSassNumber extends SassNumber { num value, List otherNumerators, List otherDenominators) { var newNumerators = otherNumerators; var mutableOtherDenominators = otherDenominators.toList(); - removeFirstWhere(mutableOtherDenominators, (denominator) { - var factor = conversionFactor(denominator!, _unit); + removeFirstWhere(mutableOtherDenominators, (denominator) { + var factor = conversionFactor(denominator, _unit); if (factor == null) return false; value *= factor; return true; }, orElse: () { newNumerators = [_unit, ...newNumerators]; - return null; }); return SassNumber.withUnits(value, diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 8bdefdfe5..fb1a433df 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -897,7 +897,14 @@ class _EvaluateVisitor var included = []; while (parent is! CssStylesheet) { if (!query.excludes(parent)) included.add(parent); - parent = parent.parent!; + + var grandparent = parent.parent; + if (grandparent == null) { + throw StateError( + "CssNodes must have a CssStylesheet transitive parent node."); + } + + parent = grandparent; } var root = _trimIncluded(included); @@ -952,10 +959,22 @@ class _EvaluateVisitor for (var i = 0; i < nodes.length; i++) { while (parent != nodes[i]) { innermostContiguous = null; - parent = parent.parent!; + + var grandparent = parent.parent; + if (grandparent == null) { + throw ArgumentError( + "Expected ${nodes[i]} to be an ancestor of $this."); + } + + parent = grandparent; } innermostContiguous ??= i; - parent = parent.parent!; + + var grandparent = parent.parent; + if (grandparent == null) { + throw ArgumentError("Expected ${nodes[i]} to be an ancestor of $this."); + } + parent = grandparent; } if (parent != _root) return _root; @@ -1065,7 +1084,7 @@ class _EvaluateVisitor _parent.addChild(ModifiableCssDeclaration(name, cssValue, node.span, parsedAsCustomProperty: node.isCustomProperty, valueSpanForMap: - _sourceMap ? null : _expressionNode(node.value!).span)); + _sourceMap ? null : node.value.andThen(_expressionNode)?.span)); } else if (name.value.startsWith('--') && cssValue != null) { throw _exception( "Custom property values may not be empty.", cssValue.span); @@ -1180,9 +1199,10 @@ class _EvaluateVisitor var name = await _interpolationToValue(node.name); var value = await node.value.andThen((value) => - _interpolationToValue(value, trim: true, warnForColor: true))!; + _interpolationToValue(value, trim: true, warnForColor: true)); - if (node.children == null) { + var children = node.children; + if (children == null) { _parent.addChild( ModifiableCssAtRule(name, node.span, childless: true, value: value)); return null; @@ -1200,7 +1220,7 @@ class _EvaluateVisitor () async { var styleRule = _styleRule; if (styleRule == null || _inKeyframes) { - for (var child in node.children!) { + for (var child in children) { await child.accept(this); } } else { @@ -1209,7 +1229,7 @@ class _EvaluateVisitor // // For example, "a {@foo {b: c}}" should produce "@foo {a {b: c}}". await _withParent(styleRule.copyWithoutChildren(), () async { - for (var child in node.children!) { + for (var child in children) { await child.accept(this); } }, scopeWhen: false); @@ -1550,12 +1570,15 @@ class _EvaluateVisitor // here should be mirrored there. var url = await _interpolationToValue(import.url); - var supports = await import.supports.andThen((supports) async => CssValue( - "supports(${supports is SupportsDeclaration ? "${await _evaluateToCss(supports.name)}: " - "${await _evaluateToCss(supports.value)}" : await supports.andThen(_visitSupportsCondition)})", - supports.span))!; + var supports = await import.supports.andThen((supports) async { + var arg = supports is SupportsDeclaration + ? "${await _evaluateToCss(supports.name)}: " + "${await _evaluateToCss(supports.value)}" + : await supports.andThen(_visitSupportsCondition); + return CssValue("supports($arg)", supports.span); + }); var rawMedia = import.media; - var mediaQuery = await rawMedia.andThen(_visitMediaQueries)!; + var mediaQuery = await rawMedia.andThen(_visitMediaQueries); var node = ModifiableCssImport(url, import.span, supports: supports, media: mediaQuery); @@ -2260,10 +2283,11 @@ class _EvaluateVisitor buffer.write(await _evaluateToCss(argument)); } - var rest = await arguments.rest?.accept(this); - if (rest != null) { + var restArg = arguments.rest; + if (restArg != null) { + var rest = await restArg.accept(this); if (!first) buffer.write(", "); - buffer.write(_serialize(rest, arguments.rest!)); + buffer.write(_serialize(rest, restArg)); } buffer.writeCharCode($rparen); @@ -2889,13 +2913,19 @@ class _EvaluateVisitor var parent = _parent; if (through != null) { while (through(parent)) { - parent = parent.parent!; + var grandparent = parent.parent; + if (grandparent == null) { + throw ArgumentError( + "through() must return false for at least one parent of $node."); + } + parent = grandparent; } // If the parent has a (visible) following sibling, we shouldn't add to // the parent. Instead, we should create a copy and add it after the // interstitial sibling. if (parent.hasFollowingSibling) { + // A node with siblings must have a parent var grandparent = parent.parent!; parent = parent.copyWithoutChildren(); grandparent.addChild(parent); diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index e2a189fa9..76d4747fa 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: 6c0d909812330091658fa30b673c5f529917e30b +// Checksum: f094bb66f285333969530af129201a999636e72f // // ignore_for_file: unused_import @@ -901,7 +901,14 @@ class _EvaluateVisitor var included = []; while (parent is! CssStylesheet) { if (!query.excludes(parent)) included.add(parent); - parent = parent.parent!; + + var grandparent = parent.parent; + if (grandparent == null) { + throw StateError( + "CssNodes must have a CssStylesheet transitive parent node."); + } + + parent = grandparent; } var root = _trimIncluded(included); @@ -956,10 +963,22 @@ class _EvaluateVisitor for (var i = 0; i < nodes.length; i++) { while (parent != nodes[i]) { innermostContiguous = null; - parent = parent.parent!; + + var grandparent = parent.parent; + if (grandparent == null) { + throw ArgumentError( + "Expected ${nodes[i]} to be an ancestor of $this."); + } + + parent = grandparent; } innermostContiguous ??= i; - parent = parent.parent!; + + var grandparent = parent.parent; + if (grandparent == null) { + throw ArgumentError("Expected ${nodes[i]} to be an ancestor of $this."); + } + parent = grandparent; } if (parent != _root) return _root; @@ -1069,7 +1088,7 @@ class _EvaluateVisitor _parent.addChild(ModifiableCssDeclaration(name, cssValue, node.span, parsedAsCustomProperty: node.isCustomProperty, valueSpanForMap: - _sourceMap ? null : _expressionNode(node.value!).span)); + _sourceMap ? null : node.value.andThen(_expressionNode)?.span)); } else if (name.value.startsWith('--') && cssValue != null) { throw _exception( "Custom property values may not be empty.", cssValue.span); @@ -1182,9 +1201,10 @@ class _EvaluateVisitor var name = _interpolationToValue(node.name); var value = node.value.andThen((value) => - _interpolationToValue(value, trim: true, warnForColor: true))!; + _interpolationToValue(value, trim: true, warnForColor: true)); - if (node.children == null) { + var children = node.children; + if (children == null) { _parent.addChild( ModifiableCssAtRule(name, node.span, childless: true, value: value)); return null; @@ -1201,7 +1221,7 @@ class _EvaluateVisitor _withParent(ModifiableCssAtRule(name, node.span, value: value), () { var styleRule = _styleRule; if (styleRule == null || _inKeyframes) { - for (var child in node.children!) { + for (var child in children) { child.accept(this); } } else { @@ -1210,7 +1230,7 @@ class _EvaluateVisitor // // For example, "a {@foo {b: c}}" should produce "@foo {a {b: c}}". _withParent(styleRule.copyWithoutChildren(), () { - for (var child in node.children!) { + for (var child in children) { child.accept(this); } }, scopeWhen: false); @@ -1548,12 +1568,15 @@ class _EvaluateVisitor // here should be mirrored there. var url = _interpolationToValue(import.url); - var supports = import.supports.andThen((supports) => CssValue( - "supports(${supports is SupportsDeclaration ? "${_evaluateToCss(supports.name)}: " - "${_evaluateToCss(supports.value)}" : supports.andThen(_visitSupportsCondition)})", - supports.span))!; + var supports = import.supports.andThen((supports) { + var arg = supports is SupportsDeclaration + ? "${_evaluateToCss(supports.name)}: " + "${_evaluateToCss(supports.value)}" + : supports.andThen(_visitSupportsCondition); + return CssValue("supports($arg)", supports.span); + }); var rawMedia = import.media; - var mediaQuery = rawMedia.andThen(_visitMediaQueries)!; + var mediaQuery = rawMedia.andThen(_visitMediaQueries); var node = ModifiableCssImport(url, import.span, supports: supports, media: mediaQuery); @@ -2245,10 +2268,11 @@ class _EvaluateVisitor buffer.write(_evaluateToCss(argument)); } - var rest = arguments.rest?.accept(this); - if (rest != null) { + var restArg = arguments.rest; + if (restArg != null) { + var rest = restArg.accept(this); if (!first) buffer.write(", "); - buffer.write(_serialize(rest, arguments.rest!)); + buffer.write(_serialize(rest, restArg)); } buffer.writeCharCode($rparen); @@ -2866,13 +2890,19 @@ class _EvaluateVisitor var parent = _parent; if (through != null) { while (through(parent)) { - parent = parent.parent!; + var grandparent = parent.parent; + if (grandparent == null) { + throw ArgumentError( + "through() must return false for at least one parent of $node."); + } + parent = grandparent; } // If the parent has a (visible) following sibling, we shouldn't add to // the parent. Instead, we should create a copy and add it after the // interstitial sibling. if (parent.hasFollowingSibling) { + // A node with siblings must have a parent var grandparent = parent.parent!; parent = parent.copyWithoutChildren(); grandparent.addChild(parent); diff --git a/lib/src/visitor/interface/expression.dart b/lib/src/visitor/interface/expression.dart index 6c78dd3ef..6ca7d3801 100644 --- a/lib/src/visitor/interface/expression.dart +++ b/lib/src/visitor/interface/expression.dart @@ -8,7 +8,7 @@ import '../../ast/sass.dart'; /// /// [visitors]: https://en.wikipedia.org/wiki/Visitor_pattern abstract class ExpressionVisitor { - T? visitBinaryOperationExpression(BinaryOperationExpression node); + T visitBinaryOperationExpression(BinaryOperationExpression node); T visitBooleanExpression(BooleanExpression node); T visitColorExpression(ColorExpression node); T visitFunctionExpression(FunctionExpression node); diff --git a/lib/src/visitor/interface/statement.dart b/lib/src/visitor/interface/statement.dart index 2f12dc196..eaf387d82 100644 --- a/lib/src/visitor/interface/statement.dart +++ b/lib/src/visitor/interface/statement.dart @@ -8,31 +8,31 @@ import '../../ast/sass.dart'; /// /// [visitors]: https://en.wikipedia.org/wiki/Visitor_pattern abstract class StatementVisitor { - T? visitAtRootRule(AtRootRule node); - T? visitAtRule(AtRule node); + T visitAtRootRule(AtRootRule node); + T visitAtRule(AtRule node); T visitContentBlock(ContentBlock node); - T? visitContentRule(ContentRule node); - T? visitDebugRule(DebugRule node); - T? visitDeclaration(Declaration node); - T? visitEachRule(EachRule node); + T visitContentRule(ContentRule node); + T visitDebugRule(DebugRule node); + T visitDeclaration(Declaration node); + T visitEachRule(EachRule node); T visitErrorRule(ErrorRule node); - T? visitExtendRule(ExtendRule node); - T? visitForRule(ForRule node); - T? visitForwardRule(ForwardRule node); - T? visitFunctionRule(FunctionRule node); - T? visitIfRule(IfRule node); - T? visitImportRule(ImportRule node); - T? visitIncludeRule(IncludeRule node); - T? visitLoudComment(LoudComment node); - T? visitMediaRule(MediaRule node); - T? visitMixinRule(MixinRule node); + T visitExtendRule(ExtendRule node); + T visitForRule(ForRule node); + T visitForwardRule(ForwardRule node); + T visitFunctionRule(FunctionRule node); + T visitIfRule(IfRule node); + T visitImportRule(ImportRule node); + T visitIncludeRule(IncludeRule node); + T visitLoudComment(LoudComment node); + T visitMediaRule(MediaRule node); + T visitMixinRule(MixinRule node); T visitReturnRule(ReturnRule node); - T? visitSilentComment(SilentComment node); - T? visitStyleRule(StyleRule node); - T? visitStylesheet(Stylesheet node); - T? visitSupportsRule(SupportsRule node); - T? visitUseRule(UseRule node); - T? visitVariableDeclaration(VariableDeclaration node); - T? visitWarnRule(WarnRule node); - T? visitWhileRule(WhileRule node); + T visitSilentComment(SilentComment node); + T visitStyleRule(StyleRule node); + T visitStylesheet(Stylesheet node); + T visitSupportsRule(SupportsRule node); + T visitUseRule(UseRule node); + T visitVariableDeclaration(VariableDeclaration node); + T visitWarnRule(WarnRule node); + T visitWhileRule(WhileRule node); } From 102d3cedc23e8c0a38cb01725d07fc773f40d070 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 18 Mar 2021 21:43:02 -0700 Subject: [PATCH 15/19] Make the IO interface align with the VM API --- bin/sass.dart | 6 +++++- lib/src/executable/watch.dart | 6 +++++- lib/src/io/interface.dart | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/bin/sass.dart b/bin/sass.dart index a456d6c81..e493fefda 100644 --- a/bin/sass.dart +++ b/bin/sass.dart @@ -88,7 +88,11 @@ Future main(List args) async { if (exitCode != 66) exitCode = 65; if (options.stopOnError) return; } on FileSystemException catch (error, stackTrace) { - printError("Error reading ${p.relative(error.path)}: ${error.message}.", + var path = error.path; + printError( + path == null + ? error.message + : "Error reading ${p.relative(path)}: ${error.message}.", options.trace ? stackTrace : null); // Error 66 indicates no input. diff --git a/lib/src/executable/watch.dart b/lib/src/executable/watch.dart index 2a158145d..36e74fa1c 100644 --- a/lib/src/executable/watch.dart +++ b/lib/src/executable/watch.dart @@ -82,7 +82,11 @@ class _Watcher { exitCode = 65; return false; } on FileSystemException catch (error, stackTrace) { - _printError("Error reading ${p.relative(error.path)}: ${error.message}.", + var path = error.path; + _printError( + path == null + ? error.message + : "Error reading ${p.relative(path)}: ${error.message}.", stackTrace); exitCode = 66; return false; diff --git a/lib/src/io/interface.dart b/lib/src/io/interface.dart index 738ce656c..0ee35bc53 100644 --- a/lib/src/io/interface.dart +++ b/lib/src/io/interface.dart @@ -22,7 +22,7 @@ class Stderr { /// An error thrown by [readFile]. class FileSystemException { String get message => throw ''; - String get path => throw ''; + String? get path => throw ''; } /// The standard error for the current process. From af0f25cb0e6d67fcdf6dcb892ebe36b55a5844ec Mon Sep 17 00:00:00 2001 From: awjin Date: Fri, 9 Apr 2021 13:19:35 -0700 Subject: [PATCH 16/19] Fix broken tests --- bin/sass.dart | 2 +- lib/src/ast/css/modifiable/declaration.dart | 2 +- lib/src/ast/selector/list.dart | 2 +- lib/src/configuration.dart | 7 +++ lib/src/exception.dart | 5 +- lib/src/executable/options.dart | 4 +- lib/src/io/node.dart | 8 ++- lib/src/node.dart | 7 ++- lib/src/node/function.dart | 4 +- lib/src/node/value/color.dart | 5 +- lib/src/node/value/list.dart | 5 +- lib/src/node/value/map.dart | 6 +- lib/src/node/value/number.dart | 5 +- lib/src/node/value/string.dart | 5 +- lib/src/parse/stylesheet.dart | 5 +- lib/src/value/number.dart | 12 +--- lib/src/visitor/async_evaluate.dart | 66 +++++++++++++------- lib/src/visitor/evaluate.dart | 68 ++++++++++++++------- package.json | 2 +- package/package.json | 2 +- test/cli/shared/source_maps.dart | 8 +-- test/ensure_npm_package.dart | 2 +- test/hybrid.dart | 8 +-- test/node_api/api.dart | 2 +- test/node_api/source_map_test.dart | 16 ++--- test/node_api/utils.dart | 6 +- test/source_map_test.dart | 5 +- test/utils.dart | 4 +- 28 files changed, 169 insertions(+), 104 deletions(-) diff --git a/bin/sass.dart b/bin/sass.dart index e493fefda..b49cdd4e2 100644 --- a/bin/sass.dart +++ b/bin/sass.dart @@ -121,7 +121,7 @@ Future main(List args) async { /// Loads and returns the current version of Sass. Future _loadVersion() async { - if (bool.hasEnvironment('version')) { + if (const bool.hasEnvironment('version')) { var version = const String.fromEnvironment('version'); if (const bool.fromEnvironment('node')) { version += " compiled with dart2js " diff --git a/lib/src/ast/css/modifiable/declaration.dart b/lib/src/ast/css/modifiable/declaration.dart index c6564fb07..5be720b18 100644 --- a/lib/src/ast/css/modifiable/declaration.dart +++ b/lib/src/ast/css/modifiable/declaration.dart @@ -25,7 +25,7 @@ class ModifiableCssDeclaration extends ModifiableCssNode ModifiableCssDeclaration(this.name, this.value, this.span, {required bool parsedAsCustomProperty, FileSpan? valueSpanForMap}) : parsedAsCustomProperty = parsedAsCustomProperty, - valueSpanForMap = valueSpanForMap ?? span { + valueSpanForMap = valueSpanForMap ?? value.span { if (parsedAsCustomProperty) { if (!isCustomProperty) { throw ArgumentError( diff --git a/lib/src/ast/selector/list.dart b/lib/src/ast/selector/list.dart index 5bb42f9a4..e8670e3a7 100644 --- a/lib/src/ast/selector/list.dart +++ b/lib/src/ast/selector/list.dart @@ -150,7 +150,7 @@ class SelectorList extends Selector { component is CompoundSelector && component.components.any((simple) { if (simple is ParentSelector) return true; - if (simple is! PseudoSelector) return true; + if (simple is! PseudoSelector) return false; var selector = simple.selector; return selector != null && selector._containsParentSelector; })); diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index 9081ef802..1522781ad 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -68,6 +68,13 @@ class Configuration { /// Returns a copy of [this] with the given [values] map. Configuration _withValues(Map values) => Configuration.implicit(values); + + String toString() => + "(" + + values.entries + .map((entry) => "\$${entry.key}: ${entry.value}") + .join(", ") + + ")"; } /// A [Configuratoin] that was created with an explicit `with` clause of a diff --git a/lib/src/exception.dart b/lib/src/exception.dart index d034f4d35..de2ddadb1 100644 --- a/lib/src/exception.dart +++ b/lib/src/exception.dart @@ -23,8 +23,9 @@ class SassException extends SourceSpanException { SassException(String message, FileSpan span) : super(message, span); String toString({Object? color}) { - var buffer = StringBuffer("Error: $message"); - span.highlight(color: color).andThen(buffer.write); + var buffer = StringBuffer() + ..writeln("Error: $message") + ..write(span.highlight(color: color)); for (var frame in trace.toString().split("\n")) { if (frame.isEmpty) continue; diff --git a/lib/src/executable/options.dart b/lib/src/executable/options.dart index 96e508a56..985b4e0e2 100644 --- a/lib/src/executable/options.dart +++ b/lib/src/executable/options.dart @@ -150,7 +150,7 @@ class ExecutableOptions { /// /// This may be `null`, indicating that this should be determined by each /// stylesheet's extension. - bool get indented => _ifParsed('indented') as bool; + bool? get indented => _ifParsed('indented') as bool?; /// Whether to use ANSI terminal colors. bool get color => _options.wasParsed('color') @@ -357,7 +357,7 @@ class ExecutableOptions { // A colon 2 characters after the separator may also be a Windows // drive letter. if (nextColon == i + 2 && _isWindowsPath(argument, i + 1)) { - nextColon = argument.indexOf(':', nextColon); + nextColon = argument.indexOf(':', nextColon + 1); } if (nextColon != -1) _fail('"$argument" may only contain one ":".'); diff --git a/lib/src/io/node.dart b/lib/src/io/node.dart index bf1689766..f8ac7f129 100644 --- a/lib/src/io/node.dart +++ b/lib/src/io/node.dart @@ -190,7 +190,13 @@ T _systemErrorToFileSystemException(T callback()) { final stderr = Stderr(process.stderr); -bool get hasTerminal => process.stdout.isTTY; +/// We can't use [process.stdout.isTTY] from `node_interop` because of +/// pulyaevskiy/node-interop#93: it declares `isTTY` as always non-nullably +/// available, but in practice it's undefined if stdout isn't a TTY. +@JS('process.stdout.isTTY') +external bool? get isTTY; + +bool get hasTerminal => isTTY == true; bool get isWindows => process.platform == 'win32'; diff --git a/lib/src/node.dart b/lib/src/node.dart index d4b13d88f..89c8e6dfa 100644 --- a/lib/src/node.dart +++ b/lib/src/node.dart @@ -235,7 +235,7 @@ List _parseFunctions(RenderOptions options, DateTime start, } else { result.add(AsyncBuiltInCallable.parsed(tuple.item1, tuple.item2, (arguments) async { - var completer = Completer(); + var completer = Completer(); var jsArguments = [ ...arguments.map(wrapValue), allowInterop(([Object? result]) => completer.complete(result)) @@ -261,8 +261,9 @@ NodeImporter _parseImporter(RenderOptions options, DateTime start) { importers = [options.importer as JSFunction]; } - late RenderContext context; - if (importers.isNotEmpty) context = _contextWithOptions(options, start); + var context = importers.isNotEmpty + ? _contextWithOptions(options, start) + : const Object(); var fiber = options.fiber; if (fiber != null) { diff --git a/lib/src/node/function.dart b/lib/src/node/function.dart index 689de737b..20153d7f6 100644 --- a/lib/src/node/function.dart +++ b/lib/src/node/function.dart @@ -10,7 +10,7 @@ class JSFunction implements Function { // Note that this just invokes the function with the given arguments, rather // than calling `Function.prototype.call()`. See sdk#31271. - external Object call([Object? arg1, Object? arg2, Object? arg3]); + external Object? call([Object? arg1, Object? arg2, Object? arg3]); - external Object apply(Object thisArg, [List? args]); + external Object? apply(Object thisArg, [List? args]); } diff --git a/lib/src/node/value/color.dart b/lib/src/node/value/color.dart index 6fa7e99aa..859d99f1b 100644 --- a/lib/src/node/value/color.dart +++ b/lib/src/node/value/color.dart @@ -22,7 +22,7 @@ Object newNodeSassColor(SassColor value) => /// The JS constructor for the `sass.types.Color` class. final Function colorConstructor = createClass('SassColor', - (_NodeSassColor thisArg, num redOrArgb, + (_NodeSassColor thisArg, num? redOrArgb, [num? green, num? blue, num? alpha, SassColor? dartValue]) { if (dartValue != null) { thisArg.dartValue = dartValue; @@ -43,7 +43,8 @@ final Function colorConstructor = createClass('SassColor', green = (argb >> 8) % 0x100; blue = argb % 0x100; } else { - red = redOrArgb; + // Either [dartValue] or [redOrArgb] must be passed. + red = redOrArgb!; } thisArg.dartValue = SassColor.rgb( diff --git a/lib/src/node/value/list.dart b/lib/src/node/value/list.dart index 4a3a7f11f..836fcacb5 100644 --- a/lib/src/node/value/list.dart +++ b/lib/src/node/value/list.dart @@ -22,10 +22,11 @@ Object newNodeSassList(SassList value) => /// The JS constructor for the `sass.types.List` class. final Function listConstructor = createClass('SassList', - (_NodeSassList thisArg, int length, + (_NodeSassList thisArg, int? length, [bool? commaSeparator, SassList? dartValue]) { thisArg.dartValue = dartValue ?? - SassList(Iterable.generate(length, (_) => sassNull), + // Either [dartValue] or [length] must be passed. + SassList(Iterable.generate(length!, (_) => sassNull), (commaSeparator ?? true) ? ListSeparator.comma : ListSeparator.space); }, { 'getValue': (_NodeSassList thisArg, int index) => diff --git a/lib/src/node/value/map.dart b/lib/src/node/value/map.dart index 1e8f92324..69b4d897e 100644 --- a/lib/src/node/value/map.dart +++ b/lib/src/node/value/map.dart @@ -22,9 +22,11 @@ Object newNodeSassMap(SassMap value) => /// The JS constructor for the `sass.types.Map` class. final Function mapConstructor = createClass('SassMap', - (_NodeSassMap thisArg, int length, [SassMap? dartValue]) { + (_NodeSassMap thisArg, int? length, [SassMap? dartValue]) { thisArg.dartValue = dartValue ?? - SassMap(Map.fromIterables(Iterable.generate(length, (i) => SassNumber(i)), + SassMap(Map.fromIterables( + // Either [dartValue] or [length] must be passed. + Iterable.generate(length!, (i) => SassNumber(i)), Iterable.generate(length, (_) => sassNull))); }, { 'getKey': (_NodeSassMap thisArg, int index) => diff --git a/lib/src/node/value/number.dart b/lib/src/node/value/number.dart index 918b82570..b967832fc 100644 --- a/lib/src/node/value/number.dart +++ b/lib/src/node/value/number.dart @@ -21,9 +21,10 @@ Object newNodeSassNumber(SassNumber value) => /// The JS constructor for the `sass.types.Number` class. final Function numberConstructor = createClass('SassNumber', - (_NodeSassNumber thisArg, num value, + (_NodeSassNumber thisArg, num? value, [String? unit, SassNumber? dartValue]) { - thisArg.dartValue = dartValue ?? _parseNumber(value, unit); + // Either [dartValue] or [value] must be passed. + thisArg.dartValue = dartValue ?? _parseNumber(value!, unit); }, { 'getValue': (_NodeSassNumber thisArg) => thisArg.dartValue.value, 'setValue': (_NodeSassNumber thisArg, num value) { diff --git a/lib/src/node/value/string.dart b/lib/src/node/value/string.dart index 17cd29cee..b79af1e16 100644 --- a/lib/src/node/value/string.dart +++ b/lib/src/node/value/string.dart @@ -21,8 +21,9 @@ Object newNodeSassString(SassString value) => /// The JS constructor for the `sass.types.String` class. final Function stringConstructor = createClass('SassString', - (_NodeSassString thisArg, String value, [SassString? dartValue]) { - thisArg.dartValue = dartValue ?? SassString(value, quotes: false); + (_NodeSassString thisArg, String? value, [SassString? dartValue]) { + // Either [dartValue] or [value] must be passed. + thisArg.dartValue = dartValue ?? SassString(value!, quotes: false); }, { 'getValue': (_NodeSassString thisArg) => thisArg.dartValue.text, 'setValue': (_NodeSassString thisArg, String value) { diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 1c6a0b8cc..86de0d88f 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -95,7 +95,7 @@ abstract class StylesheetParser extends Parser { return null; } - _statement(root: true); + return _statement(root: true); }); scanner.expectDone(); @@ -2055,7 +2055,8 @@ relase. For details, see http://bit.ly/moz-document. var singleExpression = singleExpression_; if (singleExpression != null) commaExpressions.add(singleExpression); return ListExpression(commaExpressions, ListSeparator.comma, - brackets: bracketList, span: scanner.spanFrom(beforeBracket!)); + brackets: bracketList, + span: scanner.spanFrom(beforeBracket ?? start)); } else if (bracketList && spaceExpressions != null) { resolveOperations(); return ListExpression( diff --git a/lib/src/value/number.dart b/lib/src/value/number.dart index 308f82e53..c4c3e2b0f 100644 --- a/lib/src/value/number.dart +++ b/lib/src/value/number.dart @@ -533,11 +533,7 @@ abstract class SassNumber extends Value implements ext.SassNumber { if (factor == null) return false; value /= factor; return true; - }, - orElse: () { - newNumerators.add(numerator); - return null; - } as String Function()?); + }, orElse: () => newNumerators.add(numerator)); } var mutableDenominatorUnits = denominatorUnits.toList(); @@ -547,11 +543,7 @@ abstract class SassNumber extends Value implements ext.SassNumber { if (factor == null) return false; value /= factor; return true; - }, - orElse: () { - newNumerators.add(numerator); - return null; - } as String Function()?); + }, orElse: () => newNumerators.add(numerator)); } return SassNumber.withUnits(value, diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index fb1a433df..1fe73824a 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -164,7 +164,10 @@ class _EvaluateVisitor List? _mediaQueries; /// The current parent node in the output CSS tree. - late ModifiableCssParentNode _parent; + ModifiableCssParentNode get _parent => _assertInModule(__parent, "__parent"); + set _parent(ModifiableCssParentNode value) => __parent = value; + + ModifiableCssParentNode? __parent; /// The name of the current declaration parent. String? _declarationName; @@ -242,14 +245,20 @@ class _EvaluateVisitor AsyncImporter? _importer; /// The stylesheet that's currently being evaluated. - late Stylesheet _stylesheet; + Stylesheet get _stylesheet => _assertInModule(__stylesheet, "_stylesheet"); + set _stylesheet(Stylesheet value) => __stylesheet = value; + Stylesheet? __stylesheet; /// The root stylesheet node. - late ModifiableCssStylesheet _root; + ModifiableCssStylesheet get _root => _assertInModule(__root, "_root"); + set _root(ModifiableCssStylesheet value) => __root = value; + ModifiableCssStylesheet? __root; /// The first index in [_root.children] after the initial block of CSS /// imports. - late int _endOfImports; + int get _endOfImports => _assertInModule(__endOfImports, "_endOfImports"); + set _endOfImports(int value) => __endOfImports = value; + int? __endOfImports; /// Plain-CSS imports that didn't appear in the initial block of CSS imports. /// @@ -258,11 +267,14 @@ class _EvaluateVisitor /// /// This is `null` unless there are any out-of-order imports in the current /// stylesheet. - late List? _outOfOrderImports; + List? _outOfOrderImports; /// The extension store that tracks extensions and style rules for the current /// module. - late ExtensionStore _extensionStore; + ExtensionStore get _extensionStore => + _assertInModule(__extensionStore, "_extensionStore"); + set _extensionStore(ExtensionStore value) => __extensionStore = value; + ExtensionStore? __extensionStore; /// The configuration for the current module. /// @@ -513,20 +525,31 @@ class _EvaluateVisitor callback); } + /// Asserts that [value] is not `null` and returns it. + /// + /// This is used for fields that are set whenever the evaluator is evaluating + /// a module, which is to say essentially all the time (unless running via + /// [runExpression] or [runStatement]). + T _assertInModule(T? value, String name) { + if (value != null) return value; + throw StateError("Can't access $name outside of a module."); + } + /// Runs [callback] with [importer] as [_importer] and a fake [_stylesheet] /// with [nodeWithSpan]'s source span. Future _withFakeStylesheet(AsyncImporter? importer, AstNode nodeWithSpan, FutureOr callback()) async { var oldImporter = _importer; _importer = importer; - var oldStylesheet = _stylesheet; + + assert(__stylesheet == null); _stylesheet = Stylesheet(const [], nodeWithSpan.span); try { return await callback(); } finally { _importer = oldImporter; - _stylesheet = oldStylesheet; + __stylesheet = null; } } @@ -659,12 +682,12 @@ class _EvaluateVisitor var extensionStore = ExtensionStore(); await _withEnvironment(environment, () async { var oldImporter = _importer; - var oldStylesheet = _stylesheet; - var oldRoot = _root; - var oldParent = _parent; - var oldEndOfImports = _endOfImports; + var oldStylesheet = __stylesheet; + var oldRoot = __root; + var oldParent = __parent; + var oldEndOfImports = __endOfImports; var oldOutOfOrderImports = _outOfOrderImports; - var oldExtensionStore = _extensionStore; + var oldExtensionStore = __extensionStore; var oldStyleRule = _styleRule; var oldMediaQueries = _mediaQueries; var oldDeclarationName = _declarationName; @@ -674,7 +697,7 @@ class _EvaluateVisitor var oldConfiguration = _configuration; _importer = importer; _stylesheet = stylesheet; - var root = _root = ModifiableCssStylesheet(stylesheet.span); + var root = __root = ModifiableCssStylesheet(stylesheet.span); _parent = root; _endOfImports = 0; _outOfOrderImports = null; @@ -693,12 +716,12 @@ class _EvaluateVisitor : CssStylesheet(_addOutOfOrderImports(), stylesheet.span); _importer = oldImporter; - _stylesheet = oldStylesheet; - _root = oldRoot; - _parent = oldParent; - _endOfImports = oldEndOfImports; + __stylesheet = oldStylesheet; + __root = oldRoot; + __parent = oldParent; + __endOfImports = oldEndOfImports; _outOfOrderImports = oldOutOfOrderImports; - _extensionStore = oldExtensionStore; + __extensionStore = oldExtensionStore; _styleRuleIgnoringAtRoot = oldStyleRule; _mediaQueries = oldMediaQueries; _declarationName = oldDeclarationName; @@ -1084,7 +1107,7 @@ class _EvaluateVisitor _parent.addChild(ModifiableCssDeclaration(name, cssValue, node.span, parsedAsCustomProperty: node.isCustomProperty, valueSpanForMap: - _sourceMap ? null : node.value.andThen(_expressionNode)?.span)); + _sourceMap ? node.value.andThen(_expressionNode)?.span : null)); } else if (name.value.startsWith('--') && cssValue != null) { throw _exception( "Custom property values may not be empty.", cssValue.span); @@ -1360,6 +1383,7 @@ class _EvaluateVisitor // By definition, implicit configurations are allowed to only use a subset // of their values. if (configuration is! ExplicitConfiguration) return; + if (configuration.isEmpty) return; var entry = configuration.values.entries.first; throw _exception( @@ -3084,7 +3108,7 @@ class _EvaluateVisitor try { return await callback(); } on SassRuntimeException catch (error) { - if (error.span.text.startsWith("@error")) rethrow; + if (!error.span.text.startsWith("@error")) rethrow; throw SassRuntimeException( error.message, nodeWithSpan.span, _stackTrace()); } diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 76d4747fa..82af67818 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: f094bb66f285333969530af129201a999636e72f +// Checksum: d0e1d0b2e493163e931c957d869473aa57c5b46b // // ignore_for_file: unused_import @@ -172,7 +172,10 @@ class _EvaluateVisitor List? _mediaQueries; /// The current parent node in the output CSS tree. - late ModifiableCssParentNode _parent; + ModifiableCssParentNode get _parent => _assertInModule(__parent, "__parent"); + set _parent(ModifiableCssParentNode value) => __parent = value; + + ModifiableCssParentNode? __parent; /// The name of the current declaration parent. String? _declarationName; @@ -250,14 +253,20 @@ class _EvaluateVisitor Importer? _importer; /// The stylesheet that's currently being evaluated. - late Stylesheet _stylesheet; + Stylesheet get _stylesheet => _assertInModule(__stylesheet, "_stylesheet"); + set _stylesheet(Stylesheet value) => __stylesheet = value; + Stylesheet? __stylesheet; /// The root stylesheet node. - late ModifiableCssStylesheet _root; + ModifiableCssStylesheet get _root => _assertInModule(__root, "_root"); + set _root(ModifiableCssStylesheet value) => __root = value; + ModifiableCssStylesheet? __root; /// The first index in [_root.children] after the initial block of CSS /// imports. - late int _endOfImports; + int get _endOfImports => _assertInModule(__endOfImports, "_endOfImports"); + set _endOfImports(int value) => __endOfImports = value; + int? __endOfImports; /// Plain-CSS imports that didn't appear in the initial block of CSS imports. /// @@ -266,11 +275,14 @@ class _EvaluateVisitor /// /// This is `null` unless there are any out-of-order imports in the current /// stylesheet. - late List? _outOfOrderImports; + List? _outOfOrderImports; /// The extension store that tracks extensions and style rules for the current /// module. - late ExtensionStore _extensionStore; + ExtensionStore get _extensionStore => + _assertInModule(__extensionStore, "_extensionStore"); + set _extensionStore(ExtensionStore value) => __extensionStore = value; + ExtensionStore? __extensionStore; /// The configuration for the current module. /// @@ -518,20 +530,31 @@ class _EvaluateVisitor callback); } + /// Asserts that [value] is not `null` and returns it. + /// + /// This is used for fields that are set whenever the evaluator is evaluating + /// a module, which is to say essentially all the time (unless running via + /// [runExpression] or [runStatement]). + T _assertInModule(T? value, String name) { + if (value != null) return value; + throw StateError("Can't access $name outside of a module."); + } + /// Runs [callback] with [importer] as [_importer] and a fake [_stylesheet] /// with [nodeWithSpan]'s source span. T _withFakeStylesheet( Importer? importer, AstNode nodeWithSpan, T callback()) { var oldImporter = _importer; _importer = importer; - var oldStylesheet = _stylesheet; + + assert(__stylesheet == null); _stylesheet = Stylesheet(const [], nodeWithSpan.span); try { return callback(); } finally { _importer = oldImporter; - _stylesheet = oldStylesheet; + __stylesheet = null; } } @@ -664,12 +687,12 @@ class _EvaluateVisitor var extensionStore = ExtensionStore(); _withEnvironment(environment, () { var oldImporter = _importer; - var oldStylesheet = _stylesheet; - var oldRoot = _root; - var oldParent = _parent; - var oldEndOfImports = _endOfImports; + var oldStylesheet = __stylesheet; + var oldRoot = __root; + var oldParent = __parent; + var oldEndOfImports = __endOfImports; var oldOutOfOrderImports = _outOfOrderImports; - var oldExtensionStore = _extensionStore; + var oldExtensionStore = __extensionStore; var oldStyleRule = _styleRule; var oldMediaQueries = _mediaQueries; var oldDeclarationName = _declarationName; @@ -679,7 +702,7 @@ class _EvaluateVisitor var oldConfiguration = _configuration; _importer = importer; _stylesheet = stylesheet; - var root = _root = ModifiableCssStylesheet(stylesheet.span); + var root = __root = ModifiableCssStylesheet(stylesheet.span); _parent = root; _endOfImports = 0; _outOfOrderImports = null; @@ -698,12 +721,12 @@ class _EvaluateVisitor : CssStylesheet(_addOutOfOrderImports(), stylesheet.span); _importer = oldImporter; - _stylesheet = oldStylesheet; - _root = oldRoot; - _parent = oldParent; - _endOfImports = oldEndOfImports; + __stylesheet = oldStylesheet; + __root = oldRoot; + __parent = oldParent; + __endOfImports = oldEndOfImports; _outOfOrderImports = oldOutOfOrderImports; - _extensionStore = oldExtensionStore; + __extensionStore = oldExtensionStore; _styleRuleIgnoringAtRoot = oldStyleRule; _mediaQueries = oldMediaQueries; _declarationName = oldDeclarationName; @@ -1088,7 +1111,7 @@ class _EvaluateVisitor _parent.addChild(ModifiableCssDeclaration(name, cssValue, node.span, parsedAsCustomProperty: node.isCustomProperty, valueSpanForMap: - _sourceMap ? null : node.value.andThen(_expressionNode)?.span)); + _sourceMap ? node.value.andThen(_expressionNode)?.span : null)); } else if (name.value.startsWith('--') && cssValue != null) { throw _exception( "Custom property values may not be empty.", cssValue.span); @@ -1361,6 +1384,7 @@ class _EvaluateVisitor // By definition, implicit configurations are allowed to only use a subset // of their values. if (configuration is! ExplicitConfiguration) return; + if (configuration.isEmpty) return; var entry = configuration.values.entries.first; throw _exception( @@ -3041,7 +3065,7 @@ class _EvaluateVisitor try { return callback(); } on SassRuntimeException catch (error) { - if (error.span.text.startsWith("@error")) rethrow; + if (!error.span.text.startsWith("@error")) rethrow; throw SassRuntimeException( error.message, nodeWithSpan.span, _stackTrace()); } diff --git a/package.json b/package.json index 348bfed51..236146a4a 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ ], "name": "sass", "devDependencies": { - "chokidar": ">=2.0.0 <4.0.0", + "chokidar": ">=3.0.0 <4.0.0", "fibers": ">=1.0.0 <5.0.0", "intercept-stdout": "^0.1.2", "node-sass": "^4.11.0" diff --git a/package/package.json b/package/package.json index 379ba662d..9a56c9e2b 100644 --- a/package/package.json +++ b/package/package.json @@ -17,7 +17,7 @@ "node": ">=8.9.0" }, "dependencies": { - "chokidar": ">=2.0.0 <4.0.0" + "chokidar": ">=3.0.0 <4.0.0" }, "keywords": ["style", "scss", "sass", "preprocessor", "css"] } diff --git a/test/cli/shared/source_maps.dart b/test/cli/shared/source_maps.dart index 5da4857d2..09d9a8b87 100644 --- a/test/cli/shared/source_maps.dart +++ b/test/cli/shared/source_maps.dart @@ -18,7 +18,7 @@ import '../../utils.dart'; /// Defines test that are shared between the Dart and Node.js CLI test suites. void sharedTests(Future runSass(Iterable arguments)) { group("for a simple compilation", () { - late Map map; + late Map map; setUp(() async { await d.file("test.scss", "a {b: 1 + 2}").create(); @@ -278,7 +278,7 @@ void sharedTests(Future runSass(Iterable arguments)) { await d.file("test.scss", "a {b: 1 + 2}").create(); }); - Map? map; + Map? map; group("with the target in the same directory", () { setUp(() async { await (await runSass(["--embed-source-map", "test.scss", "out.css"])) @@ -359,5 +359,5 @@ void sharedTests(Future runSass(Iterable arguments)) { } /// Reads the file at [path] within [d.sandbox] and JSON-decodes it. -Map _readJson(String path) => - jsonDecode(readFile(d.path(path))) as Map; +Map _readJson(String path) => + jsonDecode(readFile(d.path(path))) as Map; diff --git a/test/ensure_npm_package.dart b/test/ensure_npm_package.dart index 250f4be06..5e73c3c54 100644 --- a/test/ensure_npm_package.dart +++ b/test/ensure_npm_package.dart @@ -18,7 +18,7 @@ Future ensureNpmPackage() async { import 'package:cli_pkg/testing.dart' as pkg; import 'package:stream_channel/stream_channel.dart'; - void hybridMain(StreamChannel channel) async { + void hybridMain(StreamChannel channel) async { pkg.ensureExecutableUpToDate("sass", node: true); channel.sink.close(); } diff --git a/test/hybrid.dart b/test/hybrid.dart index 4311488a8..d4ec47941 100644 --- a/test/hybrid.dart +++ b/test/hybrid.dart @@ -12,15 +12,15 @@ Future createTempDir() async => (await runHybridExpression( /// Writes [text] to [path]. Future writeTextFile(String path, String text) => runHybridExpression( - 'new File(message[0]).writeAsString(message[1])', [path, text]); + 'File(message[0]).writeAsString(message[1])', [path, text]); /// Creates a directory at [path]. Future createDirectory(String path) => - runHybridExpression('new Directory(message).create()', path); + runHybridExpression('Directory(message).create()', path); /// Recursively deletes the directory at [path]. Future deleteDirectory(String path) => - runHybridExpression('new Directory(message).delete(recursive: true)', path); + runHybridExpression('Directory(message).delete(recursive: true)', path); /// Runs [expression], which may be asynchronous, in a hybrid isolate. /// @@ -34,7 +34,7 @@ Future runHybridExpression(String expression, import 'package:stream_channel/stream_channel.dart'; - hybridMain(StreamChannel channel, message) async { + hybridMain(StreamChannel channel, dynamic message) async { var result = await $expression; channel.sink.add(_isJsonSafe(result) ? jsonEncode(result) : 'null'); channel.sink.close(); diff --git a/test/node_api/api.dart b/test/node_api/api.dart index f446a5d70..8a0dbddbe 100644 --- a/test/node_api/api.dart +++ b/test/node_api/api.dart @@ -43,7 +43,7 @@ external FiberClass _requireFiber(String path); class Sass { external RenderResult renderSync(RenderOptions args); external void render(RenderOptions args, - void callback(RenderError error, RenderResult result)); + void callback(RenderError? error, RenderResult? result)); external SassTypes get types; external Object get NULL; external NodeSassBoolean get TRUE; diff --git a/test/node_api/source_map_test.dart b/test/node_api/source_map_test.dart index 51c009670..a0cf525ef 100644 --- a/test/node_api/source_map_test.dart +++ b/test/node_api/source_map_test.dart @@ -29,12 +29,12 @@ void main() { group("a basic invocation", () { late String css; - late Map map; + late Map map; setUp(() { var result = sass.renderSync( RenderOptions(data: "a {b: c}", sourceMap: true, outFile: "out.css")); css = utf8.decode(result.css); - map = _jsonUtf8.decode(result.map!) as Map; + map = _jsonUtf8.decode(result.map!) as Map; }); test("includes correct mappings", () { @@ -158,7 +158,7 @@ void main() { test("emits a source map", () { var result = sass.renderSync( RenderOptions(data: "a {b: c}", sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map!) as Map; + var map = _jsonUtf8.decode(result.map!) as Map; expect(map, containsPair("sources", ["stdin"])); }); @@ -168,7 +168,7 @@ void main() { var result = sass.renderSync(RenderOptions( file: p.join(sandbox, "test.scss"), sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map!) as Map; + var map = _jsonUtf8.decode(result.map!) as Map; expect( map, containsPair( @@ -182,7 +182,7 @@ void main() { var result = sass.renderSync(RenderOptions( file: p.join(sandbox, "test"), sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map!) as Map; + var map = _jsonUtf8.decode(result.map!) as Map; expect( map, containsPair( @@ -192,7 +192,7 @@ void main() { test("derives the target URL from stdin", () { var result = sass.renderSync( RenderOptions(data: "a {b: c}", sourceMap: "out.css.map")); - var map = _jsonUtf8.decode(result.map!) as Map; + var map = _jsonUtf8.decode(result.map!) as Map; expect(map, containsPair("file", "stdin.css")); }); @@ -331,5 +331,5 @@ void main() { } /// Renders [options] and returns the decoded source map. -Map _renderSourceMap(RenderOptions options) => - _jsonUtf8.decode(sass.renderSync(options).map!) as Map; +Map _renderSourceMap(RenderOptions options) => + _jsonUtf8.decode(sass.renderSync(options).map!) as Map; diff --git a/test/node_api/utils.dart b/test/node_api/utils.dart index 7c6fe9597..f9a186aa9 100644 --- a/test/node_api/utils.dart +++ b/test/node_api/utils.dart @@ -35,7 +35,7 @@ void useSandbox() { }); tearDown(() async { - await _sandbox.andThen(deleteDirectory); + await sandbox.andThen(deleteDirectory); }); } @@ -54,7 +54,7 @@ Future render(RenderOptions options) { sass.render(options, allowInterop(Zone.current.bindBinaryCallbackGuarded((error, result) { expect(error, isNull); - completer.complete(utf8.decode(result.css)); + completer.complete(utf8.decode(result!.css)); }))); return completer.future; } @@ -66,7 +66,7 @@ Future renderError(RenderOptions options) { sass.render(options, allowInterop(Zone.current.bindBinaryCallbackGuarded((error, result) { expect(result, isNull); - completer.complete(error); + completer.complete(error!); }))); return completer.future; } diff --git a/test/source_map_test.dart b/test/source_map_test.dart index 7f521d954..6d1c3a828 100644 --- a/test/source_map_test.dart +++ b/test/source_map_test.dart @@ -907,7 +907,10 @@ String _mapToString(SingleMapping map, String sourceText, String targetText) { targetScanner.column == entry.target.column) { var name = entryNames[Tuple2(entry.source.line, entry.source.column)]; targetBuffer.write("{{$name}}"); - entryIter.moveNext(); + if (!entryIter.moveNext()) { + targetBuffer.write(targetScanner.rest); + break; + } } targetBuffer.writeCharCode(targetScanner.readChar()); diff --git a/test/utils.dart b/test/utils.dart index a11f88792..e4cb40cca 100644 --- a/test/utils.dart +++ b/test/utils.dart @@ -23,13 +23,13 @@ Future get tick => /// Loads and decodes the source map embedded as a `data:` URI in [css]. /// /// Throws a [TestFailure] if [css] doesn't have such a source map. -Map embeddedSourceMap(String css) { +Map embeddedSourceMap(String css) { expect(css, matches(_sourceMapCommentRegExp)); var match = _sourceMapCommentRegExp.firstMatch(css)!; var data = Uri.parse(match[1]!).data!; expect(data.mimeType, equals("application/json")); - return jsonDecode(data.contentAsString()) as Map; + return jsonDecode(data.contentAsString()) as Map; } /// Returns a function with one argument that fails the test if it's ever From 5535458edbad8f130a43dec61b0b59f74af23c45 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 12 Apr 2021 16:49:12 -0700 Subject: [PATCH 17/19] Fix broken specs --- lib/src/ast/selector/pseudo.dart | 8 ++++---- lib/src/exception.dart | 2 +- lib/src/extend/extension_store.dart | 2 +- lib/src/extend/functions.dart | 2 +- lib/src/parse/stylesheet.dart | 29 +++++++++++++++-------------- lib/src/visitor/async_evaluate.dart | 2 +- lib/src/visitor/evaluate.dart | 4 ++-- lib/src/visitor/serialize.dart | 2 +- 8 files changed, 26 insertions(+), 25 deletions(-) diff --git a/lib/src/ast/selector/pseudo.dart b/lib/src/ast/selector/pseudo.dart index bbf21295c..51e9fed05 100644 --- a/lib/src/ast/selector/pseudo.dart +++ b/lib/src/ast/selector/pseudo.dart @@ -170,8 +170,8 @@ class PseudoSelector extends SimpleSelector { var minSpecificity = 0; var maxSpecificity = 0; for (var complex in selector.components) { - minSpecificity = math.max(_minSpecificity!, complex.minSpecificity); - maxSpecificity = math.max(_maxSpecificity!, complex.maxSpecificity); + minSpecificity = math.max(minSpecificity, complex.minSpecificity); + maxSpecificity = math.max(maxSpecificity, complex.maxSpecificity); } _minSpecificity = minSpecificity; _maxSpecificity = maxSpecificity; @@ -180,8 +180,8 @@ class PseudoSelector extends SimpleSelector { var minSpecificity = math.pow(super.minSpecificity, 3) as int; var maxSpecificity = 0; for (var complex in selector.components) { - minSpecificity = math.min(_minSpecificity!, complex.minSpecificity); - maxSpecificity = math.max(_maxSpecificity!, complex.maxSpecificity); + minSpecificity = math.min(minSpecificity, complex.minSpecificity); + maxSpecificity = math.max(maxSpecificity, complex.maxSpecificity); } _minSpecificity = minSpecificity; _maxSpecificity = maxSpecificity; diff --git a/lib/src/exception.dart b/lib/src/exception.dart index de2ddadb1..1811e0d96 100644 --- a/lib/src/exception.dart +++ b/lib/src/exception.dart @@ -99,7 +99,7 @@ class MultiSpanSassException extends SassException useColor = true; } - var buffer = StringBuffer("Error: $message"); + var buffer = StringBuffer("Error: $message\n"); span .highlightMultiple(primaryLabel, secondarySpans, diff --git a/lib/src/extend/extension_store.dart b/lib/src/extend/extension_store.dart index f0a87cb7c..4f705c130 100644 --- a/lib/src/extend/extension_store.dart +++ b/lib/src/extend/extension_store.dart @@ -215,7 +215,7 @@ class ExtensionStore { for (var simple in component.components) { _selectors.putIfAbsent(simple, () => {}).add(selector); - if (simple is! PseudoSelector) return; + if (simple is! PseudoSelector) continue; var selectorInPseudo = simple.selector; if (selectorInPseudo != null) { diff --git a/lib/src/extend/functions.dart b/lib/src/extend/functions.dart index 410aa8739..25bc199ce 100644 --- a/lib/src/extend/functions.dart +++ b/lib/src/extend/functions.dart @@ -785,7 +785,7 @@ bool _selectorPseudoIsSuperselector( return compound2.components.any((pseudo2) { if (pseudo2 is! PseudoSelector) return false; if (pseudo2.name != pseudo1.name) return false; - if (pseudo2.argument == pseudo1.argument) return false; + if (pseudo2.argument != pseudo1.argument) return false; var selector2 = pseudo2.selector; if (selector2 == null) return false; return selector1.isSuperselector(selector2); diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 86de0d88f..8dbddf592 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -1200,16 +1200,12 @@ abstract class StylesheetParser extends Parser { ContentBlock? content; if (contentArguments != null || lookingAtChildren()) { + var contentArguments_ = contentArguments ?? + ArgumentDeclaration.empty(span: scanner.emptySpan); var wasInContentBlock = _inContentBlock; _inContentBlock = true; - content = _withChildren( - _statement, - start, - (children, span) => ContentBlock( - contentArguments ?? - ArgumentDeclaration.empty(span: scanner.emptySpan), - children, - span)); + content = _withChildren(_statement, start, + (children, span) => ContentBlock(contentArguments_, children, span)); _inContentBlock = wasInContentBlock; } else { expectStatementSeparator(); @@ -1724,8 +1720,7 @@ relase. For details, see http://bit.ly/moz-document. } void addSingleExpression(Expression expression, {bool number = false}) { - var singleExpression = singleExpression_; - if (singleExpression != null) { + if (singleExpression_ != null) { // If we discover we're parsing a list whose first element is a division // operation, and we're in parentheses, reparse outside of a paren // context. This ensures that `(1/2 1)` doesn't perform division on its @@ -1740,7 +1735,10 @@ relase. For details, see http://bit.ly/moz-document. var spaceExpressions = spaceExpressions_ ??= []; resolveOperations(); - spaceExpressions.add(singleExpression); + + // [singleExpression_] was non-null before, and [resolveOperations] + // can't make it null, it can only change it. + spaceExpressions.add(singleExpression_!); allowSlash = number; } else if (!number) { allowSlash = false; @@ -2025,11 +2023,14 @@ relase. For details, see http://bit.ly/moz-document. } var commaExpressions = commaExpressions_ ??= []; - var singleExpression = singleExpression_; - if (singleExpression == null) scanner.error("Expected expression."); + if (singleExpression_ == null) scanner.error("Expected expression."); resolveSpaceExpressions(); - commaExpressions.add(singleExpression); + + // [resolveSpaceExpressions can modify [singleExpression_], but it + // can't set it to null`. + commaExpressions.add(singleExpression_!); + scanner.readChar(); allowSlash = true; singleExpression_ = null; diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 1fe73824a..0cac6b70b 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -2232,7 +2232,7 @@ class _EvaluateVisitor argument.name, value.withoutSlash(), evaluated.namedNodes.andGet(argument.name) ?? - _expressionNode(argument.defaultValue!)); + argument.defaultValue.andThen(_expressionNode)); } SassArgumentList? argumentList; diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 82af67818..3b8bce68a 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: d0e1d0b2e493163e931c957d869473aa57c5b46b +// Checksum: 01a7ae41ae622e64443597ea82b27b7aeb73d260 // // ignore_for_file: unused_import @@ -2219,7 +2219,7 @@ class _EvaluateVisitor argument.name, value.withoutSlash(), evaluated.namedNodes.andGet(argument.name) ?? - _expressionNode(argument.defaultValue!)); + argument.defaultValue.andThen(_expressionNode)); } SassArgumentList? argumentList; diff --git a/lib/src/visitor/serialize.dart b/lib/src/visitor/serialize.dart index 6dc757715..8e0245f9f 100644 --- a/lib/src/visitor/serialize.dart +++ b/lib/src/visitor/serialize.dart @@ -390,7 +390,7 @@ class _SerializeVisitor } minimumIndentation = - math.min(minimumIndentation, node.value.span.start.column); + math.min(minimumIndentation, node.name.span.start.column); _writeWithIndent(value, minimumIndentation); } From 59b9dac23691bb5c84f55ba0afee0f61862004df Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 14 Apr 2021 19:02:51 -0700 Subject: [PATCH 18/19] Update the pubspec --- pubspec.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index ed9f76ba7..722cd36e8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ dependencies: args: ^2.0.0 async: ^2.5.0 charcode: ^1.2.0 - cli_repl: ^0.2.1-nullsafety + cli_repl: ^0.2.1 collection: ^1.15.0 meta: ^1.3.0 node_interop: ^2.0.0 @@ -27,18 +27,16 @@ dependencies: stream_transform: ^2.0.0 string_scanner: ^1.1.0 term_glyph: ^1.2.0 - tuple: ^2.0.0-nullsafety.0 + tuple: ^2.0.0 watcher: ^1.0.0 -publish_to: none - dev_dependencies: archive: ^3.1.2 analyzer: ^1.1.0 cli_pkg: ^1.3.0 crypto: ^3.0.0 dart_style: ^2.0.0 - grinder: ^0.9.0-nullsafety.0 + grinder: ^0.9.0 node_preamble: ^2.0.0 pedantic: ^1.11.0 pub_semver: ^2.0.0 From c2e02a50da95a0e1a6e42883a0a673e5b2b4ae09 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 14 Apr 2021 19:11:10 -0700 Subject: [PATCH 19/19] Use ?[] instead of .andGet() --- lib/src/async_environment.dart | 8 ++++---- lib/src/environment.dart | 10 +++++----- lib/src/util/merged_map_view.dart | 4 +--- lib/src/util/nullable.dart | 20 -------------------- lib/src/visitor/async_evaluate.dart | 4 ++-- lib/src/visitor/evaluate.dart | 6 +++--- 6 files changed, 15 insertions(+), 37 deletions(-) diff --git a/lib/src/async_environment.dart b/lib/src/async_environment.dart index 3f03344ea..5c1b2d33b 100644 --- a/lib/src/async_environment.dart +++ b/lib/src/async_environment.dart @@ -314,7 +314,7 @@ class AsyncEnvironment { } if (type == "variable") name = "\$$name"; - var span = _forwardedModuleNodes.andGet(oldModule)?.span; + var span = _forwardedModuleNodes?[oldModule]?.span; throw MultiSpanSassScriptException( 'Two forwarded modules both define a $type named $name.', "new @forward", @@ -617,7 +617,7 @@ class AsyncEnvironment { _lastVariableName = name; _lastVariableIndex = index; _variables[index][name] = value; - _variableNodes?.andGet(index)![name] = nodeWithSpan!; + _variableNodes?[index][name] = nodeWithSpan!; } /// Sets the variable named [name] to [value], associated with @@ -636,7 +636,7 @@ class AsyncEnvironment { _variableIndices[name] = index; _variables[index][name] = value; if (nodeWithSpan != null) { - _variableNodes?.andGet(index)![name] = nodeWithSpan; + _variableNodes?[index][name] = nodeWithSpan; } } @@ -818,7 +818,7 @@ class AsyncEnvironment { var configuration = {}; for (var i = 0; i < _variables.length; i++) { var values = _variables[i]; - var nodes = _variableNodes.andGet(i) ?? {}; + var nodes = _variableNodes?[i] ?? {}; for (var entry in values.entries) { // Implicit configurations are never invalid, making [configurationSpan] // unnecessary, so we pass null here to avoid having to compute it. diff --git a/lib/src/environment.dart b/lib/src/environment.dart index b120e0511..da4910951 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: 1ef0f32701764f4b160ef045ca162b38653e0504 +// Checksum: bb0b47fc04e32f36a0f87dc73bdfe3f89dc51aa4 // // ignore_for_file: unused_import @@ -322,7 +322,7 @@ class Environment { } if (type == "variable") name = "\$$name"; - var span = _forwardedModuleNodes.andGet(oldModule)?.span; + var span = _forwardedModuleNodes?[oldModule]?.span; throw MultiSpanSassScriptException( 'Two forwarded modules both define a $type named $name.', "new @forward", @@ -625,7 +625,7 @@ class Environment { _lastVariableName = name; _lastVariableIndex = index; _variables[index][name] = value; - _variableNodes?.andGet(index)![name] = nodeWithSpan!; + _variableNodes?[index][name] = nodeWithSpan!; } /// Sets the variable named [name] to [value], associated with @@ -644,7 +644,7 @@ class Environment { _variableIndices[name] = index; _variables[index][name] = value; if (nodeWithSpan != null) { - _variableNodes?.andGet(index)![name] = nodeWithSpan; + _variableNodes?[index][name] = nodeWithSpan; } } @@ -824,7 +824,7 @@ class Environment { var configuration = {}; for (var i = 0; i < _variables.length; i++) { var values = _variables[i]; - var nodes = _variableNodes.andGet(i) ?? {}; + var nodes = _variableNodes?[i] ?? {}; for (var entry in values.entries) { // Implicit configurations are never invalid, making [configurationSpan] // unnecessary, so we pass null here to avoid having to compute it. diff --git a/lib/src/util/merged_map_view.dart b/lib/src/util/merged_map_view.dart index b84f6ad51..df749871f 100644 --- a/lib/src/util/merged_map_view.dart +++ b/lib/src/util/merged_map_view.dart @@ -5,8 +5,6 @@ import 'dart:collection'; import '../utils.dart'; -import '../util/nullable.dart'; -import 'nullable.dart'; /// An unmodifiable view of multiple maps merged together as though they were a /// single map. @@ -46,7 +44,7 @@ class MergedMapView extends MapBase { } } - V? operator [](Object? key) => _mapsByKey[key as K].andGet(key); + V? operator [](Object? key) => _mapsByKey[key as K]?[key]; operator []=(K key, V value) { var child = _mapsByKey[key]; diff --git a/lib/src/util/nullable.dart b/lib/src/util/nullable.dart index 7d1511dc8..f5bf6258c 100644 --- a/lib/src/util/nullable.dart +++ b/lib/src/util/nullable.dart @@ -13,26 +13,6 @@ extension NullableExtension on T? { } } -extension NullableListExtension on List? { - /// If [this] is `null`, returns `null`. Otherwise, returns `this[index]`. - /// - /// This is the equivalent of `list?.[key]`, if such a thing existed. - T? andGet(int index) { - var self = this; - return self == null ? null : self[index]; - } -} - -extension NullableMapExtension on Map? { - /// If [this] is `null`, returns `null`. Otherwise, returns `this[key]`. - /// - /// This is the equivalent of `map?.[key]`, if such a thing existed. - V? andGet(Object? key) { - var self = this; - return self == null ? null : self[key]; - } -} - extension SetExtension on Set { /// Destructively removes the `null` element from this set, if it exists, and /// returns a view of it casted to a non-nullable type. diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 0cac6b70b..83b910270 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -2219,7 +2219,7 @@ class _EvaluateVisitor _environment.setLocalVariable( declaredArguments[i].name, evaluated.positional[i].withoutSlash(), - evaluated.positionalNodes.andGet(i)); + evaluated.positionalNodes?[i]); } for (var i = evaluated.positional.length; @@ -2231,7 +2231,7 @@ class _EvaluateVisitor _environment.setLocalVariable( argument.name, value.withoutSlash(), - evaluated.namedNodes.andGet(argument.name) ?? + evaluated.namedNodes?[argument.name] ?? argument.defaultValue.andThen(_expressionNode)); } diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 3b8bce68a..dc2b70566 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: 01a7ae41ae622e64443597ea82b27b7aeb73d260 +// Checksum: 6b82405fdc448ac69ca703bc6bffbb8d50f3fced // // ignore_for_file: unused_import @@ -2206,7 +2206,7 @@ class _EvaluateVisitor _environment.setLocalVariable( declaredArguments[i].name, evaluated.positional[i].withoutSlash(), - evaluated.positionalNodes.andGet(i)); + evaluated.positionalNodes?[i]); } for (var i = evaluated.positional.length; @@ -2218,7 +2218,7 @@ class _EvaluateVisitor _environment.setLocalVariable( argument.name, value.withoutSlash(), - evaluated.namedNodes.andGet(argument.name) ?? + evaluated.namedNodes?[argument.name] ?? argument.defaultValue.andThen(_expressionNode)); }