diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index 91e3ca277..e9feed880 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -3,8 +3,6 @@ // https://opensource.org/licenses/MIT. import 'dart:collection'; -import 'package:uuid/uuid.dart'; - import 'ast/node.dart'; import 'ast/sass.dart'; import 'configured_value.dart'; @@ -28,21 +26,32 @@ class Configuration { final Map _values; /// Creates an implicit configuration with the given [values]. - Configuration.implicit(this._values, {String? opaqueId}) - : opaqueId = opaqueId ?? Uuid().v1(); + Configuration.implicit(this._values) : _originalConfiguration = null; + + /// Reference to the original configuration serving as an opaque ID. + final Configuration? _originalConfiguration; - /// Uniquely identifying ID for a configuration lasting across the execution - /// context. + /// Returns whether [this] and [that] [Configuration]s have the same + /// [_originalConfiguration]. /// - /// Implicit configurations will always have different IDs. - final String opaqueId; + /// An implicit configuration will always return `false` because it was not + /// created through another configuration. + /// + /// [ExplicitConfiguration]s will and configurations created [throughForward] + /// will be considered to have the same original config if they were created + /// as a copy from the same base configuration. + bool sameOriginal(Configuration that) => + _originalConfiguration != null && + _originalConfiguration == that._originalConfiguration; /// 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. - factory Configuration.empty() => Configuration.implicit({}); + const Configuration.empty() + : _values = const {}, + _originalConfiguration = null; bool get isEmpty => values.isEmpty; @@ -53,7 +62,7 @@ class Configuration { /// Creates a new configuration from this one based on a `@forward` rule. Configuration throughForward(ForwardRule forward) { - if (isEmpty) return Configuration.empty(); + if (isEmpty) return const Configuration.empty(); var newValues = _values; // Only allow variables that are visible through the `@forward` to be @@ -70,14 +79,21 @@ class Configuration { } else if (hiddenVariables != null && hiddenVariables.isNotEmpty) { newValues = LimitedMapView.blocklist(newValues, hiddenVariables); } - return _withValues(newValues, opaqueId: opaqueId); + return _copy(newValues); } /// Returns a copy of [this] with the given [values] map. - Configuration _withValues(Map values, - {String? opaqueId}) => - Configuration.implicit(values, opaqueId: opaqueId); + /// + /// The copy will have the same [_originalConfiguration] as [this] config. + /// If the original config was `null` [this] will become the original config. + Configuration _copy(Map values) => + Configuration._(values, _originalConfiguration); + /// Creates a [Configuration] with the given [_values] map and an + /// [_originalConfiguration] reference. + Configuration._(this._values, this._originalConfiguration); + + @override String toString() => "(" + values.entries @@ -97,13 +113,23 @@ class ExplicitConfiguration extends Configuration { /// The node whose span indicates where the configuration was declared. final AstNode nodeWithSpan; - ExplicitConfiguration(Map values, this.nodeWithSpan, - {String? opaqueId}) - : super.implicit(values, opaqueId: opaqueId); + /// Creates a base [ExplicitConfiguration] with a [values] map and a + /// [nodeWithSpan]. + ExplicitConfiguration(Map values, this.nodeWithSpan) + : super.implicit(values); + + /// Creates an [ExplicitConfiguration] with a [values] map, a [nodeWithSpan] + /// and if this is a copy a reference to the [_originalConfiguration]. + ExplicitConfiguration._(Map values, + this.nodeWithSpan, Configuration? originalConfiguration) + : super._(values, originalConfiguration); /// Returns a copy of [this] with the given [values] map. + /// + /// The copy will have the same [_originalConfiguration] as [this] config. + /// If the original config was `null` [this] will become the original config. @override - Configuration _withValues(Map values, - {String? opaqueId}) => - ExplicitConfiguration(values, nodeWithSpan, opaqueId: opaqueId); + Configuration _copy(Map values) => + ExplicitConfiguration._( + values, nodeWithSpan, _originalConfiguration ?? this); } diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index a4ed7d21e..50e62b723 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -141,7 +141,7 @@ class _EvaluateVisitor /// All modules that have been loaded and evaluated so far. final _modules = {}; - /// Configuration seen by a module URI. + /// The first [Configuration] used to load a module [Uri]. final _moduleConfigurations = {}; /// A map from canonical module URLs to the nodes whose spans indicate where @@ -305,7 +305,7 @@ class _EvaluateVisitor /// The configuration for the current module. /// /// If this is empty, that indicates that the current module is not configured. - var _configuration = Configuration.empty(); + var _configuration = const Configuration.empty(); /// Creates a new visitor. /// @@ -470,7 +470,7 @@ class _EvaluateVisitor var withMap = arguments[1].realNull?.assertMap("with").contents; var callableNode = _callableNode!; - var configuration = Configuration.empty(); + var configuration = const Configuration.empty(); if (withMap != null) { var values = {}; var span = callableNode.span; @@ -673,8 +673,7 @@ class _EvaluateVisitor var alreadyLoaded = _modules[url]; if (alreadyLoaded != null) { var currentConfiguration = configuration ?? _configuration; - if (_moduleConfigurations[url]!.opaqueId != - currentConfiguration.opaqueId && + if (!_moduleConfigurations[url]!.sameOriginal(currentConfiguration) && currentConfiguration is ExplicitConfiguration) { var message = namesInErrors ? "${p.prettyUri(url)} was already loaded, so it can't be " @@ -2091,7 +2090,7 @@ class _EvaluateVisitor } Future visitUseRule(UseRule node) async { - var configuration = Configuration.empty(); + var configuration = const Configuration.empty(); if (node.configuration.isNotEmpty) { var values = {}; for (var variable in node.configuration) { diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index b18b708ea..1f74f7c5a 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: 9064ba88b38e5cfcc31fd345b185d2c334b490a6 +// Checksum: ad5691d0c80b924308f6ee03863fa8c7f5e25ca0 // // ignore_for_file: unused_import @@ -149,7 +149,7 @@ class _EvaluateVisitor /// All modules that have been loaded and evaluated so far. final _modules = >{}; - /// Configuration seen by a module URI. + /// The first [Configuration] used to load a module [Uri]. final _moduleConfigurations = {}; /// A map from canonical module URLs to the nodes whose spans indicate where @@ -313,7 +313,7 @@ class _EvaluateVisitor /// The configuration for the current module. /// /// If this is empty, that indicates that the current module is not configured. - var _configuration = Configuration.empty(); + var _configuration = const Configuration.empty(); /// Creates a new visitor. /// @@ -475,7 +475,7 @@ class _EvaluateVisitor var withMap = arguments[1].realNull?.assertMap("with").contents; var callableNode = _callableNode!; - var configuration = Configuration.empty(); + var configuration = const Configuration.empty(); if (withMap != null) { var values = {}; var span = callableNode.span; @@ -678,8 +678,7 @@ class _EvaluateVisitor var alreadyLoaded = _modules[url]; if (alreadyLoaded != null) { var currentConfiguration = configuration ?? _configuration; - if (_moduleConfigurations[url]!.opaqueId != - currentConfiguration.opaqueId && + if (!_moduleConfigurations[url]!.sameOriginal(currentConfiguration) && currentConfiguration is ExplicitConfiguration) { var message = namesInErrors ? "${p.prettyUri(url)} was already loaded, so it can't be " @@ -2083,7 +2082,7 @@ class _EvaluateVisitor } Value? visitUseRule(UseRule node) { - var configuration = Configuration.empty(); + var configuration = const Configuration.empty(); if (node.configuration.isNotEmpty) { var values = {}; for (var variable in node.configuration) { diff --git a/pubspec.yaml b/pubspec.yaml index 0f8e52c42..ac1fb48b0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,7 +30,6 @@ dependencies: tuple: ^2.0.0 watcher: ^1.0.0 http: ^0.13.3 - uuid: ^3.0.6 dev_dependencies: analyzer: ^3.0.0