From b244a8a31fc6545e855a7961db2b19dc84e1bef6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 23:08:44 +0000 Subject: [PATCH 1/3] Bump dart_style from 2.3.7 to 3.0.0 Bumps [dart_style](https://github.com/dart-lang/dart_style) from 2.3.7 to 3.0.0. - [Release notes](https://github.com/dart-lang/dart_style/releases) - [Changelog](https://github.com/dart-lang/dart_style/blob/main/CHANGELOG.md) - [Commits](https://github.com/dart-lang/dart_style/compare/v2.3.7...v3.0.0) --- updated-dependencies: - dependency-name: dart_style dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index ba310f719..efc67893d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,7 +41,7 @@ dev_dependencies: analyzer: ^6.8.0 archive: ^3.1.2 crypto: ^3.0.0 - dart_style: ^2.0.0 + dart_style: ^3.0.0 dartdoc: ">=8.0.14 <8.3.1" # dart-lang/dartdoc#3947 grinder: ^0.9.0 node_preamble: ^2.0.2 From e681eb6387d1bf2a0afbb65bb130f9fd0005e767 Mon Sep 17 00:00:00 2001 From: "Carlos (Goodwine)" <2022649+Goodwine@users.noreply.github.com> Date: Mon, 16 Dec 2024 23:26:51 +0000 Subject: [PATCH 2/3] Update DartFormatter() to explicitly set the language version. This is now required in dart-style v3 --- lib/src/compile.dart | 264 +-- lib/src/deprecation.dart | 182 +- lib/src/environment.dart | 424 ++-- lib/src/import_cache.dart | 135 +- lib/src/visitor/evaluate.dart | 2721 +++++++++++++++---------- tool/grind/generate_deprecations.dart | 4 +- tool/grind/synchronize.dart | 3 +- 7 files changed, 2274 insertions(+), 1459 deletions(-) diff --git a/lib/src/compile.dart b/lib/src/compile.dart index 35bc75872..15e01e50e 100644 --- a/lib/src/compile.dart +++ b/lib/src/compile.dart @@ -37,30 +37,33 @@ import 'visitor/serialize.dart'; /// /// If both `importCache` and `nodeImporter` are provided, the importers in /// `importCache` will be evaluated before `nodeImporter`. -CompileResult compile(String path, - {Syntax? syntax, - Logger? logger, - ImportCache? importCache, - NodeImporter? nodeImporter, - Iterable? functions, - OutputStyle? style, - bool useSpaces = true, - int? indentWidth, - LineFeed? lineFeed, - bool quietDeps = false, - bool verbose = false, - bool sourceMap = false, - bool charset = true, - Iterable? silenceDeprecations, - Iterable? fatalDeprecations, - Iterable? futureDeprecations}) { +CompileResult compile( + String path, { + Syntax? syntax, + Logger? logger, + ImportCache? importCache, + NodeImporter? nodeImporter, + Iterable? functions, + OutputStyle? style, + bool useSpaces = true, + int? indentWidth, + LineFeed? lineFeed, + bool quietDeps = false, + bool verbose = false, + bool sourceMap = false, + bool charset = true, + Iterable? silenceDeprecations, + Iterable? fatalDeprecations, + Iterable? futureDeprecations, +}) { DeprecationProcessingLogger deprecationLogger = - logger = DeprecationProcessingLogger(logger ?? Logger.stderr(), - silenceDeprecations: {...?silenceDeprecations}, - fatalDeprecations: {...?fatalDeprecations}, - futureDeprecations: {...?futureDeprecations}, - limitRepetition: !verbose) - ..validate(); + logger = DeprecationProcessingLogger( + logger ?? Logger.stderr(), + silenceDeprecations: {...?silenceDeprecations}, + fatalDeprecations: {...?fatalDeprecations}, + futureDeprecations: {...?futureDeprecations}, + limitRepetition: !verbose, + )..validate(); // 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. @@ -68,29 +71,35 @@ CompileResult compile(String path, if (nodeImporter == null && (syntax == null || syntax == Syntax.forPath(path))) { importCache ??= ImportCache.none(); - stylesheet = importCache.importCanonical( - FilesystemImporter.cwd, p.toUri(canonicalize(path)), - originalUrl: p.toUri(path))!; + stylesheet = + importCache.importCanonical( + FilesystemImporter.cwd, + p.toUri(canonicalize(path)), + originalUrl: p.toUri(path), + )!; } else { stylesheet = Stylesheet.parse( - readFile(path), syntax ?? Syntax.forPath(path), - url: p.toUri(path)); + readFile(path), + syntax ?? Syntax.forPath(path), + url: p.toUri(path), + ); } var result = _compileStylesheet( - stylesheet, - logger, - importCache, - nodeImporter, - FilesystemImporter.cwd, - functions, - style, - useSpaces, - indentWidth, - lineFeed, - quietDeps, - sourceMap, - charset); + stylesheet, + logger, + importCache, + nodeImporter, + FilesystemImporter.cwd, + functions, + style, + useSpaces, + indentWidth, + lineFeed, + quietDeps, + sourceMap, + charset, + ); deprecationLogger.summarize(js: nodeImporter != null); return result; @@ -100,51 +109,55 @@ CompileResult compile(String path, /// support the node-sass compatible API. /// /// 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, - bool useSpaces = true, - int? indentWidth, - LineFeed? lineFeed, - Object? url, - bool quietDeps = false, - bool verbose = false, - bool sourceMap = false, - bool charset = true, - Iterable? silenceDeprecations, - Iterable? fatalDeprecations, - Iterable? futureDeprecations}) { +CompileResult compileString( + String source, { + 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, + bool quietDeps = false, + bool verbose = false, + bool sourceMap = false, + bool charset = true, + Iterable? silenceDeprecations, + Iterable? fatalDeprecations, + Iterable? futureDeprecations, +}) { DeprecationProcessingLogger deprecationLogger = - logger = DeprecationProcessingLogger(logger ?? Logger.stderr(), - silenceDeprecations: {...?silenceDeprecations}, - fatalDeprecations: {...?fatalDeprecations}, - futureDeprecations: {...?futureDeprecations}, - limitRepetition: !verbose) - ..validate(); + logger = DeprecationProcessingLogger( + logger ?? Logger.stderr(), + silenceDeprecations: {...?silenceDeprecations}, + fatalDeprecations: {...?fatalDeprecations}, + futureDeprecations: {...?futureDeprecations}, + limitRepetition: !verbose, + )..validate(); var stylesheet = Stylesheet.parse(source, syntax ?? Syntax.scss, url: url); var result = _compileStylesheet( - stylesheet, - logger, - importCache, - nodeImporter, - importer ?? (isBrowser ? NoOpImporter() : FilesystemImporter.cwd), - functions, - style, - useSpaces, - indentWidth, - lineFeed, - quietDeps, - sourceMap, - charset); + stylesheet, + logger, + importCache, + nodeImporter, + importer ?? (isBrowser ? NoOpImporter() : FilesystemImporter.cwd), + functions, + style, + useSpaces, + indentWidth, + lineFeed, + quietDeps, + sourceMap, + charset, + ); deprecationLogger.summarize(js: nodeImporter != null); return result; @@ -154,53 +167,62 @@ CompileResult compileString(String source, /// /// Arguments are handled as for [compileString]. CompileResult _compileStylesheet( - Stylesheet stylesheet, - Logger? logger, - ImportCache? importCache, - NodeImporter? nodeImporter, - Importer importer, - Iterable? functions, - OutputStyle? style, - bool useSpaces, - int? indentWidth, - LineFeed? lineFeed, - bool quietDeps, - bool sourceMap, - bool charset) { + Stylesheet stylesheet, + Logger? logger, + ImportCache? importCache, + NodeImporter? nodeImporter, + Importer importer, + Iterable? functions, + OutputStyle? style, + bool useSpaces, + int? indentWidth, + LineFeed? lineFeed, + bool quietDeps, + bool sourceMap, + bool charset, +) { if (nodeImporter != null) { logger?.warnForDeprecation( - Deprecation.legacyJsApi, - 'The legacy JS API is deprecated and will be removed in ' - 'Dart Sass 2.0.0.\n\n' - 'More info: https://sass-lang.com/d/legacy-js-api'); + Deprecation.legacyJsApi, + 'The legacy JS API is deprecated and will be removed in ' + 'Dart Sass 2.0.0.\n\n' + 'More info: https://sass-lang.com/d/legacy-js-api', + ); } - var evaluateResult = evaluate(stylesheet, - importCache: importCache, - nodeImporter: nodeImporter, - importer: importer, - functions: functions, - logger: logger, - quietDeps: quietDeps, - sourceMap: sourceMap); - - var serializeResult = serialize(evaluateResult.stylesheet, - style: style, - useSpaces: useSpaces, - indentWidth: indentWidth, - lineFeed: lineFeed, - logger: logger, - sourceMap: sourceMap, - charset: charset); + var evaluateResult = evaluate( + stylesheet, + importCache: importCache, + nodeImporter: nodeImporter, + importer: importer, + functions: functions, + logger: logger, + quietDeps: quietDeps, + sourceMap: sourceMap, + ); + + var serializeResult = serialize( + evaluateResult.stylesheet, + style: style, + useSpaces: useSpaces, + indentWidth: indentWidth, + lineFeed: lineFeed, + logger: logger, + sourceMap: sourceMap, + charset: charset, + ); var resultSourceMap = serializeResult.sourceMap; if (resultSourceMap != null && importCache != null) { mapInPlace( - resultSourceMap.urls, - (url) => url == '' - ? Uri.dataFromString(stylesheet.span.file.getText(0), - encoding: utf8) - .toString() - : importCache.sourceMapUrl(Uri.parse(url)).toString()); + resultSourceMap.urls, + (url) => + url == '' + ? Uri.dataFromString( + stylesheet.span.file.getText(0), + encoding: utf8, + ).toString() + : importCache.sourceMapUrl(Uri.parse(url)).toString(), + ); } return CompileResult(evaluateResult, serializeResult); diff --git a/lib/src/deprecation.dart b/lib/src/deprecation.dart index 3281e720d..af8517112 100644 --- a/lib/src/deprecation.dart +++ b/lib/src/deprecation.dart @@ -18,109 +18,153 @@ enum Deprecation { // Checksum: 47c97f7824eb25d7f1e64e3230938b88330d40b4 /// Deprecation for passing a string directly to meta.call(). - callString('call-string', - deprecatedIn: '0.0.0', - description: 'Passing a string directly to meta.call().'), + callString( + 'call-string', + deprecatedIn: '0.0.0', + description: 'Passing a string directly to meta.call().', + ), /// Deprecation for @elseif. elseif('elseif', deprecatedIn: '1.3.2', description: '@elseif.'), /// Deprecation for @-moz-document. - mozDocument('moz-document', - deprecatedIn: '1.7.2', description: '@-moz-document.'), + mozDocument( + 'moz-document', + deprecatedIn: '1.7.2', + description: '@-moz-document.', + ), /// Deprecation for imports using relative canonical URLs. - relativeCanonical('relative-canonical', - deprecatedIn: '1.14.2', - description: 'Imports using relative canonical URLs.'), + relativeCanonical( + 'relative-canonical', + deprecatedIn: '1.14.2', + description: 'Imports using relative canonical URLs.', + ), /// Deprecation for declaring new variables with !global. - newGlobal('new-global', - deprecatedIn: '1.17.2', - description: 'Declaring new variables with !global.'), + newGlobal( + 'new-global', + deprecatedIn: '1.17.2', + description: 'Declaring new variables with !global.', + ), /// Deprecation for using color module functions in place of plain CSS functions. - colorModuleCompat('color-module-compat', - deprecatedIn: '1.23.0', - description: - 'Using color module functions in place of plain CSS functions.'), + colorModuleCompat( + 'color-module-compat', + deprecatedIn: '1.23.0', + description: + 'Using color module functions in place of plain CSS functions.', + ), /// Deprecation for / operator for division. - slashDiv('slash-div', - deprecatedIn: '1.33.0', description: '/ operator for division.'), + slashDiv( + 'slash-div', + deprecatedIn: '1.33.0', + description: '/ operator for division.', + ), /// Deprecation for leading, trailing, and repeated combinators. - bogusCombinators('bogus-combinators', - deprecatedIn: '1.54.0', - description: 'Leading, trailing, and repeated combinators.'), + bogusCombinators( + 'bogus-combinators', + deprecatedIn: '1.54.0', + description: 'Leading, trailing, and repeated combinators.', + ), /// Deprecation for ambiguous + and - operators. - strictUnary('strict-unary', - deprecatedIn: '1.55.0', description: 'Ambiguous + and - operators.'), + strictUnary( + 'strict-unary', + deprecatedIn: '1.55.0', + description: 'Ambiguous + and - operators.', + ), /// Deprecation for passing invalid units to built-in functions. - functionUnits('function-units', - deprecatedIn: '1.56.0', - description: 'Passing invalid units to built-in functions.'), + functionUnits( + 'function-units', + deprecatedIn: '1.56.0', + description: 'Passing invalid units to built-in functions.', + ), /// Deprecation for using !default or !global multiple times for one variable. - duplicateVarFlags('duplicate-var-flags', - deprecatedIn: '1.62.0', - description: - 'Using !default or !global multiple times for one variable.'), + duplicateVarFlags( + 'duplicate-var-flags', + deprecatedIn: '1.62.0', + description: 'Using !default or !global multiple times for one variable.', + ), /// Deprecation for passing null as alpha in the ${isJS ? 'JS': 'Dart'} API. - nullAlpha('null-alpha', - deprecatedIn: '1.62.3', - description: 'Passing null as alpha in the ${isJS ? 'JS' : 'Dart'} API.'), + nullAlpha( + 'null-alpha', + deprecatedIn: '1.62.3', + description: 'Passing null as alpha in the ${isJS ? 'JS' : 'Dart'} API.', + ), /// Deprecation for passing percentages to the Sass abs() function. - absPercent('abs-percent', - deprecatedIn: '1.65.0', - description: 'Passing percentages to the Sass abs() function.'), + absPercent( + 'abs-percent', + deprecatedIn: '1.65.0', + description: 'Passing percentages to the Sass abs() function.', + ), /// Deprecation for using the current working directory as an implicit load path. - fsImporterCwd('fs-importer-cwd', - deprecatedIn: '1.73.0', - description: - 'Using the current working directory as an implicit load path.'), + fsImporterCwd( + 'fs-importer-cwd', + deprecatedIn: '1.73.0', + description: + 'Using the current working directory as an implicit load path.', + ), /// Deprecation for function and mixin names beginning with --. - cssFunctionMixin('css-function-mixin', - deprecatedIn: '1.76.0', - description: 'Function and mixin names beginning with --.'), + cssFunctionMixin( + 'css-function-mixin', + deprecatedIn: '1.76.0', + description: 'Function and mixin names beginning with --.', + ), /// Deprecation for declarations after or between nested rules. - mixedDecls('mixed-decls', - deprecatedIn: '1.77.7', - description: 'Declarations after or between nested rules.'), + mixedDecls( + 'mixed-decls', + deprecatedIn: '1.77.7', + description: 'Declarations after or between nested rules.', + ), /// Deprecation for meta.feature-exists - featureExists('feature-exists', - deprecatedIn: '1.78.0', description: 'meta.feature-exists'), + featureExists( + 'feature-exists', + deprecatedIn: '1.78.0', + description: 'meta.feature-exists', + ), /// Deprecation for certain uses of built-in sass:color functions. - color4Api('color-4-api', - deprecatedIn: '1.79.0', - description: 'Certain uses of built-in sass:color functions.'), + color4Api( + 'color-4-api', + deprecatedIn: '1.79.0', + description: 'Certain uses of built-in sass:color functions.', + ), /// Deprecation for using global color functions instead of sass:color. - colorFunctions('color-functions', - deprecatedIn: '1.79.0', - description: 'Using global color functions instead of sass:color.'), + colorFunctions( + 'color-functions', + deprecatedIn: '1.79.0', + description: 'Using global color functions instead of sass:color.', + ), /// Deprecation for legacy JS API. - legacyJsApi('legacy-js-api', - deprecatedIn: '1.79.0', description: 'Legacy JS API.'), + legacyJsApi( + 'legacy-js-api', + deprecatedIn: '1.79.0', + description: 'Legacy JS API.', + ), /// Deprecation for @import rules. import('import', deprecatedIn: '1.80.0', description: '@import rules.'), /// Deprecation for global built-in functions that are available in sass: modules. - globalBuiltin('global-builtin', - deprecatedIn: '1.80.0', - description: - 'Global built-in functions that are available in sass: modules.'), + globalBuiltin( + 'global-builtin', + deprecatedIn: '1.80.0', + description: + 'Global built-in functions that are available in sass: modules.', + ), // END AUTOGENERATED CODE @@ -176,30 +220,32 @@ enum Deprecation { /// Constructs a regular deprecation. const Deprecation(this.id, {required String? deprecatedIn, this.description}) - : _deprecatedIn = deprecatedIn, - _obsoleteIn = null, - isFuture = false; + : _deprecatedIn = deprecatedIn, + _obsoleteIn = null, + isFuture = false; /// Constructs a future deprecation. // ignore: unused_element const Deprecation.future(this.id, {this.description}) - : _deprecatedIn = null, - _obsoleteIn = null, - isFuture = true; + : _deprecatedIn = null, + _obsoleteIn = null, + isFuture = true; @override String toString() => id; /// Returns the deprecation with a given ID, or null if none exists. - static Deprecation? fromId(String id) => Deprecation.values - .firstWhereOrNull((deprecation) => deprecation.id == id); + static Deprecation? fromId(String id) => Deprecation.values.firstWhereOrNull( + (deprecation) => deprecation.id == id, + ); /// Returns the set of all deprecations done in or before [version]. static Set forVersion(Version version) { var range = VersionRange(max: version, includeMax: true); return { for (var deprecation in Deprecation.values) - if (deprecation.deprecatedIn.andThen(range.allows) ?? false) deprecation + if (deprecation.deprecatedIn.andThen(range.allows) ?? false) + deprecation, }; } } diff --git a/lib/src/environment.dart b/lib/src/environment.dart index 3aa0fa45d..151a37173 100644 --- a/lib/src/environment.dart +++ b/lib/src/environment.dart @@ -154,41 +154,42 @@ final class Environment { /// /// If [sourceMap] is `true`, this tracks variables' source locations Environment() - : _modules = {}, - _namespaceNodes = {}, - _globalModules = {}, - _importedModules = {}, - _forwardedModules = null, - _nestedForwardedModules = null, - _allModules = [], - _variables = [{}], - _variableNodes = [{}], - _variableIndices = {}, - _functions = [{}], - _functionIndices = {}, - _mixins = [{}], - _mixinIndices = {}; + : _modules = {}, + _namespaceNodes = {}, + _globalModules = {}, + _importedModules = {}, + _forwardedModules = null, + _nestedForwardedModules = null, + _allModules = [], + _variables = [{}], + _variableNodes = [{}], + _variableIndices = {}, + _functions = [{}], + _functionIndices = {}, + _mixins = [{}], + _mixinIndices = {}; Environment._( - this._modules, - this._namespaceNodes, - this._globalModules, - this._importedModules, - this._forwardedModules, - this._nestedForwardedModules, - this._allModules, - this._variables, - this._variableNodes, - this._functions, - this._mixins, - this._content) - // Lazily fill in the indices rather than eagerly copying them from the - // existing environment in closure() because the copying took a lot of - // time and was rarely helpful. This saves a bunch of time on Susy's - // tests. - : _variableIndices = {}, - _functionIndices = {}, - _mixinIndices = {}; + this._modules, + this._namespaceNodes, + this._globalModules, + this._importedModules, + this._forwardedModules, + this._nestedForwardedModules, + this._allModules, + this._variables, + this._variableNodes, + this._functions, + this._mixins, + this._content, + ) + // Lazily fill in the indices rather than eagerly copying them from the + // existing environment in closure() because the copying took a lot of + // time and was rarely helpful. This saves a bunch of time on Susy's + // tests. + : _variableIndices = {}, + _functionIndices = {}, + _mixinIndices = {}; /// Creates a closure based on this environment. /// @@ -196,18 +197,19 @@ final class Environment { /// However, any new declarations or assignments in scopes that are visible /// when the closure was created will be reflected. Environment closure() => Environment._( - _modules, - _namespaceNodes, - _globalModules, - _importedModules, - _forwardedModules, - _nestedForwardedModules, - _allModules, - _variables.toList(), - _variableNodes.toList(), - _functions.toList(), - _mixins.toList(), - _content); + _modules, + _namespaceNodes, + _globalModules, + _importedModules, + _forwardedModules, + _nestedForwardedModules, + _allModules, + _variables.toList(), + _variableNodes.toList(), + _functions.toList(), + _mixins.toList(), + _content, + ); /// Returns a new environment to use for an imported file. /// @@ -215,18 +217,19 @@ final class Environment { /// and mixins, but excludes most modules (except for global modules that /// result from importing a file with forwards). Environment forImport() => Environment._( - {}, - {}, - {}, - _importedModules, - null, - null, - [], - _variables.toList(), - _variableNodes.toList(), - _functions.toList(), - _mixins.toList(), - _content); + {}, + {}, + {}, + _importedModules, + null, + null, + [], + _variables.toList(), + _variableNodes.toList(), + _functions.toList(), + _mixins.toList(), + _content, + ); /// Adds [module] to the set of modules visible in this environment. /// @@ -238,8 +241,11 @@ final class Environment { /// 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[module] = nodeWithSpan; _allModules.add(module); @@ -247,16 +253,18 @@ final class Environment { if (_variables.first.keys.firstWhereOrNull(module.variables.containsKey) case var name?) { throw SassScriptException( - 'This module and the new module both define a variable named ' - '"\$$name".'); + 'This module and the new module both define a variable named ' + '"\$$name".', + ); } } else { if (_modules.containsKey(namespace)) { var span = _namespaceNodes[namespace]?.span; throw MultiSpanSassScriptException( - "There's already a module with namespace \"$namespace\".", - "new @use", - {if (span != null) span: "original @use"}); + "There's already a module with namespace \"$namespace\".", + "new @use", + {if (span != null) span: "original @use"}, + ); } _modules[namespace] = module; @@ -273,9 +281,19 @@ final class Environment { var view = ForwardedModuleView.ifNecessary(module, rule); for (var other in forwardedModules.keys) { _assertNoConflicts( - view.variables, other.variables, view, other, "variable"); + view.variables, + other.variables, + view, + other, + "variable", + ); _assertNoConflicts( - view.functions, other.functions, view, other, "function"); + view.functions, + other.functions, + view, + other, + "function", + ); _assertNoConflicts(view.mixins, other.mixins, view, other, "mixin"); } @@ -292,11 +310,12 @@ final class Environment { /// /// The [type] and [newModuleNodeWithSpan] are used for error reporting. void _assertNoConflicts( - Map newMembers, - Map oldMembers, - Module newModule, - Module oldModule, - String type) { + Map newMembers, + Map oldMembers, + Module newModule, + Module oldModule, + String type, + ) { Map smaller; Map larger; if (newMembers.length < oldMembers.length) { @@ -319,9 +338,10 @@ final class Environment { if (type == "variable") name = "\$$name"; var span = _forwardedModules?[oldModule]?.span; throw MultiSpanSassScriptException( - 'Two forwarded modules both define a $type named $name.', - "new @forward", - {if (span != null) span: "original @forward"}); + 'Two forwarded modules both define a $type named $name.', + "new @forward", + {if (span != null) span: "original @forward"}, + ); } } @@ -349,23 +369,25 @@ final class Environment { } var forwardedVariableNames = { - for (var module in forwarded.keys) ...module.variables.keys + for (var module in forwarded.keys) ...module.variables.keys, }; var forwardedFunctionNames = { - for (var module in forwarded.keys) ...module.functions.keys + for (var module in forwarded.keys) ...module.functions.keys, }; var forwardedMixinNames = { - for (var module in forwarded.keys) ...module.mixins.keys + for (var module in forwarded.keys) ...module.mixins.keys, }; if (atRoot) { // Hide members from modules that have already been imported or // forwarded that would otherwise conflict with the @imported members. for (var (module, node) in _importedModules.pairs.toList()) { - var shadowed = ShadowedModuleView.ifNecessary(module, - variables: forwardedVariableNames, - mixins: forwardedMixinNames, - functions: forwardedFunctionNames); + var shadowed = ShadowedModuleView.ifNecessary( + module, + variables: forwardedVariableNames, + mixins: forwardedMixinNames, + functions: forwardedFunctionNames, + ); if (shadowed != null) { _importedModules.remove(module); if (!shadowed.isEmpty) _importedModules[shadowed] = node; @@ -373,10 +395,12 @@ final class Environment { } for (var (module, node) in forwardedModules.pairs.toList()) { - var shadowed = ShadowedModuleView.ifNecessary(module, - variables: forwardedVariableNames, - mixins: forwardedMixinNames, - functions: forwardedFunctionNames); + var shadowed = ShadowedModuleView.ifNecessary( + module, + variables: forwardedVariableNames, + mixins: forwardedMixinNames, + functions: forwardedFunctionNames, + ); if (shadowed != null) { forwardedModules.remove(module); if (!shadowed.isEmpty) forwardedModules[shadowed] = node; @@ -386,8 +410,10 @@ final class Environment { _importedModules.addAll(forwarded); forwardedModules.addAll(forwarded); } else { - (_nestedForwardedModules ??= - List.generate(_variables.length - 1, (_) => [])) + (_nestedForwardedModules ??= List.generate( + _variables.length - 1, + (_) => [], + )) .last .addAll(forwarded.keys); } @@ -536,8 +562,13 @@ final 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; @@ -555,8 +586,11 @@ final class Environment { // If this module doesn't already contain a variable named [name], try // setting it in a global module. if (!_variables.first.containsKey(name)) { - var moduleWithName = _fromOneModule(name, "variable", - (module) => module.variables.containsKey(name) ? module : null); + var moduleWithName = _fromOneModule( + name, + "variable", + (module) => module.variables.containsKey(name) ? module : null, + ); if (moduleWithName != null) { moduleWithName.setVariable(name, value, nodeWithSpan); return; @@ -582,10 +616,13 @@ final class Environment { } } - var index = _lastVariableName == name - ? _lastVariableIndex! - : _variableIndices.putIfAbsent( - name, () => _variableIndex(name) ?? _variables.length - 1); + var index = + _lastVariableName == name + ? _lastVariableIndex! + : _variableIndices.putIfAbsent( + name, + () => _variableIndex(name) ?? _variables.length - 1, + ); if (!_inSemiGlobalScope && index == 0) { index = _variables.length - 1; _variableIndices[name] = index; @@ -800,12 +837,18 @@ final class Environment { /// that contains [css] and [preModuleComments] as its CSS, which can be /// extended using [extensionStore]. Module toModule( - CssStylesheet css, - Map, List> preModuleComments, - ExtensionStore extensionStore) { + CssStylesheet css, + Map, List> preModuleComments, + ExtensionStore extensionStore, + ) { assert(atRoot); - return _EnvironmentModule(this, css, preModuleComments, extensionStore, - forwarded: _forwardedModules.andThen((modules) => MapKeySet(modules))); + return _EnvironmentModule( + this, + css, + preModuleComments, + extensionStore, + forwarded: _forwardedModules.andThen((modules) => MapKeySet(modules)), + ); } /// Returns a module with the same members and upstream modules as `this`, but @@ -815,19 +858,23 @@ final class Environment { /// members into the current scope. It's the only situation in which a nested /// environment can become a module. Module toDummyModule() => _EnvironmentModule( - this, - CssStylesheet(const [], - SourceFile.decoded(const [], url: "").span(0)), - const {}, - ExtensionStore.empty, - forwarded: _forwardedModules.andThen((modules) => MapKeySet(modules))); + this, + CssStylesheet( + const [], + SourceFile.decoded(const [], url: "").span(0), + ), + const {}, + ExtensionStore.empty, + forwarded: _forwardedModules.andThen((modules) => MapKeySet(modules)), + ); /// Returns the module with the given [namespace], or throws a /// [SassScriptException] if none exists. Module _getModule(String namespace) { if (_modules[namespace] case var module?) return module; throw SassScriptException( - 'There is no module with the namespace "$namespace".'); + 'There is no module with the namespace "$namespace".', + ); } /// Returns the result of [callback] if it returns non-`null` for exactly one @@ -842,7 +889,10 @@ final 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)) { + String name, + String type, + T? callback(Module module), + ) { if (_nestedForwardedModules case var nestedForwardedModules?) { for (var modules in nestedForwardedModules.reversed) { for (var module in modules.reversed) { @@ -860,18 +910,21 @@ final class Environment { var valueInModule = callback(module); if (valueInModule == null) continue; - Object? identityFromModule = valueInModule is Callable - ? valueInModule - : module.variableIdentity(name); + Object? identityFromModule = + valueInModule is Callable + ? valueInModule + : module.variableIdentity(name); if (identityFromModule == identity) continue; if (value != null) { throw MultiSpanSassScriptException( - 'This $type is available from multiple global modules.', - '$type use', { - for (var (module, node) in _globalModules.pairs) - if (callback(module) != null) node.span: 'includes $type' - }); + 'This $type is available from multiple global modules.', + '$type use', + { + for (var (module, node) in _globalModules.pairs) + if (callback(module) != null) node.span: 'includes $type', + }, + ); } value = valueInModule; @@ -908,41 +961,56 @@ final class _EnvironmentModule implements Module { final Map> _modulesByVariable; factory _EnvironmentModule( - Environment environment, - CssStylesheet css, - Map, List> preModuleComments, - ExtensionStore extensionStore, - {Set>? forwarded}) { + Environment environment, + CssStylesheet css, + Map, List> preModuleComments, + ExtensionStore extensionStore, { + Set>? forwarded, + }) { forwarded ??= const {}; return _EnvironmentModule._( - environment, - css, - Map.unmodifiable({ - for (var (module, comments) in preModuleComments.pairs) - module: List.unmodifiable(comments) - }), - extensionStore, - _makeModulesByVariable(forwarded), - _memberMap(environment._variables.first, - forwarded.map((module) => module.variables)), - _memberMap(environment._variableNodes.first, - forwarded.map((module) => module.variableNodes)), - _memberMap(environment._functions.first, - forwarded.map((module) => module.functions)), - _memberMap(environment._mixins.first, - forwarded.map((module) => module.mixins)), - transitivelyContainsCss: css.children.isNotEmpty || - preModuleComments.isNotEmpty || - environment._allModules - .any((module) => module.transitivelyContainsCss), - transitivelyContainsExtensions: !extensionStore.isEmpty || - environment._allModules - .any((module) => module.transitivelyContainsExtensions)); + environment, + css, + Map.unmodifiable({ + for (var (module, comments) in preModuleComments.pairs) + module: List.unmodifiable(comments), + }), + extensionStore, + _makeModulesByVariable(forwarded), + _memberMap( + environment._variables.first, + forwarded.map((module) => module.variables), + ), + _memberMap( + environment._variableNodes.first, + forwarded.map((module) => module.variableNodes), + ), + _memberMap( + environment._functions.first, + forwarded.map((module) => module.functions), + ), + _memberMap( + environment._mixins.first, + forwarded.map((module) => module.mixins), + ), + transitivelyContainsCss: + css.children.isNotEmpty || + preModuleComments.isNotEmpty || + environment._allModules.any( + (module) => module.transitivelyContainsCss, + ), + transitivelyContainsExtensions: + !extensionStore.isEmpty || + environment._allModules.any( + (module) => module.transitivelyContainsExtensions, + ), + ); } /// Create [_modulesByVariable] for a set of forwarded modules. static Map> _makeModulesByVariable( - Set> forwarded) { + Set> forwarded, + ) { if (forwarded.isEmpty) return const {}; var modulesByVariable = >{}; @@ -952,8 +1020,11 @@ final class _EnvironmentModule implements Module { for (var child in module._modulesByVariable.values) { setAll(modulesByVariable, child.variables.keys, child); } - setAll(modulesByVariable, module._environment._variables.first.keys, - module); + setAll( + modulesByVariable, + module._environment._variables.first.keys, + module, + ); } else { setAll(modulesByVariable, module.variables.keys, module); } @@ -964,14 +1035,16 @@ final class _EnvironmentModule implements Module { /// Returns a map that exposes the public members of [localMap] as well as all /// the members of [otherMaps]. static Map _memberMap( - Map localMap, Iterable> otherMaps) { + Map localMap, + Iterable> otherMaps, + ) { localMap = PublicMemberMapView(localMap); if (otherMaps.isEmpty) return localMap; var allMaps = [ for (var map in otherMaps) if (map.isNotEmpty) map, - localMap + localMap, ]; if (allMaps.length == 1) return localMap; @@ -979,18 +1052,18 @@ final class _EnvironmentModule implements Module { } _EnvironmentModule._( - this._environment, - this.css, - this.preModuleComments, - this.extensionStore, - this._modulesByVariable, - this.variables, - this.variableNodes, - this.functions, - this.mixins, - {required this.transitivelyContainsCss, - required this.transitivelyContainsExtensions}) - : upstream = _environment._allModules; + this._environment, + this.css, + this.preModuleComments, + this.extensionStore, + this._modulesByVariable, + this.variables, + this.variableNodes, + this.functions, + this.mixins, { + required this.transitivelyContainsCss, + required this.transitivelyContainsExtensions, + }) : upstream = _environment._allModules; void setVariable(String name, Value value, AstNode nodeWithSpan) { if (_modulesByVariable[name] case var module?) { @@ -1016,20 +1089,23 @@ final class _EnvironmentModule implements Module { Module cloneCss() { if (!transitivelyContainsCss) return this; - var (newStylesheet, newExtensionStore) = - cloneCssStylesheet(css, extensionStore); + var (newStylesheet, newExtensionStore) = cloneCssStylesheet( + css, + extensionStore, + ); return _EnvironmentModule._( - _environment, - newStylesheet, - preModuleComments, - newExtensionStore, - _modulesByVariable, - variables, - variableNodes, - functions, - mixins, - transitivelyContainsCss: transitivelyContainsCss, - transitivelyContainsExtensions: transitivelyContainsExtensions); + _environment, + newStylesheet, + preModuleComments, + newExtensionStore, + _modulesByVariable, + variables, + variableNodes, + functions, + mixins, + transitivelyContainsCss: transitivelyContainsCss, + transitivelyContainsExtensions: transitivelyContainsExtensions, + ); } String toString() => url == null ? "" : p.prettyUri(url); diff --git a/lib/src/import_cache.dart b/lib/src/import_cache.dart index b6d38d15f..fa15d41ca 100644 --- a/lib/src/import_cache.dart +++ b/lib/src/import_cache.dart @@ -91,11 +91,11 @@ final 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}) - : _importers = _toImporters(importers, loadPaths, packageConfig); + ImportCache({ + Iterable? importers, + Iterable? loadPaths, + PackageConfig? packageConfig, + }) : _importers = _toImporters(importers, loadPaths, packageConfig); /// Creates an import cache without any globally-available importers. ImportCache.none() : _importers = const []; @@ -103,12 +103,15 @@ final class ImportCache { /// Creates an import cache without any globally-available importers, and only /// the passed in importers. ImportCache.only(Iterable importers) - : _importers = List.unmodifiable(importers); + : _importers = List.unmodifiable(importers); /// 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'); if (isBrowser) return [...?importers]; return [ @@ -118,7 +121,7 @@ final class ImportCache { if (sassPath != null) for (var path in sassPath.split(isWindows ? ';' : ':')) FilesystemImporter(path), - if (packageConfig != null) PackageImporter(packageConfig) + if (packageConfig != null) PackageImporter(packageConfig), ]; } @@ -137,8 +140,12 @@ final 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`. - CanonicalizeResult? canonicalize(Uri url, - {Importer? baseImporter, Uri? baseUrl, bool forImport = false}) { + CanonicalizeResult? canonicalize( + Uri url, { + Importer? baseImporter, + Uri? baseUrl, + bool forImport = false, + }) { if (isBrowser && (baseImporter == null || baseImporter is NoOpImporter) && _importers.isEmpty) { @@ -150,12 +157,17 @@ final class ImportCache { var resolvedUrl = baseUrl?.resolveUri(url) ?? url; var key = (baseImporter, resolvedUrl, forImport: forImport); var relativeResult = _perImporterCanonicalizeCache.putIfAbsent(key, () { - var (result, cacheable) = - _canonicalize(baseImporter, resolvedUrl, baseUrl, forImport); + var (result, cacheable) = _canonicalize( + baseImporter, + resolvedUrl, + baseUrl, + forImport, + ); assert( - cacheable, - "Relative loads should always be cacheable because they never " - "provide access to the containing URL."); + cacheable, + "Relative loads should always be cacheable because they never " + "provide access to the containing URL.", + ); if (baseUrl != null) _nonCanonicalRelativeUrls[key] = url; return result; }); @@ -198,10 +210,11 @@ final class ImportCache { // future uses of this importer. for (var j = 0; j < i; j++) { _perImporterCanonicalizeCache[( - _importers[j], - url, - forImport: forImport - )] = null; + _importers[j], + url, + forImport: forImport, + )] = + null; } cacheable = false; } @@ -220,15 +233,24 @@ final class ImportCache { /// This returns both the result of the call to `canonicalize()` and whether /// that result is cacheable at all. (CanonicalizeResult?, bool cacheable) _canonicalize( - Importer importer, Uri url, Uri? baseUrl, bool forImport) { - var passContainingUrl = baseUrl != null && + Importer importer, + Uri url, + Uri? baseUrl, + bool forImport, + ) { + var passContainingUrl = + baseUrl != null && (url.scheme == '' || importer.isNonCanonicalScheme(url.scheme)); - var canonicalizeContext = - CanonicalizeContext(passContainingUrl ? baseUrl : null, forImport); + var canonicalizeContext = CanonicalizeContext( + passContainingUrl ? baseUrl : null, + forImport, + ); var result = withCanonicalizeContext( - canonicalizeContext, () => importer.canonicalize(url)); + canonicalizeContext, + () => importer.canonicalize(url), + ); var cacheable = !passContainingUrl || !canonicalizeContext.wasContainingUrlAccessed; @@ -255,13 +277,24 @@ final class ImportCache { /// parsed stylesheet. Otherwise, returns `null`. /// /// Caches the result of the import and uses cached results if possible. - (Importer, Stylesheet)? import(Uri url, - {Importer? baseImporter, Uri? baseUrl, bool forImport = false}) { - if (canonicalize(url, - baseImporter: baseImporter, baseUrl: baseUrl, forImport: forImport) + (Importer, Stylesheet)? import( + Uri url, { + Importer? baseImporter, + Uri? baseUrl, + bool forImport = false, + }) { + if (canonicalize( + url, + baseImporter: baseImporter, + baseUrl: baseUrl, + forImport: forImport, + ) case (var importer, var canonicalUrl, :var originalUrl)) { - return importCanonical(importer, canonicalUrl, originalUrl: originalUrl) - .andThen((stylesheet) => (importer, stylesheet)); + return importCanonical( + importer, + canonicalUrl, + originalUrl: originalUrl, + ).andThen((stylesheet) => (importer, stylesheet)); } else { return null; } @@ -277,8 +310,11 @@ final class ImportCache { /// 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, + }) { return _importCache.putIfAbsent(canonicalUrl, () { var loadTime = DateTime.now(); var result = importer.load(canonicalUrl); @@ -286,12 +322,16 @@ final class ImportCache { _loadTimes[canonicalUrl] = loadTime; _resultsCache[canonicalUrl] = result; - return Stylesheet.parse(result.contents, result.syntax, - // For backwards-compatibility, relative canonical URLs are resolved - // relative to [originalUrl]. - url: originalUrl == null - ? canonicalUrl - : originalUrl.resolveUri(canonicalUrl)); + return Stylesheet.parse( + result.contents, + result.syntax, + // For backwards-compatibility, relative canonical URLs are resolved + // relative to [originalUrl]. + url: + originalUrl == null + ? canonicalUrl + : originalUrl.resolveUri(canonicalUrl), + ); }); } @@ -302,14 +342,15 @@ final class ImportCache { // If multiple original URLs canonicalize to the same thing, choose the // shortest one. minBy( - _canonicalizeCache.values.nonNulls - .where((result) => result.$2 == canonicalUrl) - .map((result) => result.originalUrl), - (url) => url.path.length) - // Use the canonicalized basename so that we display e.g. - // package:example/_example.scss rather than package:example/example - // in stack traces. - .andThen((url) => url.resolve(p.url.basename(canonicalUrl.path))) ?? + _canonicalizeCache.values.nonNulls + .where((result) => result.$2 == canonicalUrl) + .map((result) => result.originalUrl), + (url) => url.path.length, + ) + // Use the canonicalized basename so that we display e.g. + // package:example/_example.scss rather than package:example/example + // in stack traces. + .andThen((url) => url.resolve(p.url.basename(canonicalUrl.path))) ?? // If we don't have an original URL cached, display the canonical URL // as-is. canonicalUrl; diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index a0d0eb7a9..ad9ed0a00 100644 --- a/lib/src/visitor/evaluate.dart +++ b/lib/src/visitor/evaluate.dart @@ -86,22 +86,23 @@ typedef _ScopeCallback = void Function(void Function() callback); /// declarations. /// /// Throws a [SassRuntimeException] if evaluation fails. -EvaluateResult evaluate(Stylesheet stylesheet, - {ImportCache? importCache, - NodeImporter? nodeImporter, - Importer? importer, - Iterable? functions, - Logger? logger, - bool quietDeps = false, - bool sourceMap = false}) => - _EvaluateVisitor( - importCache: importCache, - nodeImporter: nodeImporter, - functions: functions, - logger: logger, - quietDeps: quietDeps, - sourceMap: sourceMap) - .run(importer, stylesheet); +EvaluateResult evaluate( + Stylesheet stylesheet, { + ImportCache? importCache, + NodeImporter? nodeImporter, + Importer? importer, + Iterable? functions, + Logger? logger, + bool quietDeps = false, + bool sourceMap = false, +}) => _EvaluateVisitor( + importCache: importCache, + nodeImporter: nodeImporter, + functions: functions, + logger: logger, + quietDeps: quietDeps, + sourceMap: sourceMap, +).run(importer, stylesheet); /// A class that can evaluate multiple independent statements and expressions /// in the context of a single module. @@ -115,14 +116,17 @@ final class Evaluator { /// Creates an evaluator. /// /// Arguments are the same as for [evaluate]. - Evaluator( - {ImportCache? importCache, - Importer? importer, - Iterable? functions, - Logger? logger}) - : _visitor = _EvaluateVisitor( - importCache: importCache, functions: functions, logger: logger), - _importer = importer; + Evaluator({ + 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); @@ -339,63 +343,80 @@ final class _EvaluateVisitor /// Creates a new visitor. /// /// Most arguments are the same as those to [evaluate]. - _EvaluateVisitor( - {ImportCache? importCache, - NodeImporter? nodeImporter, - Iterable? functions, - Logger? logger, - bool quietDeps = false, - bool sourceMap = false}) - : _importCache = - importCache ?? (nodeImporter == null ? ImportCache.none() : null), - _nodeImporter = nodeImporter, - _logger = logger ?? const Logger.stderr(), - _quietDeps = quietDeps, - _sourceMap = sourceMap, - // The default environment is overridden in [_execute] for full - // stylesheets, but for [AsyncEvaluator] this environment is used. - _environment = Environment() { + _EvaluateVisitor({ + ImportCache? importCache, + NodeImporter? nodeImporter, + Iterable? functions, + Logger? logger, + bool quietDeps = false, + bool sourceMap = false, + }) : _importCache = + importCache ?? (nodeImporter == null ? ImportCache.none() : null), + _nodeImporter = nodeImporter, + _logger = logger ?? const Logger.stderr(), + _quietDeps = quietDeps, + _sourceMap = sourceMap, + // The default environment is overridden in [_execute] for full + // stylesheets, but for [AsyncEvaluator] this environment is used. + _environment = Environment() { var metaFunctions = [ // These functions are defined in the context of the evaluator because // they need access to the [_environment] or other local state. BuiltInCallable.function( - "global-variable-exists", r"$name, $module: null", (arguments) { - var variable = arguments[0].assertString("name"); - var module = arguments[1].realNull?.assertString("module"); - return SassBoolean(_environment.globalVariableExists( - variable.text.replaceAll("_", "-"), - namespace: module?.text)); - }, url: "sass:meta"), + "global-variable-exists", + r"$name, $module: null", + (arguments) { + var variable = arguments[0].assertString("name"); + var module = arguments[1].realNull?.assertString("module"); + return SassBoolean( + _environment.globalVariableExists( + variable.text.replaceAll("_", "-"), + namespace: module?.text, + ), + ); + }, + url: "sass:meta", + ), BuiltInCallable.function("variable-exists", r"$name", (arguments) { var variable = arguments[0].assertString("name"); return SassBoolean( - _environment.variableExists(variable.text.replaceAll("_", "-"))); + _environment.variableExists(variable.text.replaceAll("_", "-")), + ); }, url: "sass:meta"), - BuiltInCallable.function("function-exists", r"$name, $module: null", - (arguments) { + BuiltInCallable.function("function-exists", r"$name, $module: null", ( + arguments, + ) { var variable = arguments[0].assertString("name"); var module = arguments[1].realNull?.assertString("module"); - return SassBoolean(_environment.functionExists( + return SassBoolean( + _environment.functionExists( variable.text.replaceAll("_", "-"), - namespace: module?.text) || - _builtInFunctions.containsKey(variable.text)); + namespace: module?.text, + ) || + _builtInFunctions.containsKey(variable.text), + ); }, url: "sass:meta"), - BuiltInCallable.function("mixin-exists", r"$name, $module: null", - (arguments) { + BuiltInCallable.function("mixin-exists", r"$name, $module: null", ( + arguments, + ) { var variable = arguments[0].assertString("name"); var module = arguments[1].realNull?.assertString("module"); - return SassBoolean(_environment.mixinExists( + return SassBoolean( + _environment.mixinExists( variable.text.replaceAll("_", "-"), - namespace: module?.text)); + namespace: module?.text, + ), + ); }, url: "sass:meta"), BuiltInCallable.function("content-exists", "", (arguments) { if (!_environment.inMixin) { throw SassScriptException( - "content-exists() may only be called within a mixin."); + "content-exists() may only be called within a mixin.", + ); } return SassBoolean(_environment.content != null); }, url: "sass:meta"), @@ -409,7 +430,7 @@ final class _EvaluateVisitor return SassMap({ for (var (name, value) in module.variables.pairs) - SassString(name): value + SassString(name): value, }); }, url: "sass:meta"), @@ -422,7 +443,7 @@ final class _EvaluateVisitor return SassMap({ for (var (name, value) in module.functions.pairs) - SassString(name): SassFunction(value) + SassString(name): SassFunction(value), }); }, url: "sass:meta"), @@ -435,45 +456,55 @@ final class _EvaluateVisitor return SassMap({ for (var (name, value) in module.mixins.pairs) - SassString(name): SassMixin(value) + SassString(name): SassMixin(value), }); }, url: "sass:meta"), BuiltInCallable.function( - "get-function", r"$name, $css: false, $module: null", (arguments) { - var name = arguments[0].assertString("name"); - var css = arguments[1].isTruthy; - var module = arguments[2].realNull?.assertString("module"); - - if (css) { - if (module != null) { - throw r"$css and $module may not both be passed at once."; + "get-function", + r"$name, $css: false, $module: null", + (arguments) { + var name = arguments[0].assertString("name"); + var css = arguments[1].isTruthy; + var module = arguments[2].realNull?.assertString("module"); + + if (css) { + if (module != null) { + throw r"$css and $module may not both be passed at once."; + } + return SassFunction(PlainCssCallable(name.text)); } - return SassFunction(PlainCssCallable(name.text)); - } - var callable = _addExceptionSpan(_callableNode!, () { - var normalizedName = name.text.replaceAll("_", "-"); - var namespace = module?.text; - var local = - _environment.getFunction(normalizedName, namespace: namespace); - if (local != null || namespace != null) return local; - return _builtInFunctions[normalizedName]; - }); - if (callable == null) throw "Function not found: $name"; + var callable = _addExceptionSpan(_callableNode!, () { + var normalizedName = name.text.replaceAll("_", "-"); + var namespace = module?.text; + var local = _environment.getFunction( + normalizedName, + namespace: namespace, + ); + if (local != null || namespace != null) return local; + return _builtInFunctions[normalizedName]; + }); + if (callable == null) throw "Function not found: $name"; - return SassFunction(callable); - }, url: "sass:meta"), + return SassFunction(callable); + }, + url: "sass:meta", + ), - BuiltInCallable.function("get-mixin", r"$name, $module: null", - (arguments) { + BuiltInCallable.function("get-mixin", r"$name, $module: null", ( + arguments, + ) { var name = arguments[0].assertString("name"); var module = arguments[1].realNull?.assertString("module"); var callable = _addExceptionSpan( - _callableNode!, - () => _environment.getMixin(name.text.replaceAll("_", "-"), - namespace: module?.text)); + _callableNode!, + () => _environment.getMixin( + name.text.replaceAll("_", "-"), + namespace: module?.text, + ), + ); if (callable == null) throw "Mixin not found: $name"; return SassMixin(callable); @@ -484,28 +515,38 @@ final class _EvaluateVisitor var args = arguments[1] as SassArgumentList; var callableNode = _callableNode!; - var invocation = ArgumentList([], {}, callableNode.span, - rest: ValueExpression(args, callableNode.span), - keywordRest: args.keywords.isEmpty - ? null - : ValueExpression( + var invocation = ArgumentList( + [], + {}, + callableNode.span, + rest: ValueExpression(args, callableNode.span), + keywordRest: + args.keywords.isEmpty + ? null + : ValueExpression( SassMap({ for (var (name, value) in args.keywords.pairs) - SassString(name, quotes: false): value + SassString(name, quotes: false): value, }), - callableNode.span)); + callableNode.span, + ), + ); if (function is SassString) { warnForDeprecation( - "Passing a string to call() is deprecated and will be illegal in " - "Dart Sass 2.0.0.\n" - "\n" - "Recommendation: call(get-function($function))", - Deprecation.callString); + "Passing a string to call() is deprecated and will be illegal in " + "Dart Sass 2.0.0.\n" + "\n" + "Recommendation: call(get-function($function))", + Deprecation.callString, + ); var callableNode = _callableNode!; - var expression = - FunctionExpression(function.text, invocation, callableNode.span); + var expression = FunctionExpression( + function.text, + invocation, + callableNode.span, + ); return expression.accept(this); } @@ -515,10 +556,11 @@ final class _EvaluateVisitor return _runFunctionCallable(invocation, callable, _callableNode!); } else { throw SassScriptException( - "The function ${callable.name} is asynchronous.\n" - "This is probably caused by a bug in a Sass plugin."); + "The function ${callable.name} is asynchronous.\n" + "This is probably caused by a bug in a Sass plugin.", + ); } - }, url: "sass:meta") + }, url: "sass:meta"), ]; var metaMixins = [ @@ -532,8 +574,10 @@ final class _EvaluateVisitor var values = {}; var span = callableNode.span; withMap.forEach((variable, value) { - var name = - variable.assertString("with key").text.replaceAll("_", "-"); + var name = variable + .assertString("with key") + .text + .replaceAll("_", "-"); if (values.containsKey(name)) { throw "The variable \$$name was configured twice."; } @@ -543,43 +587,61 @@ final class _EvaluateVisitor configuration = ExplicitConfiguration(values, callableNode); } - _loadModule(url, "load-css()", callableNode, - (module, _) => _combineCss(module, clone: true).accept(this), - baseUrl: callableNode.span.sourceUrl, - configuration: configuration, - namesInErrors: true); + _loadModule( + url, + "load-css()", + callableNode, + (module, _) => _combineCss(module, clone: true).accept(this), + baseUrl: callableNode.span.sourceUrl, + configuration: configuration, + namesInErrors: true, + ); _assertConfigurationIsEmpty(configuration, nameInError: true); }, url: "sass:meta"), - BuiltInCallable.mixin("apply", r"$mixin, $args...", (arguments) { - var mixin = arguments[0]; - var args = arguments[1] as SassArgumentList; - - var callableNode = _callableNode!; - var invocation = ArgumentList( - const [], - const {}, - callableNode.span, - rest: ValueExpression(args, callableNode.span), - ); + BuiltInCallable.mixin( + "apply", + r"$mixin, $args...", + (arguments) { + var mixin = arguments[0]; + var args = arguments[1] as SassArgumentList; - var callable = mixin.assertMixin("mixin").callable; - var content = _environment.content; - - // ignore: unnecessary_type_check - if (callable is Callable) { - _applyMixin( - callable, content, invocation, callableNode, callableNode); - } else { - throw SassScriptException( + var callableNode = _callableNode!; + var invocation = ArgumentList( + const [], + const {}, + callableNode.span, + rest: ValueExpression(args, callableNode.span), + ); + + var callable = mixin.assertMixin("mixin").callable; + var content = _environment.content; + + // ignore: unnecessary_type_check + if (callable is Callable) { + _applyMixin( + callable, + content, + invocation, + callableNode, + callableNode, + ); + } else { + throw SassScriptException( "The mixin ${callable.name} is asynchronous.\n" - "This is probably caused by a bug in a Sass plugin."); - } - }, url: "sass:meta", acceptsContent: true), + "This is probably caused by a bug in a Sass plugin.", + ); + } + }, + url: "sass:meta", + acceptsContent: true, + ), ]; - var metaModule = BuiltInModule("meta", - functions: [...meta.moduleFunctions, ...metaFunctions], - mixins: metaMixins); + var metaModule = BuiltInModule( + "meta", + functions: [...meta.moduleFunctions, ...metaFunctions], + mixins: metaMixins, + ); for (var module in [...coreModules, metaModule]) { _builtInModules[module.url] = module; @@ -590,8 +652,8 @@ final class _EvaluateVisitor ...globalFunctions, ...[ for (var function in metaFunctions) - function.withDeprecationWarning('meta') - ] + function.withDeprecationWarning('meta'), + ], ]; for (var function in functions) { _builtInFunctions[function.name.replaceAll("_", "-")] = function; @@ -616,15 +678,23 @@ final class _EvaluateVisitor Value runExpression(Importer? importer, Expression expression) => withEvaluationContext( - _EvaluationContext(this, expression), - () => _withFakeStylesheet(importer, expression, - () => _addExceptionTrace(() => expression.accept(this)))); + _EvaluationContext(this, expression), + () => _withFakeStylesheet( + importer, + expression, + () => _addExceptionTrace(() => expression.accept(this)), + ), + ); void runStatement(Importer? importer, Statement statement) => withEvaluationContext( - _EvaluationContext(this, statement), - () => _withFakeStylesheet(importer, statement, - () => _addExceptionTrace(() => statement.accept(this)))); + _EvaluationContext(this, statement), + () => _withFakeStylesheet( + importer, + statement, + () => _addExceptionTrace(() => statement.accept(this)), + ), + ); /// Asserts that [value] is not `null` and returns it. /// @@ -639,7 +709,10 @@ final 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; @@ -672,18 +745,23 @@ final 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 callback(Module module, bool firstLoad), - {Uri? baseUrl, - Configuration? configuration, - bool namesInErrors = false}) { + void _loadModule( + Uri url, + String stackFrame, + AstNode nodeWithSpan, + void callback(Module module, bool firstLoad), { + Uri? baseUrl, + Configuration? configuration, + bool namesInErrors = false, + }) { if (_builtInModules[url] case var builtInModule?) { if (configuration is ExplicitConfiguration) { throw _exception( - namesInErrors - ? "Built-in module $url can't be configured." - : "Built-in modules can't be configured.", - configuration.nodeWithSpan.span); + namesInErrors + ? "Built-in module $url can't be configured." + : "Built-in modules can't be configured.", + configuration.nodeWithSpan.span, + ); } // Always consider built-in stylesheets to be "already loaded", since they @@ -693,20 +771,26 @@ final class _EvaluateVisitor } _withStackFrame(stackFrame, nodeWithSpan, () { - var (stylesheet, :importer, :isDependency) = - _loadStylesheet(url.toString(), nodeWithSpan.span, baseUrl: baseUrl); + var (stylesheet, :importer, :isDependency) = _loadStylesheet( + url.toString(), + nodeWithSpan.span, + baseUrl: baseUrl, + ); var canonicalUrl = stylesheet.span.sourceUrl; if (canonicalUrl != null) { if (_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) => - _multiSpanException(message, "new load", - {previousLoad.span: "original load"})) ?? + 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) => _multiSpanException(message, "new load", { + previousLoad.span: "original load", + }), + ) ?? _exception(message); } else { _activeModules[canonicalUrl] = nodeWithSpan; @@ -718,17 +802,23 @@ final class _EvaluateVisitor _inDependency = isDependency; Module module; try { - module = _execute(importer, stylesheet, - configuration: configuration, - nodeWithSpan: nodeWithSpan, - namesInErrors: namesInErrors); + module = _execute( + importer, + stylesheet, + configuration: configuration, + nodeWithSpan: nodeWithSpan, + namesInErrors: namesInErrors, + ); } finally { _activeModules.remove(canonicalUrl); _inDependency = oldInDependency; } - _addExceptionSpan(nodeWithSpan, () => callback(module, firstLoad), - addStackFrame: false); + _addExceptionSpan( + nodeWithSpan, + () => callback(module, firstLoad), + addStackFrame: false, + ); }); } @@ -740,29 +830,34 @@ final 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, - bool namesInErrors = false}) { + Module _execute( + Importer? importer, + Stylesheet stylesheet, { + Configuration? configuration, + AstNode? nodeWithSpan, + bool namesInErrors = false, + }) { var url = stylesheet.span.sourceUrl; if (_modules[url] case var alreadyLoaded?) { var currentConfiguration = configuration ?? _configuration; if (!_moduleConfigurations[url]!.sameOriginal(currentConfiguration) && 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 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 existingSpan = _moduleNodes[url]?.span; - var configurationSpan = configuration == null - ? currentConfiguration.nodeWithSpan.span - : null; + var configurationSpan = + configuration == null + ? currentConfiguration.nodeWithSpan.span + : null; var secondarySpans = { if (existingSpan != null) existingSpan: "original load", - if (configurationSpan != null) configurationSpan: "configuration" + if (configurationSpan != null) configurationSpan: "configuration", }; throw secondarySpans.isEmpty @@ -809,9 +904,10 @@ final class _EvaluateVisitor if (configuration != null) _configuration = configuration; visitStylesheet(stylesheet); - css = _outOfOrderImports == null - ? root - : CssStylesheet(_addOutOfOrderImports(), stylesheet.span); + css = + _outOfOrderImports == null + ? root + : CssStylesheet(_addOutOfOrderImports(), stylesheet.span); preModuleComments = _preModuleComments; _importer = oldImporter; @@ -832,7 +928,10 @@ final class _EvaluateVisitor }); var module = environment.toModule( - css, preModuleComments ?? const {}, extensionStore); + css, + preModuleComments ?? const {}, + extensionStore, + ); if (url != null) { _modules[url] = module; _moduleConfigurations[url] = _configuration; @@ -848,10 +947,10 @@ final class _EvaluateVisitor switch (_outOfOrderImports) { null => _root.children, var outOfOrderImports => [ - ..._root.children.take(_endOfImports), - ...outOfOrderImports, - ..._root.children.skip(_endOfImports) - ] + ..._root.children.take(_endOfImports), + ...outOfOrderImports, + ..._root.children.skip(_endOfImports), + ], }; /// Returns a new stylesheet containing [root]'s CSS as well as the CSS of all @@ -943,11 +1042,15 @@ final class _EvaluateVisitor // Add all as-yet-unsatisfied extensions before adding downstream // [ExtensionStore]s, because those are all in [unsatisfiedExtensions] // already. - unsatisfiedExtensions.addAll(module.extensionStore.extensionsWhereTarget( - (target) => !originalSelectors.contains(target))); + unsatisfiedExtensions.addAll( + module.extensionStore.extensionsWhereTarget( + (target) => !originalSelectors.contains(target), + ), + ); - downstreamExtensionStores[module.url] - .andThen(module.extensionStore.addExtensions); + downstreamExtensionStores[module.url].andThen( + module.extensionStore.addExtensions, + ); if (module.extensionStore.isEmpty) continue; for (var upstream in module.upstream) { @@ -961,8 +1064,9 @@ final class _EvaluateVisitor // Remove all extensions that are now satisfied after adding downstream // [ExtensionStore]s so it counts any downstream extensions that have been // newly satisfied. - unsatisfiedExtensions.removeAll(module.extensionStore - .extensionsWhereTarget(originalSelectors.contains)); + unsatisfiedExtensions.removeAll( + module.extensionStore.extensionsWhereTarget(originalSelectors.contains), + ); } if (unsatisfiedExtensions.isNotEmpty) { @@ -973,9 +1077,10 @@ final class _EvaluateVisitor /// Throws an exception indicating that [extension] is unsatisfied. Never _throwForUnsatisfiedExtension(Extension extension) { throw SassException( - 'The target selector was not found.\n' - 'Use "@extend ${extension.target} !optional" to avoid this error.', - extension.span); + 'The target selector was not found.\n' + 'Use "@extend ${extension.target} !optional" to avoid this error.', + extension.span, + ); } /// Returns the index of the first node in [statements] that comes after all @@ -1010,7 +1115,8 @@ final class _EvaluateVisitor // module's definition, even if their assignments aren't reached. for (var (name, span) in node.globalVariables.pairs) { visitVariableDeclaration( - VariableDeclaration(name, NullExpression(span), span, guarded: true)); + VariableDeclaration(name, NullExpression(span), span, guarded: true), + ); } return null; @@ -1019,8 +1125,10 @@ final class _EvaluateVisitor Value? visitAtRootRule(AtRootRule node) { var query = AtRootQuery.defaultQuery; if (node.query case var unparsedQuery?) { - var (resolved, map) = - _performInterpolationWithMap(unparsedQuery, warnForColor: true); + var (resolved, map) = _performInterpolationWithMap( + unparsedQuery, + warnForColor: true, + ); query = AtRootQuery.parse(resolved, interpolationMap: map); } @@ -1033,7 +1141,8 @@ final class _EvaluateVisitor parent = grandparent; } else { throw StateError( - "CssNodes must have a CssStylesheet transitive parent node."); + "CssNodes must have a CssStylesheet transitive parent node.", + ); } } var root = _trimIncluded(included); @@ -1094,7 +1203,8 @@ final class _EvaluateVisitor parent = grandparent; } else { throw ArgumentError( - "Expected ${nodes[i]} to be an ancestor of $this."); + "Expected ${nodes[i]} to be an ancestor of $this.", + ); } } innermostContiguous ??= i; @@ -1118,10 +1228,11 @@ final class _EvaluateVisitor /// duration, based on which rules are excluded by [query]. It always assigns /// [_parent] to [newParent]. _ScopeCallback _scopeForAtRoot( - AtRootRule node, - ModifiableCssParentNode newParent, - AtRootQuery query, - List included) { + AtRootRule node, + ModifiableCssParentNode newParent, + AtRootQuery query, + List included, + ) { var scope = (void callback()) { // We can't use [_withParent] here because it'll add the node to the tree // in the wrong place. @@ -1143,8 +1254,9 @@ final class _EvaluateVisitor if (_mediaQueries != null && query.excludesName('media')) { var innerScope = scope; - scope = (callback) => - _withMediaQueries(null, null, () => innerScope(callback)); + scope = + (callback) => + _withMediaQueries(null, null, () => innerScope(callback)); } if (_inKeyframes && query.excludesName('keyframes')) { @@ -1170,8 +1282,10 @@ final class _EvaluateVisitor return scope; } - Value visitContentBlock(ContentBlock node) => throw UnsupportedError( - "Evaluation handles @include and its content block together."); + Value visitContentBlock(ContentBlock node) => + throw UnsupportedError( + "Evaluation handles @include and its content block together.", + ); Value? visitContentRule(ContentRule node) { var content = _environment.content; @@ -1190,20 +1304,24 @@ final class _EvaluateVisitor Value? visitDebugRule(DebugRule node) { var value = node.expression.accept(this); _logger.debug( - value is SassString ? value.text : serializeValue(value, inspect: true), - node.span); + value is SassString ? value.text : serializeValue(value, inspect: true), + node.span, + ); return null; } Value? visitDeclaration(Declaration node) { if (_styleRule == null && !_inUnknownAtRule && !_inKeyframes) { throw _exception( - "Declarations may only be used within style rules.", node.span); + "Declarations may only be used within style rules.", + node.span, + ); } if (_declarationName != null && node.isCustomProperty) { throw _exception( - 'Declarations whose names begin with "--" may not be nested.', - node.span); + 'Declarations whose names begin with "--" may not be nested.', + node.span, + ); } var siblings = _parent.parent!.children; @@ -1211,8 +1329,7 @@ final class _EvaluateVisitor if (siblings.last != _parent && // Reproduce this condition from [_warn] so that we don't add anything to // [interleavedRules] for declarations in dependencies. - !(_quietDeps && - (_inDependency || (_currentCallable?.inDependency ?? false)))) { + !(_quietDeps && (_inDependency || (_currentCallable?.inDependency ?? false)))) { loop: for (var sibling in siblings.skip(siblings.indexOf(_parent) + 1)) { switch (sibling) { @@ -1227,18 +1344,20 @@ final class _EvaluateVisitor // add no specificity and they're nested in the same parent as this // declaration. _warn( - "Sass's behavior for declarations that appear after nested\n" - "rules will be changing to match the behavior specified by CSS " - "in an upcoming\n" - "version. To keep the existing behavior, move the declaration " - "above the nested\n" - "rule. To opt into the new behavior, wrap the declaration in " - "`& {}`.\n" - "\n" - "More info: https://sass-lang.com/d/mixed-decls", - MultiSpan( - node.span, 'declaration', {sibling.span: 'nested rule'}), - Deprecation.mixedDecls); + "Sass's behavior for declarations that appear after nested\n" + "rules will be changing to match the behavior specified by CSS " + "in an upcoming\n" + "version. To keep the existing behavior, move the declaration " + "above the nested\n" + "rule. To opt into the new behavior, wrap the declaration in " + "`& {}`.\n" + "\n" + "More info: https://sass-lang.com/d/mixed-decls", + MultiSpan(node.span, 'declaration', { + sibling.span: 'nested rule', + }), + Deprecation.mixedDecls, + ); interleavedRules.clear(); break; } @@ -1255,16 +1374,23 @@ final class _EvaluateVisitor // If the value is an empty list, preserve it, because converting it to CSS // will throw an error that we want the user to see. if (!value.isBlank || _isEmptyList(value)) { - _parent.addChild(ModifiableCssDeclaration( - name, CssValue(value, expression.span), node.span, + _parent.addChild( + ModifiableCssDeclaration( + name, + CssValue(value, expression.span), + node.span, parsedAsCustomProperty: node.isCustomProperty, interleavedRules: interleavedRules, trace: interleavedRules.isEmpty ? null : _stackTrace(node.span), valueSpanForMap: - _sourceMap ? node.value.andThen(_expressionNode)?.span : null)); + _sourceMap ? node.value.andThen(_expressionNode)?.span : null, + ), + ); } else if (name.value.startsWith('--')) { throw _exception( - "Custom property values may not be empty.", expression.span); + "Custom property values may not be empty.", + expression.span, + ); } } @@ -1289,16 +1415,22 @@ final class _EvaluateVisitor var list = node.list.accept(this); var nodeWithSpan = _expressionNode(node.list); var setVariables = switch (node.variables) { - [var variable] => (Value value) => _environment.setLocalVariable( - variable, _withoutSlash(value, nodeWithSpan), nodeWithSpan), - var variables => (Value value) => - _setMultipleVariables(variables, value, nodeWithSpan) + [var variable] => + (Value value) => _environment.setLocalVariable( + variable, + _withoutSlash(value, nodeWithSpan), + nodeWithSpan, + ), + var variables => + (Value value) => _setMultipleVariables(variables, value, nodeWithSpan), }; return _environment.scope(() { return _handleReturn(list.asList, (element) { setVariables(element); return _handleReturn( - node.children, (child) => child.accept(this)); + node.children, + (child) => child.accept(this), + ); }); }, semiGlobal: true); } @@ -1306,12 +1438,18 @@ final 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++) { _environment.setLocalVariable( - variables[i], _withoutSlash(list[i], nodeWithSpan), nodeWithSpan); + variables[i], + _withoutSlash(list[i], nodeWithSpan), + nodeWithSpan, + ); } for (var i = minLength; i < variables.length; i++) { _environment.setLocalVariable(variables[i], sassNull, nodeWithSpan); @@ -1326,28 +1464,37 @@ final class _EvaluateVisitor var styleRule = _styleRule; if (styleRule == null || _declarationName != null) { throw _exception( - "@extend may only be used within style rules.", node.span); + "@extend may only be used within style rules.", + node.span, + ); } for (var complex in styleRule.originalSelector.components) { if (!complex.isBogus) continue; _warn( - 'The selector "${complex.toString().trim()}" is invalid CSS and ' + - (complex.isUseless ? "can't" : "shouldn't") + - ' be an extender.\n' - 'This will be an error in Dart Sass 2.0.0.\n' - '\n' - 'More info: https://sass-lang.com/d/bogus-combinators', - MultiSpan(complex.span.trimRight(), 'invalid selector', - {node.span: '@extend rule'}), - Deprecation.bogusCombinators); + 'The selector "${complex.toString().trim()}" is invalid CSS and ' + + (complex.isUseless ? "can't" : "shouldn't") + + ' be an extender.\n' + 'This will be an error in Dart Sass 2.0.0.\n' + '\n' + 'More info: https://sass-lang.com/d/bogus-combinators', + MultiSpan(complex.span.trimRight(), 'invalid selector', { + node.span: '@extend rule', + }), + Deprecation.bogusCombinators, + ); } - var (targetText, targetMap) = - _performInterpolationWithMap(node.selector, warnForColor: true); + var (targetText, targetMap) = _performInterpolationWithMap( + node.selector, + warnForColor: true, + ); - var list = SelectorList.parse(trimAscii(targetText, excludeEscape: true), - interpolationMap: targetMap, allowParent: false); + var list = SelectorList.parse( + trimAscii(targetText, excludeEscape: true), + interpolationMap: targetMap, + allowParent: false, + ); for (var complex in list.components) { var compound = complex.singleCompound; @@ -1355,20 +1502,27 @@ final class _EvaluateVisitor // If the selector was a compound selector but not a simple // selector, emit a more explicit error. throw SassFormatException( - "complex selectors may not be extended.", complex.span); + "complex selectors may not be extended.", + complex.span, + ); } var simple = compound.singleSimple; if (simple == null) { throw SassFormatException( - "compound selectors may no longer be extended.\n" - "Consider `@extend ${compound.components.join(', ')}` instead.\n" - "See https://sass-lang.com/d/extend-compound for details.\n", - compound.span); + "compound selectors may no longer be extended.\n" + "Consider `@extend ${compound.components.join(', ')}` instead.\n" + "See https://sass-lang.com/d/extend-compound for details.\n", + compound.span, + ); } _extensionStore.addExtension( - styleRule.selector, simple, node, _mediaQueries); + styleRule.selector, + simple, + node, + _mediaQueries, + ); } return null; @@ -1380,18 +1534,22 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "At-rules may not be used within nested declarations.", node.span); + "At-rules may not be used within nested declarations.", + node.span, + ); } var name = _interpolationToValue(node.name); - var value = node.value.andThen((value) => - _interpolationToValue(value, trim: true, warnForColor: true)); + var value = node.value.andThen( + (value) => _interpolationToValue(value, trim: true, warnForColor: true), + ); var children = node.children; if (children == null) { _parent.addChild( - ModifiableCssAtRule(name, node.span, childless: true, value: value)); + ModifiableCssAtRule(name, node.span, childless: true, value: value), + ); return null; } @@ -1403,28 +1561,31 @@ final class _EvaluateVisitor _inUnknownAtRule = true; } - _withParent(ModifiableCssAtRule(name, node.span, value: value), () { - var styleRule = _styleRule; - if (styleRule == null || _inKeyframes || name.value == 'font-face') { - // Special-cased at-rules within style blocks are pulled out to the - // root. Equivalent to prepending "@at-root" on them. - for (var child in children) { - child.accept(this); - } - } else { - // If we're in a style rule, copy it into the at-rule so that - // declarations immediately inside it have somewhere to go. - // - // For example, "a {@foo {b: c}}" should produce "@foo {a {b: c}}". - _withParent(styleRule.copyWithoutChildren(), () { + _withParent( + ModifiableCssAtRule(name, node.span, value: value), + () { + var styleRule = _styleRule; + if (styleRule == null || _inKeyframes || name.value == 'font-face') { + // Special-cased at-rules within style blocks are pulled out to the + // root. Equivalent to prepending "@at-root" on them. for (var child in children) { child.accept(this); } - }, scopeWhen: false); - } - }, - through: (node) => node is CssStyleRule, - scopeWhen: node.hasDeclarations); + } else { + // If we're in a style rule, copy it into the at-rule so that + // declarations immediately inside it have somewhere to go. + // + // For example, "a {@foo {b: c}}" should produce "@foo {a {b: c}}". + _withParent(styleRule.copyWithoutChildren(), () { + for (var child in children) { + child.accept(this); + } + }, scopeWhen: false); + } + }, + through: (node) => node is CssStyleRule, + scopeWhen: node.hasDeclarations, + ); _inUnknownAtRule = wasInUnknownAtRule; _inKeyframes = wasInKeyframes; @@ -1433,16 +1594,22 @@ final class _EvaluateVisitor Value? visitForRule(ForRule node) { var fromNumber = _addExceptionSpan( - node.from, () => node.from.accept(this).assertNumber()); - var toNumber = - _addExceptionSpan(node.to, () => node.to.accept(this).assertNumber()); + node.from, + () => node.from.accept(this).assertNumber(), + ); + var toNumber = _addExceptionSpan( + node.to, + () => node.to.accept(this).assertNumber(), + ); var from = _addExceptionSpan(node.from, () => fromNumber.assertInt()); var to = _addExceptionSpan( - node.to, - () => toNumber - .coerce(fromNumber.numeratorUnits, fromNumber.denominatorUnits) - .assertInt()); + node.to, + () => + toNumber + .coerce(fromNumber.numeratorUnits, fromNumber.denominatorUnits) + .assertInt(), + ); var direction = from > to ? -1 : 1; if (!node.isExclusive) to += direction; @@ -1452,13 +1619,18 @@ final class _EvaluateVisitor var nodeWithSpan = _expressionNode(node.from); for (var i = from; i != to; i += direction) { _environment.setLocalVariable( - node.variable, - SassNumber.withUnits(i, - numeratorUnits: fromNumber.numeratorUnits, - denominatorUnits: fromNumber.denominatorUnits), - nodeWithSpan); + node.variable, + SassNumber.withUnits( + i, + numeratorUnits: fromNumber.numeratorUnits, + denominatorUnits: fromNumber.denominatorUnits, + ), + nodeWithSpan, + ); if (_handleReturn( - node.children, (child) => child.accept(this)) + node.children, + (child) => child.accept(this), + ) case var result?) { return result; } @@ -1472,26 +1644,31 @@ final class _EvaluateVisitor var adjustedConfiguration = oldConfiguration.throughForward(node); if (node.configuration.isNotEmpty) { - var newConfiguration = - _addForwardConfiguration(adjustedConfiguration, node); + var newConfiguration = _addForwardConfiguration( + adjustedConfiguration, + node, + ); _loadModule(node.url, "@forward", node, (module, firstLoad) { if (firstLoad) _registerCommentsForModule(module); _environment.forwardModule(module, node); }, configuration: newConfiguration); - _removeUsedConfiguration(adjustedConfiguration, newConfiguration, - except: { - for (var variable in node.configuration) - if (!variable.isGuarded) variable.name - }); + _removeUsedConfiguration( + adjustedConfiguration, + newConfiguration, + except: { + for (var variable in node.configuration) + if (!variable.isGuarded) variable.name, + }, + ); // Remove all the variables that weren't configured by this particular // `@forward` before checking that the configuration is empty. Errors for // outer `with` clauses will be thrown once those clauses finish // executing. var configuredVariables = { - for (var variable in node.configuration) variable.name + for (var variable in node.configuration) variable.name, }; for (var name in newConfiguration.values.keys.toList()) { if (!configuredVariables.contains(name)) newConfiguration.remove(name); @@ -1513,7 +1690,9 @@ final class _EvaluateVisitor /// Updates [configuration] to include [node]'s configuration and returns the /// result. Configuration _addForwardConfiguration( - Configuration configuration, ForwardRule node) { + Configuration configuration, + ForwardRule node, + ) { var newValues = Map.of(configuration.values); for (var variable in node.configuration) { if (variable.isGuarded) { @@ -1526,9 +1705,10 @@ final class _EvaluateVisitor var variableNodeWithSpan = _expressionNode(variable.expression); newValues[variable.name] = ConfiguredValue.explicit( - _withoutSlash(variable.expression.accept(this), variableNodeWithSpan), - variable.span, - variableNodeWithSpan); + _withoutSlash(variable.expression.accept(this), variableNodeWithSpan), + variable.span, + variableNodeWithSpan, + ); } if (configuration is ExplicitConfiguration || configuration.isEmpty) { @@ -1555,8 +1735,10 @@ final class _EvaluateVisitor /// Remove configured values from [upstream] that have been removed from /// [downstream], unless they match a name in [except]. void _removeUsedConfiguration( - Configuration upstream, Configuration downstream, - {required Set except}) { + Configuration upstream, + Configuration downstream, { + required Set except, + }) { for (var name in upstream.values.keys.toList()) { if (except.contains(name)) continue; if (!downstream.values.containsKey(name)) upstream.remove(name); @@ -1571,8 +1753,10 @@ final class _EvaluateVisitor /// If [nameInError] is `true`, this includes the name of the configured /// 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, - {bool nameInError = false}) { + void _assertConfigurationIsEmpty( + Configuration configuration, { + bool nameInError = false, + }) { // By definition, implicit configurations are allowed to only use a subset // of their values. if (configuration is! ExplicitConfiguration) return; @@ -1580,17 +1764,23 @@ final class _EvaluateVisitor var (name, value) = configuration.values.pairs.first; 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); + nameInError + ? "\$$name was not declared with !default in the @used " + "module." + : "This variable was not declared with !default in the @used " + "module.", + value.configurationSpan, + ); } Value? visitFunctionRule(FunctionRule node) { - _environment.setFunction(UserDefinedCallable(node, _environment.closure(), - inDependency: _inDependency)); + _environment.setFunction( + UserDefinedCallable( + node, + _environment.closure(), + inDependency: _inDependency, + ), + ); return null; } @@ -1603,11 +1793,16 @@ final class _EvaluateVisitor } } - return clause.andThen((clause) => _environment.scope( + return clause.andThen( + (clause) => _environment.scope( () => _handleReturn( - clause.children, (child) => child.accept(this)), + clause.children, + (child) => child.accept(this), + ), semiGlobal: true, - when: clause.hasDeclarations)); + when: clause.hasDeclarations, + ), + ); } Value? visitImportRule(ImportRule node) { @@ -1624,15 +1819,22 @@ final class _EvaluateVisitor /// Adds the stylesheet imported by [import] to the current document. void _visitDynamicImport(DynamicImport import) { return _withStackFrame("@import", import, () { - var (stylesheet, :importer, :isDependency) = - _loadStylesheet(import.urlString, import.span, forImport: true); + var (stylesheet, :importer, :isDependency) = _loadStylesheet( + import.urlString, + import.span, + forImport: true, + ); var url = stylesheet.span.sourceUrl; if (url != null) { if (_activeModules.containsKey(url)) { - throw _activeModules[url].andThen((previousLoad) => - _multiSpanException("This file is already being loaded.", - "new load", {previousLoad.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; @@ -1664,7 +1866,7 @@ final class _EvaluateVisitor // new one. var loadsUserDefinedModules = stylesheet.uses.any((rule) => rule.url.scheme != 'sass') || - stylesheet.forwards.any((rule) => rule.url.scheme != 'sass'); + stylesheet.forwards.any((rule) => rule.url.scheme != 'sass'); late List children; var environment = _environment.forImport(); @@ -1719,8 +1921,10 @@ final class _EvaluateVisitor // clone all modules' CSS. Otherwise, it's possible that they'll be // used or imported from another location that shouldn't have the same // extensions applied. - _combineCss(module, clone: module.transitivelyContainsExtensions) - .accept(this); + _combineCss( + module, + clone: module.transitivelyContainsExtensions, + ).accept(this); } var visitor = _ImportedCssVisitor(this); @@ -1738,23 +1942,32 @@ final class _EvaluateVisitor /// /// This first tries loading [url] relative to [baseUrl], which defaults to /// `_stylesheet.span.sourceUrl`. - _LoadedStylesheet _loadStylesheet(String url, FileSpan span, - {Uri? baseUrl, bool forImport = false}) { + _LoadedStylesheet _loadStylesheet( + String url, + FileSpan span, { + Uri? baseUrl, + bool forImport = false, + }) { try { assert(_importSpan == null); _importSpan = span; if (_importCache case var importCache?) { baseUrl ??= _stylesheet.span.sourceUrl; - if (importCache.canonicalize(Uri.parse(url), - baseImporter: _importer, baseUrl: baseUrl, forImport: forImport) + if (importCache.canonicalize( + Uri.parse(url), + baseImporter: _importer, + baseUrl: baseUrl, + forImport: forImport, + ) case (var importer, var canonicalUrl, :var originalUrl)) { if (canonicalUrl.scheme == '') { _logger.warnForDeprecation( - Deprecation.relativeCanonical, - "Importer $importer canonicalized $url to $canonicalUrl.\n" - "Relative canonical URLs are deprecated and will eventually be " - "disallowed."); + Deprecation.relativeCanonical, + "Importer $importer canonicalized $url to $canonicalUrl.\n" + "Relative canonical URLs are deprecated and will eventually be " + "disallowed.", + ); } // Make sure we record the canonical URL as "loaded" even if the // actual load fails, because watchers should watch it to see if it @@ -1762,8 +1975,11 @@ final class _EvaluateVisitor _loadedUrls.add(canonicalUrl); var isDependency = _inDependency || importer != _importer; - if (importCache.importCanonical(importer, canonicalUrl, - originalUrl: originalUrl) + if (importCache.importCanonical( + importer, + canonicalUrl, + originalUrl: originalUrl, + ) case var stylesheet?) { return (stylesheet, importer: importer, isDependency: isDependency); } @@ -1772,7 +1988,10 @@ final class _EvaluateVisitor if (_nodeImporter != null) { if (_importLikeNode( - url, baseUrl ?? _stylesheet.span.sourceUrl, forImport) + url, + baseUrl ?? _stylesheet.span.sourceUrl, + forImport, + ) case var result?) { result.$1.span.sourceUrl.andThen(_loadedUrls.add); return result; @@ -1801,7 +2020,10 @@ final class _EvaluateVisitor /// /// Returns the [Stylesheet], or `null` if the import failed. _LoadedStylesheet? _importLikeNode( - String originalUrl, Uri? previous, bool forImport) { + String originalUrl, + Uri? previous, + bool forImport, + ) { var result = _nodeImporter!.loadRelative(originalUrl, previous, forImport); bool isDependency; @@ -1816,10 +2038,12 @@ final class _EvaluateVisitor var (contents, url) = result; return ( Stylesheet.parse( - contents, url.startsWith('file') ? Syntax.forPath(url) : Syntax.scss, - url: url), + contents, + url.startsWith('file') ? Syntax.forPath(url) : Syntax.scss, + url: url, + ), importer: null, - isDependency: isDependency + isDependency: isDependency, ); } @@ -1829,9 +2053,12 @@ final class _EvaluateVisitor // here should be mirrored there. var node = ModifiableCssImport( - _interpolationToValue(import.url), import.span, - modifiers: - import.modifiers.andThen?>(_interpolationToValue)); + _interpolationToValue(import.url), + import.span, + modifiers: import.modifiers.andThen?>( + _interpolationToValue, + ), + ); if (_parent != _root) { _parent.addChild(node); @@ -1845,11 +2072,12 @@ final class _EvaluateVisitor /// Evaluate a given [mixin] with [arguments] and [contentCallable] void _applyMixin( - Callable? mixin, - UserDefinedCallable? contentCallable, - ArgumentList arguments, - AstNode nodeWithSpan, - AstNode nodeWithSpanWithoutContent) { + Callable? mixin, + UserDefinedCallable? contentCallable, + ArgumentList arguments, + AstNode nodeWithSpan, + AstNode nodeWithSpanWithoutContent, + ) { switch (mixin) { case null: throw _exception("Undefined mixin.", nodeWithSpan.span); @@ -1858,13 +2086,16 @@ final class _EvaluateVisitor { var evaluated = _evaluateArguments(arguments); var (overload, _) = mixin.callbackFor( - evaluated.positional.length, MapKeySet(evaluated.named)); + evaluated.positional.length, + MapKeySet(evaluated.named), + ); throw MultiSpanSassRuntimeException( - "Mixin doesn't accept a content block.", - nodeWithSpanWithoutContent.span, - "invocation", - {overload.spanWithName: "declaration"}, - _stackTrace(nodeWithSpanWithoutContent.span)); + "Mixin doesn't accept a content block.", + nodeWithSpanWithoutContent.span, + "invocation", + {overload.spanWithName: "declaration"}, + _stackTrace(nodeWithSpanWithoutContent.span), + ); } case BuiltInCallable(): _environment.withContent(contentCallable, () { @@ -1874,28 +2105,35 @@ final class _EvaluateVisitor }); case UserDefinedCallable( - declaration: MixinRule(hasContent: false) + declaration: MixinRule(hasContent: false), ) when contentCallable != null: throw MultiSpanSassRuntimeException( - "Mixin doesn't accept a content block.", - nodeWithSpanWithoutContent.span, - "invocation", - {mixin.declaration.parameters.spanWithName: "declaration"}, - _stackTrace(nodeWithSpanWithoutContent.span)); + "Mixin doesn't accept a content block.", + nodeWithSpanWithoutContent.span, + "invocation", + {mixin.declaration.parameters.spanWithName: "declaration"}, + _stackTrace(nodeWithSpanWithoutContent.span), + ); case UserDefinedCallable(): - _runUserDefinedCallable(arguments, mixin, nodeWithSpanWithoutContent, - () { - _environment.withContent(contentCallable, () { - _environment.asMixin(() { - for (var statement in mixin.declaration.children) { - _addErrorSpan( - nodeWithSpanWithoutContent, () => statement.accept(this)); - } + _runUserDefinedCallable( + arguments, + mixin, + nodeWithSpanWithoutContent, + () { + _environment.withContent(contentCallable, () { + _environment.asMixin(() { + for (var statement in mixin.declaration.children) { + _addErrorSpan( + nodeWithSpanWithoutContent, + () => statement.accept(this), + ); + } + }); }); - }); - }); + }, + ); case _: throw UnsupportedError("Unknown callable type $mixin."); @@ -1903,36 +2141,54 @@ final class _EvaluateVisitor } Value? visitIncludeRule(IncludeRule node) { - var mixin = _addExceptionSpan(node, - () => _environment.getMixin(node.name, namespace: node.namespace)); + var mixin = _addExceptionSpan( + node, + () => _environment.getMixin(node.name, namespace: node.namespace), + ); if (node.originalName.startsWith('--') && mixin is UserDefinedCallable && !mixin.declaration.originalName.startsWith('--')) { _warn( - 'Sass @mixin names beginning with -- are deprecated for forward-' - 'compatibility with plain CSS mixins.\n' - '\n' - 'For details, see https://sass-lang.com/d/css-function-mixin', - node.nameSpan, - Deprecation.cssFunctionMixin); + 'Sass @mixin names beginning with -- are deprecated for forward-' + 'compatibility with plain CSS mixins.\n' + '\n' + 'For details, see https://sass-lang.com/d/css-function-mixin', + node.nameSpan, + Deprecation.cssFunctionMixin, + ); } - var contentCallable = node.content.andThen((content) => UserDefinedCallable( - content, _environment.closure(), - inDependency: _inDependency)); + var contentCallable = node.content.andThen( + (content) => UserDefinedCallable( + content, + _environment.closure(), + inDependency: _inDependency, + ), + ); - var nodeWithSpanWithoutContent = - AstNode.fake(() => node.spanWithoutContent); + var nodeWithSpanWithoutContent = AstNode.fake( + () => node.spanWithoutContent, + ); - _applyMixin(mixin, contentCallable, node.arguments, node, - nodeWithSpanWithoutContent); + _applyMixin( + mixin, + contentCallable, + node.arguments, + node, + nodeWithSpanWithoutContent, + ); return null; } Value? visitMixinRule(MixinRule node) { - _environment.setMixin(UserDefinedCallable(node, _environment.closure(), - inDependency: _inDependency)); + _environment.setMixin( + UserDefinedCallable( + node, + _environment.closure(), + inDependency: _inDependency, + ), + ); return null; } @@ -1960,45 +2216,52 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "Media rules may not be used within nested declarations.", node.span); + "Media rules may not be used within nested declarations.", + node.span, + ); } var queries = _visitMediaQueries(node.query); - var mergedQueries = _mediaQueries - .andThen((mediaQueries) => _mergeMediaQueries(mediaQueries, queries)); + var mergedQueries = _mediaQueries.andThen( + (mediaQueries) => _mergeMediaQueries(mediaQueries, queries), + ); if (mergedQueries != null && mergedQueries.isEmpty) return null; - var mergedSources = mergedQueries == null - ? const {} - : {..._mediaQuerySources!, ..._mediaQueries!, ...queries}; + var mergedSources = + mergedQueries == null + ? const {} + : {..._mediaQuerySources!, ..._mediaQueries!, ...queries}; - _withParent(ModifiableCssMediaRule(mergedQueries ?? queries, node.span), - () { - _withMediaQueries(mergedQueries ?? queries, mergedSources, () { - if (_styleRule case var styleRule?) { - // If we're in a style rule, copy it into the media query so that - // declarations immediately inside @media have somewhere to go. - // - // For example, "a {@media screen {b: c}}" should produce - // "@media screen {a {b: c}}". - _withParent(styleRule.copyWithoutChildren(), () { + _withParent( + ModifiableCssMediaRule(mergedQueries ?? queries, node.span), + () { + _withMediaQueries(mergedQueries ?? queries, mergedSources, () { + if (_styleRule case var styleRule?) { + // If we're in a style rule, copy it into the media query so that + // declarations immediately inside @media have somewhere to go. + // + // For example, "a {@media screen {b: c}}" should produce + // "@media screen {a {b: c}}". + _withParent(styleRule.copyWithoutChildren(), () { + for (var child in node.children) { + child.accept(this); + } + }, scopeWhen: false); + } else { for (var child in node.children) { child.accept(this); } - }, scopeWhen: false); - } else { - for (var child in node.children) { - child.accept(this); } - } - }); - }, - through: (node) => - node is CssStyleRule || - (mergedSources.isNotEmpty && - node is CssMediaRule && - node.queries.every(mergedSources.contains)), - scopeWhen: node.hasDeclarations); + }); + }, + through: + (node) => + node is CssStyleRule || + (mergedSources.isNotEmpty && + node is CssMediaRule && + node.queries.every(mergedSources.contains)), + scopeWhen: node.hasDeclarations, + ); return null; } @@ -2006,8 +2269,10 @@ final class _EvaluateVisitor /// Evaluates [interpolation] and parses the result as a list of media /// queries. List _visitMediaQueries(Interpolation interpolation) { - var (resolved, map) = - _performInterpolationWithMap(interpolation, warnForColor: true); + var (resolved, map) = _performInterpolationWithMap( + interpolation, + warnForColor: true, + ); return CssMediaQuery.parseList(resolved, interpolationMap: map); } @@ -2018,7 +2283,9 @@ final class _EvaluateVisitor /// and [queries2], or `null` if there are contexts that can't be represented /// by media queries. List? _mergeMediaQueries( - Iterable queries1, Iterable queries2) { + Iterable queries1, + Iterable queries2, + ) { var queries = []; for (var query1 in queries1) { inner: @@ -2047,71 +2314,97 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "Style rules may not be used within nested declarations.", node.span); + "Style rules may not be used within nested declarations.", + node.span, + ); } else if (_inKeyframes && _parent is CssKeyframeBlock) { throw _exception( - "Style rules may not be used within keyframe blocks.", node.span); + "Style rules may not be used within keyframe blocks.", + node.span, + ); } - var (selectorText, selectorMap) = - _performInterpolationWithMap(node.selector, warnForColor: true); + var (selectorText, selectorMap) = _performInterpolationWithMap( + node.selector, + warnForColor: true, + ); if (_inKeyframes) { // NOTE: this logic is largely duplicated in [visitCssKeyframeBlock]. Most // changes here should be mirrored there. var parsedSelector = - KeyframeSelectorParser(selectorText, interpolationMap: selectorMap) - .parse(); + KeyframeSelectorParser( + selectorText, + interpolationMap: selectorMap, + ).parse(); var rule = ModifiableCssKeyframeBlock( - CssValue(List.unmodifiable(parsedSelector), node.selector.span), - node.span); - _withParent(rule, () { - for (var child in node.children) { - child.accept(this); - } - }, - through: (node) => node is CssStyleRule, - scopeWhen: node.hasDeclarations); + CssValue(List.unmodifiable(parsedSelector), node.selector.span), + node.span, + ); + _withParent( + rule, + () { + for (var child in node.children) { + child.accept(this); + } + }, + through: (node) => node is CssStyleRule, + scopeWhen: node.hasDeclarations, + ); return null; } - var parsedSelector = SelectorList.parse(selectorText, - interpolationMap: selectorMap, plainCss: _stylesheet.plainCss); + var parsedSelector = SelectorList.parse( + selectorText, + interpolationMap: selectorMap, + plainCss: _stylesheet.plainCss, + ); var nest = !(_styleRule?.fromPlainCss ?? false); if (nest) { if (_stylesheet.plainCss) { for (var complex in parsedSelector.components) { - if (complex.leadingCombinators case [var first, ...] - when _stylesheet.plainCss) { + if (complex.leadingCombinators case [ + var first, + ..., + ] when _stylesheet.plainCss) { throw _exception( - "Top-level leading combinators aren't allowed in plain CSS.", - first.span); + "Top-level leading combinators aren't allowed in plain CSS.", + first.span, + ); } } } parsedSelector = parsedSelector.nestWithin( - _styleRuleIgnoringAtRoot?.originalSelector, - implicitParent: !_atRootExcludingStyleRule, - preserveParentSelectors: _stylesheet.plainCss); + _styleRuleIgnoringAtRoot?.originalSelector, + implicitParent: !_atRootExcludingStyleRule, + preserveParentSelectors: _stylesheet.plainCss, + ); } var selector = _extensionStore.addSelector(parsedSelector, _mediaQueries); - var rule = ModifiableCssStyleRule(selector, node.span, - originalSelector: parsedSelector, fromPlainCss: _stylesheet.plainCss); + var rule = ModifiableCssStyleRule( + selector, + node.span, + originalSelector: parsedSelector, + fromPlainCss: _stylesheet.plainCss, + ); var oldAtRootExcludingStyleRule = _atRootExcludingStyleRule; _atRootExcludingStyleRule = false; - _withParent(rule, () { - _withStyleRule(rule, () { - for (var child in node.children) { - child.accept(this); - } - }); - }, - through: nest ? (node) => node is CssStyleRule : null, - scopeWhen: node.hasDeclarations); + _withParent( + rule, + () { + _withStyleRule(rule, () { + for (var child in node.children) { + child.accept(this); + } + }); + }, + through: nest ? (node) => node is CssStyleRule : null, + scopeWhen: node.hasDeclarations, + ); _atRootExcludingStyleRule = oldAtRootExcludingStyleRule; _warnForBogusCombinators(rule); @@ -2132,42 +2425,46 @@ final class _EvaluateVisitor if (complex.isUseless) { _warn( - 'The selector "${complex.toString().trim()}" is invalid CSS. It ' - 'will be omitted from the generated CSS.\n' + 'The selector "${complex.toString().trim()}" is invalid CSS. It ' + 'will be omitted from the generated CSS.\n' + 'This will be an error in Dart Sass 2.0.0.\n' + '\n' + 'More info: https://sass-lang.com/d/bogus-combinators', + complex.span.trimRight(), + Deprecation.bogusCombinators, + ); + } else if (complex.leadingCombinators.isNotEmpty) { + if (!_stylesheet.plainCss) { + _warn( + 'The selector "${complex.toString().trim()}" is invalid CSS.\n' 'This will be an error in Dart Sass 2.0.0.\n' '\n' 'More info: https://sass-lang.com/d/bogus-combinators', complex.span.trimRight(), - Deprecation.bogusCombinators); - } else if (complex.leadingCombinators.isNotEmpty) { - if (!_stylesheet.plainCss) { - _warn( - 'The selector "${complex.toString().trim()}" is invalid CSS.\n' - 'This will be an error in Dart Sass 2.0.0.\n' - '\n' - 'More info: https://sass-lang.com/d/bogus-combinators', - complex.span.trimRight(), - Deprecation.bogusCombinators); + Deprecation.bogusCombinators, + ); } } else { _warn( - 'The selector "${complex.toString().trim()}" is only valid for ' - "nesting and shouldn't\n" - 'have children other than style rules.' + - (complex.isBogusOtherThanLeadingCombinator - ? ' It will be omitted from the generated CSS.' - : '') + - '\n' - 'This will be an error in Dart Sass 2.0.0.\n' - '\n' - 'More info: https://sass-lang.com/d/bogus-combinators', - MultiSpan(complex.span.trimRight(), 'invalid selector', { - rule.children.first.span: "this is not a style rule" + - (rule.children.every((child) => child is CssComment) - ? '\n(try converting to a //-style comment)' - : '') - }), - Deprecation.bogusCombinators); + 'The selector "${complex.toString().trim()}" is only valid for ' + "nesting and shouldn't\n" + 'have children other than style rules.' + + (complex.isBogusOtherThanLeadingCombinator + ? ' It will be omitted from the generated CSS.' + : '') + + '\n' + 'This will be an error in Dart Sass 2.0.0.\n' + '\n' + 'More info: https://sass-lang.com/d/bogus-combinators', + MultiSpan(complex.span.trimRight(), 'invalid selector', { + rule.children.first.span: + "this is not a style rule" + + (rule.children.every((child) => child is CssComment) + ? '\n(try converting to a //-style comment)' + : ''), + }), + Deprecation.bogusCombinators, + ); } } } @@ -2179,32 +2476,38 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "Supports rules may not be used within nested declarations.", - node.span); - } - - var condition = - CssValue(_visitSupportsCondition(node.condition), node.condition.span); - _withParent(ModifiableCssSupportsRule(condition, node.span), () { - if (_styleRule case var styleRule?) { - // If we're in a style rule, copy it into the supports rule so that - // declarations immediately inside @supports have somewhere to go. - // - // For example, "a {@supports (a: b) {b: c}}" should produce "@supports - // (a: b) {a {b: c}}". - _withParent(styleRule.copyWithoutChildren(), () { + "Supports rules may not be used within nested declarations.", + node.span, + ); + } + + var condition = CssValue( + _visitSupportsCondition(node.condition), + node.condition.span, + ); + _withParent( + ModifiableCssSupportsRule(condition, node.span), + () { + if (_styleRule case var styleRule?) { + // If we're in a style rule, copy it into the supports rule so that + // declarations immediately inside @supports have somewhere to go. + // + // For example, "a {@supports (a: b) {b: c}}" should produce "@supports + // (a: b) {a {b: c}}". + _withParent(styleRule.copyWithoutChildren(), () { + for (var child in node.children) { + child.accept(this); + } + }); + } else { for (var child in node.children) { child.accept(this); } - }); - } else { - for (var child in node.children) { - child.accept(this); } - } - }, - through: (node) => node is CssStyleRule, - scopeWhen: node.hasDeclarations); + }, + through: (node) => node is CssStyleRule, + scopeWhen: node.hasDeclarations, + ); return null; } @@ -2217,18 +2520,25 @@ final class _EvaluateVisitor "${operation.operator} " "${_parenthesize(operation.right, operation.operator)}", SupportsNegation negation => "not ${_parenthesize(negation.condition)}", - SupportsInterpolation interpolation => - _evaluateToCss(interpolation.expression, quote: false), - SupportsDeclaration declaration => - _withSupportsDeclaration(() => "(${_evaluateToCss(declaration.name)}:" + SupportsInterpolation interpolation => _evaluateToCss( + interpolation.expression, + quote: false, + ), + SupportsDeclaration declaration => _withSupportsDeclaration( + () => + "(${_evaluateToCss(declaration.name)}:" "${declaration.isCustomProperty ? '' : ' '}" - "${_evaluateToCss(declaration.value)})"), - SupportsFunction function => "${_performInterpolation(function.name)}(" - "${_performInterpolation(function.arguments)})", + "${_evaluateToCss(declaration.value)})", + ), + SupportsFunction function => + "${_performInterpolation(function.name)}(" + "${_performInterpolation(function.arguments)})", SupportsAnything anything => "(${_performInterpolation(anything.contents)})", - var condition => throw ArgumentError( - "Unknown supports condition type ${condition.runtimeType}.") + var condition => + throw ArgumentError( + "Unknown supports condition type ${condition.runtimeType}.", + ), }; /// Runs [callback] in a context where [_inSupportsDeclaration] is true. @@ -2267,41 +2577,51 @@ final class _EvaluateVisitor when override.value != sassNull) { _addExceptionSpan(node, () { _environment.setVariable( - node.name, override.value, override.assignmentNode, - global: true); + node.name, + override.value, + override.assignmentNode, + global: true, + ); }); return null; } } - var value = _addExceptionSpan(node, - () => _environment.getVariable(node.name, namespace: node.namespace)); + var value = _addExceptionSpan( + node, + () => _environment.getVariable(node.name, namespace: node.namespace), + ); if (value != null && value != sassNull) return null; } if (node.isGlobal && !_environment.globalVariableExists(node.name)) { _warn( - _environment.atRoot - ? "As of Dart Sass 2.0.0, !global assignments won't be able to " - "declare new variables.\n" - "\n" - "Since this assignment is at the root of the stylesheet, the " - "!global flag is\n" - "unnecessary and can safely be removed." - : "As of Dart Sass 2.0.0, !global assignments won't be able to " - "declare new variables.\n" - "\n" - "Recommendation: add `${node.originalName}: null` at the " - "stylesheet root.", - node.span, - Deprecation.newGlobal); + _environment.atRoot + ? "As of Dart Sass 2.0.0, !global assignments won't be able to " + "declare new variables.\n" + "\n" + "Since this assignment is at the root of the stylesheet, the " + "!global flag is\n" + "unnecessary and can safely be removed." + : "As of Dart Sass 2.0.0, !global assignments won't be able to " + "declare new variables.\n" + "\n" + "Recommendation: add `${node.originalName}: null` at the " + "stylesheet root.", + node.span, + Deprecation.newGlobal, + ); } var value = _withoutSlash(node.expression.accept(this), node.expression); _addExceptionSpan(node, () { _environment.setVariable( - node.name, value, _expressionNode(node.expression), - namespace: node.namespace, global: node.isGlobal); + node.name, + value, + _expressionNode(node.expression), + namespace: node.namespace, + global: node.isGlobal, + ); }); return null; } @@ -2313,10 +2633,10 @@ final class _EvaluateVisitor for (var variable in node.configuration) { var variableNodeWithSpan = _expressionNode(variable.expression); values[variable.name] = ConfiguredValue.explicit( - _withoutSlash( - variable.expression.accept(this), variableNodeWithSpan), - variable.span, - variableNodeWithSpan); + _withoutSlash(variable.expression.accept(this), variableNodeWithSpan), + variable.span, + variableNodeWithSpan, + ); } configuration = ExplicitConfiguration(values, node); } @@ -2333,22 +2653,29 @@ final class _EvaluateVisitor Value? visitWarnRule(WarnRule node) { var value = _addExceptionSpan(node, () => node.expression.accept(this)); _logger.warn( - value is SassString ? value.text : _serialize(value, node.expression), - trace: _stackTrace(node.span)); + value is SassString ? value.text : _serialize(value, node.expression), + trace: _stackTrace(node.span), + ); return null; } Value? visitWhileRule(WhileRule node) { - return _environment.scope(() { - while (node.condition.accept(this).isTruthy) { - if (_handleReturn( - node.children, (child) => child.accept(this)) - case var result?) { - return result; + return _environment.scope( + () { + while (node.condition.accept(this).isTruthy) { + if (_handleReturn( + node.children, + (child) => child.accept(this), + ) + case var result?) { + return result; + } } - } - return null; - }, semiGlobal: true, when: node.hasDeclarations); + return null; + }, + semiGlobal: true, + when: node.hasDeclarations, + ); } // ## Expressions @@ -2358,30 +2685,36 @@ final class _EvaluateVisitor node.operator != BinaryOperator.singleEquals && node.operator != BinaryOperator.dividedBy) { throw _exception( - "Operators aren't allowed in plain CSS.", node.operatorSpan); + "Operators aren't allowed in plain CSS.", + node.operatorSpan, + ); } return _addExceptionSpan(node, () { var left = node.left.accept(this); return switch (node.operator) { - BinaryOperator.singleEquals => - left.singleEquals(node.right.accept(this)), + BinaryOperator.singleEquals => left.singleEquals( + node.right.accept(this), + ), BinaryOperator.or => left.isTruthy ? left : node.right.accept(this), BinaryOperator.and => left.isTruthy ? node.right.accept(this) : left, BinaryOperator.equals => SassBoolean(left == node.right.accept(this)), - BinaryOperator.notEquals => - SassBoolean(left != node.right.accept(this)), + BinaryOperator.notEquals => SassBoolean( + left != node.right.accept(this), + ), BinaryOperator.greaterThan => left.greaterThan(node.right.accept(this)), - BinaryOperator.greaterThanOrEquals => - left.greaterThanOrEquals(node.right.accept(this)), + BinaryOperator.greaterThanOrEquals => left.greaterThanOrEquals( + node.right.accept(this), + ), BinaryOperator.lessThan => left.lessThan(node.right.accept(this)), - BinaryOperator.lessThanOrEquals => - left.lessThanOrEquals(node.right.accept(this)), + BinaryOperator.lessThanOrEquals => left.lessThanOrEquals( + node.right.accept(this), + ), BinaryOperator.plus => left.plus(node.right.accept(this)), BinaryOperator.minus => left.minus(node.right.accept(this)), BinaryOperator.times => left.times(node.right.accept(this)), BinaryOperator.dividedBy => _slash(left, node.right.accept(this), node), - BinaryOperator.modulo => left.modulo(node.right.accept(this)) + BinaryOperator.modulo => left.modulo(node.right.accept(this)), }; }); } @@ -2399,27 +2732,28 @@ final class _EvaluateVisitor case (SassNumber(), SassNumber()): String recommendation(Expression expression) => switch (expression) { - BinaryOperationExpression( - operator: BinaryOperator.dividedBy, - :var left, - :var right - ) => - "math.div(${recommendation(left)}, ${recommendation(right)})", - ParenthesizedExpression() => expression.expression.toString(), - _ => expression.toString() - }; + BinaryOperationExpression( + operator: BinaryOperator.dividedBy, + :var left, + :var right, + ) => + "math.div(${recommendation(left)}, ${recommendation(right)})", + ParenthesizedExpression() => expression.expression.toString(), + _ => expression.toString(), + }; _warn( - "Using / for division outside of calc() is deprecated " - "and will be removed in Dart Sass 2.0.0.\n" - "\n" - "Recommendation: ${recommendation(node)} or " - "${expressionToCalc(node)}\n" - "\n" - "More info and automated migrator: " - "https://sass-lang.com/d/slash-div", - node.span, - Deprecation.slashDiv); + "Using / for division outside of calc() is deprecated " + "and will be removed in Dart Sass 2.0.0.\n" + "\n" + "Recommendation: ${recommendation(node)} or " + "${expressionToCalc(node)}\n" + "\n" + "More info and automated migrator: " + "https://sass-lang.com/d/slash-div", + node.span, + Deprecation.slashDiv, + ); return result; case _: @@ -2438,15 +2772,17 @@ final class _EvaluateVisitor const { "calc", "clamp", "hypot", "sin", "cos", "tan", "asin", "acos", // "atan", "sqrt", "exp", "sign", "mod", "rem", "atan2", "pow", // - "log", "calc-size" + "log", "calc-size", }.contains(node.name.toLowerCase()) && _environment.getFunction(node.name) == null); Value visitValueExpression(ValueExpression node) => node.value; Value visitVariableExpression(VariableExpression node) { - var result = _addExceptionSpan(node, - () => _environment.getVariable(node.name, namespace: node.namespace)); + var result = _addExceptionSpan( + node, + () => _environment.getVariable(node.name, namespace: node.namespace), + ); if (result != null) return result; throw _exception("Undefined variable.", node.span); } @@ -2458,7 +2794,7 @@ final class _EvaluateVisitor UnaryOperator.plus => operand.unaryPlus(), UnaryOperator.minus => operand.unaryMinus(), UnaryOperator.divide => operand.unaryDivide(), - UnaryOperator.not => operand.unaryNot() + UnaryOperator.not => operand.unaryNot(), }; }); } @@ -2487,15 +2823,18 @@ final class _EvaluateVisitor Value visitParenthesizedExpression(ParenthesizedExpression node) => _stylesheet.plainCss ? throw _exception( - "Parentheses aren't allowed in plain CSS.", node.span) + "Parentheses aren't allowed in plain CSS.", + node.span, + ) : node.expression.accept(this); SassColor visitColorExpression(ColorExpression node) => node.value; SassList visitListExpression(ListExpression node) => SassList( - node.contents.map((Expression expression) => expression.accept(this)), - node.separator, - brackets: node.hasBrackets); + node.contents.map((Expression expression) => expression.accept(this)), + node.separator, + brackets: node.hasBrackets, + ); SassMap visitMapExpression(MapExpression node) { var map = {}; @@ -2507,11 +2846,12 @@ final class _EvaluateVisitor if (map.containsKey(keyValue)) { var oldValueSpan = keyNodes[keyValue]?.span; throw MultiSpanSassRuntimeException( - 'Duplicate key.', - key.span, - 'second key', - {if (oldValueSpan != null) oldValueSpan: 'first key'}, - _stackTrace(key.span)); + 'Duplicate key.', + key.span, + 'second key', + {if (oldValueSpan != null) oldValueSpan: 'first key'}, + _stackTrace(key.span), + ); } map[keyValue] = valueValue; keyNodes[keyValue] = key; @@ -2520,12 +2860,16 @@ final class _EvaluateVisitor } Value visitFunctionExpression(FunctionExpression node) { - var function = _stylesheet.plainCss - ? null - : _addExceptionSpan( - node, - () => - _environment.getFunction(node.name, namespace: node.namespace)); + var function = + _stylesheet.plainCss + ? null + : _addExceptionSpan( + node, + () => _environment.getFunction( + node.name, + namespace: node.namespace, + ), + ); if (function == null) { if (node.namespace != null) { throw _exception("Undefined function.", node.span); @@ -2537,32 +2881,34 @@ final class _EvaluateVisitor case ("min" || "max" || "round" || "abs") && var name when node.arguments.named.isEmpty && node.arguments.rest == null && - node.arguments.positional - .every((argument) => argument.isCalculationSafe): + node.arguments.positional.every( + (argument) => argument.isCalculationSafe, + ): return _visitCalculation(node, inLegacySassFunction: name); case "calc" || - "clamp" || - "hypot" || - "sin" || - "cos" || - "tan" || - "asin" || - "acos" || - "atan" || - "sqrt" || - "exp" || - "sign" || - "mod" || - "rem" || - "atan2" || - "pow" || - "log" || - "calc-size": + "clamp" || + "hypot" || + "sin" || + "cos" || + "tan" || + "asin" || + "acos" || + "atan" || + "sqrt" || + "exp" || + "sign" || + "mod" || + "rem" || + "atan2" || + "pow" || + "log" || + "calc-size": return _visitCalculation(node); } - function = (_stylesheet.plainCss ? null : _builtInFunctions[node.name]) ?? + function = + (_stylesheet.plainCss ? null : _builtInFunctions[node.name]) ?? PlainCssCallable(node.originalName); } @@ -2582,7 +2928,9 @@ final class _EvaluateVisitor var oldInFunction = _inFunction; _inFunction = true; var result = _addErrorSpan( - node, () => _runFunctionCallable(node.arguments, function, node)); + node, + () => _runFunctionCallable(node.arguments, function, node), + ); _inFunction = oldInFunction; return result; } @@ -2594,21 +2942,29 @@ final class _EvaluateVisitor /// with the old global `min()`, `max()`, `round()`, and `abs()` functions. /// The parameter is the name of the function, which is used for reporting /// deprecation warnings. - Value _visitCalculation(FunctionExpression node, - {String? inLegacySassFunction}) { + Value _visitCalculation( + FunctionExpression node, { + String? inLegacySassFunction, + }) { if (node.arguments.named.isNotEmpty) { throw _exception( - "Keyword arguments can't be used with calculations.", node.span); + "Keyword arguments can't be used with calculations.", + node.span, + ); } else if (node.arguments.rest != null) { throw _exception( - "Rest arguments can't be used with calculations.", node.span); + "Rest arguments can't be used with calculations.", + node.span, + ); } _checkCalculationArguments(node); var arguments = [ for (var argument in node.arguments.positional) - _visitCalculationExpression(argument, - inLegacySassFunction: inLegacySassFunction) + _visitCalculationExpression( + argument, + inLegacySassFunction: inLegacySassFunction, + ), ]; if (_inSupportsDeclaration) { return SassCalculation.unsimplified(node.name, arguments); @@ -2633,27 +2989,46 @@ final class _EvaluateVisitor "min" => SassCalculation.min(arguments), "max" => SassCalculation.max(arguments), "hypot" => SassCalculation.hypot(arguments), - "pow" => - SassCalculation.pow(arguments[0], arguments.elementAtOrNull(1)), - "atan2" => - SassCalculation.atan2(arguments[0], arguments.elementAtOrNull(1)), - "log" => - SassCalculation.log(arguments[0], arguments.elementAtOrNull(1)), - "mod" => - SassCalculation.mod(arguments[0], arguments.elementAtOrNull(1)), - "rem" => - SassCalculation.rem(arguments[0], arguments.elementAtOrNull(1)), - "round" => SassCalculation.roundInternal(arguments[0], - arguments.elementAtOrNull(1), arguments.elementAtOrNull(2), - span: node.span, - inLegacySassFunction: inLegacySassFunction, - warn: (message, [deprecation]) => - _warn(message, node.span, deprecation)), - "clamp" => SassCalculation.clamp(arguments[0], - arguments.elementAtOrNull(1), arguments.elementAtOrNull(2)), - "calc-size" => - SassCalculation.calcSize(arguments[0], arguments.elementAtOrNull(1)), - _ => throw UnsupportedError('Unknown calculation name "${node.name}".') + "pow" => SassCalculation.pow( + arguments[0], + arguments.elementAtOrNull(1), + ), + "atan2" => SassCalculation.atan2( + arguments[0], + arguments.elementAtOrNull(1), + ), + "log" => SassCalculation.log( + arguments[0], + arguments.elementAtOrNull(1), + ), + "mod" => SassCalculation.mod( + arguments[0], + arguments.elementAtOrNull(1), + ), + "rem" => SassCalculation.rem( + arguments[0], + arguments.elementAtOrNull(1), + ), + "round" => SassCalculation.roundInternal( + arguments[0], + arguments.elementAtOrNull(1), + arguments.elementAtOrNull(2), + span: node.span, + inLegacySassFunction: inLegacySassFunction, + warn: + (message, [deprecation]) => + _warn(message, node.span, deprecation), + ), + "clamp" => SassCalculation.clamp( + arguments[0], + arguments.elementAtOrNull(1), + arguments.elementAtOrNull(2), + ), + "calc-size" => SassCalculation.calcSize( + arguments[0], + arguments.elementAtOrNull(1), + ), + _ => throw UnsupportedError('Unknown calculation name "${node.name}".'), }; } on SassScriptException catch (error, stackTrace) { // The simplification logic in the [SassCalculation] static methods will @@ -2676,27 +3051,31 @@ final class _EvaluateVisitor } else if (maxArgs != null && node.arguments.positional.length > maxArgs) { throw _exception( - "Only $maxArgs ${pluralize('argument', maxArgs)} allowed, but " - "${node.arguments.positional.length} " + - pluralize('was', node.arguments.positional.length, - plural: 'were') + - " passed.", - node.span); + "Only $maxArgs ${pluralize('argument', maxArgs)} allowed, but " + "${node.arguments.positional.length} " + + pluralize( + 'was', + node.arguments.positional.length, + plural: 'were', + ) + + " passed.", + node.span, + ); } } switch (node.name.toLowerCase()) { case "calc" || - "sqrt" || - "sin" || - "cos" || - "tan" || - "asin" || - "acos" || - "atan" || - "abs" || - "exp" || - "sign": + "sqrt" || + "sin" || + "cos" || + "tan" || + "asin" || + "acos" || + "atan" || + "abs" || + "exp" || + "sign": check(1); case "min" || "max" || "hypot": check(); @@ -2714,14 +3093,18 @@ final class _EvaluateVisitor /// /// The [nodesWithSpans] should correspond to the spans for [args]. void _verifyCompatibleNumbers( - List args, List nodesWithSpans) { + List args, + List nodesWithSpans, + ) { // Note: this logic is largely duplicated in // SassCalculation._verifyCompatibleNumbers and most changes here should // also be reflected there. for (var i = 0; i < args.length; i++) { if (args[i] case SassNumber arg when arg.hasComplexUnits) { - throw _exception("Number $arg isn't compatible with CSS calculations.", - nodesWithSpans[i].span); + throw _exception( + "Number $arg isn't compatible with CSS calculations.", + nodesWithSpans[i].span, + ); } } @@ -2735,11 +3118,12 @@ final class _EvaluateVisitor if (number1.hasPossiblyCompatibleUnits(number2)) continue; throw MultiSpanSassRuntimeException( - "$number1 and $number2 are incompatible.", - nodesWithSpans[i].span, - number1.toString(), - {nodesWithSpans[j].span: number2.toString()}, - _stackTrace(nodesWithSpans[i].span)); + "$number1 and $number2 are incompatible.", + nodesWithSpans[i].span, + number1.toString(), + {nodesWithSpans[j].span: number2.toString()}, + _stackTrace(nodesWithSpans[i].span), + ); } } } @@ -2751,12 +3135,16 @@ final class _EvaluateVisitor /// with the old global `min()`, `max()`, `round()`, and `abs()` functions. /// The parameter is the name of the function, which is used for reporting /// deprecation warnings. - Object _visitCalculationExpression(Expression node, - {required String? inLegacySassFunction}) { + Object _visitCalculationExpression( + Expression node, { + required String? inLegacySassFunction, + }) { switch (node) { case ParenthesizedExpression(expression: var inner): - var result = _visitCalculationExpression(inner, - inLegacySassFunction: inLegacySassFunction); + var result = _visitCalculationExpression( + inner, + inLegacySassFunction: inLegacySassFunction, + ); return result is SassString ? SassString('(${result.text})', quotes: false) : result; @@ -2769,45 +3157,57 @@ final class _EvaluateVisitor 'infinity' => SassNumber(double.infinity), '-infinity' => SassNumber(double.negativeInfinity), 'nan' => SassNumber(double.nan), - _ => SassString(_performInterpolation(node.text), quotes: false) + _ => SassString(_performInterpolation(node.text), quotes: false), }; case BinaryOperationExpression(:var operator, :var left, :var right): _checkWhitespaceAroundCalculationOperator(node); return _addExceptionSpan( - node, - () => SassCalculation.operateInternal( - _binaryOperatorToCalculationOperator(operator, node), - _visitCalculationExpression(left, - inLegacySassFunction: inLegacySassFunction), - _visitCalculationExpression(right, - inLegacySassFunction: inLegacySassFunction), - inLegacySassFunction: inLegacySassFunction, - simplify: !_inSupportsDeclaration, - warn: (message, [deprecation]) => - _warn(message, node.span, deprecation))); + node, + () => SassCalculation.operateInternal( + _binaryOperatorToCalculationOperator(operator, node), + _visitCalculationExpression( + left, + inLegacySassFunction: inLegacySassFunction, + ), + _visitCalculationExpression( + right, + inLegacySassFunction: inLegacySassFunction, + ), + inLegacySassFunction: inLegacySassFunction, + simplify: !_inSupportsDeclaration, + warn: + (message, [deprecation]) => + _warn(message, node.span, deprecation), + ), + ); case NumberExpression() || - VariableExpression() || - FunctionExpression() || - IfExpression(): + VariableExpression() || + FunctionExpression() || + IfExpression(): return switch (node.accept(this)) { SassNumber result => result, SassCalculation result => result, SassString result when !result.hasQuotes => result, - var result => throw _exception( - "Value $result can't be used in a calculation.", node.span) + var result => + throw _exception( + "Value $result can't be used in a calculation.", + node.span, + ), }; case ListExpression( - hasBrackets: false, - separator: ListSeparator.space, - contents: [_, _, ...] - ): + hasBrackets: false, + separator: ListSeparator.space, + contents: [_, _, ...], + ): var elements = [ for (var element in node.contents) - _visitCalculationExpression(element, - inLegacySassFunction: inLegacySassFunction) + _visitCalculationExpression( + element, + inLegacySassFunction: inLegacySassFunction, + ), ]; _checkAdjacentCalculationValues(elements, node); @@ -2824,14 +3224,17 @@ final class _EvaluateVisitor case _: assert(!node.isCalculationSafe); throw _exception( - "This expression can't be used in a calculation.", node.span); + "This expression can't be used in a calculation.", + node.span, + ); } } /// Throws an error if [node] requires whitespace around its operator in a /// calculation but doesn't have it. void _checkWhitespaceAroundCalculationOperator( - BinaryOperationExpression node) { + BinaryOperationExpression node, + ) { if (node.operator != BinaryOperator.plus && node.operator != BinaryOperator.minus) { return; @@ -2843,33 +3246,42 @@ final class _EvaluateVisitor if (node.left.span.file != node.right.span.file) return; if (node.left.span.end.offset >= node.right.span.start.offset) return; - var textBetweenOperands = node.left.span.file - .getText(node.left.span.end.offset, node.right.span.start.offset); + var textBetweenOperands = node.left.span.file.getText( + node.left.span.end.offset, + node.right.span.start.offset, + ); var first = textBetweenOperands.codeUnitAt(0); var last = textBetweenOperands.codeUnitAt(textBetweenOperands.length - 1); if (!(first.isWhitespace || first == $slash) || !(last.isWhitespace || last == $slash)) { throw _exception( - '"+" and "-" must be surrounded by whitespace in calculations.', - node.operatorSpan); + '"+" and "-" must be surrounded by whitespace in calculations.', + node.operatorSpan, + ); } } /// Returns the [CalculationOperator] that corresponds to [operator]. CalculationOperator _binaryOperatorToCalculationOperator( - BinaryOperator operator, BinaryOperationExpression node) => - switch (operator) { - BinaryOperator.plus => CalculationOperator.plus, - BinaryOperator.minus => CalculationOperator.minus, - BinaryOperator.times => CalculationOperator.times, - BinaryOperator.dividedBy => CalculationOperator.dividedBy, - _ => throw _exception( - "This operation can't be used in a calculation.", node.operatorSpan) - }; + BinaryOperator operator, + BinaryOperationExpression node, + ) => switch (operator) { + BinaryOperator.plus => CalculationOperator.plus, + BinaryOperator.minus => CalculationOperator.minus, + BinaryOperator.times => CalculationOperator.times, + BinaryOperator.dividedBy => CalculationOperator.dividedBy, + _ => + throw _exception( + "This operation can't be used in a calculation.", + node.operatorSpan, + ), + }; /// Throws an error if [elements] contains two adjacent non-string values. void _checkAdjacentCalculationValues( - List elements, ListExpression node) { + List elements, + ListExpression node, + ) { assert(elements.length > 1); for (var i = 1; i < elements.length; i++) { @@ -2881,7 +3293,7 @@ final class _EvaluateVisitor var currentNode = node.contents[i]; if (currentNode case UnaryOperationExpression( - operator: UnaryOperator.minus || UnaryOperator.plus + operator: UnaryOperator.minus || UnaryOperator.plus, ) || NumberExpression(value: < 0)) { // `calc(1 -2)` parses as a space-separated list whose second value is a @@ -2889,22 +3301,28 @@ final class _EvaluateVisitor // expression doesn't help the user understand what's going wrong. We // add special case error handling to help clarify the issue. throw _exception( - '"+" and "-" must be surrounded by whitespace in calculations.', - currentNode.span.subspan(0, 1)); + '"+" and "-" must be surrounded by whitespace in calculations.', + currentNode.span.subspan(0, 1), + ); } else { - throw _exception('Missing math operator.', - previousNode.span.expand(currentNode.span)); + throw _exception( + 'Missing math operator.', + previousNode.span.expand(currentNode.span), + ); } } } Value visitInterpolatedFunctionExpression( - InterpolatedFunctionExpression node) { + InterpolatedFunctionExpression node, + ) { var function = PlainCssCallable(_performInterpolation(node.name)); var oldInFunction = _inFunction; _inFunction = true; var result = _addErrorSpan( - node, () => _runFunctionCallable(node.arguments, function, node)); + node, + () => _runFunctionCallable(node.arguments, function, node), + ); _inFunction = oldInFunction; return result; } @@ -2912,10 +3330,11 @@ final class _EvaluateVisitor /// Evaluates the arguments in [arguments] as applied to [callable], and /// invokes [run] in a scope with those arguments defined. V _runUserDefinedCallable( - ArgumentList arguments, - UserDefinedCallable callable, - AstNode nodeWithSpan, - V run()) { + ArgumentList arguments, + UserDefinedCallable callable, + AstNode nodeWithSpan, + V run(), + ) { // TODO(nweiz): Set [trackSpans] to `null` once we're no longer emitting // deprecation warnings for /-as-division. var evaluated = _evaluateArguments(arguments); @@ -2930,45 +3349,65 @@ final class _EvaluateVisitor // don't affect the underlying environment closure. return _withEnvironment(callable.environment.closure(), () { return _environment.scope(() { - _verifyArguments(evaluated.positional.length, evaluated.named, - callable.declaration.parameters, nodeWithSpan); + _verifyArguments( + evaluated.positional.length, + evaluated.named, + callable.declaration.parameters, + nodeWithSpan, + ); var parameters = callable.declaration.parameters.parameters; - var minLength = - math.min(evaluated.positional.length, parameters.length); + var minLength = math.min( + evaluated.positional.length, + parameters.length, + ); for (var i = 0; i < minLength; i++) { - _environment.setLocalVariable(parameters[i].name, - evaluated.positional[i], evaluated.positionalNodes[i]); + _environment.setLocalVariable( + parameters[i].name, + evaluated.positional[i], + evaluated.positionalNodes[i], + ); } - for (var i = evaluated.positional.length; - i < parameters.length; - i++) { + for ( + var i = evaluated.positional.length; + i < parameters.length; + i++ + ) { var parameter = parameters[i]; - var value = evaluated.named.remove(parameter.name) ?? - _withoutSlash(parameter.defaultValue!.accept(this), - _expressionNode(parameter.defaultValue!)); + var value = + evaluated.named.remove(parameter.name) ?? + _withoutSlash( + parameter.defaultValue!.accept(this), + _expressionNode(parameter.defaultValue!), + ); _environment.setLocalVariable( - parameter.name, - value, - evaluated.namedNodes[parameter.name] ?? - _expressionNode(parameter.defaultValue!)); + parameter.name, + value, + evaluated.namedNodes[parameter.name] ?? + _expressionNode(parameter.defaultValue!), + ); } SassArgumentList? argumentList; var restParameter = callable.declaration.parameters.restParameter; if (restParameter != null) { - var rest = evaluated.positional.length > parameters.length - ? evaluated.positional.sublist(parameters.length) - : const []; + var rest = + evaluated.positional.length > parameters.length + ? evaluated.positional.sublist(parameters.length) + : const []; argumentList = SassArgumentList( - rest, - evaluated.named, - evaluated.separator == ListSeparator.undecided - ? ListSeparator.comma - : evaluated.separator); + rest, + evaluated.named, + evaluated.separator == ListSeparator.undecided + ? ListSeparator.comma + : evaluated.separator, + ); _environment.setLocalVariable( - restParameter, argumentList, nodeWithSpan); + restParameter, + argumentList, + nodeWithSpan, + ); } var result = run(); @@ -2977,16 +3416,21 @@ final class _EvaluateVisitor if (evaluated.named.isEmpty) return result; if (argumentList.wereKeywordsAccessed) return result; - var parameterWord = - pluralize('parameter', evaluated.named.keys.length); - var parameterNames = - toSentence(evaluated.named.keys.map((name) => "\$$name"), 'or'); + var parameterWord = pluralize( + 'parameter', + evaluated.named.keys.length, + ); + var parameterNames = toSentence( + evaluated.named.keys.map((name) => "\$$name"), + 'or', + ); throw MultiSpanSassRuntimeException( - "No $parameterWord named $parameterNames.", - nodeWithSpan.span, - "invocation", - {callable.declaration.parameters.spanWithName: "declaration"}, - _stackTrace(nodeWithSpan.span)); + "No $parameterWord named $parameterNames.", + nodeWithSpan.span, + "invocation", + {callable.declaration.parameters.spanWithName: "declaration"}, + _stackTrace(nodeWithSpan.span), + ); }); }); }); @@ -2996,10 +3440,15 @@ final class _EvaluateVisitor /// Evaluates [arguments] as applied to [callable]. Value _runFunctionCallable( - ArgumentList arguments, Callable? callable, AstNode nodeWithSpan) { + ArgumentList arguments, + Callable? callable, + AstNode nodeWithSpan, + ) { if (callable is BuiltInCallable) { return _withoutSlash( - _runBuiltInCallable(arguments, callable, nodeWithSpan), nodeWithSpan); + _runBuiltInCallable(arguments, callable, nodeWithSpan), + nodeWithSpan, + ); } else if (callable is UserDefinedCallable) { return _runUserDefinedCallable(arguments, callable, nodeWithSpan, () { for (var statement in callable.declaration.children) { @@ -3008,12 +3457,16 @@ final class _EvaluateVisitor } throw _exception( - "Function finished without @return.", callable.declaration.span); + "Function finished without @return.", + callable.declaration.span, + ); }); } else if (callable is PlainCssCallable) { if (arguments.named.isNotEmpty || arguments.keywordRest != null) { - throw _exception("Plain CSS functions don't support keyword arguments.", - nodeWithSpan.span); + throw _exception( + "Plain CSS functions don't support keyword arguments.", + nodeWithSpan.span, + ); } var buffer = StringBuffer("${callable.name}("); @@ -3038,11 +3491,12 @@ final class _EvaluateVisitor } on SassRuntimeException catch (error) { if (!error.message.endsWith("isn't a valid CSS value.")) rethrow; throw MultiSpanSassRuntimeException( - error.message, - error.span, - "value", - {nodeWithSpan.span: "unknown function treated as plain CSS"}, - error.trace); + error.message, + error.span, + "value", + {nodeWithSpan.span: "unknown function treated as plain CSS"}, + error.trace, + ); } buffer.writeCharCode($rparen); @@ -3055,24 +3509,35 @@ final class _EvaluateVisitor /// Evaluates [invocation] as applied to [callable], and invokes [callable]'s /// body. Value _runBuiltInCallable( - ArgumentList arguments, BuiltInCallable callable, AstNode nodeWithSpan) { + ArgumentList arguments, + BuiltInCallable callable, + AstNode nodeWithSpan, + ) { var evaluated = _evaluateArguments(arguments); var oldCallableNode = _callableNode; _callableNode = nodeWithSpan; var namedSet = MapKeySet(evaluated.named); - var (overload, callback) = - callable.callbackFor(evaluated.positional.length, namedSet); - _addExceptionSpan(nodeWithSpan, - () => overload.verify(evaluated.positional.length, namedSet)); + var (overload, callback) = callable.callbackFor( + evaluated.positional.length, + namedSet, + ); + _addExceptionSpan( + nodeWithSpan, + () => overload.verify(evaluated.positional.length, namedSet), + ); var parameters = overload.parameters; for (var i = evaluated.positional.length; i < parameters.length; i++) { var parameter = parameters[i]; - evaluated.positional.add(evaluated.named.remove(parameter.name) ?? - _withoutSlash( - parameter.defaultValue!.accept(this), parameter.defaultValue!)); + evaluated.positional.add( + evaluated.named.remove(parameter.name) ?? + _withoutSlash( + parameter.defaultValue!.accept(this), + parameter.defaultValue!, + ), + ); } SassArgumentList? argumentList; @@ -3080,28 +3545,36 @@ final class _EvaluateVisitor var rest = const []; if (evaluated.positional.length > parameters.length) { rest = evaluated.positional.sublist(parameters.length); - evaluated.positional - .removeRange(parameters.length, evaluated.positional.length); + evaluated.positional.removeRange( + parameters.length, + evaluated.positional.length, + ); } argumentList = SassArgumentList( - rest, - evaluated.named, - evaluated.separator == ListSeparator.undecided - ? ListSeparator.comma - : evaluated.separator); + rest, + evaluated.named, + evaluated.separator == ListSeparator.undecided + ? ListSeparator.comma + : evaluated.separator, + ); evaluated.positional.add(argumentList); } Value result; try { - result = - _addExceptionSpan(nodeWithSpan, () => callback(evaluated.positional)); + result = _addExceptionSpan( + nodeWithSpan, + () => callback(evaluated.positional), + ); } on SassException { rethrow; } catch (error, stackTrace) { - throwWithTrace(_exception(_getErrorMessage(error), nodeWithSpan.span), - error, stackTrace); + throwWithTrace( + _exception(_getErrorMessage(error), nodeWithSpan.span), + error, + stackTrace, + ); } _callableNode = oldCallableNode; @@ -3110,12 +3583,13 @@ final class _EvaluateVisitor if (argumentList.wereKeywordsAccessed) return result; throw MultiSpanSassRuntimeException( - "No ${pluralize('parameter', evaluated.named.keys.length)} named " - "${toSentence(evaluated.named.keys.map((name) => "\$$name"), 'or')}.", - nodeWithSpan.span, - "invocation", - {overload.spanWithName: "declaration"}, - _stackTrace(nodeWithSpan.span)); + "No ${pluralize('parameter', evaluated.named.keys.length)} named " + "${toSentence(evaluated.named.keys.map((name) => "\$$name"), 'or')}.", + nodeWithSpan.span, + "invocation", + {overload.spanWithName: "declaration"}, + _stackTrace(nodeWithSpan.span), + ); } /// Returns the evaluated values of the given [arguments]. @@ -3149,7 +3623,7 @@ final class _EvaluateVisitor positionalNodes: positionalNodes, named: named, namedNodes: namedNodes, - separator: ListSeparator.undecided + separator: ListSeparator.undecided, ); } @@ -3160,11 +3634,12 @@ final class _EvaluateVisitor _addRestMap(named, rest, restArgs, (value) => value); namedNodes.addAll({ for (var key in rest.contents.keys) - (key as SassString).text: restNodeForSpan + (key as SassString).text: restNodeForSpan, }); } else if (rest is SassList) { positional.addAll( - rest.asList.map((value) => _withoutSlash(value, restNodeForSpan))); + rest.asList.map((value) => _withoutSlash(value, restNodeForSpan)), + ); positionalNodes.addAll(List.filled(rest.lengthAsList, restNodeForSpan)); separator = rest.separator; @@ -3186,7 +3661,7 @@ final class _EvaluateVisitor positionalNodes: positionalNodes, named: named, namedNodes: namedNodes, - separator: separator + separator: separator, ); } @@ -3196,19 +3671,20 @@ final class _EvaluateVisitor _addRestMap(named, keywordRest, keywordRestArgs, (value) => value); namedNodes.addAll({ for (var key in keywordRest.contents.keys) - (key as SassString).text: keywordRestNodeForSpan + (key as SassString).text: keywordRestNodeForSpan, }); return ( positional: positional, positionalNodes: positionalNodes, named: named, namedNodes: namedNodes, - separator: separator + separator: separator, ); } else { throw _exception( - "Variable keyword arguments must be a map (was $keywordRest).", - keywordRestArgs.span); + "Variable keyword arguments must be a map (was $keywordRest).", + keywordRestArgs.span, + ); } } @@ -3218,7 +3694,7 @@ final class _EvaluateVisitor /// Returns the arguments as expressions so that they can be lazily evaluated /// for macros such as `if()`. (List positional, Map named) - _evaluateMacroArguments(CallableInvocation invocation) { + _evaluateMacroArguments(CallableInvocation invocation) { var restArgs_ = invocation.arguments.rest; if (restArgs_ == null) { return (invocation.arguments.positional, invocation.arguments.named); @@ -3230,20 +3706,33 @@ final class _EvaluateVisitor var rest = restArgs.accept(this); var restNodeForSpan = _expressionNode(restArgs); if (rest is SassMap) { - _addRestMap(named, rest, invocation, - (value) => ValueExpression(value, restArgs.span)); + _addRestMap( + named, + rest, + invocation, + (value) => ValueExpression(value, restArgs.span), + ); } else if (rest is SassList) { - positional.addAll(rest.asList.map((value) => ValueExpression( - _withoutSlash(value, restNodeForSpan), restArgs.span))); + positional.addAll( + rest.asList.map( + (value) => ValueExpression( + _withoutSlash(value, restNodeForSpan), + restArgs.span, + ), + ), + ); if (rest is SassArgumentList) { rest.keywords.forEach((key, value) { named[key] = ValueExpression( - _withoutSlash(value, restNodeForSpan), restArgs.span); + _withoutSlash(value, restNodeForSpan), + restArgs.span, + ); }); } } else { positional.add( - ValueExpression(_withoutSlash(rest, restNodeForSpan), restArgs.span)); + ValueExpression(_withoutSlash(rest, restNodeForSpan), restArgs.span), + ); } var keywordRestArgs_ = invocation.arguments.keywordRest; @@ -3254,17 +3743,20 @@ final class _EvaluateVisitor var keywordRestNodeForSpan = _expressionNode(keywordRestArgs); if (keywordRest is SassMap) { _addRestMap( - named, - keywordRest, - invocation, - (value) => ValueExpression( - _withoutSlash(value, keywordRestNodeForSpan), - keywordRestArgs.span)); + named, + keywordRest, + invocation, + (value) => ValueExpression( + _withoutSlash(value, keywordRestNodeForSpan), + keywordRestArgs.span, + ), + ); return (positional, named); } else { throw _exception( - "Variable keyword arguments must be a map (was $keywordRest).", - keywordRestArgs.span); + "Variable keyword arguments must be a map (was $keywordRest).", + keywordRestArgs.span, + ); } } @@ -3279,27 +3771,37 @@ final 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. - void _addRestMap(Map values, SassMap map, AstNode nodeWithSpan, - T convert(Value value)) { + void _addRestMap( + Map values, + SassMap map, + AstNode nodeWithSpan, + T convert(Value value), + ) { var expressionNode = _expressionNode(nodeWithSpan); map.contents.forEach((key, value) { if (key is SassString) { values[key.text] = convert(_withoutSlash(value, expressionNode)); } else { throw _exception( - "Variable keyword argument map must have string keys.\n" - "$key is not a string in $map.", - nodeWithSpan.span); + "Variable keyword argument map must have string keys.\n" + "$key is not a string in $map.", + nodeWithSpan.span, + ); } }); } /// Throws a [SassRuntimeException] if [positional] and [named] aren't valid /// when applied to [parameters]. - void _verifyArguments(int positional, Map named, - ParameterList parameters, AstNode nodeWithSpan) => - _addExceptionSpan( - nodeWithSpan, () => parameters.verify(positional, MapKeySet(named))); + void _verifyArguments( + int positional, + Map named, + ParameterList parameters, + AstNode nodeWithSpan, + ) => _addExceptionSpan( + nodeWithSpan, + () => parameters.verify(positional, MapKeySet(named)), + ); Value visitSelectorExpression(SelectorExpression node) => _styleRuleIgnoringAtRoot?.originalSelector.asSassList ?? sassNull; @@ -3310,18 +3812,19 @@ final class _EvaluateVisitor var oldInSupportsDeclaration = _inSupportsDeclaration; _inSupportsDeclaration = false; var result = SassString( - [ - for (var value in node.text.contents) - switch (value) { - String() => value, - Expression() => switch (value.accept(this)) { - SassString(:var text) => text, - var result => _serialize(result, value, quote: false) - }, - _ => throw UnsupportedError("Unknown interpolation value $value") - } - ].join(), - quotes: node.hasQuotes); + [ + for (var value in node.text.contents) + switch (value) { + String() => value, + Expression() => switch (value.accept(this)) { + SassString(:var text) => text, + var result => _serialize(result, value, quote: false), + }, + _ => throw UnsupportedError("Unknown interpolation value $value"), + }, + ].join(), + quotes: node.hasQuotes, + ); _inSupportsDeclaration = oldInSupportsDeclaration; return result; } @@ -3346,12 +3849,20 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "At-rules may not be used within nested declarations.", node.span); + "At-rules may not be used within nested declarations.", + node.span, + ); } if (node.isChildless) { - _parent.addChild(ModifiableCssAtRule(node.name, node.span, - childless: true, value: node.value)); + _parent.addChild( + ModifiableCssAtRule( + node.name, + node.span, + childless: true, + value: node.value, + ), + ); return; } @@ -3363,15 +3874,19 @@ final class _EvaluateVisitor _inUnknownAtRule = true; } - _withParent(ModifiableCssAtRule(node.name, node.span, value: node.value), - () { - // We don't have to check for an unknown at-rule in a style rule here, - // because the previous compilation has already bubbled the at-rule to the - // root. - for (var child in node.children) { - child.accept(this); - } - }, through: (node) => node is CssStyleRule, scopeWhen: false); + _withParent( + ModifiableCssAtRule(node.name, node.span, value: node.value), + () { + // We don't have to check for an unknown at-rule in a style rule here, + // because the previous compilation has already bubbled the at-rule to the + // root. + for (var child in node.children) { + child.accept(this); + } + }, + through: (node) => node is CssStyleRule, + scopeWhen: false, + ); _inUnknownAtRule = wasInUnknownAtRule; _inKeyframes = wasInKeyframes; @@ -3390,17 +3905,26 @@ final class _EvaluateVisitor } void visitCssDeclaration(CssDeclaration node) { - _parent.addChild(ModifiableCssDeclaration(node.name, node.value, node.span, + _parent.addChild( + ModifiableCssDeclaration( + node.name, + node.value, + node.span, parsedAsCustomProperty: node.parsedAsCustomProperty, - valueSpanForMap: node.valueSpanForMap)); + valueSpanForMap: node.valueSpanForMap, + ), + ); } void visitCssImport(CssImport node) { // NOTE: this logic is largely duplicated in [_visitStaticImport]. Most // changes here should be mirrored there. - var modifiableNode = - ModifiableCssImport(node.url, node.span, modifiers: node.modifiers); + var modifiableNode = ModifiableCssImport( + node.url, + node.span, + modifiers: node.modifiers, + ); if (_parent != _root) { _parent.addChild(modifiableNode); } else if (_endOfImports == _root.children.length) { @@ -3416,11 +3940,16 @@ final class _EvaluateVisitor // here should be mirrored there. var rule = ModifiableCssKeyframeBlock(node.selector, node.span); - _withParent(rule, () { - for (var child in node.children) { - child.accept(this); - } - }, through: (node) => node is CssStyleRule, scopeWhen: false); + _withParent( + rule, + () { + for (var child in node.children) { + child.accept(this); + } + }, + through: (node) => node is CssStyleRule, + scopeWhen: false, + ); } void visitCssMediaRule(CssMediaRule node) { @@ -3429,44 +3958,51 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "Media rules may not be used within nested declarations.", node.span); + "Media rules may not be used within nested declarations.", + node.span, + ); } var mergedQueries = _mediaQueries.andThen( - (mediaQueries) => _mergeMediaQueries(mediaQueries, node.queries)); + (mediaQueries) => _mergeMediaQueries(mediaQueries, node.queries), + ); if (mergedQueries != null && mergedQueries.isEmpty) return; - var mergedSources = mergedQueries == null - ? const {} - : {..._mediaQuerySources!, ..._mediaQueries!, ...node.queries}; + var mergedSources = + mergedQueries == null + ? const {} + : {..._mediaQuerySources!, ..._mediaQueries!, ...node.queries}; _withParent( - ModifiableCssMediaRule(mergedQueries ?? node.queries, node.span), () { - _withMediaQueries(mergedQueries ?? node.queries, mergedSources, () { - if (_styleRule case var styleRule?) { - // If we're in a style rule, copy it into the media query so that - // declarations immediately inside @media have somewhere to go. - // - // For example, "a {@media screen {b: c}}" should produce - // "@media screen {a {b: c}}". - _withParent(styleRule.copyWithoutChildren(), () { + ModifiableCssMediaRule(mergedQueries ?? node.queries, node.span), + () { + _withMediaQueries(mergedQueries ?? node.queries, mergedSources, () { + if (_styleRule case var styleRule?) { + // If we're in a style rule, copy it into the media query so that + // declarations immediately inside @media have somewhere to go. + // + // For example, "a {@media screen {b: c}}" should produce + // "@media screen {a {b: c}}". + _withParent(styleRule.copyWithoutChildren(), () { + for (var child in node.children) { + child.accept(this); + } + }, scopeWhen: false); + } else { for (var child in node.children) { child.accept(this); } - }, scopeWhen: false); - } else { - for (var child in node.children) { - child.accept(this); } - } - }); - }, - through: (node) => - node is CssStyleRule || - (mergedSources.isNotEmpty && - node is CssMediaRule && - node.queries.every(mergedSources.contains)), - scopeWhen: false); + }); + }, + through: + (node) => + node is CssStyleRule || + (mergedSources.isNotEmpty && + node is CssMediaRule && + node.queries.every(mergedSources.contains)), + scopeWhen: false, + ); } void visitCssStyleRule(CssStyleRule node) { @@ -3475,31 +4011,47 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "Style rules may not be used within nested declarations.", node.span); + "Style rules may not be used within nested declarations.", + node.span, + ); } else if (_inKeyframes && _parent is CssKeyframeBlock) { throw _exception( - "Style rules may not be used within keyframe blocks.", node.span); + "Style rules may not be used within keyframe blocks.", + node.span, + ); } var styleRule = _styleRule; var nest = !(_styleRule?.fromPlainCss ?? false); - var originalSelector = nest - ? node.selector.nestWithin(styleRule?.originalSelector, - implicitParent: !_atRootExcludingStyleRule, - preserveParentSelectors: node.fromPlainCss) - : node.selector; + var originalSelector = + nest + ? node.selector.nestWithin( + styleRule?.originalSelector, + implicitParent: !_atRootExcludingStyleRule, + preserveParentSelectors: node.fromPlainCss, + ) + : node.selector; var selector = _extensionStore.addSelector(originalSelector, _mediaQueries); - var rule = ModifiableCssStyleRule(selector, node.span, - originalSelector: originalSelector, fromPlainCss: node.fromPlainCss); + var rule = ModifiableCssStyleRule( + selector, + node.span, + originalSelector: originalSelector, + fromPlainCss: node.fromPlainCss, + ); var oldAtRootExcludingStyleRule = _atRootExcludingStyleRule; _atRootExcludingStyleRule = false; - _withParent(rule, () { - _withStyleRule(rule, () { - for (var child in node.children) { - child.accept(this); - } - }); - }, through: nest ? (node) => node is CssStyleRule : null, scopeWhen: false); + _withParent( + rule, + () { + _withStyleRule(rule, () { + for (var child in node.children) { + child.accept(this); + } + }); + }, + through: nest ? (node) => node is CssStyleRule : null, + scopeWhen: false, + ); _atRootExcludingStyleRule = oldAtRootExcludingStyleRule; if (_parent.children case [..., var lastChild] when styleRule == null) { @@ -3519,28 +4071,34 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "Supports rules may not be used within nested declarations.", - node.span); - } - - _withParent(ModifiableCssSupportsRule(node.condition, node.span), () { - if (_styleRule case var styleRule?) { - // If we're in a style rule, copy it into the supports rule so that - // declarations immediately inside @supports have somewhere to go. - // - // For example, "a {@supports (a: b) {b: c}}" should produce "@supports - // (a: b) {a {b: c}}". - _withParent(styleRule.copyWithoutChildren(), () { + "Supports rules may not be used within nested declarations.", + node.span, + ); + } + + _withParent( + ModifiableCssSupportsRule(node.condition, node.span), + () { + if (_styleRule case var styleRule?) { + // If we're in a style rule, copy it into the supports rule so that + // declarations immediately inside @supports have somewhere to go. + // + // For example, "a {@supports (a: b) {b: c}}" should produce "@supports + // (a: b) {a {b: c}}". + _withParent(styleRule.copyWithoutChildren(), () { + for (var child in node.children) { + child.accept(this); + } + }); + } else { for (var child in node.children) { child.accept(this); } - }); - } else { - for (var child in node.children) { - child.accept(this); } - } - }, through: (node) => node is CssStyleRule, scopeWhen: false); + }, + through: (node) => node is CssStyleRule, + scopeWhen: false, + ); } // ## Utilities @@ -3570,22 +4128,34 @@ final class _EvaluateVisitor /// If [trim] is `true`, removes whitespace around the result. If /// [warnForColor] is `true`, this will emit a warning for any named color /// values passed into the interpolation. - CssValue _interpolationToValue(Interpolation interpolation, - {bool trim = false, bool warnForColor = false}) { - var result = - _performInterpolation(interpolation, warnForColor: warnForColor); - return CssValue(trim ? trimAscii(result, excludeEscape: true) : result, - interpolation.span); + CssValue _interpolationToValue( + Interpolation interpolation, { + bool trim = false, + bool warnForColor = false, + }) { + var result = _performInterpolation( + interpolation, + warnForColor: warnForColor, + ); + return CssValue( + trim ? trimAscii(result, excludeEscape: true) : result, + interpolation.span, + ); } /// Evaluates [interpolation]. /// /// If [warnForColor] is `true`, this will emit a warning for any named color /// values passed into the interpolation. - String _performInterpolation(Interpolation interpolation, - {bool warnForColor = false}) { - var (result, _) = _performInterpolationHelper(interpolation, - sourceMap: false, warnForColor: warnForColor); + String _performInterpolation( + Interpolation interpolation, { + bool warnForColor = false, + }) { + var (result, _) = _performInterpolationHelper( + interpolation, + sourceMap: false, + warnForColor: warnForColor, + ); return result; } @@ -3593,19 +4163,24 @@ final class _EvaluateVisitor /// can map spans from the resulting string back to the original /// [interpolation]. (String, InterpolationMap) _performInterpolationWithMap( - Interpolation interpolation, - {bool warnForColor = false}) { - var (result, map) = _performInterpolationHelper(interpolation, - sourceMap: true, warnForColor: warnForColor); + Interpolation interpolation, { + bool warnForColor = false, + }) { + var (result, map) = _performInterpolationHelper( + interpolation, + sourceMap: true, + warnForColor: warnForColor, + ); return (result, map!); } /// A helper that implements the core logic of both [_performInterpolation] /// and [_performInterpolationWithMap]. (String, InterpolationMap?) _performInterpolationHelper( - Interpolation interpolation, - {required bool sourceMap, - bool warnForColor = false}) { + Interpolation interpolation, { + required bool sourceMap, + bool warnForColor = false, + }) { var targetLocations = sourceMap ? [] : null; var oldInSupportsDeclaration = _inSupportsDeclaration; _inSupportsDeclaration = false; @@ -3625,19 +4200,23 @@ final class _EvaluateVisitor if (warnForColor && namesByColor.containsKey(result)) { var alternative = BinaryOperationExpression( - BinaryOperator.plus, - StringExpression(Interpolation.plain("", interpolation.span), - quotes: true), - expression); + BinaryOperator.plus, + StringExpression( + Interpolation.plain("", interpolation.span), + quotes: true, + ), + expression, + ); _warn( - "You probably don't mean to use the color value " - "${namesByColor[result]} in interpolation here.\n" - "It may end up represented as $result, which will likely produce " - "invalid CSS.\n" - "Always quote color names when using them as strings or map keys " - '(for example, "${namesByColor[result]}").\n' - "If you really want to use the color value here, use '$alternative'.", - expression.span); + "You probably don't mean to use the color value " + "${namesByColor[result]} in interpolation here.\n" + "It may end up represented as $result, which will likely produce " + "invalid CSS.\n" + "Always quote color names when using them as strings or map keys " + '(for example, "${namesByColor[result]}").\n' + "If you really want to use the color value here, use '$alternative'.", + expression.span, + ); } buffer.write(_serialize(result, expression, quote: false)); @@ -3647,7 +4226,8 @@ final class _EvaluateVisitor return ( buffer.toString(), targetLocations.andThen( - (targetLocations) => InterpolationMap(interpolation, targetLocations)) + (targetLocations) => InterpolationMap(interpolation, targetLocations), + ), ); } @@ -3682,9 +4262,12 @@ final class _EvaluateVisitor if (expression is VariableExpression) { return _addExceptionSpan( - expression, - () => _environment.getVariableNode(expression.name, - namespace: expression.namespace)) ?? + expression, + () => _environment.getVariableNode( + expression.name, + namespace: expression.namespace, + ), + ) ?? expression; } else { return expression; @@ -3699,8 +4282,12 @@ final class _EvaluateVisitor /// lattermost child of its parent. /// /// Runs [callback] in a new environment scope unless [scopeWhen] is false. - T _withParent(S node, T callback(), - {bool through(CssNode node)?, bool scopeWhen = true}) { + T _withParent( + S node, + T callback(), { + bool through(CssNode node)?, + bool scopeWhen = true, + }) { _addChild(node, through: through); var oldParent = _parent; @@ -3725,7 +4312,8 @@ final class _EvaluateVisitor parent = grandparent; } else { throw ArgumentError( - "through() must return false for at least one parent of $node."); + "through() must return false for at least one parent of $node.", + ); } } @@ -3764,7 +4352,10 @@ final class _EvaluateVisitor /// merged together to create [queries]. This is used to determine when it's /// safe to bubble one query through another. T _withMediaQueries( - List? queries, Set? sources, T callback()) { + List? queries, + Set? sources, + T callback(), + ) { var oldMediaQueries = _mediaQueries; var oldSources = _mediaQuerySources; _mediaQueries = queries; @@ -3798,21 +4389,22 @@ final class _EvaluateVisitor Value _withoutSlash(Value value, AstNode nodeForSpan) { if (value case SassNumber(asSlash: _?)) { String recommendation(SassNumber number) => switch (number.asSlash) { - (var before, var after) => - "math.div(${recommendation(before)}, ${recommendation(after)})", - _ => number.toString() - }; + (var before, var after) => + "math.div(${recommendation(before)}, ${recommendation(after)})", + _ => number.toString(), + }; _warn( - "Using / for division is deprecated and will be removed in Dart Sass " - "2.0.0.\n" - "\n" - "Recommendation: ${recommendation(value)}\n" - "\n" - "More info and automated migrator: " - "https://sass-lang.com/d/slash-div", - nodeForSpan.span, - Deprecation.slashDiv); + "Using / for division is deprecated and will be removed in Dart Sass " + "2.0.0.\n" + "\n" + "Recommendation: ${recommendation(value)}\n" + "\n" + "More info and automated migrator: " + "https://sass-lang.com/d/slash-div", + nodeForSpan.span, + Deprecation.slashDiv, + ); } return value.withoutSlash(); @@ -3820,8 +4412,11 @@ final 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. /// @@ -3830,7 +4425,7 @@ final class _EvaluateVisitor var frames = [ for (var (member, nodeWithSpan) in _stack) _stackFrame(member, nodeWithSpan.span), - if (span != null) _stackFrame(_member, span) + if (span != null) _stackFrame(_member, span), ]; return Trace(frames.reversed); } @@ -3847,8 +4442,12 @@ final class _EvaluateVisitor if (deprecation == null) { _logger.warn(message, span: span, trace: trace); } else { - _logger.warnForDeprecation(deprecation, message, - span: span, trace: trace); + _logger.warnForDeprecation( + deprecation, + message, + span: span, + trace: trace, + ); } } @@ -3857,16 +4456,26 @@ final class _EvaluateVisitor /// If [span] is passed, it's used for the innermost stack frame. SassRuntimeException _exception(String message, [FileSpan? span]) => SassRuntimeException( - message, span ?? _stack.last.$2.span, _stackTrace(span)); + message, + span ?? _stack.last.$2.span, + _stackTrace(span), + ); /// Returns a [MultiSpanSassRuntimeException] with the given [message], /// [primaryLabel], and [secondaryLabels]. /// /// The primary span is taken from the current stack trace span. - SassRuntimeException _multiSpanException(String message, String primaryLabel, - Map secondaryLabels) => - MultiSpanSassRuntimeException(message, _stack.last.$2.span, primaryLabel, - secondaryLabels, _stackTrace()); + SassRuntimeException _multiSpanException( + String message, + String primaryLabel, + Map secondaryLabels, + ) => MultiSpanSassRuntimeException( + message, + _stack.last.$2.span, + primaryLabel, + secondaryLabels, + _stackTrace(), + ); /// Runs [callback], and converts any [SassScriptException]s it throws to /// [SassRuntimeException]s with [nodeWithSpan]'s source span. @@ -3877,17 +4486,21 @@ final class _EvaluateVisitor /// /// If [addStackFrame] is true (the default), this will add an innermost stack /// frame for [nodeWithSpan]. Otherwise, it will use the existing stack as-is. - T _addExceptionSpan(AstNode nodeWithSpan, T callback(), - {bool addStackFrame = true}) { + T _addExceptionSpan( + AstNode nodeWithSpan, + T callback(), { + bool addStackFrame = true, + }) { try { return callback(); } on SassScriptException catch (error, stackTrace) { throwWithTrace( - error - .withSpan(nodeWithSpan.span) - .withTrace(_stackTrace(addStackFrame ? nodeWithSpan.span : null)), - error, - stackTrace); + error + .withSpan(nodeWithSpan.span) + .withTrace(_stackTrace(addStackFrame ? nodeWithSpan.span : null)), + error, + stackTrace, + ); } } @@ -3901,7 +4514,10 @@ final class _EvaluateVisitor rethrow; } on SassException catch (error, stackTrace) { throwWithTrace( - error.withTrace(_stackTrace(error.span)), error, stackTrace); + error.withTrace(_stackTrace(error.span)), + error, + stackTrace, + ); } } @@ -3914,9 +4530,10 @@ final class _EvaluateVisitor } on SassRuntimeException catch (error, stackTrace) { if (!error.span.text.startsWith("@error")) rethrow; throwWithTrace( - SassRuntimeException(error.message, nodeWithSpan.span, _stackTrace()), - error, - stackTrace); + SassRuntimeException(error.message, nodeWithSpan.span, _stackTrace()), + error, + stackTrace, + ); } } @@ -3951,8 +4568,10 @@ final class _ImportedCssVisitor implements ModifiableCssVisitor { _ImportedCssVisitor(this._visitor); void visitCssAtRule(ModifiableCssAtRule node) { - _visitor._addChild(node, - through: node.isChildless ? null : (node) => node is CssStyleRule); + _visitor._addChild( + node, + through: node.isChildless ? null : (node) => node is CssStyleRule, + ); } void visitCssComment(ModifiableCssComment node) => _visitor._addChild(node); @@ -3981,12 +4600,16 @@ final class _ImportedCssVisitor implements ModifiableCssVisitor { // has been merged, merging again is a no-op; if it hasn't been merged, // merging again will fail. var mediaQueries = _visitor._mediaQueries; - var hasBeenMerged = mediaQueries == null || + var hasBeenMerged = + mediaQueries == null || _visitor._mergeMediaQueries(mediaQueries, node.queries) != null; - _visitor._addChild(node, - through: (node) => - node is CssStyleRule || (hasBeenMerged && node is CssMediaRule)); + _visitor._addChild( + node, + through: + (node) => + node is CssStyleRule || (hasBeenMerged && node is CssMediaRule), + ); } void visitCssStyleRule(ModifiableCssStyleRule node) => @@ -4021,52 +4644,56 @@ final class _EvaluationContext implements EvaluationContext { void warn(String message, [Deprecation? deprecation]) { _visitor._warn( - message, - _visitor._importSpan ?? - _visitor._callableNode?.span ?? - _defaultWarnNodeWithSpan.span, - deprecation); + message, + _visitor._importSpan ?? + _visitor._callableNode?.span ?? + _defaultWarnNodeWithSpan.span, + deprecation, + ); } } /// The result of evaluating arguments to a function or mixin. -typedef _ArgumentResults = ({ - /// Arguments passed by position. - List positional, - - /// The [AstNode]s that hold the spans for each [positional] argument. - /// - /// 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. - List positionalNodes, - - /// Arguments passed by name. - Map named, - - /// The [AstNode]s that hold the spans for each [named] argument. - /// - /// 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. - Map namedNodes, - - /// The separator used for the rest argument list, if any. - ListSeparator separator -}); +typedef _ArgumentResults = + ({ + /// Arguments passed by position. + List positional, + + /// The [AstNode]s that hold the spans for each [positional] argument. + /// + /// 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. + List positionalNodes, + + /// Arguments passed by name. + Map named, + + /// The [AstNode]s that hold the spans for each [named] argument. + /// + /// 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. + Map namedNodes, + + /// The separator used for the rest argument list, if any. + ListSeparator separator, + }); /// The result of loading a stylesheet via [Evaluator._loadStylesheet]. -typedef _LoadedStylesheet = ( - /// The stylesheet itself. - Stylesheet stylesheet, { - /// The importer that was used to load the stylesheet. - /// - /// This is `null` when running in Node Sass compatibility mode. - Importer? importer, +typedef _LoadedStylesheet = + ( + /// The stylesheet itself. + Stylesheet stylesheet, { + + /// The importer that was used to load the stylesheet. + /// + /// This is `null` when running in Node Sass compatibility mode. + Importer? importer, - /// Whether this load counts as a dependency. - /// - /// That is, whether this was (transitively) loaded through a load path or - /// importer rather than relative to the entrypoint. - bool isDependency -}); + /// Whether this load counts as a dependency. + /// + /// That is, whether this was (transitively) loaded through a load path or + /// importer rather than relative to the entrypoint. + bool isDependency, + }); diff --git a/tool/grind/generate_deprecations.dart b/tool/grind/generate_deprecations.dart index 21ce6f58e..71d128dde 100644 --- a/tool/grind/generate_deprecations.dart +++ b/tool/grind/generate_deprecations.dart @@ -78,5 +78,7 @@ void deprecations() { fail("Couldn't find block for generated code in lib/src/deprecation.dart"); } var newCode = dartText.replaceFirst(_blockRegex, buffer.toString()); - dartFile.writeAsStringSync(DartFormatter().format(newCode)); + dartFile.writeAsStringSync( + DartFormatter(languageVersion: DartFormatter.latestLanguageVersion) + .format(newCode)); } diff --git a/tool/grind/synchronize.dart b/tool/grind/synchronize.dart index de18066d2..efdca7fac 100644 --- a/tool/grind/synchronize.dart +++ b/tool/grind/synchronize.dart @@ -58,7 +58,8 @@ String synchronizeFile(String source) { parseFile(path: source, featureSet: FeatureSet.latestLanguageVersion()) .unit .accept(visitor); - return DartFormatter().format(visitor.result); + return DartFormatter(languageVersion: DartFormatter.latestLanguageVersion) + .format(visitor.result); } /// The visitor that traverses the asynchronous parse tree and converts it to From adb872a4618d98196cd320b80ca00b7de9563feb Mon Sep 17 00:00:00 2001 From: "Carlos (Goodwine)" <2022649+Goodwine@users.noreply.github.com> Date: Mon, 16 Dec 2024 23:46:59 +0000 Subject: [PATCH 3/3] use set the dart version from the Platform library instead of the ananlyzer library because that analyzer library may use beta versions --- lib/src/compile.dart | 264 ++- lib/src/deprecation.dart | 178 +- lib/src/environment.dart | 424 ++-- lib/src/import_cache.dart | 135 +- lib/src/visitor/evaluate.dart | 2721 ++++++++++--------------- tool/grind/generate_deprecations.dart | 7 +- tool/grind/synchronize.dart | 4 +- 7 files changed, 1463 insertions(+), 2270 deletions(-) diff --git a/lib/src/compile.dart b/lib/src/compile.dart index 15e01e50e..35bc75872 100644 --- a/lib/src/compile.dart +++ b/lib/src/compile.dart @@ -37,33 +37,30 @@ import 'visitor/serialize.dart'; /// /// If both `importCache` and `nodeImporter` are provided, the importers in /// `importCache` will be evaluated before `nodeImporter`. -CompileResult compile( - String path, { - Syntax? syntax, - Logger? logger, - ImportCache? importCache, - NodeImporter? nodeImporter, - Iterable? functions, - OutputStyle? style, - bool useSpaces = true, - int? indentWidth, - LineFeed? lineFeed, - bool quietDeps = false, - bool verbose = false, - bool sourceMap = false, - bool charset = true, - Iterable? silenceDeprecations, - Iterable? fatalDeprecations, - Iterable? futureDeprecations, -}) { +CompileResult compile(String path, + {Syntax? syntax, + Logger? logger, + ImportCache? importCache, + NodeImporter? nodeImporter, + Iterable? functions, + OutputStyle? style, + bool useSpaces = true, + int? indentWidth, + LineFeed? lineFeed, + bool quietDeps = false, + bool verbose = false, + bool sourceMap = false, + bool charset = true, + Iterable? silenceDeprecations, + Iterable? fatalDeprecations, + Iterable? futureDeprecations}) { DeprecationProcessingLogger deprecationLogger = - logger = DeprecationProcessingLogger( - logger ?? Logger.stderr(), - silenceDeprecations: {...?silenceDeprecations}, - fatalDeprecations: {...?fatalDeprecations}, - futureDeprecations: {...?futureDeprecations}, - limitRepetition: !verbose, - )..validate(); + logger = DeprecationProcessingLogger(logger ?? Logger.stderr(), + silenceDeprecations: {...?silenceDeprecations}, + fatalDeprecations: {...?fatalDeprecations}, + futureDeprecations: {...?futureDeprecations}, + limitRepetition: !verbose) + ..validate(); // 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. @@ -71,35 +68,29 @@ CompileResult compile( if (nodeImporter == null && (syntax == null || syntax == Syntax.forPath(path))) { importCache ??= ImportCache.none(); - stylesheet = - importCache.importCanonical( - FilesystemImporter.cwd, - p.toUri(canonicalize(path)), - originalUrl: p.toUri(path), - )!; + stylesheet = importCache.importCanonical( + FilesystemImporter.cwd, p.toUri(canonicalize(path)), + originalUrl: p.toUri(path))!; } else { stylesheet = Stylesheet.parse( - readFile(path), - syntax ?? Syntax.forPath(path), - url: p.toUri(path), - ); + readFile(path), syntax ?? Syntax.forPath(path), + url: p.toUri(path)); } var result = _compileStylesheet( - stylesheet, - logger, - importCache, - nodeImporter, - FilesystemImporter.cwd, - functions, - style, - useSpaces, - indentWidth, - lineFeed, - quietDeps, - sourceMap, - charset, - ); + stylesheet, + logger, + importCache, + nodeImporter, + FilesystemImporter.cwd, + functions, + style, + useSpaces, + indentWidth, + lineFeed, + quietDeps, + sourceMap, + charset); deprecationLogger.summarize(js: nodeImporter != null); return result; @@ -109,55 +100,51 @@ CompileResult compile( /// support the node-sass compatible API. /// /// 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, - bool useSpaces = true, - int? indentWidth, - LineFeed? lineFeed, - Object? url, - bool quietDeps = false, - bool verbose = false, - bool sourceMap = false, - bool charset = true, - Iterable? silenceDeprecations, - Iterable? fatalDeprecations, - Iterable? futureDeprecations, -}) { +CompileResult compileString(String source, + {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, + bool quietDeps = false, + bool verbose = false, + bool sourceMap = false, + bool charset = true, + Iterable? silenceDeprecations, + Iterable? fatalDeprecations, + Iterable? futureDeprecations}) { DeprecationProcessingLogger deprecationLogger = - logger = DeprecationProcessingLogger( - logger ?? Logger.stderr(), - silenceDeprecations: {...?silenceDeprecations}, - fatalDeprecations: {...?fatalDeprecations}, - futureDeprecations: {...?futureDeprecations}, - limitRepetition: !verbose, - )..validate(); + logger = DeprecationProcessingLogger(logger ?? Logger.stderr(), + silenceDeprecations: {...?silenceDeprecations}, + fatalDeprecations: {...?fatalDeprecations}, + futureDeprecations: {...?futureDeprecations}, + limitRepetition: !verbose) + ..validate(); var stylesheet = Stylesheet.parse(source, syntax ?? Syntax.scss, url: url); var result = _compileStylesheet( - stylesheet, - logger, - importCache, - nodeImporter, - importer ?? (isBrowser ? NoOpImporter() : FilesystemImporter.cwd), - functions, - style, - useSpaces, - indentWidth, - lineFeed, - quietDeps, - sourceMap, - charset, - ); + stylesheet, + logger, + importCache, + nodeImporter, + importer ?? (isBrowser ? NoOpImporter() : FilesystemImporter.cwd), + functions, + style, + useSpaces, + indentWidth, + lineFeed, + quietDeps, + sourceMap, + charset); deprecationLogger.summarize(js: nodeImporter != null); return result; @@ -167,62 +154,53 @@ CompileResult compileString( /// /// Arguments are handled as for [compileString]. CompileResult _compileStylesheet( - Stylesheet stylesheet, - Logger? logger, - ImportCache? importCache, - NodeImporter? nodeImporter, - Importer importer, - Iterable? functions, - OutputStyle? style, - bool useSpaces, - int? indentWidth, - LineFeed? lineFeed, - bool quietDeps, - bool sourceMap, - bool charset, -) { + Stylesheet stylesheet, + Logger? logger, + ImportCache? importCache, + NodeImporter? nodeImporter, + Importer importer, + Iterable? functions, + OutputStyle? style, + bool useSpaces, + int? indentWidth, + LineFeed? lineFeed, + bool quietDeps, + bool sourceMap, + bool charset) { if (nodeImporter != null) { logger?.warnForDeprecation( - Deprecation.legacyJsApi, - 'The legacy JS API is deprecated and will be removed in ' - 'Dart Sass 2.0.0.\n\n' - 'More info: https://sass-lang.com/d/legacy-js-api', - ); + Deprecation.legacyJsApi, + 'The legacy JS API is deprecated and will be removed in ' + 'Dart Sass 2.0.0.\n\n' + 'More info: https://sass-lang.com/d/legacy-js-api'); } - var evaluateResult = evaluate( - stylesheet, - importCache: importCache, - nodeImporter: nodeImporter, - importer: importer, - functions: functions, - logger: logger, - quietDeps: quietDeps, - sourceMap: sourceMap, - ); - - var serializeResult = serialize( - evaluateResult.stylesheet, - style: style, - useSpaces: useSpaces, - indentWidth: indentWidth, - lineFeed: lineFeed, - logger: logger, - sourceMap: sourceMap, - charset: charset, - ); + var evaluateResult = evaluate(stylesheet, + importCache: importCache, + nodeImporter: nodeImporter, + importer: importer, + functions: functions, + logger: logger, + quietDeps: quietDeps, + sourceMap: sourceMap); + + var serializeResult = serialize(evaluateResult.stylesheet, + style: style, + useSpaces: useSpaces, + indentWidth: indentWidth, + lineFeed: lineFeed, + logger: logger, + sourceMap: sourceMap, + charset: charset); var resultSourceMap = serializeResult.sourceMap; if (resultSourceMap != null && importCache != null) { mapInPlace( - resultSourceMap.urls, - (url) => - url == '' - ? Uri.dataFromString( - stylesheet.span.file.getText(0), - encoding: utf8, - ).toString() - : importCache.sourceMapUrl(Uri.parse(url)).toString(), - ); + resultSourceMap.urls, + (url) => url == '' + ? Uri.dataFromString(stylesheet.span.file.getText(0), + encoding: utf8) + .toString() + : importCache.sourceMapUrl(Uri.parse(url)).toString()); } return CompileResult(evaluateResult, serializeResult); diff --git a/lib/src/deprecation.dart b/lib/src/deprecation.dart index af8517112..1962ec627 100644 --- a/lib/src/deprecation.dart +++ b/lib/src/deprecation.dart @@ -18,153 +18,109 @@ enum Deprecation { // Checksum: 47c97f7824eb25d7f1e64e3230938b88330d40b4 /// Deprecation for passing a string directly to meta.call(). - callString( - 'call-string', - deprecatedIn: '0.0.0', - description: 'Passing a string directly to meta.call().', - ), + callString('call-string', + deprecatedIn: '0.0.0', + description: 'Passing a string directly to meta.call().'), /// Deprecation for @elseif. elseif('elseif', deprecatedIn: '1.3.2', description: '@elseif.'), /// Deprecation for @-moz-document. - mozDocument( - 'moz-document', - deprecatedIn: '1.7.2', - description: '@-moz-document.', - ), + mozDocument('moz-document', + deprecatedIn: '1.7.2', description: '@-moz-document.'), /// Deprecation for imports using relative canonical URLs. - relativeCanonical( - 'relative-canonical', - deprecatedIn: '1.14.2', - description: 'Imports using relative canonical URLs.', - ), + relativeCanonical('relative-canonical', + deprecatedIn: '1.14.2', + description: 'Imports using relative canonical URLs.'), /// Deprecation for declaring new variables with !global. - newGlobal( - 'new-global', - deprecatedIn: '1.17.2', - description: 'Declaring new variables with !global.', - ), + newGlobal('new-global', + deprecatedIn: '1.17.2', + description: 'Declaring new variables with !global.'), /// Deprecation for using color module functions in place of plain CSS functions. - colorModuleCompat( - 'color-module-compat', - deprecatedIn: '1.23.0', - description: - 'Using color module functions in place of plain CSS functions.', - ), + colorModuleCompat('color-module-compat', + deprecatedIn: '1.23.0', + description: + 'Using color module functions in place of plain CSS functions.'), /// Deprecation for / operator for division. - slashDiv( - 'slash-div', - deprecatedIn: '1.33.0', - description: '/ operator for division.', - ), + slashDiv('slash-div', + deprecatedIn: '1.33.0', description: '/ operator for division.'), /// Deprecation for leading, trailing, and repeated combinators. - bogusCombinators( - 'bogus-combinators', - deprecatedIn: '1.54.0', - description: 'Leading, trailing, and repeated combinators.', - ), + bogusCombinators('bogus-combinators', + deprecatedIn: '1.54.0', + description: 'Leading, trailing, and repeated combinators.'), /// Deprecation for ambiguous + and - operators. - strictUnary( - 'strict-unary', - deprecatedIn: '1.55.0', - description: 'Ambiguous + and - operators.', - ), + strictUnary('strict-unary', + deprecatedIn: '1.55.0', description: 'Ambiguous + and - operators.'), /// Deprecation for passing invalid units to built-in functions. - functionUnits( - 'function-units', - deprecatedIn: '1.56.0', - description: 'Passing invalid units to built-in functions.', - ), + functionUnits('function-units', + deprecatedIn: '1.56.0', + description: 'Passing invalid units to built-in functions.'), /// Deprecation for using !default or !global multiple times for one variable. - duplicateVarFlags( - 'duplicate-var-flags', - deprecatedIn: '1.62.0', - description: 'Using !default or !global multiple times for one variable.', - ), + duplicateVarFlags('duplicate-var-flags', + deprecatedIn: '1.62.0', + description: + 'Using !default or !global multiple times for one variable.'), /// Deprecation for passing null as alpha in the ${isJS ? 'JS': 'Dart'} API. - nullAlpha( - 'null-alpha', - deprecatedIn: '1.62.3', - description: 'Passing null as alpha in the ${isJS ? 'JS' : 'Dart'} API.', - ), + nullAlpha('null-alpha', + deprecatedIn: '1.62.3', + description: 'Passing null as alpha in the ${isJS ? 'JS' : 'Dart'} API.'), /// Deprecation for passing percentages to the Sass abs() function. - absPercent( - 'abs-percent', - deprecatedIn: '1.65.0', - description: 'Passing percentages to the Sass abs() function.', - ), + absPercent('abs-percent', + deprecatedIn: '1.65.0', + description: 'Passing percentages to the Sass abs() function.'), /// Deprecation for using the current working directory as an implicit load path. - fsImporterCwd( - 'fs-importer-cwd', - deprecatedIn: '1.73.0', - description: - 'Using the current working directory as an implicit load path.', - ), + fsImporterCwd('fs-importer-cwd', + deprecatedIn: '1.73.0', + description: + 'Using the current working directory as an implicit load path.'), /// Deprecation for function and mixin names beginning with --. - cssFunctionMixin( - 'css-function-mixin', - deprecatedIn: '1.76.0', - description: 'Function and mixin names beginning with --.', - ), + cssFunctionMixin('css-function-mixin', + deprecatedIn: '1.76.0', + description: 'Function and mixin names beginning with --.'), /// Deprecation for declarations after or between nested rules. - mixedDecls( - 'mixed-decls', - deprecatedIn: '1.77.7', - description: 'Declarations after or between nested rules.', - ), + mixedDecls('mixed-decls', + deprecatedIn: '1.77.7', + description: 'Declarations after or between nested rules.'), /// Deprecation for meta.feature-exists - featureExists( - 'feature-exists', - deprecatedIn: '1.78.0', - description: 'meta.feature-exists', - ), + featureExists('feature-exists', + deprecatedIn: '1.78.0', description: 'meta.feature-exists'), /// Deprecation for certain uses of built-in sass:color functions. - color4Api( - 'color-4-api', - deprecatedIn: '1.79.0', - description: 'Certain uses of built-in sass:color functions.', - ), + color4Api('color-4-api', + deprecatedIn: '1.79.0', + description: 'Certain uses of built-in sass:color functions.'), /// Deprecation for using global color functions instead of sass:color. - colorFunctions( - 'color-functions', - deprecatedIn: '1.79.0', - description: 'Using global color functions instead of sass:color.', - ), + colorFunctions('color-functions', + deprecatedIn: '1.79.0', + description: 'Using global color functions instead of sass:color.'), /// Deprecation for legacy JS API. - legacyJsApi( - 'legacy-js-api', - deprecatedIn: '1.79.0', - description: 'Legacy JS API.', - ), + legacyJsApi('legacy-js-api', + deprecatedIn: '1.79.0', description: 'Legacy JS API.'), /// Deprecation for @import rules. import('import', deprecatedIn: '1.80.0', description: '@import rules.'), /// Deprecation for global built-in functions that are available in sass: modules. - globalBuiltin( - 'global-builtin', - deprecatedIn: '1.80.0', - description: - 'Global built-in functions that are available in sass: modules.', - ), + globalBuiltin('global-builtin', + deprecatedIn: '1.80.0', + description: + 'Global built-in functions that are available in sass: modules.'), // END AUTOGENERATED CODE @@ -220,24 +176,24 @@ enum Deprecation { /// Constructs a regular deprecation. const Deprecation(this.id, {required String? deprecatedIn, this.description}) - : _deprecatedIn = deprecatedIn, - _obsoleteIn = null, - isFuture = false; + : _deprecatedIn = deprecatedIn, + _obsoleteIn = null, + isFuture = false; /// Constructs a future deprecation. // ignore: unused_element const Deprecation.future(this.id, {this.description}) - : _deprecatedIn = null, - _obsoleteIn = null, - isFuture = true; + : _deprecatedIn = null, + _obsoleteIn = null, + isFuture = true; @override String toString() => id; /// Returns the deprecation with a given ID, or null if none exists. static Deprecation? fromId(String id) => Deprecation.values.firstWhereOrNull( - (deprecation) => deprecation.id == id, - ); + (deprecation) => deprecation.id == id, + ); /// Returns the set of all deprecations done in or before [version]. static Set forVersion(Version version) { diff --git a/lib/src/environment.dart b/lib/src/environment.dart index 151a37173..3aa0fa45d 100644 --- a/lib/src/environment.dart +++ b/lib/src/environment.dart @@ -154,42 +154,41 @@ final class Environment { /// /// If [sourceMap] is `true`, this tracks variables' source locations Environment() - : _modules = {}, - _namespaceNodes = {}, - _globalModules = {}, - _importedModules = {}, - _forwardedModules = null, - _nestedForwardedModules = null, - _allModules = [], - _variables = [{}], - _variableNodes = [{}], - _variableIndices = {}, - _functions = [{}], - _functionIndices = {}, - _mixins = [{}], - _mixinIndices = {}; + : _modules = {}, + _namespaceNodes = {}, + _globalModules = {}, + _importedModules = {}, + _forwardedModules = null, + _nestedForwardedModules = null, + _allModules = [], + _variables = [{}], + _variableNodes = [{}], + _variableIndices = {}, + _functions = [{}], + _functionIndices = {}, + _mixins = [{}], + _mixinIndices = {}; Environment._( - this._modules, - this._namespaceNodes, - this._globalModules, - this._importedModules, - this._forwardedModules, - this._nestedForwardedModules, - this._allModules, - this._variables, - this._variableNodes, - this._functions, - this._mixins, - this._content, - ) - // Lazily fill in the indices rather than eagerly copying them from the - // existing environment in closure() because the copying took a lot of - // time and was rarely helpful. This saves a bunch of time on Susy's - // tests. - : _variableIndices = {}, - _functionIndices = {}, - _mixinIndices = {}; + this._modules, + this._namespaceNodes, + this._globalModules, + this._importedModules, + this._forwardedModules, + this._nestedForwardedModules, + this._allModules, + this._variables, + this._variableNodes, + this._functions, + this._mixins, + this._content) + // Lazily fill in the indices rather than eagerly copying them from the + // existing environment in closure() because the copying took a lot of + // time and was rarely helpful. This saves a bunch of time on Susy's + // tests. + : _variableIndices = {}, + _functionIndices = {}, + _mixinIndices = {}; /// Creates a closure based on this environment. /// @@ -197,19 +196,18 @@ final class Environment { /// However, any new declarations or assignments in scopes that are visible /// when the closure was created will be reflected. Environment closure() => Environment._( - _modules, - _namespaceNodes, - _globalModules, - _importedModules, - _forwardedModules, - _nestedForwardedModules, - _allModules, - _variables.toList(), - _variableNodes.toList(), - _functions.toList(), - _mixins.toList(), - _content, - ); + _modules, + _namespaceNodes, + _globalModules, + _importedModules, + _forwardedModules, + _nestedForwardedModules, + _allModules, + _variables.toList(), + _variableNodes.toList(), + _functions.toList(), + _mixins.toList(), + _content); /// Returns a new environment to use for an imported file. /// @@ -217,19 +215,18 @@ final class Environment { /// and mixins, but excludes most modules (except for global modules that /// result from importing a file with forwards). Environment forImport() => Environment._( - {}, - {}, - {}, - _importedModules, - null, - null, - [], - _variables.toList(), - _variableNodes.toList(), - _functions.toList(), - _mixins.toList(), - _content, - ); + {}, + {}, + {}, + _importedModules, + null, + null, + [], + _variables.toList(), + _variableNodes.toList(), + _functions.toList(), + _mixins.toList(), + _content); /// Adds [module] to the set of modules visible in this environment. /// @@ -241,11 +238,8 @@ final class Environment { /// 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[module] = nodeWithSpan; _allModules.add(module); @@ -253,18 +247,16 @@ final class Environment { if (_variables.first.keys.firstWhereOrNull(module.variables.containsKey) case var name?) { throw SassScriptException( - 'This module and the new module both define a variable named ' - '"\$$name".', - ); + 'This module and the new module both define a variable named ' + '"\$$name".'); } } else { if (_modules.containsKey(namespace)) { var span = _namespaceNodes[namespace]?.span; throw MultiSpanSassScriptException( - "There's already a module with namespace \"$namespace\".", - "new @use", - {if (span != null) span: "original @use"}, - ); + "There's already a module with namespace \"$namespace\".", + "new @use", + {if (span != null) span: "original @use"}); } _modules[namespace] = module; @@ -281,19 +273,9 @@ final class Environment { var view = ForwardedModuleView.ifNecessary(module, rule); for (var other in forwardedModules.keys) { _assertNoConflicts( - view.variables, - other.variables, - view, - other, - "variable", - ); + view.variables, other.variables, view, other, "variable"); _assertNoConflicts( - view.functions, - other.functions, - view, - other, - "function", - ); + view.functions, other.functions, view, other, "function"); _assertNoConflicts(view.mixins, other.mixins, view, other, "mixin"); } @@ -310,12 +292,11 @@ final class Environment { /// /// The [type] and [newModuleNodeWithSpan] are used for error reporting. void _assertNoConflicts( - Map newMembers, - Map oldMembers, - Module newModule, - Module oldModule, - String type, - ) { + Map newMembers, + Map oldMembers, + Module newModule, + Module oldModule, + String type) { Map smaller; Map larger; if (newMembers.length < oldMembers.length) { @@ -338,10 +319,9 @@ final class Environment { if (type == "variable") name = "\$$name"; var span = _forwardedModules?[oldModule]?.span; throw MultiSpanSassScriptException( - 'Two forwarded modules both define a $type named $name.', - "new @forward", - {if (span != null) span: "original @forward"}, - ); + 'Two forwarded modules both define a $type named $name.', + "new @forward", + {if (span != null) span: "original @forward"}); } } @@ -369,25 +349,23 @@ final class Environment { } var forwardedVariableNames = { - for (var module in forwarded.keys) ...module.variables.keys, + for (var module in forwarded.keys) ...module.variables.keys }; var forwardedFunctionNames = { - for (var module in forwarded.keys) ...module.functions.keys, + for (var module in forwarded.keys) ...module.functions.keys }; var forwardedMixinNames = { - for (var module in forwarded.keys) ...module.mixins.keys, + for (var module in forwarded.keys) ...module.mixins.keys }; if (atRoot) { // Hide members from modules that have already been imported or // forwarded that would otherwise conflict with the @imported members. for (var (module, node) in _importedModules.pairs.toList()) { - var shadowed = ShadowedModuleView.ifNecessary( - module, - variables: forwardedVariableNames, - mixins: forwardedMixinNames, - functions: forwardedFunctionNames, - ); + var shadowed = ShadowedModuleView.ifNecessary(module, + variables: forwardedVariableNames, + mixins: forwardedMixinNames, + functions: forwardedFunctionNames); if (shadowed != null) { _importedModules.remove(module); if (!shadowed.isEmpty) _importedModules[shadowed] = node; @@ -395,12 +373,10 @@ final class Environment { } for (var (module, node) in forwardedModules.pairs.toList()) { - var shadowed = ShadowedModuleView.ifNecessary( - module, - variables: forwardedVariableNames, - mixins: forwardedMixinNames, - functions: forwardedFunctionNames, - ); + var shadowed = ShadowedModuleView.ifNecessary(module, + variables: forwardedVariableNames, + mixins: forwardedMixinNames, + functions: forwardedFunctionNames); if (shadowed != null) { forwardedModules.remove(module); if (!shadowed.isEmpty) forwardedModules[shadowed] = node; @@ -410,10 +386,8 @@ final class Environment { _importedModules.addAll(forwarded); forwardedModules.addAll(forwarded); } else { - (_nestedForwardedModules ??= List.generate( - _variables.length - 1, - (_) => [], - )) + (_nestedForwardedModules ??= + List.generate(_variables.length - 1, (_) => [])) .last .addAll(forwarded.keys); } @@ -562,13 +536,8 @@ final 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; @@ -586,11 +555,8 @@ final class Environment { // If this module doesn't already contain a variable named [name], try // setting it in a global module. if (!_variables.first.containsKey(name)) { - var moduleWithName = _fromOneModule( - name, - "variable", - (module) => module.variables.containsKey(name) ? module : null, - ); + var moduleWithName = _fromOneModule(name, "variable", + (module) => module.variables.containsKey(name) ? module : null); if (moduleWithName != null) { moduleWithName.setVariable(name, value, nodeWithSpan); return; @@ -616,13 +582,10 @@ final class Environment { } } - var index = - _lastVariableName == name - ? _lastVariableIndex! - : _variableIndices.putIfAbsent( - name, - () => _variableIndex(name) ?? _variables.length - 1, - ); + var index = _lastVariableName == name + ? _lastVariableIndex! + : _variableIndices.putIfAbsent( + name, () => _variableIndex(name) ?? _variables.length - 1); if (!_inSemiGlobalScope && index == 0) { index = _variables.length - 1; _variableIndices[name] = index; @@ -837,18 +800,12 @@ final class Environment { /// that contains [css] and [preModuleComments] as its CSS, which can be /// extended using [extensionStore]. Module toModule( - CssStylesheet css, - Map, List> preModuleComments, - ExtensionStore extensionStore, - ) { + CssStylesheet css, + Map, List> preModuleComments, + ExtensionStore extensionStore) { assert(atRoot); - return _EnvironmentModule( - this, - css, - preModuleComments, - extensionStore, - forwarded: _forwardedModules.andThen((modules) => MapKeySet(modules)), - ); + return _EnvironmentModule(this, css, preModuleComments, extensionStore, + forwarded: _forwardedModules.andThen((modules) => MapKeySet(modules))); } /// Returns a module with the same members and upstream modules as `this`, but @@ -858,23 +815,19 @@ final class Environment { /// members into the current scope. It's the only situation in which a nested /// environment can become a module. Module toDummyModule() => _EnvironmentModule( - this, - CssStylesheet( - const [], - SourceFile.decoded(const [], url: "").span(0), - ), - const {}, - ExtensionStore.empty, - forwarded: _forwardedModules.andThen((modules) => MapKeySet(modules)), - ); + this, + CssStylesheet(const [], + SourceFile.decoded(const [], url: "").span(0)), + const {}, + ExtensionStore.empty, + forwarded: _forwardedModules.andThen((modules) => MapKeySet(modules))); /// Returns the module with the given [namespace], or throws a /// [SassScriptException] if none exists. Module _getModule(String namespace) { if (_modules[namespace] case var module?) return module; throw SassScriptException( - 'There is no module with the namespace "$namespace".', - ); + 'There is no module with the namespace "$namespace".'); } /// Returns the result of [callback] if it returns non-`null` for exactly one @@ -889,10 +842,7 @@ final 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), - ) { + String name, String type, T? callback(Module module)) { if (_nestedForwardedModules case var nestedForwardedModules?) { for (var modules in nestedForwardedModules.reversed) { for (var module in modules.reversed) { @@ -910,21 +860,18 @@ final class Environment { var valueInModule = callback(module); if (valueInModule == null) continue; - Object? identityFromModule = - valueInModule is Callable - ? valueInModule - : module.variableIdentity(name); + Object? identityFromModule = valueInModule is Callable + ? valueInModule + : module.variableIdentity(name); if (identityFromModule == identity) continue; if (value != null) { throw MultiSpanSassScriptException( - 'This $type is available from multiple global modules.', - '$type use', - { - for (var (module, node) in _globalModules.pairs) - if (callback(module) != null) node.span: 'includes $type', - }, - ); + 'This $type is available from multiple global modules.', + '$type use', { + for (var (module, node) in _globalModules.pairs) + if (callback(module) != null) node.span: 'includes $type' + }); } value = valueInModule; @@ -961,56 +908,41 @@ final class _EnvironmentModule implements Module { final Map> _modulesByVariable; factory _EnvironmentModule( - Environment environment, - CssStylesheet css, - Map, List> preModuleComments, - ExtensionStore extensionStore, { - Set>? forwarded, - }) { + Environment environment, + CssStylesheet css, + Map, List> preModuleComments, + ExtensionStore extensionStore, + {Set>? forwarded}) { forwarded ??= const {}; return _EnvironmentModule._( - environment, - css, - Map.unmodifiable({ - for (var (module, comments) in preModuleComments.pairs) - module: List.unmodifiable(comments), - }), - extensionStore, - _makeModulesByVariable(forwarded), - _memberMap( - environment._variables.first, - forwarded.map((module) => module.variables), - ), - _memberMap( - environment._variableNodes.first, - forwarded.map((module) => module.variableNodes), - ), - _memberMap( - environment._functions.first, - forwarded.map((module) => module.functions), - ), - _memberMap( - environment._mixins.first, - forwarded.map((module) => module.mixins), - ), - transitivelyContainsCss: - css.children.isNotEmpty || - preModuleComments.isNotEmpty || - environment._allModules.any( - (module) => module.transitivelyContainsCss, - ), - transitivelyContainsExtensions: - !extensionStore.isEmpty || - environment._allModules.any( - (module) => module.transitivelyContainsExtensions, - ), - ); + environment, + css, + Map.unmodifiable({ + for (var (module, comments) in preModuleComments.pairs) + module: List.unmodifiable(comments) + }), + extensionStore, + _makeModulesByVariable(forwarded), + _memberMap(environment._variables.first, + forwarded.map((module) => module.variables)), + _memberMap(environment._variableNodes.first, + forwarded.map((module) => module.variableNodes)), + _memberMap(environment._functions.first, + forwarded.map((module) => module.functions)), + _memberMap(environment._mixins.first, + forwarded.map((module) => module.mixins)), + transitivelyContainsCss: css.children.isNotEmpty || + preModuleComments.isNotEmpty || + environment._allModules + .any((module) => module.transitivelyContainsCss), + transitivelyContainsExtensions: !extensionStore.isEmpty || + environment._allModules + .any((module) => module.transitivelyContainsExtensions)); } /// Create [_modulesByVariable] for a set of forwarded modules. static Map> _makeModulesByVariable( - Set> forwarded, - ) { + Set> forwarded) { if (forwarded.isEmpty) return const {}; var modulesByVariable = >{}; @@ -1020,11 +952,8 @@ final class _EnvironmentModule implements Module { for (var child in module._modulesByVariable.values) { setAll(modulesByVariable, child.variables.keys, child); } - setAll( - modulesByVariable, - module._environment._variables.first.keys, - module, - ); + setAll(modulesByVariable, module._environment._variables.first.keys, + module); } else { setAll(modulesByVariable, module.variables.keys, module); } @@ -1035,16 +964,14 @@ final class _EnvironmentModule implements Module { /// Returns a map that exposes the public members of [localMap] as well as all /// the members of [otherMaps]. static Map _memberMap( - Map localMap, - Iterable> otherMaps, - ) { + Map localMap, Iterable> otherMaps) { localMap = PublicMemberMapView(localMap); if (otherMaps.isEmpty) return localMap; var allMaps = [ for (var map in otherMaps) if (map.isNotEmpty) map, - localMap, + localMap ]; if (allMaps.length == 1) return localMap; @@ -1052,18 +979,18 @@ final class _EnvironmentModule implements Module { } _EnvironmentModule._( - this._environment, - this.css, - this.preModuleComments, - this.extensionStore, - this._modulesByVariable, - this.variables, - this.variableNodes, - this.functions, - this.mixins, { - required this.transitivelyContainsCss, - required this.transitivelyContainsExtensions, - }) : upstream = _environment._allModules; + this._environment, + this.css, + this.preModuleComments, + this.extensionStore, + this._modulesByVariable, + this.variables, + this.variableNodes, + this.functions, + this.mixins, + {required this.transitivelyContainsCss, + required this.transitivelyContainsExtensions}) + : upstream = _environment._allModules; void setVariable(String name, Value value, AstNode nodeWithSpan) { if (_modulesByVariable[name] case var module?) { @@ -1089,23 +1016,20 @@ final class _EnvironmentModule implements Module { Module cloneCss() { if (!transitivelyContainsCss) return this; - var (newStylesheet, newExtensionStore) = cloneCssStylesheet( - css, - extensionStore, - ); + var (newStylesheet, newExtensionStore) = + cloneCssStylesheet(css, extensionStore); return _EnvironmentModule._( - _environment, - newStylesheet, - preModuleComments, - newExtensionStore, - _modulesByVariable, - variables, - variableNodes, - functions, - mixins, - transitivelyContainsCss: transitivelyContainsCss, - transitivelyContainsExtensions: transitivelyContainsExtensions, - ); + _environment, + newStylesheet, + preModuleComments, + newExtensionStore, + _modulesByVariable, + variables, + variableNodes, + functions, + mixins, + transitivelyContainsCss: transitivelyContainsCss, + transitivelyContainsExtensions: transitivelyContainsExtensions); } String toString() => url == null ? "" : p.prettyUri(url); diff --git a/lib/src/import_cache.dart b/lib/src/import_cache.dart index fa15d41ca..b6d38d15f 100644 --- a/lib/src/import_cache.dart +++ b/lib/src/import_cache.dart @@ -91,11 +91,11 @@ final 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, - }) : _importers = _toImporters(importers, loadPaths, packageConfig); + ImportCache( + {Iterable? importers, + Iterable? loadPaths, + PackageConfig? packageConfig}) + : _importers = _toImporters(importers, loadPaths, packageConfig); /// Creates an import cache without any globally-available importers. ImportCache.none() : _importers = const []; @@ -103,15 +103,12 @@ final class ImportCache { /// Creates an import cache without any globally-available importers, and only /// the passed in importers. ImportCache.only(Iterable importers) - : _importers = List.unmodifiable(importers); + : _importers = List.unmodifiable(importers); /// 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'); if (isBrowser) return [...?importers]; return [ @@ -121,7 +118,7 @@ final class ImportCache { if (sassPath != null) for (var path in sassPath.split(isWindows ? ';' : ':')) FilesystemImporter(path), - if (packageConfig != null) PackageImporter(packageConfig), + if (packageConfig != null) PackageImporter(packageConfig) ]; } @@ -140,12 +137,8 @@ final 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`. - CanonicalizeResult? canonicalize( - Uri url, { - Importer? baseImporter, - Uri? baseUrl, - bool forImport = false, - }) { + CanonicalizeResult? canonicalize(Uri url, + {Importer? baseImporter, Uri? baseUrl, bool forImport = false}) { if (isBrowser && (baseImporter == null || baseImporter is NoOpImporter) && _importers.isEmpty) { @@ -157,17 +150,12 @@ final class ImportCache { var resolvedUrl = baseUrl?.resolveUri(url) ?? url; var key = (baseImporter, resolvedUrl, forImport: forImport); var relativeResult = _perImporterCanonicalizeCache.putIfAbsent(key, () { - var (result, cacheable) = _canonicalize( - baseImporter, - resolvedUrl, - baseUrl, - forImport, - ); + var (result, cacheable) = + _canonicalize(baseImporter, resolvedUrl, baseUrl, forImport); assert( - cacheable, - "Relative loads should always be cacheable because they never " - "provide access to the containing URL.", - ); + cacheable, + "Relative loads should always be cacheable because they never " + "provide access to the containing URL."); if (baseUrl != null) _nonCanonicalRelativeUrls[key] = url; return result; }); @@ -210,11 +198,10 @@ final class ImportCache { // future uses of this importer. for (var j = 0; j < i; j++) { _perImporterCanonicalizeCache[( - _importers[j], - url, - forImport: forImport, - )] = - null; + _importers[j], + url, + forImport: forImport + )] = null; } cacheable = false; } @@ -233,24 +220,15 @@ final class ImportCache { /// This returns both the result of the call to `canonicalize()` and whether /// that result is cacheable at all. (CanonicalizeResult?, bool cacheable) _canonicalize( - Importer importer, - Uri url, - Uri? baseUrl, - bool forImport, - ) { - var passContainingUrl = - baseUrl != null && + Importer importer, Uri url, Uri? baseUrl, bool forImport) { + var passContainingUrl = baseUrl != null && (url.scheme == '' || importer.isNonCanonicalScheme(url.scheme)); - var canonicalizeContext = CanonicalizeContext( - passContainingUrl ? baseUrl : null, - forImport, - ); + var canonicalizeContext = + CanonicalizeContext(passContainingUrl ? baseUrl : null, forImport); var result = withCanonicalizeContext( - canonicalizeContext, - () => importer.canonicalize(url), - ); + canonicalizeContext, () => importer.canonicalize(url)); var cacheable = !passContainingUrl || !canonicalizeContext.wasContainingUrlAccessed; @@ -277,24 +255,13 @@ final class ImportCache { /// parsed stylesheet. Otherwise, returns `null`. /// /// Caches the result of the import and uses cached results if possible. - (Importer, Stylesheet)? import( - Uri url, { - Importer? baseImporter, - Uri? baseUrl, - bool forImport = false, - }) { - if (canonicalize( - url, - baseImporter: baseImporter, - baseUrl: baseUrl, - forImport: forImport, - ) + (Importer, Stylesheet)? import(Uri url, + {Importer? baseImporter, Uri? baseUrl, bool forImport = false}) { + if (canonicalize(url, + baseImporter: baseImporter, baseUrl: baseUrl, forImport: forImport) case (var importer, var canonicalUrl, :var originalUrl)) { - return importCanonical( - importer, - canonicalUrl, - originalUrl: originalUrl, - ).andThen((stylesheet) => (importer, stylesheet)); + return importCanonical(importer, canonicalUrl, originalUrl: originalUrl) + .andThen((stylesheet) => (importer, stylesheet)); } else { return null; } @@ -310,11 +277,8 @@ final class ImportCache { /// 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}) { return _importCache.putIfAbsent(canonicalUrl, () { var loadTime = DateTime.now(); var result = importer.load(canonicalUrl); @@ -322,16 +286,12 @@ final class ImportCache { _loadTimes[canonicalUrl] = loadTime; _resultsCache[canonicalUrl] = result; - return Stylesheet.parse( - result.contents, - result.syntax, - // For backwards-compatibility, relative canonical URLs are resolved - // relative to [originalUrl]. - url: - originalUrl == null - ? canonicalUrl - : originalUrl.resolveUri(canonicalUrl), - ); + return Stylesheet.parse(result.contents, result.syntax, + // For backwards-compatibility, relative canonical URLs are resolved + // relative to [originalUrl]. + url: originalUrl == null + ? canonicalUrl + : originalUrl.resolveUri(canonicalUrl)); }); } @@ -342,15 +302,14 @@ final class ImportCache { // If multiple original URLs canonicalize to the same thing, choose the // shortest one. minBy( - _canonicalizeCache.values.nonNulls - .where((result) => result.$2 == canonicalUrl) - .map((result) => result.originalUrl), - (url) => url.path.length, - ) - // Use the canonicalized basename so that we display e.g. - // package:example/_example.scss rather than package:example/example - // in stack traces. - .andThen((url) => url.resolve(p.url.basename(canonicalUrl.path))) ?? + _canonicalizeCache.values.nonNulls + .where((result) => result.$2 == canonicalUrl) + .map((result) => result.originalUrl), + (url) => url.path.length) + // Use the canonicalized basename so that we display e.g. + // package:example/_example.scss rather than package:example/example + // in stack traces. + .andThen((url) => url.resolve(p.url.basename(canonicalUrl.path))) ?? // If we don't have an original URL cached, display the canonical URL // as-is. canonicalUrl; diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index ad9ed0a00..a0d0eb7a9 100644 --- a/lib/src/visitor/evaluate.dart +++ b/lib/src/visitor/evaluate.dart @@ -86,23 +86,22 @@ typedef _ScopeCallback = void Function(void Function() callback); /// declarations. /// /// Throws a [SassRuntimeException] if evaluation fails. -EvaluateResult evaluate( - Stylesheet stylesheet, { - ImportCache? importCache, - NodeImporter? nodeImporter, - Importer? importer, - Iterable? functions, - Logger? logger, - bool quietDeps = false, - bool sourceMap = false, -}) => _EvaluateVisitor( - importCache: importCache, - nodeImporter: nodeImporter, - functions: functions, - logger: logger, - quietDeps: quietDeps, - sourceMap: sourceMap, -).run(importer, stylesheet); +EvaluateResult evaluate(Stylesheet stylesheet, + {ImportCache? importCache, + NodeImporter? nodeImporter, + Importer? importer, + Iterable? functions, + Logger? logger, + bool quietDeps = false, + bool sourceMap = false}) => + _EvaluateVisitor( + importCache: importCache, + nodeImporter: nodeImporter, + functions: functions, + logger: logger, + quietDeps: quietDeps, + sourceMap: sourceMap) + .run(importer, stylesheet); /// A class that can evaluate multiple independent statements and expressions /// in the context of a single module. @@ -116,17 +115,14 @@ final class Evaluator { /// Creates an evaluator. /// /// Arguments are the same as for [evaluate]. - Evaluator({ - ImportCache? importCache, - Importer? importer, - Iterable? functions, - Logger? logger, - }) : _visitor = _EvaluateVisitor( - importCache: importCache, - functions: functions, - logger: logger, - ), - _importer = importer; + Evaluator( + {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); @@ -343,80 +339,63 @@ final class _EvaluateVisitor /// Creates a new visitor. /// /// Most arguments are the same as those to [evaluate]. - _EvaluateVisitor({ - ImportCache? importCache, - NodeImporter? nodeImporter, - Iterable? functions, - Logger? logger, - bool quietDeps = false, - bool sourceMap = false, - }) : _importCache = - importCache ?? (nodeImporter == null ? ImportCache.none() : null), - _nodeImporter = nodeImporter, - _logger = logger ?? const Logger.stderr(), - _quietDeps = quietDeps, - _sourceMap = sourceMap, - // The default environment is overridden in [_execute] for full - // stylesheets, but for [AsyncEvaluator] this environment is used. - _environment = Environment() { + _EvaluateVisitor( + {ImportCache? importCache, + NodeImporter? nodeImporter, + Iterable? functions, + Logger? logger, + bool quietDeps = false, + bool sourceMap = false}) + : _importCache = + importCache ?? (nodeImporter == null ? ImportCache.none() : null), + _nodeImporter = nodeImporter, + _logger = logger ?? const Logger.stderr(), + _quietDeps = quietDeps, + _sourceMap = sourceMap, + // The default environment is overridden in [_execute] for full + // stylesheets, but for [AsyncEvaluator] this environment is used. + _environment = Environment() { var metaFunctions = [ // These functions are defined in the context of the evaluator because // they need access to the [_environment] or other local state. BuiltInCallable.function( - "global-variable-exists", - r"$name, $module: null", - (arguments) { - var variable = arguments[0].assertString("name"); - var module = arguments[1].realNull?.assertString("module"); - return SassBoolean( - _environment.globalVariableExists( - variable.text.replaceAll("_", "-"), - namespace: module?.text, - ), - ); - }, - url: "sass:meta", - ), + "global-variable-exists", r"$name, $module: null", (arguments) { + var variable = arguments[0].assertString("name"); + var module = arguments[1].realNull?.assertString("module"); + return SassBoolean(_environment.globalVariableExists( + variable.text.replaceAll("_", "-"), + namespace: module?.text)); + }, url: "sass:meta"), BuiltInCallable.function("variable-exists", r"$name", (arguments) { var variable = arguments[0].assertString("name"); return SassBoolean( - _environment.variableExists(variable.text.replaceAll("_", "-")), - ); + _environment.variableExists(variable.text.replaceAll("_", "-"))); }, url: "sass:meta"), - BuiltInCallable.function("function-exists", r"$name, $module: null", ( - arguments, - ) { + BuiltInCallable.function("function-exists", r"$name, $module: null", + (arguments) { var variable = arguments[0].assertString("name"); var module = arguments[1].realNull?.assertString("module"); - return SassBoolean( - _environment.functionExists( + return SassBoolean(_environment.functionExists( variable.text.replaceAll("_", "-"), - namespace: module?.text, - ) || - _builtInFunctions.containsKey(variable.text), - ); + namespace: module?.text) || + _builtInFunctions.containsKey(variable.text)); }, url: "sass:meta"), - BuiltInCallable.function("mixin-exists", r"$name, $module: null", ( - arguments, - ) { + BuiltInCallable.function("mixin-exists", r"$name, $module: null", + (arguments) { var variable = arguments[0].assertString("name"); var module = arguments[1].realNull?.assertString("module"); - return SassBoolean( - _environment.mixinExists( + return SassBoolean(_environment.mixinExists( variable.text.replaceAll("_", "-"), - namespace: module?.text, - ), - ); + namespace: module?.text)); }, url: "sass:meta"), BuiltInCallable.function("content-exists", "", (arguments) { if (!_environment.inMixin) { throw SassScriptException( - "content-exists() may only be called within a mixin.", - ); + "content-exists() may only be called within a mixin."); } return SassBoolean(_environment.content != null); }, url: "sass:meta"), @@ -430,7 +409,7 @@ final class _EvaluateVisitor return SassMap({ for (var (name, value) in module.variables.pairs) - SassString(name): value, + SassString(name): value }); }, url: "sass:meta"), @@ -443,7 +422,7 @@ final class _EvaluateVisitor return SassMap({ for (var (name, value) in module.functions.pairs) - SassString(name): SassFunction(value), + SassString(name): SassFunction(value) }); }, url: "sass:meta"), @@ -456,55 +435,45 @@ final class _EvaluateVisitor return SassMap({ for (var (name, value) in module.mixins.pairs) - SassString(name): SassMixin(value), + SassString(name): SassMixin(value) }); }, url: "sass:meta"), BuiltInCallable.function( - "get-function", - r"$name, $css: false, $module: null", - (arguments) { - var name = arguments[0].assertString("name"); - var css = arguments[1].isTruthy; - var module = arguments[2].realNull?.assertString("module"); - - if (css) { - if (module != null) { - throw r"$css and $module may not both be passed at once."; - } - return SassFunction(PlainCssCallable(name.text)); + "get-function", r"$name, $css: false, $module: null", (arguments) { + var name = arguments[0].assertString("name"); + var css = arguments[1].isTruthy; + var module = arguments[2].realNull?.assertString("module"); + + if (css) { + if (module != null) { + throw r"$css and $module may not both be passed at once."; } + return SassFunction(PlainCssCallable(name.text)); + } - var callable = _addExceptionSpan(_callableNode!, () { - var normalizedName = name.text.replaceAll("_", "-"); - var namespace = module?.text; - var local = _environment.getFunction( - normalizedName, - namespace: namespace, - ); - if (local != null || namespace != null) return local; - return _builtInFunctions[normalizedName]; - }); - if (callable == null) throw "Function not found: $name"; + var callable = _addExceptionSpan(_callableNode!, () { + var normalizedName = name.text.replaceAll("_", "-"); + var namespace = module?.text; + var local = + _environment.getFunction(normalizedName, namespace: namespace); + if (local != null || namespace != null) return local; + return _builtInFunctions[normalizedName]; + }); + if (callable == null) throw "Function not found: $name"; - return SassFunction(callable); - }, - url: "sass:meta", - ), + return SassFunction(callable); + }, url: "sass:meta"), - BuiltInCallable.function("get-mixin", r"$name, $module: null", ( - arguments, - ) { + BuiltInCallable.function("get-mixin", r"$name, $module: null", + (arguments) { var name = arguments[0].assertString("name"); var module = arguments[1].realNull?.assertString("module"); var callable = _addExceptionSpan( - _callableNode!, - () => _environment.getMixin( - name.text.replaceAll("_", "-"), - namespace: module?.text, - ), - ); + _callableNode!, + () => _environment.getMixin(name.text.replaceAll("_", "-"), + namespace: module?.text)); if (callable == null) throw "Mixin not found: $name"; return SassMixin(callable); @@ -515,38 +484,28 @@ final class _EvaluateVisitor var args = arguments[1] as SassArgumentList; var callableNode = _callableNode!; - var invocation = ArgumentList( - [], - {}, - callableNode.span, - rest: ValueExpression(args, callableNode.span), - keywordRest: - args.keywords.isEmpty - ? null - : ValueExpression( + var invocation = ArgumentList([], {}, callableNode.span, + rest: ValueExpression(args, callableNode.span), + keywordRest: args.keywords.isEmpty + ? null + : ValueExpression( SassMap({ for (var (name, value) in args.keywords.pairs) - SassString(name, quotes: false): value, + SassString(name, quotes: false): value }), - callableNode.span, - ), - ); + callableNode.span)); if (function is SassString) { warnForDeprecation( - "Passing a string to call() is deprecated and will be illegal in " - "Dart Sass 2.0.0.\n" - "\n" - "Recommendation: call(get-function($function))", - Deprecation.callString, - ); + "Passing a string to call() is deprecated and will be illegal in " + "Dart Sass 2.0.0.\n" + "\n" + "Recommendation: call(get-function($function))", + Deprecation.callString); var callableNode = _callableNode!; - var expression = FunctionExpression( - function.text, - invocation, - callableNode.span, - ); + var expression = + FunctionExpression(function.text, invocation, callableNode.span); return expression.accept(this); } @@ -556,11 +515,10 @@ final class _EvaluateVisitor return _runFunctionCallable(invocation, callable, _callableNode!); } else { throw SassScriptException( - "The function ${callable.name} is asynchronous.\n" - "This is probably caused by a bug in a Sass plugin.", - ); + "The function ${callable.name} is asynchronous.\n" + "This is probably caused by a bug in a Sass plugin."); } - }, url: "sass:meta"), + }, url: "sass:meta") ]; var metaMixins = [ @@ -574,10 +532,8 @@ final class _EvaluateVisitor var values = {}; var span = callableNode.span; withMap.forEach((variable, value) { - var name = variable - .assertString("with key") - .text - .replaceAll("_", "-"); + var name = + variable.assertString("with key").text.replaceAll("_", "-"); if (values.containsKey(name)) { throw "The variable \$$name was configured twice."; } @@ -587,61 +543,43 @@ final class _EvaluateVisitor configuration = ExplicitConfiguration(values, callableNode); } - _loadModule( - url, - "load-css()", - callableNode, - (module, _) => _combineCss(module, clone: true).accept(this), - baseUrl: callableNode.span.sourceUrl, - configuration: configuration, - namesInErrors: true, - ); + _loadModule(url, "load-css()", callableNode, + (module, _) => _combineCss(module, clone: true).accept(this), + baseUrl: callableNode.span.sourceUrl, + configuration: configuration, + namesInErrors: true); _assertConfigurationIsEmpty(configuration, nameInError: true); }, url: "sass:meta"), - BuiltInCallable.mixin( - "apply", - r"$mixin, $args...", - (arguments) { - var mixin = arguments[0]; - var args = arguments[1] as SassArgumentList; + BuiltInCallable.mixin("apply", r"$mixin, $args...", (arguments) { + var mixin = arguments[0]; + var args = arguments[1] as SassArgumentList; - var callableNode = _callableNode!; - var invocation = ArgumentList( - const [], - const {}, - callableNode.span, - rest: ValueExpression(args, callableNode.span), - ); - - var callable = mixin.assertMixin("mixin").callable; - var content = _environment.content; - - // ignore: unnecessary_type_check - if (callable is Callable) { - _applyMixin( - callable, - content, - invocation, - callableNode, - callableNode, - ); - } else { - throw SassScriptException( + var callableNode = _callableNode!; + var invocation = ArgumentList( + const [], + const {}, + callableNode.span, + rest: ValueExpression(args, callableNode.span), + ); + + var callable = mixin.assertMixin("mixin").callable; + var content = _environment.content; + + // ignore: unnecessary_type_check + if (callable is Callable) { + _applyMixin( + callable, content, invocation, callableNode, callableNode); + } else { + throw SassScriptException( "The mixin ${callable.name} is asynchronous.\n" - "This is probably caused by a bug in a Sass plugin.", - ); - } - }, - url: "sass:meta", - acceptsContent: true, - ), + "This is probably caused by a bug in a Sass plugin."); + } + }, url: "sass:meta", acceptsContent: true), ]; - var metaModule = BuiltInModule( - "meta", - functions: [...meta.moduleFunctions, ...metaFunctions], - mixins: metaMixins, - ); + var metaModule = BuiltInModule("meta", + functions: [...meta.moduleFunctions, ...metaFunctions], + mixins: metaMixins); for (var module in [...coreModules, metaModule]) { _builtInModules[module.url] = module; @@ -652,8 +590,8 @@ final class _EvaluateVisitor ...globalFunctions, ...[ for (var function in metaFunctions) - function.withDeprecationWarning('meta'), - ], + function.withDeprecationWarning('meta') + ] ]; for (var function in functions) { _builtInFunctions[function.name.replaceAll("_", "-")] = function; @@ -678,23 +616,15 @@ final class _EvaluateVisitor Value runExpression(Importer? importer, Expression expression) => withEvaluationContext( - _EvaluationContext(this, expression), - () => _withFakeStylesheet( - importer, - expression, - () => _addExceptionTrace(() => expression.accept(this)), - ), - ); + _EvaluationContext(this, expression), + () => _withFakeStylesheet(importer, expression, + () => _addExceptionTrace(() => expression.accept(this)))); void runStatement(Importer? importer, Statement statement) => withEvaluationContext( - _EvaluationContext(this, statement), - () => _withFakeStylesheet( - importer, - statement, - () => _addExceptionTrace(() => statement.accept(this)), - ), - ); + _EvaluationContext(this, statement), + () => _withFakeStylesheet(importer, statement, + () => _addExceptionTrace(() => statement.accept(this)))); /// Asserts that [value] is not `null` and returns it. /// @@ -709,10 +639,7 @@ final 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; @@ -745,23 +672,18 @@ final 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 callback(Module module, bool firstLoad), { - Uri? baseUrl, - Configuration? configuration, - bool namesInErrors = false, - }) { + void _loadModule(Uri url, String stackFrame, AstNode nodeWithSpan, + void callback(Module module, bool firstLoad), + {Uri? baseUrl, + Configuration? configuration, + bool namesInErrors = false}) { if (_builtInModules[url] case var builtInModule?) { if (configuration is ExplicitConfiguration) { throw _exception( - namesInErrors - ? "Built-in module $url can't be configured." - : "Built-in modules can't be configured.", - configuration.nodeWithSpan.span, - ); + namesInErrors + ? "Built-in module $url can't be configured." + : "Built-in modules can't be configured.", + configuration.nodeWithSpan.span); } // Always consider built-in stylesheets to be "already loaded", since they @@ -771,26 +693,20 @@ final class _EvaluateVisitor } _withStackFrame(stackFrame, nodeWithSpan, () { - var (stylesheet, :importer, :isDependency) = _loadStylesheet( - url.toString(), - nodeWithSpan.span, - baseUrl: baseUrl, - ); + var (stylesheet, :importer, :isDependency) = + _loadStylesheet(url.toString(), nodeWithSpan.span, baseUrl: baseUrl); var canonicalUrl = stylesheet.span.sourceUrl; if (canonicalUrl != null) { if (_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) => _multiSpanException(message, "new load", { - previousLoad.span: "original load", - }), - ) ?? + 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) => + _multiSpanException(message, "new load", + {previousLoad.span: "original load"})) ?? _exception(message); } else { _activeModules[canonicalUrl] = nodeWithSpan; @@ -802,23 +718,17 @@ final class _EvaluateVisitor _inDependency = isDependency; Module module; try { - module = _execute( - importer, - stylesheet, - configuration: configuration, - nodeWithSpan: nodeWithSpan, - namesInErrors: namesInErrors, - ); + module = _execute(importer, stylesheet, + configuration: configuration, + nodeWithSpan: nodeWithSpan, + namesInErrors: namesInErrors); } finally { _activeModules.remove(canonicalUrl); _inDependency = oldInDependency; } - _addExceptionSpan( - nodeWithSpan, - () => callback(module, firstLoad), - addStackFrame: false, - ); + _addExceptionSpan(nodeWithSpan, () => callback(module, firstLoad), + addStackFrame: false); }); } @@ -830,34 +740,29 @@ final 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, - bool namesInErrors = false, - }) { + Module _execute(Importer? importer, Stylesheet stylesheet, + {Configuration? configuration, + AstNode? nodeWithSpan, + bool namesInErrors = false}) { var url = stylesheet.span.sourceUrl; if (_modules[url] case var alreadyLoaded?) { var currentConfiguration = configuration ?? _configuration; if (!_moduleConfigurations[url]!.sameOriginal(currentConfiguration) && 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 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 existingSpan = _moduleNodes[url]?.span; - var configurationSpan = - configuration == null - ? currentConfiguration.nodeWithSpan.span - : null; + var configurationSpan = configuration == null + ? currentConfiguration.nodeWithSpan.span + : null; var secondarySpans = { if (existingSpan != null) existingSpan: "original load", - if (configurationSpan != null) configurationSpan: "configuration", + if (configurationSpan != null) configurationSpan: "configuration" }; throw secondarySpans.isEmpty @@ -904,10 +809,9 @@ final class _EvaluateVisitor if (configuration != null) _configuration = configuration; visitStylesheet(stylesheet); - css = - _outOfOrderImports == null - ? root - : CssStylesheet(_addOutOfOrderImports(), stylesheet.span); + css = _outOfOrderImports == null + ? root + : CssStylesheet(_addOutOfOrderImports(), stylesheet.span); preModuleComments = _preModuleComments; _importer = oldImporter; @@ -928,10 +832,7 @@ final class _EvaluateVisitor }); var module = environment.toModule( - css, - preModuleComments ?? const {}, - extensionStore, - ); + css, preModuleComments ?? const {}, extensionStore); if (url != null) { _modules[url] = module; _moduleConfigurations[url] = _configuration; @@ -947,10 +848,10 @@ final class _EvaluateVisitor switch (_outOfOrderImports) { null => _root.children, var outOfOrderImports => [ - ..._root.children.take(_endOfImports), - ...outOfOrderImports, - ..._root.children.skip(_endOfImports), - ], + ..._root.children.take(_endOfImports), + ...outOfOrderImports, + ..._root.children.skip(_endOfImports) + ] }; /// Returns a new stylesheet containing [root]'s CSS as well as the CSS of all @@ -1042,15 +943,11 @@ final class _EvaluateVisitor // Add all as-yet-unsatisfied extensions before adding downstream // [ExtensionStore]s, because those are all in [unsatisfiedExtensions] // already. - unsatisfiedExtensions.addAll( - module.extensionStore.extensionsWhereTarget( - (target) => !originalSelectors.contains(target), - ), - ); + unsatisfiedExtensions.addAll(module.extensionStore.extensionsWhereTarget( + (target) => !originalSelectors.contains(target))); - downstreamExtensionStores[module.url].andThen( - module.extensionStore.addExtensions, - ); + downstreamExtensionStores[module.url] + .andThen(module.extensionStore.addExtensions); if (module.extensionStore.isEmpty) continue; for (var upstream in module.upstream) { @@ -1064,9 +961,8 @@ final class _EvaluateVisitor // Remove all extensions that are now satisfied after adding downstream // [ExtensionStore]s so it counts any downstream extensions that have been // newly satisfied. - unsatisfiedExtensions.removeAll( - module.extensionStore.extensionsWhereTarget(originalSelectors.contains), - ); + unsatisfiedExtensions.removeAll(module.extensionStore + .extensionsWhereTarget(originalSelectors.contains)); } if (unsatisfiedExtensions.isNotEmpty) { @@ -1077,10 +973,9 @@ final class _EvaluateVisitor /// Throws an exception indicating that [extension] is unsatisfied. Never _throwForUnsatisfiedExtension(Extension extension) { throw SassException( - 'The target selector was not found.\n' - 'Use "@extend ${extension.target} !optional" to avoid this error.', - extension.span, - ); + 'The target selector was not found.\n' + 'Use "@extend ${extension.target} !optional" to avoid this error.', + extension.span); } /// Returns the index of the first node in [statements] that comes after all @@ -1115,8 +1010,7 @@ final class _EvaluateVisitor // module's definition, even if their assignments aren't reached. for (var (name, span) in node.globalVariables.pairs) { visitVariableDeclaration( - VariableDeclaration(name, NullExpression(span), span, guarded: true), - ); + VariableDeclaration(name, NullExpression(span), span, guarded: true)); } return null; @@ -1125,10 +1019,8 @@ final class _EvaluateVisitor Value? visitAtRootRule(AtRootRule node) { var query = AtRootQuery.defaultQuery; if (node.query case var unparsedQuery?) { - var (resolved, map) = _performInterpolationWithMap( - unparsedQuery, - warnForColor: true, - ); + var (resolved, map) = + _performInterpolationWithMap(unparsedQuery, warnForColor: true); query = AtRootQuery.parse(resolved, interpolationMap: map); } @@ -1141,8 +1033,7 @@ final class _EvaluateVisitor parent = grandparent; } else { throw StateError( - "CssNodes must have a CssStylesheet transitive parent node.", - ); + "CssNodes must have a CssStylesheet transitive parent node."); } } var root = _trimIncluded(included); @@ -1203,8 +1094,7 @@ final class _EvaluateVisitor parent = grandparent; } else { throw ArgumentError( - "Expected ${nodes[i]} to be an ancestor of $this.", - ); + "Expected ${nodes[i]} to be an ancestor of $this."); } } innermostContiguous ??= i; @@ -1228,11 +1118,10 @@ final class _EvaluateVisitor /// duration, based on which rules are excluded by [query]. It always assigns /// [_parent] to [newParent]. _ScopeCallback _scopeForAtRoot( - AtRootRule node, - ModifiableCssParentNode newParent, - AtRootQuery query, - List included, - ) { + AtRootRule node, + ModifiableCssParentNode newParent, + AtRootQuery query, + List included) { var scope = (void callback()) { // We can't use [_withParent] here because it'll add the node to the tree // in the wrong place. @@ -1254,9 +1143,8 @@ final class _EvaluateVisitor if (_mediaQueries != null && query.excludesName('media')) { var innerScope = scope; - scope = - (callback) => - _withMediaQueries(null, null, () => innerScope(callback)); + scope = (callback) => + _withMediaQueries(null, null, () => innerScope(callback)); } if (_inKeyframes && query.excludesName('keyframes')) { @@ -1282,10 +1170,8 @@ final class _EvaluateVisitor return scope; } - Value visitContentBlock(ContentBlock node) => - throw UnsupportedError( - "Evaluation handles @include and its content block together.", - ); + Value visitContentBlock(ContentBlock node) => throw UnsupportedError( + "Evaluation handles @include and its content block together."); Value? visitContentRule(ContentRule node) { var content = _environment.content; @@ -1304,24 +1190,20 @@ final class _EvaluateVisitor Value? visitDebugRule(DebugRule node) { var value = node.expression.accept(this); _logger.debug( - value is SassString ? value.text : serializeValue(value, inspect: true), - node.span, - ); + value is SassString ? value.text : serializeValue(value, inspect: true), + node.span); return null; } Value? visitDeclaration(Declaration node) { if (_styleRule == null && !_inUnknownAtRule && !_inKeyframes) { throw _exception( - "Declarations may only be used within style rules.", - node.span, - ); + "Declarations may only be used within style rules.", node.span); } if (_declarationName != null && node.isCustomProperty) { throw _exception( - 'Declarations whose names begin with "--" may not be nested.', - node.span, - ); + 'Declarations whose names begin with "--" may not be nested.', + node.span); } var siblings = _parent.parent!.children; @@ -1329,7 +1211,8 @@ final class _EvaluateVisitor if (siblings.last != _parent && // Reproduce this condition from [_warn] so that we don't add anything to // [interleavedRules] for declarations in dependencies. - !(_quietDeps && (_inDependency || (_currentCallable?.inDependency ?? false)))) { + !(_quietDeps && + (_inDependency || (_currentCallable?.inDependency ?? false)))) { loop: for (var sibling in siblings.skip(siblings.indexOf(_parent) + 1)) { switch (sibling) { @@ -1344,20 +1227,18 @@ final class _EvaluateVisitor // add no specificity and they're nested in the same parent as this // declaration. _warn( - "Sass's behavior for declarations that appear after nested\n" - "rules will be changing to match the behavior specified by CSS " - "in an upcoming\n" - "version. To keep the existing behavior, move the declaration " - "above the nested\n" - "rule. To opt into the new behavior, wrap the declaration in " - "`& {}`.\n" - "\n" - "More info: https://sass-lang.com/d/mixed-decls", - MultiSpan(node.span, 'declaration', { - sibling.span: 'nested rule', - }), - Deprecation.mixedDecls, - ); + "Sass's behavior for declarations that appear after nested\n" + "rules will be changing to match the behavior specified by CSS " + "in an upcoming\n" + "version. To keep the existing behavior, move the declaration " + "above the nested\n" + "rule. To opt into the new behavior, wrap the declaration in " + "`& {}`.\n" + "\n" + "More info: https://sass-lang.com/d/mixed-decls", + MultiSpan( + node.span, 'declaration', {sibling.span: 'nested rule'}), + Deprecation.mixedDecls); interleavedRules.clear(); break; } @@ -1374,23 +1255,16 @@ final class _EvaluateVisitor // If the value is an empty list, preserve it, because converting it to CSS // will throw an error that we want the user to see. if (!value.isBlank || _isEmptyList(value)) { - _parent.addChild( - ModifiableCssDeclaration( - name, - CssValue(value, expression.span), - node.span, + _parent.addChild(ModifiableCssDeclaration( + name, CssValue(value, expression.span), node.span, parsedAsCustomProperty: node.isCustomProperty, interleavedRules: interleavedRules, trace: interleavedRules.isEmpty ? null : _stackTrace(node.span), valueSpanForMap: - _sourceMap ? node.value.andThen(_expressionNode)?.span : null, - ), - ); + _sourceMap ? node.value.andThen(_expressionNode)?.span : null)); } else if (name.value.startsWith('--')) { throw _exception( - "Custom property values may not be empty.", - expression.span, - ); + "Custom property values may not be empty.", expression.span); } } @@ -1415,22 +1289,16 @@ final class _EvaluateVisitor var list = node.list.accept(this); var nodeWithSpan = _expressionNode(node.list); var setVariables = switch (node.variables) { - [var variable] => - (Value value) => _environment.setLocalVariable( - variable, - _withoutSlash(value, nodeWithSpan), - nodeWithSpan, - ), - var variables => - (Value value) => _setMultipleVariables(variables, value, nodeWithSpan), + [var variable] => (Value value) => _environment.setLocalVariable( + variable, _withoutSlash(value, nodeWithSpan), nodeWithSpan), + var variables => (Value value) => + _setMultipleVariables(variables, value, nodeWithSpan) }; return _environment.scope(() { return _handleReturn(list.asList, (element) { setVariables(element); return _handleReturn( - node.children, - (child) => child.accept(this), - ); + node.children, (child) => child.accept(this)); }); }, semiGlobal: true); } @@ -1438,18 +1306,12 @@ final 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++) { _environment.setLocalVariable( - variables[i], - _withoutSlash(list[i], nodeWithSpan), - nodeWithSpan, - ); + variables[i], _withoutSlash(list[i], nodeWithSpan), nodeWithSpan); } for (var i = minLength; i < variables.length; i++) { _environment.setLocalVariable(variables[i], sassNull, nodeWithSpan); @@ -1464,37 +1326,28 @@ final class _EvaluateVisitor var styleRule = _styleRule; if (styleRule == null || _declarationName != null) { throw _exception( - "@extend may only be used within style rules.", - node.span, - ); + "@extend may only be used within style rules.", node.span); } for (var complex in styleRule.originalSelector.components) { if (!complex.isBogus) continue; _warn( - 'The selector "${complex.toString().trim()}" is invalid CSS and ' + - (complex.isUseless ? "can't" : "shouldn't") + - ' be an extender.\n' - 'This will be an error in Dart Sass 2.0.0.\n' - '\n' - 'More info: https://sass-lang.com/d/bogus-combinators', - MultiSpan(complex.span.trimRight(), 'invalid selector', { - node.span: '@extend rule', - }), - Deprecation.bogusCombinators, - ); + 'The selector "${complex.toString().trim()}" is invalid CSS and ' + + (complex.isUseless ? "can't" : "shouldn't") + + ' be an extender.\n' + 'This will be an error in Dart Sass 2.0.0.\n' + '\n' + 'More info: https://sass-lang.com/d/bogus-combinators', + MultiSpan(complex.span.trimRight(), 'invalid selector', + {node.span: '@extend rule'}), + Deprecation.bogusCombinators); } - var (targetText, targetMap) = _performInterpolationWithMap( - node.selector, - warnForColor: true, - ); + var (targetText, targetMap) = + _performInterpolationWithMap(node.selector, warnForColor: true); - var list = SelectorList.parse( - trimAscii(targetText, excludeEscape: true), - interpolationMap: targetMap, - allowParent: false, - ); + var list = SelectorList.parse(trimAscii(targetText, excludeEscape: true), + interpolationMap: targetMap, allowParent: false); for (var complex in list.components) { var compound = complex.singleCompound; @@ -1502,27 +1355,20 @@ final class _EvaluateVisitor // If the selector was a compound selector but not a simple // selector, emit a more explicit error. throw SassFormatException( - "complex selectors may not be extended.", - complex.span, - ); + "complex selectors may not be extended.", complex.span); } var simple = compound.singleSimple; if (simple == null) { throw SassFormatException( - "compound selectors may no longer be extended.\n" - "Consider `@extend ${compound.components.join(', ')}` instead.\n" - "See https://sass-lang.com/d/extend-compound for details.\n", - compound.span, - ); + "compound selectors may no longer be extended.\n" + "Consider `@extend ${compound.components.join(', ')}` instead.\n" + "See https://sass-lang.com/d/extend-compound for details.\n", + compound.span); } _extensionStore.addExtension( - styleRule.selector, - simple, - node, - _mediaQueries, - ); + styleRule.selector, simple, node, _mediaQueries); } return null; @@ -1534,22 +1380,18 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "At-rules may not be used within nested declarations.", - node.span, - ); + "At-rules may not be used within nested declarations.", node.span); } var name = _interpolationToValue(node.name); - var value = node.value.andThen( - (value) => _interpolationToValue(value, trim: true, warnForColor: true), - ); + var value = node.value.andThen((value) => + _interpolationToValue(value, trim: true, warnForColor: true)); var children = node.children; if (children == null) { _parent.addChild( - ModifiableCssAtRule(name, node.span, childless: true, value: value), - ); + ModifiableCssAtRule(name, node.span, childless: true, value: value)); return null; } @@ -1561,31 +1403,28 @@ final class _EvaluateVisitor _inUnknownAtRule = true; } - _withParent( - ModifiableCssAtRule(name, node.span, value: value), - () { - var styleRule = _styleRule; - if (styleRule == null || _inKeyframes || name.value == 'font-face') { - // Special-cased at-rules within style blocks are pulled out to the - // root. Equivalent to prepending "@at-root" on them. + _withParent(ModifiableCssAtRule(name, node.span, value: value), () { + var styleRule = _styleRule; + if (styleRule == null || _inKeyframes || name.value == 'font-face') { + // Special-cased at-rules within style blocks are pulled out to the + // root. Equivalent to prepending "@at-root" on them. + for (var child in children) { + child.accept(this); + } + } else { + // If we're in a style rule, copy it into the at-rule so that + // declarations immediately inside it have somewhere to go. + // + // For example, "a {@foo {b: c}}" should produce "@foo {a {b: c}}". + _withParent(styleRule.copyWithoutChildren(), () { for (var child in children) { child.accept(this); } - } else { - // If we're in a style rule, copy it into the at-rule so that - // declarations immediately inside it have somewhere to go. - // - // For example, "a {@foo {b: c}}" should produce "@foo {a {b: c}}". - _withParent(styleRule.copyWithoutChildren(), () { - for (var child in children) { - child.accept(this); - } - }, scopeWhen: false); - } - }, - through: (node) => node is CssStyleRule, - scopeWhen: node.hasDeclarations, - ); + }, scopeWhen: false); + } + }, + through: (node) => node is CssStyleRule, + scopeWhen: node.hasDeclarations); _inUnknownAtRule = wasInUnknownAtRule; _inKeyframes = wasInKeyframes; @@ -1594,22 +1433,16 @@ final class _EvaluateVisitor Value? visitForRule(ForRule node) { var fromNumber = _addExceptionSpan( - node.from, - () => node.from.accept(this).assertNumber(), - ); - var toNumber = _addExceptionSpan( - node.to, - () => node.to.accept(this).assertNumber(), - ); + node.from, () => node.from.accept(this).assertNumber()); + var toNumber = + _addExceptionSpan(node.to, () => node.to.accept(this).assertNumber()); var from = _addExceptionSpan(node.from, () => fromNumber.assertInt()); var to = _addExceptionSpan( - node.to, - () => - toNumber - .coerce(fromNumber.numeratorUnits, fromNumber.denominatorUnits) - .assertInt(), - ); + node.to, + () => toNumber + .coerce(fromNumber.numeratorUnits, fromNumber.denominatorUnits) + .assertInt()); var direction = from > to ? -1 : 1; if (!node.isExclusive) to += direction; @@ -1619,18 +1452,13 @@ final class _EvaluateVisitor var nodeWithSpan = _expressionNode(node.from); for (var i = from; i != to; i += direction) { _environment.setLocalVariable( - node.variable, - SassNumber.withUnits( - i, - numeratorUnits: fromNumber.numeratorUnits, - denominatorUnits: fromNumber.denominatorUnits, - ), - nodeWithSpan, - ); + node.variable, + SassNumber.withUnits(i, + numeratorUnits: fromNumber.numeratorUnits, + denominatorUnits: fromNumber.denominatorUnits), + nodeWithSpan); if (_handleReturn( - node.children, - (child) => child.accept(this), - ) + node.children, (child) => child.accept(this)) case var result?) { return result; } @@ -1644,31 +1472,26 @@ final class _EvaluateVisitor var adjustedConfiguration = oldConfiguration.throughForward(node); if (node.configuration.isNotEmpty) { - var newConfiguration = _addForwardConfiguration( - adjustedConfiguration, - node, - ); + var newConfiguration = + _addForwardConfiguration(adjustedConfiguration, node); _loadModule(node.url, "@forward", node, (module, firstLoad) { if (firstLoad) _registerCommentsForModule(module); _environment.forwardModule(module, node); }, configuration: newConfiguration); - _removeUsedConfiguration( - adjustedConfiguration, - newConfiguration, - except: { - for (var variable in node.configuration) - if (!variable.isGuarded) variable.name, - }, - ); + _removeUsedConfiguration(adjustedConfiguration, newConfiguration, + except: { + for (var variable in node.configuration) + if (!variable.isGuarded) variable.name + }); // Remove all the variables that weren't configured by this particular // `@forward` before checking that the configuration is empty. Errors for // outer `with` clauses will be thrown once those clauses finish // executing. var configuredVariables = { - for (var variable in node.configuration) variable.name, + for (var variable in node.configuration) variable.name }; for (var name in newConfiguration.values.keys.toList()) { if (!configuredVariables.contains(name)) newConfiguration.remove(name); @@ -1690,9 +1513,7 @@ final class _EvaluateVisitor /// Updates [configuration] to include [node]'s configuration and returns the /// result. Configuration _addForwardConfiguration( - Configuration configuration, - ForwardRule node, - ) { + Configuration configuration, ForwardRule node) { var newValues = Map.of(configuration.values); for (var variable in node.configuration) { if (variable.isGuarded) { @@ -1705,10 +1526,9 @@ final class _EvaluateVisitor var variableNodeWithSpan = _expressionNode(variable.expression); newValues[variable.name] = ConfiguredValue.explicit( - _withoutSlash(variable.expression.accept(this), variableNodeWithSpan), - variable.span, - variableNodeWithSpan, - ); + _withoutSlash(variable.expression.accept(this), variableNodeWithSpan), + variable.span, + variableNodeWithSpan); } if (configuration is ExplicitConfiguration || configuration.isEmpty) { @@ -1735,10 +1555,8 @@ final class _EvaluateVisitor /// Remove configured values from [upstream] that have been removed from /// [downstream], unless they match a name in [except]. void _removeUsedConfiguration( - Configuration upstream, - Configuration downstream, { - required Set except, - }) { + Configuration upstream, Configuration downstream, + {required Set except}) { for (var name in upstream.values.keys.toList()) { if (except.contains(name)) continue; if (!downstream.values.containsKey(name)) upstream.remove(name); @@ -1753,10 +1571,8 @@ final class _EvaluateVisitor /// If [nameInError] is `true`, this includes the name of the configured /// 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, { - bool nameInError = false, - }) { + void _assertConfigurationIsEmpty(Configuration configuration, + {bool nameInError = false}) { // By definition, implicit configurations are allowed to only use a subset // of their values. if (configuration is! ExplicitConfiguration) return; @@ -1764,23 +1580,17 @@ final class _EvaluateVisitor var (name, value) = configuration.values.pairs.first; 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, - ); + nameInError + ? "\$$name was not declared with !default in the @used " + "module." + : "This variable was not declared with !default in the @used " + "module.", + value.configurationSpan); } Value? visitFunctionRule(FunctionRule node) { - _environment.setFunction( - UserDefinedCallable( - node, - _environment.closure(), - inDependency: _inDependency, - ), - ); + _environment.setFunction(UserDefinedCallable(node, _environment.closure(), + inDependency: _inDependency)); return null; } @@ -1793,16 +1603,11 @@ final class _EvaluateVisitor } } - return clause.andThen( - (clause) => _environment.scope( + return clause.andThen((clause) => _environment.scope( () => _handleReturn( - clause.children, - (child) => child.accept(this), - ), + clause.children, (child) => child.accept(this)), semiGlobal: true, - when: clause.hasDeclarations, - ), - ); + when: clause.hasDeclarations)); } Value? visitImportRule(ImportRule node) { @@ -1819,22 +1624,15 @@ final class _EvaluateVisitor /// Adds the stylesheet imported by [import] to the current document. void _visitDynamicImport(DynamicImport import) { return _withStackFrame("@import", import, () { - var (stylesheet, :importer, :isDependency) = _loadStylesheet( - import.urlString, - import.span, - forImport: true, - ); + var (stylesheet, :importer, :isDependency) = + _loadStylesheet(import.urlString, import.span, forImport: true); var url = stylesheet.span.sourceUrl; if (url != null) { if (_activeModules.containsKey(url)) { - throw _activeModules[url].andThen( - (previousLoad) => _multiSpanException( - "This file is already being loaded.", - "new load", - {previousLoad.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; @@ -1866,7 +1664,7 @@ final class _EvaluateVisitor // new one. var loadsUserDefinedModules = stylesheet.uses.any((rule) => rule.url.scheme != 'sass') || - stylesheet.forwards.any((rule) => rule.url.scheme != 'sass'); + stylesheet.forwards.any((rule) => rule.url.scheme != 'sass'); late List children; var environment = _environment.forImport(); @@ -1921,10 +1719,8 @@ final class _EvaluateVisitor // clone all modules' CSS. Otherwise, it's possible that they'll be // used or imported from another location that shouldn't have the same // extensions applied. - _combineCss( - module, - clone: module.transitivelyContainsExtensions, - ).accept(this); + _combineCss(module, clone: module.transitivelyContainsExtensions) + .accept(this); } var visitor = _ImportedCssVisitor(this); @@ -1942,32 +1738,23 @@ final class _EvaluateVisitor /// /// This first tries loading [url] relative to [baseUrl], which defaults to /// `_stylesheet.span.sourceUrl`. - _LoadedStylesheet _loadStylesheet( - String url, - FileSpan span, { - Uri? baseUrl, - bool forImport = false, - }) { + _LoadedStylesheet _loadStylesheet(String url, FileSpan span, + {Uri? baseUrl, bool forImport = false}) { try { assert(_importSpan == null); _importSpan = span; if (_importCache case var importCache?) { baseUrl ??= _stylesheet.span.sourceUrl; - if (importCache.canonicalize( - Uri.parse(url), - baseImporter: _importer, - baseUrl: baseUrl, - forImport: forImport, - ) + if (importCache.canonicalize(Uri.parse(url), + baseImporter: _importer, baseUrl: baseUrl, forImport: forImport) case (var importer, var canonicalUrl, :var originalUrl)) { if (canonicalUrl.scheme == '') { _logger.warnForDeprecation( - Deprecation.relativeCanonical, - "Importer $importer canonicalized $url to $canonicalUrl.\n" - "Relative canonical URLs are deprecated and will eventually be " - "disallowed.", - ); + Deprecation.relativeCanonical, + "Importer $importer canonicalized $url to $canonicalUrl.\n" + "Relative canonical URLs are deprecated and will eventually be " + "disallowed."); } // Make sure we record the canonical URL as "loaded" even if the // actual load fails, because watchers should watch it to see if it @@ -1975,11 +1762,8 @@ final class _EvaluateVisitor _loadedUrls.add(canonicalUrl); var isDependency = _inDependency || importer != _importer; - if (importCache.importCanonical( - importer, - canonicalUrl, - originalUrl: originalUrl, - ) + if (importCache.importCanonical(importer, canonicalUrl, + originalUrl: originalUrl) case var stylesheet?) { return (stylesheet, importer: importer, isDependency: isDependency); } @@ -1988,10 +1772,7 @@ final class _EvaluateVisitor if (_nodeImporter != null) { if (_importLikeNode( - url, - baseUrl ?? _stylesheet.span.sourceUrl, - forImport, - ) + url, baseUrl ?? _stylesheet.span.sourceUrl, forImport) case var result?) { result.$1.span.sourceUrl.andThen(_loadedUrls.add); return result; @@ -2020,10 +1801,7 @@ final class _EvaluateVisitor /// /// Returns the [Stylesheet], or `null` if the import failed. _LoadedStylesheet? _importLikeNode( - String originalUrl, - Uri? previous, - bool forImport, - ) { + String originalUrl, Uri? previous, bool forImport) { var result = _nodeImporter!.loadRelative(originalUrl, previous, forImport); bool isDependency; @@ -2038,12 +1816,10 @@ final class _EvaluateVisitor var (contents, url) = result; return ( Stylesheet.parse( - contents, - url.startsWith('file') ? Syntax.forPath(url) : Syntax.scss, - url: url, - ), + contents, url.startsWith('file') ? Syntax.forPath(url) : Syntax.scss, + url: url), importer: null, - isDependency: isDependency, + isDependency: isDependency ); } @@ -2053,12 +1829,9 @@ final class _EvaluateVisitor // here should be mirrored there. var node = ModifiableCssImport( - _interpolationToValue(import.url), - import.span, - modifiers: import.modifiers.andThen?>( - _interpolationToValue, - ), - ); + _interpolationToValue(import.url), import.span, + modifiers: + import.modifiers.andThen?>(_interpolationToValue)); if (_parent != _root) { _parent.addChild(node); @@ -2072,12 +1845,11 @@ final class _EvaluateVisitor /// Evaluate a given [mixin] with [arguments] and [contentCallable] void _applyMixin( - Callable? mixin, - UserDefinedCallable? contentCallable, - ArgumentList arguments, - AstNode nodeWithSpan, - AstNode nodeWithSpanWithoutContent, - ) { + Callable? mixin, + UserDefinedCallable? contentCallable, + ArgumentList arguments, + AstNode nodeWithSpan, + AstNode nodeWithSpanWithoutContent) { switch (mixin) { case null: throw _exception("Undefined mixin.", nodeWithSpan.span); @@ -2086,16 +1858,13 @@ final class _EvaluateVisitor { var evaluated = _evaluateArguments(arguments); var (overload, _) = mixin.callbackFor( - evaluated.positional.length, - MapKeySet(evaluated.named), - ); + evaluated.positional.length, MapKeySet(evaluated.named)); throw MultiSpanSassRuntimeException( - "Mixin doesn't accept a content block.", - nodeWithSpanWithoutContent.span, - "invocation", - {overload.spanWithName: "declaration"}, - _stackTrace(nodeWithSpanWithoutContent.span), - ); + "Mixin doesn't accept a content block.", + nodeWithSpanWithoutContent.span, + "invocation", + {overload.spanWithName: "declaration"}, + _stackTrace(nodeWithSpanWithoutContent.span)); } case BuiltInCallable(): _environment.withContent(contentCallable, () { @@ -2105,35 +1874,28 @@ final class _EvaluateVisitor }); case UserDefinedCallable( - declaration: MixinRule(hasContent: false), + declaration: MixinRule(hasContent: false) ) when contentCallable != null: throw MultiSpanSassRuntimeException( - "Mixin doesn't accept a content block.", - nodeWithSpanWithoutContent.span, - "invocation", - {mixin.declaration.parameters.spanWithName: "declaration"}, - _stackTrace(nodeWithSpanWithoutContent.span), - ); + "Mixin doesn't accept a content block.", + nodeWithSpanWithoutContent.span, + "invocation", + {mixin.declaration.parameters.spanWithName: "declaration"}, + _stackTrace(nodeWithSpanWithoutContent.span)); case UserDefinedCallable(): - _runUserDefinedCallable( - arguments, - mixin, - nodeWithSpanWithoutContent, - () { - _environment.withContent(contentCallable, () { - _environment.asMixin(() { - for (var statement in mixin.declaration.children) { - _addErrorSpan( - nodeWithSpanWithoutContent, - () => statement.accept(this), - ); - } - }); + _runUserDefinedCallable(arguments, mixin, nodeWithSpanWithoutContent, + () { + _environment.withContent(contentCallable, () { + _environment.asMixin(() { + for (var statement in mixin.declaration.children) { + _addErrorSpan( + nodeWithSpanWithoutContent, () => statement.accept(this)); + } }); - }, - ); + }); + }); case _: throw UnsupportedError("Unknown callable type $mixin."); @@ -2141,54 +1903,36 @@ final class _EvaluateVisitor } Value? visitIncludeRule(IncludeRule node) { - var mixin = _addExceptionSpan( - node, - () => _environment.getMixin(node.name, namespace: node.namespace), - ); + var mixin = _addExceptionSpan(node, + () => _environment.getMixin(node.name, namespace: node.namespace)); if (node.originalName.startsWith('--') && mixin is UserDefinedCallable && !mixin.declaration.originalName.startsWith('--')) { _warn( - 'Sass @mixin names beginning with -- are deprecated for forward-' - 'compatibility with plain CSS mixins.\n' - '\n' - 'For details, see https://sass-lang.com/d/css-function-mixin', - node.nameSpan, - Deprecation.cssFunctionMixin, - ); + 'Sass @mixin names beginning with -- are deprecated for forward-' + 'compatibility with plain CSS mixins.\n' + '\n' + 'For details, see https://sass-lang.com/d/css-function-mixin', + node.nameSpan, + Deprecation.cssFunctionMixin); } - var contentCallable = node.content.andThen( - (content) => UserDefinedCallable( - content, - _environment.closure(), - inDependency: _inDependency, - ), - ); + var contentCallable = node.content.andThen((content) => UserDefinedCallable( + content, _environment.closure(), + inDependency: _inDependency)); - var nodeWithSpanWithoutContent = AstNode.fake( - () => node.spanWithoutContent, - ); + var nodeWithSpanWithoutContent = + AstNode.fake(() => node.spanWithoutContent); - _applyMixin( - mixin, - contentCallable, - node.arguments, - node, - nodeWithSpanWithoutContent, - ); + _applyMixin(mixin, contentCallable, node.arguments, node, + nodeWithSpanWithoutContent); return null; } Value? visitMixinRule(MixinRule node) { - _environment.setMixin( - UserDefinedCallable( - node, - _environment.closure(), - inDependency: _inDependency, - ), - ); + _environment.setMixin(UserDefinedCallable(node, _environment.closure(), + inDependency: _inDependency)); return null; } @@ -2216,52 +1960,45 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "Media rules may not be used within nested declarations.", - node.span, - ); + "Media rules may not be used within nested declarations.", node.span); } var queries = _visitMediaQueries(node.query); - var mergedQueries = _mediaQueries.andThen( - (mediaQueries) => _mergeMediaQueries(mediaQueries, queries), - ); + var mergedQueries = _mediaQueries + .andThen((mediaQueries) => _mergeMediaQueries(mediaQueries, queries)); if (mergedQueries != null && mergedQueries.isEmpty) return null; - var mergedSources = - mergedQueries == null - ? const {} - : {..._mediaQuerySources!, ..._mediaQueries!, ...queries}; + var mergedSources = mergedQueries == null + ? const {} + : {..._mediaQuerySources!, ..._mediaQueries!, ...queries}; - _withParent( - ModifiableCssMediaRule(mergedQueries ?? queries, node.span), - () { - _withMediaQueries(mergedQueries ?? queries, mergedSources, () { - if (_styleRule case var styleRule?) { - // If we're in a style rule, copy it into the media query so that - // declarations immediately inside @media have somewhere to go. - // - // For example, "a {@media screen {b: c}}" should produce - // "@media screen {a {b: c}}". - _withParent(styleRule.copyWithoutChildren(), () { - for (var child in node.children) { - child.accept(this); - } - }, scopeWhen: false); - } else { + _withParent(ModifiableCssMediaRule(mergedQueries ?? queries, node.span), + () { + _withMediaQueries(mergedQueries ?? queries, mergedSources, () { + if (_styleRule case var styleRule?) { + // If we're in a style rule, copy it into the media query so that + // declarations immediately inside @media have somewhere to go. + // + // For example, "a {@media screen {b: c}}" should produce + // "@media screen {a {b: c}}". + _withParent(styleRule.copyWithoutChildren(), () { for (var child in node.children) { child.accept(this); } + }, scopeWhen: false); + } else { + for (var child in node.children) { + child.accept(this); } - }); - }, - through: - (node) => - node is CssStyleRule || - (mergedSources.isNotEmpty && - node is CssMediaRule && - node.queries.every(mergedSources.contains)), - scopeWhen: node.hasDeclarations, - ); + } + }); + }, + through: (node) => + node is CssStyleRule || + (mergedSources.isNotEmpty && + node is CssMediaRule && + node.queries.every(mergedSources.contains)), + scopeWhen: node.hasDeclarations); return null; } @@ -2269,10 +2006,8 @@ final class _EvaluateVisitor /// Evaluates [interpolation] and parses the result as a list of media /// queries. List _visitMediaQueries(Interpolation interpolation) { - var (resolved, map) = _performInterpolationWithMap( - interpolation, - warnForColor: true, - ); + var (resolved, map) = + _performInterpolationWithMap(interpolation, warnForColor: true); return CssMediaQuery.parseList(resolved, interpolationMap: map); } @@ -2283,9 +2018,7 @@ final class _EvaluateVisitor /// and [queries2], or `null` if there are contexts that can't be represented /// by media queries. List? _mergeMediaQueries( - Iterable queries1, - Iterable queries2, - ) { + Iterable queries1, Iterable queries2) { var queries = []; for (var query1 in queries1) { inner: @@ -2314,97 +2047,71 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "Style rules may not be used within nested declarations.", - node.span, - ); + "Style rules may not be used within nested declarations.", node.span); } else if (_inKeyframes && _parent is CssKeyframeBlock) { throw _exception( - "Style rules may not be used within keyframe blocks.", - node.span, - ); + "Style rules may not be used within keyframe blocks.", node.span); } - var (selectorText, selectorMap) = _performInterpolationWithMap( - node.selector, - warnForColor: true, - ); + var (selectorText, selectorMap) = + _performInterpolationWithMap(node.selector, warnForColor: true); if (_inKeyframes) { // NOTE: this logic is largely duplicated in [visitCssKeyframeBlock]. Most // changes here should be mirrored there. var parsedSelector = - KeyframeSelectorParser( - selectorText, - interpolationMap: selectorMap, - ).parse(); + KeyframeSelectorParser(selectorText, interpolationMap: selectorMap) + .parse(); var rule = ModifiableCssKeyframeBlock( - CssValue(List.unmodifiable(parsedSelector), node.selector.span), - node.span, - ); - _withParent( - rule, - () { - for (var child in node.children) { - child.accept(this); - } - }, - through: (node) => node is CssStyleRule, - scopeWhen: node.hasDeclarations, - ); + CssValue(List.unmodifiable(parsedSelector), node.selector.span), + node.span); + _withParent(rule, () { + for (var child in node.children) { + child.accept(this); + } + }, + through: (node) => node is CssStyleRule, + scopeWhen: node.hasDeclarations); return null; } - var parsedSelector = SelectorList.parse( - selectorText, - interpolationMap: selectorMap, - plainCss: _stylesheet.plainCss, - ); + var parsedSelector = SelectorList.parse(selectorText, + interpolationMap: selectorMap, plainCss: _stylesheet.plainCss); var nest = !(_styleRule?.fromPlainCss ?? false); if (nest) { if (_stylesheet.plainCss) { for (var complex in parsedSelector.components) { - if (complex.leadingCombinators case [ - var first, - ..., - ] when _stylesheet.plainCss) { + if (complex.leadingCombinators case [var first, ...] + when _stylesheet.plainCss) { throw _exception( - "Top-level leading combinators aren't allowed in plain CSS.", - first.span, - ); + "Top-level leading combinators aren't allowed in plain CSS.", + first.span); } } } parsedSelector = parsedSelector.nestWithin( - _styleRuleIgnoringAtRoot?.originalSelector, - implicitParent: !_atRootExcludingStyleRule, - preserveParentSelectors: _stylesheet.plainCss, - ); + _styleRuleIgnoringAtRoot?.originalSelector, + implicitParent: !_atRootExcludingStyleRule, + preserveParentSelectors: _stylesheet.plainCss); } var selector = _extensionStore.addSelector(parsedSelector, _mediaQueries); - var rule = ModifiableCssStyleRule( - selector, - node.span, - originalSelector: parsedSelector, - fromPlainCss: _stylesheet.plainCss, - ); + var rule = ModifiableCssStyleRule(selector, node.span, + originalSelector: parsedSelector, fromPlainCss: _stylesheet.plainCss); var oldAtRootExcludingStyleRule = _atRootExcludingStyleRule; _atRootExcludingStyleRule = false; - _withParent( - rule, - () { - _withStyleRule(rule, () { - for (var child in node.children) { - child.accept(this); - } - }); - }, - through: nest ? (node) => node is CssStyleRule : null, - scopeWhen: node.hasDeclarations, - ); + _withParent(rule, () { + _withStyleRule(rule, () { + for (var child in node.children) { + child.accept(this); + } + }); + }, + through: nest ? (node) => node is CssStyleRule : null, + scopeWhen: node.hasDeclarations); _atRootExcludingStyleRule = oldAtRootExcludingStyleRule; _warnForBogusCombinators(rule); @@ -2425,46 +2132,42 @@ final class _EvaluateVisitor if (complex.isUseless) { _warn( - 'The selector "${complex.toString().trim()}" is invalid CSS. It ' - 'will be omitted from the generated CSS.\n' - 'This will be an error in Dart Sass 2.0.0.\n' - '\n' - 'More info: https://sass-lang.com/d/bogus-combinators', - complex.span.trimRight(), - Deprecation.bogusCombinators, - ); - } else if (complex.leadingCombinators.isNotEmpty) { - if (!_stylesheet.plainCss) { - _warn( - 'The selector "${complex.toString().trim()}" is invalid CSS.\n' + 'The selector "${complex.toString().trim()}" is invalid CSS. It ' + 'will be omitted from the generated CSS.\n' 'This will be an error in Dart Sass 2.0.0.\n' '\n' 'More info: https://sass-lang.com/d/bogus-combinators', complex.span.trimRight(), - Deprecation.bogusCombinators, - ); + Deprecation.bogusCombinators); + } else if (complex.leadingCombinators.isNotEmpty) { + if (!_stylesheet.plainCss) { + _warn( + 'The selector "${complex.toString().trim()}" is invalid CSS.\n' + 'This will be an error in Dart Sass 2.0.0.\n' + '\n' + 'More info: https://sass-lang.com/d/bogus-combinators', + complex.span.trimRight(), + Deprecation.bogusCombinators); } } else { _warn( - 'The selector "${complex.toString().trim()}" is only valid for ' - "nesting and shouldn't\n" - 'have children other than style rules.' + - (complex.isBogusOtherThanLeadingCombinator - ? ' It will be omitted from the generated CSS.' - : '') + - '\n' - 'This will be an error in Dart Sass 2.0.0.\n' - '\n' - 'More info: https://sass-lang.com/d/bogus-combinators', - MultiSpan(complex.span.trimRight(), 'invalid selector', { - rule.children.first.span: - "this is not a style rule" + - (rule.children.every((child) => child is CssComment) - ? '\n(try converting to a //-style comment)' - : ''), - }), - Deprecation.bogusCombinators, - ); + 'The selector "${complex.toString().trim()}" is only valid for ' + "nesting and shouldn't\n" + 'have children other than style rules.' + + (complex.isBogusOtherThanLeadingCombinator + ? ' It will be omitted from the generated CSS.' + : '') + + '\n' + 'This will be an error in Dart Sass 2.0.0.\n' + '\n' + 'More info: https://sass-lang.com/d/bogus-combinators', + MultiSpan(complex.span.trimRight(), 'invalid selector', { + rule.children.first.span: "this is not a style rule" + + (rule.children.every((child) => child is CssComment) + ? '\n(try converting to a //-style comment)' + : '') + }), + Deprecation.bogusCombinators); } } } @@ -2476,38 +2179,32 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "Supports rules may not be used within nested declarations.", - node.span, - ); - } - - var condition = CssValue( - _visitSupportsCondition(node.condition), - node.condition.span, - ); - _withParent( - ModifiableCssSupportsRule(condition, node.span), - () { - if (_styleRule case var styleRule?) { - // If we're in a style rule, copy it into the supports rule so that - // declarations immediately inside @supports have somewhere to go. - // - // For example, "a {@supports (a: b) {b: c}}" should produce "@supports - // (a: b) {a {b: c}}". - _withParent(styleRule.copyWithoutChildren(), () { - for (var child in node.children) { - child.accept(this); - } - }); - } else { + "Supports rules may not be used within nested declarations.", + node.span); + } + + var condition = + CssValue(_visitSupportsCondition(node.condition), node.condition.span); + _withParent(ModifiableCssSupportsRule(condition, node.span), () { + if (_styleRule case var styleRule?) { + // If we're in a style rule, copy it into the supports rule so that + // declarations immediately inside @supports have somewhere to go. + // + // For example, "a {@supports (a: b) {b: c}}" should produce "@supports + // (a: b) {a {b: c}}". + _withParent(styleRule.copyWithoutChildren(), () { for (var child in node.children) { child.accept(this); } + }); + } else { + for (var child in node.children) { + child.accept(this); } - }, - through: (node) => node is CssStyleRule, - scopeWhen: node.hasDeclarations, - ); + } + }, + through: (node) => node is CssStyleRule, + scopeWhen: node.hasDeclarations); return null; } @@ -2520,25 +2217,18 @@ final class _EvaluateVisitor "${operation.operator} " "${_parenthesize(operation.right, operation.operator)}", SupportsNegation negation => "not ${_parenthesize(negation.condition)}", - SupportsInterpolation interpolation => _evaluateToCss( - interpolation.expression, - quote: false, - ), - SupportsDeclaration declaration => _withSupportsDeclaration( - () => - "(${_evaluateToCss(declaration.name)}:" + SupportsInterpolation interpolation => + _evaluateToCss(interpolation.expression, quote: false), + SupportsDeclaration declaration => + _withSupportsDeclaration(() => "(${_evaluateToCss(declaration.name)}:" "${declaration.isCustomProperty ? '' : ' '}" - "${_evaluateToCss(declaration.value)})", - ), - SupportsFunction function => - "${_performInterpolation(function.name)}(" - "${_performInterpolation(function.arguments)})", + "${_evaluateToCss(declaration.value)})"), + SupportsFunction function => "${_performInterpolation(function.name)}(" + "${_performInterpolation(function.arguments)})", SupportsAnything anything => "(${_performInterpolation(anything.contents)})", - var condition => - throw ArgumentError( - "Unknown supports condition type ${condition.runtimeType}.", - ), + var condition => throw ArgumentError( + "Unknown supports condition type ${condition.runtimeType}.") }; /// Runs [callback] in a context where [_inSupportsDeclaration] is true. @@ -2577,51 +2267,41 @@ final class _EvaluateVisitor when override.value != sassNull) { _addExceptionSpan(node, () { _environment.setVariable( - node.name, - override.value, - override.assignmentNode, - global: true, - ); + node.name, override.value, override.assignmentNode, + global: true); }); return null; } } - var value = _addExceptionSpan( - node, - () => _environment.getVariable(node.name, namespace: node.namespace), - ); + var value = _addExceptionSpan(node, + () => _environment.getVariable(node.name, namespace: node.namespace)); if (value != null && value != sassNull) return null; } if (node.isGlobal && !_environment.globalVariableExists(node.name)) { _warn( - _environment.atRoot - ? "As of Dart Sass 2.0.0, !global assignments won't be able to " - "declare new variables.\n" - "\n" - "Since this assignment is at the root of the stylesheet, the " - "!global flag is\n" - "unnecessary and can safely be removed." - : "As of Dart Sass 2.0.0, !global assignments won't be able to " - "declare new variables.\n" - "\n" - "Recommendation: add `${node.originalName}: null` at the " - "stylesheet root.", - node.span, - Deprecation.newGlobal, - ); + _environment.atRoot + ? "As of Dart Sass 2.0.0, !global assignments won't be able to " + "declare new variables.\n" + "\n" + "Since this assignment is at the root of the stylesheet, the " + "!global flag is\n" + "unnecessary and can safely be removed." + : "As of Dart Sass 2.0.0, !global assignments won't be able to " + "declare new variables.\n" + "\n" + "Recommendation: add `${node.originalName}: null` at the " + "stylesheet root.", + node.span, + Deprecation.newGlobal); } var value = _withoutSlash(node.expression.accept(this), node.expression); _addExceptionSpan(node, () { _environment.setVariable( - node.name, - value, - _expressionNode(node.expression), - namespace: node.namespace, - global: node.isGlobal, - ); + node.name, value, _expressionNode(node.expression), + namespace: node.namespace, global: node.isGlobal); }); return null; } @@ -2633,10 +2313,10 @@ final class _EvaluateVisitor for (var variable in node.configuration) { var variableNodeWithSpan = _expressionNode(variable.expression); values[variable.name] = ConfiguredValue.explicit( - _withoutSlash(variable.expression.accept(this), variableNodeWithSpan), - variable.span, - variableNodeWithSpan, - ); + _withoutSlash( + variable.expression.accept(this), variableNodeWithSpan), + variable.span, + variableNodeWithSpan); } configuration = ExplicitConfiguration(values, node); } @@ -2653,29 +2333,22 @@ final class _EvaluateVisitor Value? visitWarnRule(WarnRule node) { var value = _addExceptionSpan(node, () => node.expression.accept(this)); _logger.warn( - value is SassString ? value.text : _serialize(value, node.expression), - trace: _stackTrace(node.span), - ); + value is SassString ? value.text : _serialize(value, node.expression), + trace: _stackTrace(node.span)); return null; } Value? visitWhileRule(WhileRule node) { - return _environment.scope( - () { - while (node.condition.accept(this).isTruthy) { - if (_handleReturn( - node.children, - (child) => child.accept(this), - ) - case var result?) { - return result; - } + return _environment.scope(() { + while (node.condition.accept(this).isTruthy) { + if (_handleReturn( + node.children, (child) => child.accept(this)) + case var result?) { + return result; } - return null; - }, - semiGlobal: true, - when: node.hasDeclarations, - ); + } + return null; + }, semiGlobal: true, when: node.hasDeclarations); } // ## Expressions @@ -2685,36 +2358,30 @@ final class _EvaluateVisitor node.operator != BinaryOperator.singleEquals && node.operator != BinaryOperator.dividedBy) { throw _exception( - "Operators aren't allowed in plain CSS.", - node.operatorSpan, - ); + "Operators aren't allowed in plain CSS.", node.operatorSpan); } return _addExceptionSpan(node, () { var left = node.left.accept(this); return switch (node.operator) { - BinaryOperator.singleEquals => left.singleEquals( - node.right.accept(this), - ), + BinaryOperator.singleEquals => + left.singleEquals(node.right.accept(this)), BinaryOperator.or => left.isTruthy ? left : node.right.accept(this), BinaryOperator.and => left.isTruthy ? node.right.accept(this) : left, BinaryOperator.equals => SassBoolean(left == node.right.accept(this)), - BinaryOperator.notEquals => SassBoolean( - left != node.right.accept(this), - ), + BinaryOperator.notEquals => + SassBoolean(left != node.right.accept(this)), BinaryOperator.greaterThan => left.greaterThan(node.right.accept(this)), - BinaryOperator.greaterThanOrEquals => left.greaterThanOrEquals( - node.right.accept(this), - ), + BinaryOperator.greaterThanOrEquals => + left.greaterThanOrEquals(node.right.accept(this)), BinaryOperator.lessThan => left.lessThan(node.right.accept(this)), - BinaryOperator.lessThanOrEquals => left.lessThanOrEquals( - node.right.accept(this), - ), + BinaryOperator.lessThanOrEquals => + left.lessThanOrEquals(node.right.accept(this)), BinaryOperator.plus => left.plus(node.right.accept(this)), BinaryOperator.minus => left.minus(node.right.accept(this)), BinaryOperator.times => left.times(node.right.accept(this)), BinaryOperator.dividedBy => _slash(left, node.right.accept(this), node), - BinaryOperator.modulo => left.modulo(node.right.accept(this)), + BinaryOperator.modulo => left.modulo(node.right.accept(this)) }; }); } @@ -2732,28 +2399,27 @@ final class _EvaluateVisitor case (SassNumber(), SassNumber()): String recommendation(Expression expression) => switch (expression) { - BinaryOperationExpression( - operator: BinaryOperator.dividedBy, - :var left, - :var right, - ) => - "math.div(${recommendation(left)}, ${recommendation(right)})", - ParenthesizedExpression() => expression.expression.toString(), - _ => expression.toString(), - }; + BinaryOperationExpression( + operator: BinaryOperator.dividedBy, + :var left, + :var right + ) => + "math.div(${recommendation(left)}, ${recommendation(right)})", + ParenthesizedExpression() => expression.expression.toString(), + _ => expression.toString() + }; _warn( - "Using / for division outside of calc() is deprecated " - "and will be removed in Dart Sass 2.0.0.\n" - "\n" - "Recommendation: ${recommendation(node)} or " - "${expressionToCalc(node)}\n" - "\n" - "More info and automated migrator: " - "https://sass-lang.com/d/slash-div", - node.span, - Deprecation.slashDiv, - ); + "Using / for division outside of calc() is deprecated " + "and will be removed in Dart Sass 2.0.0.\n" + "\n" + "Recommendation: ${recommendation(node)} or " + "${expressionToCalc(node)}\n" + "\n" + "More info and automated migrator: " + "https://sass-lang.com/d/slash-div", + node.span, + Deprecation.slashDiv); return result; case _: @@ -2772,17 +2438,15 @@ final class _EvaluateVisitor const { "calc", "clamp", "hypot", "sin", "cos", "tan", "asin", "acos", // "atan", "sqrt", "exp", "sign", "mod", "rem", "atan2", "pow", // - "log", "calc-size", + "log", "calc-size" }.contains(node.name.toLowerCase()) && _environment.getFunction(node.name) == null); Value visitValueExpression(ValueExpression node) => node.value; Value visitVariableExpression(VariableExpression node) { - var result = _addExceptionSpan( - node, - () => _environment.getVariable(node.name, namespace: node.namespace), - ); + var result = _addExceptionSpan(node, + () => _environment.getVariable(node.name, namespace: node.namespace)); if (result != null) return result; throw _exception("Undefined variable.", node.span); } @@ -2794,7 +2458,7 @@ final class _EvaluateVisitor UnaryOperator.plus => operand.unaryPlus(), UnaryOperator.minus => operand.unaryMinus(), UnaryOperator.divide => operand.unaryDivide(), - UnaryOperator.not => operand.unaryNot(), + UnaryOperator.not => operand.unaryNot() }; }); } @@ -2823,18 +2487,15 @@ final class _EvaluateVisitor Value visitParenthesizedExpression(ParenthesizedExpression node) => _stylesheet.plainCss ? throw _exception( - "Parentheses aren't allowed in plain CSS.", - node.span, - ) + "Parentheses aren't allowed in plain CSS.", node.span) : node.expression.accept(this); SassColor visitColorExpression(ColorExpression node) => node.value; SassList visitListExpression(ListExpression node) => SassList( - node.contents.map((Expression expression) => expression.accept(this)), - node.separator, - brackets: node.hasBrackets, - ); + node.contents.map((Expression expression) => expression.accept(this)), + node.separator, + brackets: node.hasBrackets); SassMap visitMapExpression(MapExpression node) { var map = {}; @@ -2846,12 +2507,11 @@ final class _EvaluateVisitor if (map.containsKey(keyValue)) { var oldValueSpan = keyNodes[keyValue]?.span; throw MultiSpanSassRuntimeException( - 'Duplicate key.', - key.span, - 'second key', - {if (oldValueSpan != null) oldValueSpan: 'first key'}, - _stackTrace(key.span), - ); + 'Duplicate key.', + key.span, + 'second key', + {if (oldValueSpan != null) oldValueSpan: 'first key'}, + _stackTrace(key.span)); } map[keyValue] = valueValue; keyNodes[keyValue] = key; @@ -2860,16 +2520,12 @@ final class _EvaluateVisitor } Value visitFunctionExpression(FunctionExpression node) { - var function = - _stylesheet.plainCss - ? null - : _addExceptionSpan( - node, - () => _environment.getFunction( - node.name, - namespace: node.namespace, - ), - ); + var function = _stylesheet.plainCss + ? null + : _addExceptionSpan( + node, + () => + _environment.getFunction(node.name, namespace: node.namespace)); if (function == null) { if (node.namespace != null) { throw _exception("Undefined function.", node.span); @@ -2881,34 +2537,32 @@ final class _EvaluateVisitor case ("min" || "max" || "round" || "abs") && var name when node.arguments.named.isEmpty && node.arguments.rest == null && - node.arguments.positional.every( - (argument) => argument.isCalculationSafe, - ): + node.arguments.positional + .every((argument) => argument.isCalculationSafe): return _visitCalculation(node, inLegacySassFunction: name); case "calc" || - "clamp" || - "hypot" || - "sin" || - "cos" || - "tan" || - "asin" || - "acos" || - "atan" || - "sqrt" || - "exp" || - "sign" || - "mod" || - "rem" || - "atan2" || - "pow" || - "log" || - "calc-size": + "clamp" || + "hypot" || + "sin" || + "cos" || + "tan" || + "asin" || + "acos" || + "atan" || + "sqrt" || + "exp" || + "sign" || + "mod" || + "rem" || + "atan2" || + "pow" || + "log" || + "calc-size": return _visitCalculation(node); } - function = - (_stylesheet.plainCss ? null : _builtInFunctions[node.name]) ?? + function = (_stylesheet.plainCss ? null : _builtInFunctions[node.name]) ?? PlainCssCallable(node.originalName); } @@ -2928,9 +2582,7 @@ final class _EvaluateVisitor var oldInFunction = _inFunction; _inFunction = true; var result = _addErrorSpan( - node, - () => _runFunctionCallable(node.arguments, function, node), - ); + node, () => _runFunctionCallable(node.arguments, function, node)); _inFunction = oldInFunction; return result; } @@ -2942,29 +2594,21 @@ final class _EvaluateVisitor /// with the old global `min()`, `max()`, `round()`, and `abs()` functions. /// The parameter is the name of the function, which is used for reporting /// deprecation warnings. - Value _visitCalculation( - FunctionExpression node, { - String? inLegacySassFunction, - }) { + Value _visitCalculation(FunctionExpression node, + {String? inLegacySassFunction}) { if (node.arguments.named.isNotEmpty) { throw _exception( - "Keyword arguments can't be used with calculations.", - node.span, - ); + "Keyword arguments can't be used with calculations.", node.span); } else if (node.arguments.rest != null) { throw _exception( - "Rest arguments can't be used with calculations.", - node.span, - ); + "Rest arguments can't be used with calculations.", node.span); } _checkCalculationArguments(node); var arguments = [ for (var argument in node.arguments.positional) - _visitCalculationExpression( - argument, - inLegacySassFunction: inLegacySassFunction, - ), + _visitCalculationExpression(argument, + inLegacySassFunction: inLegacySassFunction) ]; if (_inSupportsDeclaration) { return SassCalculation.unsimplified(node.name, arguments); @@ -2989,46 +2633,27 @@ final class _EvaluateVisitor "min" => SassCalculation.min(arguments), "max" => SassCalculation.max(arguments), "hypot" => SassCalculation.hypot(arguments), - "pow" => SassCalculation.pow( - arguments[0], - arguments.elementAtOrNull(1), - ), - "atan2" => SassCalculation.atan2( - arguments[0], - arguments.elementAtOrNull(1), - ), - "log" => SassCalculation.log( - arguments[0], - arguments.elementAtOrNull(1), - ), - "mod" => SassCalculation.mod( - arguments[0], - arguments.elementAtOrNull(1), - ), - "rem" => SassCalculation.rem( - arguments[0], - arguments.elementAtOrNull(1), - ), - "round" => SassCalculation.roundInternal( - arguments[0], - arguments.elementAtOrNull(1), - arguments.elementAtOrNull(2), - span: node.span, - inLegacySassFunction: inLegacySassFunction, - warn: - (message, [deprecation]) => - _warn(message, node.span, deprecation), - ), - "clamp" => SassCalculation.clamp( - arguments[0], - arguments.elementAtOrNull(1), - arguments.elementAtOrNull(2), - ), - "calc-size" => SassCalculation.calcSize( - arguments[0], - arguments.elementAtOrNull(1), - ), - _ => throw UnsupportedError('Unknown calculation name "${node.name}".'), + "pow" => + SassCalculation.pow(arguments[0], arguments.elementAtOrNull(1)), + "atan2" => + SassCalculation.atan2(arguments[0], arguments.elementAtOrNull(1)), + "log" => + SassCalculation.log(arguments[0], arguments.elementAtOrNull(1)), + "mod" => + SassCalculation.mod(arguments[0], arguments.elementAtOrNull(1)), + "rem" => + SassCalculation.rem(arguments[0], arguments.elementAtOrNull(1)), + "round" => SassCalculation.roundInternal(arguments[0], + arguments.elementAtOrNull(1), arguments.elementAtOrNull(2), + span: node.span, + inLegacySassFunction: inLegacySassFunction, + warn: (message, [deprecation]) => + _warn(message, node.span, deprecation)), + "clamp" => SassCalculation.clamp(arguments[0], + arguments.elementAtOrNull(1), arguments.elementAtOrNull(2)), + "calc-size" => + SassCalculation.calcSize(arguments[0], arguments.elementAtOrNull(1)), + _ => throw UnsupportedError('Unknown calculation name "${node.name}".') }; } on SassScriptException catch (error, stackTrace) { // The simplification logic in the [SassCalculation] static methods will @@ -3051,31 +2676,27 @@ final class _EvaluateVisitor } else if (maxArgs != null && node.arguments.positional.length > maxArgs) { throw _exception( - "Only $maxArgs ${pluralize('argument', maxArgs)} allowed, but " - "${node.arguments.positional.length} " + - pluralize( - 'was', - node.arguments.positional.length, - plural: 'were', - ) + - " passed.", - node.span, - ); + "Only $maxArgs ${pluralize('argument', maxArgs)} allowed, but " + "${node.arguments.positional.length} " + + pluralize('was', node.arguments.positional.length, + plural: 'were') + + " passed.", + node.span); } } switch (node.name.toLowerCase()) { case "calc" || - "sqrt" || - "sin" || - "cos" || - "tan" || - "asin" || - "acos" || - "atan" || - "abs" || - "exp" || - "sign": + "sqrt" || + "sin" || + "cos" || + "tan" || + "asin" || + "acos" || + "atan" || + "abs" || + "exp" || + "sign": check(1); case "min" || "max" || "hypot": check(); @@ -3093,18 +2714,14 @@ final class _EvaluateVisitor /// /// The [nodesWithSpans] should correspond to the spans for [args]. void _verifyCompatibleNumbers( - List args, - List nodesWithSpans, - ) { + List args, List nodesWithSpans) { // Note: this logic is largely duplicated in // SassCalculation._verifyCompatibleNumbers and most changes here should // also be reflected there. for (var i = 0; i < args.length; i++) { if (args[i] case SassNumber arg when arg.hasComplexUnits) { - throw _exception( - "Number $arg isn't compatible with CSS calculations.", - nodesWithSpans[i].span, - ); + throw _exception("Number $arg isn't compatible with CSS calculations.", + nodesWithSpans[i].span); } } @@ -3118,12 +2735,11 @@ final class _EvaluateVisitor if (number1.hasPossiblyCompatibleUnits(number2)) continue; throw MultiSpanSassRuntimeException( - "$number1 and $number2 are incompatible.", - nodesWithSpans[i].span, - number1.toString(), - {nodesWithSpans[j].span: number2.toString()}, - _stackTrace(nodesWithSpans[i].span), - ); + "$number1 and $number2 are incompatible.", + nodesWithSpans[i].span, + number1.toString(), + {nodesWithSpans[j].span: number2.toString()}, + _stackTrace(nodesWithSpans[i].span)); } } } @@ -3135,16 +2751,12 @@ final class _EvaluateVisitor /// with the old global `min()`, `max()`, `round()`, and `abs()` functions. /// The parameter is the name of the function, which is used for reporting /// deprecation warnings. - Object _visitCalculationExpression( - Expression node, { - required String? inLegacySassFunction, - }) { + Object _visitCalculationExpression(Expression node, + {required String? inLegacySassFunction}) { switch (node) { case ParenthesizedExpression(expression: var inner): - var result = _visitCalculationExpression( - inner, - inLegacySassFunction: inLegacySassFunction, - ); + var result = _visitCalculationExpression(inner, + inLegacySassFunction: inLegacySassFunction); return result is SassString ? SassString('(${result.text})', quotes: false) : result; @@ -3157,57 +2769,45 @@ final class _EvaluateVisitor 'infinity' => SassNumber(double.infinity), '-infinity' => SassNumber(double.negativeInfinity), 'nan' => SassNumber(double.nan), - _ => SassString(_performInterpolation(node.text), quotes: false), + _ => SassString(_performInterpolation(node.text), quotes: false) }; case BinaryOperationExpression(:var operator, :var left, :var right): _checkWhitespaceAroundCalculationOperator(node); return _addExceptionSpan( - node, - () => SassCalculation.operateInternal( - _binaryOperatorToCalculationOperator(operator, node), - _visitCalculationExpression( - left, - inLegacySassFunction: inLegacySassFunction, - ), - _visitCalculationExpression( - right, - inLegacySassFunction: inLegacySassFunction, - ), - inLegacySassFunction: inLegacySassFunction, - simplify: !_inSupportsDeclaration, - warn: - (message, [deprecation]) => - _warn(message, node.span, deprecation), - ), - ); + node, + () => SassCalculation.operateInternal( + _binaryOperatorToCalculationOperator(operator, node), + _visitCalculationExpression(left, + inLegacySassFunction: inLegacySassFunction), + _visitCalculationExpression(right, + inLegacySassFunction: inLegacySassFunction), + inLegacySassFunction: inLegacySassFunction, + simplify: !_inSupportsDeclaration, + warn: (message, [deprecation]) => + _warn(message, node.span, deprecation))); case NumberExpression() || - VariableExpression() || - FunctionExpression() || - IfExpression(): + VariableExpression() || + FunctionExpression() || + IfExpression(): return switch (node.accept(this)) { SassNumber result => result, SassCalculation result => result, SassString result when !result.hasQuotes => result, - var result => - throw _exception( - "Value $result can't be used in a calculation.", - node.span, - ), + var result => throw _exception( + "Value $result can't be used in a calculation.", node.span) }; case ListExpression( - hasBrackets: false, - separator: ListSeparator.space, - contents: [_, _, ...], - ): + hasBrackets: false, + separator: ListSeparator.space, + contents: [_, _, ...] + ): var elements = [ for (var element in node.contents) - _visitCalculationExpression( - element, - inLegacySassFunction: inLegacySassFunction, - ), + _visitCalculationExpression(element, + inLegacySassFunction: inLegacySassFunction) ]; _checkAdjacentCalculationValues(elements, node); @@ -3224,17 +2824,14 @@ final class _EvaluateVisitor case _: assert(!node.isCalculationSafe); throw _exception( - "This expression can't be used in a calculation.", - node.span, - ); + "This expression can't be used in a calculation.", node.span); } } /// Throws an error if [node] requires whitespace around its operator in a /// calculation but doesn't have it. void _checkWhitespaceAroundCalculationOperator( - BinaryOperationExpression node, - ) { + BinaryOperationExpression node) { if (node.operator != BinaryOperator.plus && node.operator != BinaryOperator.minus) { return; @@ -3246,42 +2843,33 @@ final class _EvaluateVisitor if (node.left.span.file != node.right.span.file) return; if (node.left.span.end.offset >= node.right.span.start.offset) return; - var textBetweenOperands = node.left.span.file.getText( - node.left.span.end.offset, - node.right.span.start.offset, - ); + var textBetweenOperands = node.left.span.file + .getText(node.left.span.end.offset, node.right.span.start.offset); var first = textBetweenOperands.codeUnitAt(0); var last = textBetweenOperands.codeUnitAt(textBetweenOperands.length - 1); if (!(first.isWhitespace || first == $slash) || !(last.isWhitespace || last == $slash)) { throw _exception( - '"+" and "-" must be surrounded by whitespace in calculations.', - node.operatorSpan, - ); + '"+" and "-" must be surrounded by whitespace in calculations.', + node.operatorSpan); } } /// Returns the [CalculationOperator] that corresponds to [operator]. CalculationOperator _binaryOperatorToCalculationOperator( - BinaryOperator operator, - BinaryOperationExpression node, - ) => switch (operator) { - BinaryOperator.plus => CalculationOperator.plus, - BinaryOperator.minus => CalculationOperator.minus, - BinaryOperator.times => CalculationOperator.times, - BinaryOperator.dividedBy => CalculationOperator.dividedBy, - _ => - throw _exception( - "This operation can't be used in a calculation.", - node.operatorSpan, - ), - }; + BinaryOperator operator, BinaryOperationExpression node) => + switch (operator) { + BinaryOperator.plus => CalculationOperator.plus, + BinaryOperator.minus => CalculationOperator.minus, + BinaryOperator.times => CalculationOperator.times, + BinaryOperator.dividedBy => CalculationOperator.dividedBy, + _ => throw _exception( + "This operation can't be used in a calculation.", node.operatorSpan) + }; /// Throws an error if [elements] contains two adjacent non-string values. void _checkAdjacentCalculationValues( - List elements, - ListExpression node, - ) { + List elements, ListExpression node) { assert(elements.length > 1); for (var i = 1; i < elements.length; i++) { @@ -3293,7 +2881,7 @@ final class _EvaluateVisitor var currentNode = node.contents[i]; if (currentNode case UnaryOperationExpression( - operator: UnaryOperator.minus || UnaryOperator.plus, + operator: UnaryOperator.minus || UnaryOperator.plus ) || NumberExpression(value: < 0)) { // `calc(1 -2)` parses as a space-separated list whose second value is a @@ -3301,28 +2889,22 @@ final class _EvaluateVisitor // expression doesn't help the user understand what's going wrong. We // add special case error handling to help clarify the issue. throw _exception( - '"+" and "-" must be surrounded by whitespace in calculations.', - currentNode.span.subspan(0, 1), - ); + '"+" and "-" must be surrounded by whitespace in calculations.', + currentNode.span.subspan(0, 1)); } else { - throw _exception( - 'Missing math operator.', - previousNode.span.expand(currentNode.span), - ); + throw _exception('Missing math operator.', + previousNode.span.expand(currentNode.span)); } } } Value visitInterpolatedFunctionExpression( - InterpolatedFunctionExpression node, - ) { + InterpolatedFunctionExpression node) { var function = PlainCssCallable(_performInterpolation(node.name)); var oldInFunction = _inFunction; _inFunction = true; var result = _addErrorSpan( - node, - () => _runFunctionCallable(node.arguments, function, node), - ); + node, () => _runFunctionCallable(node.arguments, function, node)); _inFunction = oldInFunction; return result; } @@ -3330,11 +2912,10 @@ final class _EvaluateVisitor /// Evaluates the arguments in [arguments] as applied to [callable], and /// invokes [run] in a scope with those arguments defined. V _runUserDefinedCallable( - ArgumentList arguments, - UserDefinedCallable callable, - AstNode nodeWithSpan, - V run(), - ) { + ArgumentList arguments, + UserDefinedCallable callable, + AstNode nodeWithSpan, + V run()) { // TODO(nweiz): Set [trackSpans] to `null` once we're no longer emitting // deprecation warnings for /-as-division. var evaluated = _evaluateArguments(arguments); @@ -3349,65 +2930,45 @@ final class _EvaluateVisitor // don't affect the underlying environment closure. return _withEnvironment(callable.environment.closure(), () { return _environment.scope(() { - _verifyArguments( - evaluated.positional.length, - evaluated.named, - callable.declaration.parameters, - nodeWithSpan, - ); + _verifyArguments(evaluated.positional.length, evaluated.named, + callable.declaration.parameters, nodeWithSpan); var parameters = callable.declaration.parameters.parameters; - var minLength = math.min( - evaluated.positional.length, - parameters.length, - ); + var minLength = + math.min(evaluated.positional.length, parameters.length); for (var i = 0; i < minLength; i++) { - _environment.setLocalVariable( - parameters[i].name, - evaluated.positional[i], - evaluated.positionalNodes[i], - ); + _environment.setLocalVariable(parameters[i].name, + evaluated.positional[i], evaluated.positionalNodes[i]); } - for ( - var i = evaluated.positional.length; - i < parameters.length; - i++ - ) { + for (var i = evaluated.positional.length; + i < parameters.length; + i++) { var parameter = parameters[i]; - var value = - evaluated.named.remove(parameter.name) ?? - _withoutSlash( - parameter.defaultValue!.accept(this), - _expressionNode(parameter.defaultValue!), - ); + var value = evaluated.named.remove(parameter.name) ?? + _withoutSlash(parameter.defaultValue!.accept(this), + _expressionNode(parameter.defaultValue!)); _environment.setLocalVariable( - parameter.name, - value, - evaluated.namedNodes[parameter.name] ?? - _expressionNode(parameter.defaultValue!), - ); + parameter.name, + value, + evaluated.namedNodes[parameter.name] ?? + _expressionNode(parameter.defaultValue!)); } SassArgumentList? argumentList; var restParameter = callable.declaration.parameters.restParameter; if (restParameter != null) { - var rest = - evaluated.positional.length > parameters.length - ? evaluated.positional.sublist(parameters.length) - : const []; + var rest = evaluated.positional.length > parameters.length + ? evaluated.positional.sublist(parameters.length) + : const []; argumentList = SassArgumentList( - rest, - evaluated.named, - evaluated.separator == ListSeparator.undecided - ? ListSeparator.comma - : evaluated.separator, - ); + rest, + evaluated.named, + evaluated.separator == ListSeparator.undecided + ? ListSeparator.comma + : evaluated.separator); _environment.setLocalVariable( - restParameter, - argumentList, - nodeWithSpan, - ); + restParameter, argumentList, nodeWithSpan); } var result = run(); @@ -3416,21 +2977,16 @@ final class _EvaluateVisitor if (evaluated.named.isEmpty) return result; if (argumentList.wereKeywordsAccessed) return result; - var parameterWord = pluralize( - 'parameter', - evaluated.named.keys.length, - ); - var parameterNames = toSentence( - evaluated.named.keys.map((name) => "\$$name"), - 'or', - ); + var parameterWord = + pluralize('parameter', evaluated.named.keys.length); + var parameterNames = + toSentence(evaluated.named.keys.map((name) => "\$$name"), 'or'); throw MultiSpanSassRuntimeException( - "No $parameterWord named $parameterNames.", - nodeWithSpan.span, - "invocation", - {callable.declaration.parameters.spanWithName: "declaration"}, - _stackTrace(nodeWithSpan.span), - ); + "No $parameterWord named $parameterNames.", + nodeWithSpan.span, + "invocation", + {callable.declaration.parameters.spanWithName: "declaration"}, + _stackTrace(nodeWithSpan.span)); }); }); }); @@ -3440,15 +2996,10 @@ final class _EvaluateVisitor /// Evaluates [arguments] as applied to [callable]. Value _runFunctionCallable( - ArgumentList arguments, - Callable? callable, - AstNode nodeWithSpan, - ) { + ArgumentList arguments, Callable? callable, AstNode nodeWithSpan) { if (callable is BuiltInCallable) { return _withoutSlash( - _runBuiltInCallable(arguments, callable, nodeWithSpan), - nodeWithSpan, - ); + _runBuiltInCallable(arguments, callable, nodeWithSpan), nodeWithSpan); } else if (callable is UserDefinedCallable) { return _runUserDefinedCallable(arguments, callable, nodeWithSpan, () { for (var statement in callable.declaration.children) { @@ -3457,16 +3008,12 @@ final class _EvaluateVisitor } throw _exception( - "Function finished without @return.", - callable.declaration.span, - ); + "Function finished without @return.", callable.declaration.span); }); } else if (callable is PlainCssCallable) { if (arguments.named.isNotEmpty || arguments.keywordRest != null) { - throw _exception( - "Plain CSS functions don't support keyword arguments.", - nodeWithSpan.span, - ); + throw _exception("Plain CSS functions don't support keyword arguments.", + nodeWithSpan.span); } var buffer = StringBuffer("${callable.name}("); @@ -3491,12 +3038,11 @@ final class _EvaluateVisitor } on SassRuntimeException catch (error) { if (!error.message.endsWith("isn't a valid CSS value.")) rethrow; throw MultiSpanSassRuntimeException( - error.message, - error.span, - "value", - {nodeWithSpan.span: "unknown function treated as plain CSS"}, - error.trace, - ); + error.message, + error.span, + "value", + {nodeWithSpan.span: "unknown function treated as plain CSS"}, + error.trace); } buffer.writeCharCode($rparen); @@ -3509,35 +3055,24 @@ final class _EvaluateVisitor /// Evaluates [invocation] as applied to [callable], and invokes [callable]'s /// body. Value _runBuiltInCallable( - ArgumentList arguments, - BuiltInCallable callable, - AstNode nodeWithSpan, - ) { + ArgumentList arguments, BuiltInCallable callable, AstNode nodeWithSpan) { var evaluated = _evaluateArguments(arguments); var oldCallableNode = _callableNode; _callableNode = nodeWithSpan; var namedSet = MapKeySet(evaluated.named); - var (overload, callback) = callable.callbackFor( - evaluated.positional.length, - namedSet, - ); - _addExceptionSpan( - nodeWithSpan, - () => overload.verify(evaluated.positional.length, namedSet), - ); + var (overload, callback) = + callable.callbackFor(evaluated.positional.length, namedSet); + _addExceptionSpan(nodeWithSpan, + () => overload.verify(evaluated.positional.length, namedSet)); var parameters = overload.parameters; for (var i = evaluated.positional.length; i < parameters.length; i++) { var parameter = parameters[i]; - evaluated.positional.add( - evaluated.named.remove(parameter.name) ?? - _withoutSlash( - parameter.defaultValue!.accept(this), - parameter.defaultValue!, - ), - ); + evaluated.positional.add(evaluated.named.remove(parameter.name) ?? + _withoutSlash( + parameter.defaultValue!.accept(this), parameter.defaultValue!)); } SassArgumentList? argumentList; @@ -3545,36 +3080,28 @@ final class _EvaluateVisitor var rest = const []; if (evaluated.positional.length > parameters.length) { rest = evaluated.positional.sublist(parameters.length); - evaluated.positional.removeRange( - parameters.length, - evaluated.positional.length, - ); + evaluated.positional + .removeRange(parameters.length, evaluated.positional.length); } argumentList = SassArgumentList( - rest, - evaluated.named, - evaluated.separator == ListSeparator.undecided - ? ListSeparator.comma - : evaluated.separator, - ); + rest, + evaluated.named, + evaluated.separator == ListSeparator.undecided + ? ListSeparator.comma + : evaluated.separator); evaluated.positional.add(argumentList); } Value result; try { - result = _addExceptionSpan( - nodeWithSpan, - () => callback(evaluated.positional), - ); + result = + _addExceptionSpan(nodeWithSpan, () => callback(evaluated.positional)); } on SassException { rethrow; } catch (error, stackTrace) { - throwWithTrace( - _exception(_getErrorMessage(error), nodeWithSpan.span), - error, - stackTrace, - ); + throwWithTrace(_exception(_getErrorMessage(error), nodeWithSpan.span), + error, stackTrace); } _callableNode = oldCallableNode; @@ -3583,13 +3110,12 @@ final class _EvaluateVisitor if (argumentList.wereKeywordsAccessed) return result; throw MultiSpanSassRuntimeException( - "No ${pluralize('parameter', evaluated.named.keys.length)} named " - "${toSentence(evaluated.named.keys.map((name) => "\$$name"), 'or')}.", - nodeWithSpan.span, - "invocation", - {overload.spanWithName: "declaration"}, - _stackTrace(nodeWithSpan.span), - ); + "No ${pluralize('parameter', evaluated.named.keys.length)} named " + "${toSentence(evaluated.named.keys.map((name) => "\$$name"), 'or')}.", + nodeWithSpan.span, + "invocation", + {overload.spanWithName: "declaration"}, + _stackTrace(nodeWithSpan.span)); } /// Returns the evaluated values of the given [arguments]. @@ -3623,7 +3149,7 @@ final class _EvaluateVisitor positionalNodes: positionalNodes, named: named, namedNodes: namedNodes, - separator: ListSeparator.undecided, + separator: ListSeparator.undecided ); } @@ -3634,12 +3160,11 @@ final class _EvaluateVisitor _addRestMap(named, rest, restArgs, (value) => value); namedNodes.addAll({ for (var key in rest.contents.keys) - (key as SassString).text: restNodeForSpan, + (key as SassString).text: restNodeForSpan }); } else if (rest is SassList) { positional.addAll( - rest.asList.map((value) => _withoutSlash(value, restNodeForSpan)), - ); + rest.asList.map((value) => _withoutSlash(value, restNodeForSpan))); positionalNodes.addAll(List.filled(rest.lengthAsList, restNodeForSpan)); separator = rest.separator; @@ -3661,7 +3186,7 @@ final class _EvaluateVisitor positionalNodes: positionalNodes, named: named, namedNodes: namedNodes, - separator: separator, + separator: separator ); } @@ -3671,20 +3196,19 @@ final class _EvaluateVisitor _addRestMap(named, keywordRest, keywordRestArgs, (value) => value); namedNodes.addAll({ for (var key in keywordRest.contents.keys) - (key as SassString).text: keywordRestNodeForSpan, + (key as SassString).text: keywordRestNodeForSpan }); return ( positional: positional, positionalNodes: positionalNodes, named: named, namedNodes: namedNodes, - separator: separator, + separator: separator ); } else { throw _exception( - "Variable keyword arguments must be a map (was $keywordRest).", - keywordRestArgs.span, - ); + "Variable keyword arguments must be a map (was $keywordRest).", + keywordRestArgs.span); } } @@ -3694,7 +3218,7 @@ final class _EvaluateVisitor /// Returns the arguments as expressions so that they can be lazily evaluated /// for macros such as `if()`. (List positional, Map named) - _evaluateMacroArguments(CallableInvocation invocation) { + _evaluateMacroArguments(CallableInvocation invocation) { var restArgs_ = invocation.arguments.rest; if (restArgs_ == null) { return (invocation.arguments.positional, invocation.arguments.named); @@ -3706,33 +3230,20 @@ final class _EvaluateVisitor var rest = restArgs.accept(this); var restNodeForSpan = _expressionNode(restArgs); if (rest is SassMap) { - _addRestMap( - named, - rest, - invocation, - (value) => ValueExpression(value, restArgs.span), - ); + _addRestMap(named, rest, invocation, + (value) => ValueExpression(value, restArgs.span)); } else if (rest is SassList) { - positional.addAll( - rest.asList.map( - (value) => ValueExpression( - _withoutSlash(value, restNodeForSpan), - restArgs.span, - ), - ), - ); + positional.addAll(rest.asList.map((value) => ValueExpression( + _withoutSlash(value, restNodeForSpan), restArgs.span))); if (rest is SassArgumentList) { rest.keywords.forEach((key, value) { named[key] = ValueExpression( - _withoutSlash(value, restNodeForSpan), - restArgs.span, - ); + _withoutSlash(value, restNodeForSpan), restArgs.span); }); } } else { positional.add( - ValueExpression(_withoutSlash(rest, restNodeForSpan), restArgs.span), - ); + ValueExpression(_withoutSlash(rest, restNodeForSpan), restArgs.span)); } var keywordRestArgs_ = invocation.arguments.keywordRest; @@ -3743,20 +3254,17 @@ final class _EvaluateVisitor var keywordRestNodeForSpan = _expressionNode(keywordRestArgs); if (keywordRest is SassMap) { _addRestMap( - named, - keywordRest, - invocation, - (value) => ValueExpression( - _withoutSlash(value, keywordRestNodeForSpan), - keywordRestArgs.span, - ), - ); + named, + keywordRest, + invocation, + (value) => ValueExpression( + _withoutSlash(value, keywordRestNodeForSpan), + keywordRestArgs.span)); return (positional, named); } else { throw _exception( - "Variable keyword arguments must be a map (was $keywordRest).", - keywordRestArgs.span, - ); + "Variable keyword arguments must be a map (was $keywordRest).", + keywordRestArgs.span); } } @@ -3771,37 +3279,27 @@ final 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. - void _addRestMap( - Map values, - SassMap map, - AstNode nodeWithSpan, - T convert(Value value), - ) { + void _addRestMap(Map values, SassMap map, AstNode nodeWithSpan, + T convert(Value value)) { var expressionNode = _expressionNode(nodeWithSpan); map.contents.forEach((key, value) { if (key is SassString) { values[key.text] = convert(_withoutSlash(value, expressionNode)); } else { throw _exception( - "Variable keyword argument map must have string keys.\n" - "$key is not a string in $map.", - nodeWithSpan.span, - ); + "Variable keyword argument map must have string keys.\n" + "$key is not a string in $map.", + nodeWithSpan.span); } }); } /// Throws a [SassRuntimeException] if [positional] and [named] aren't valid /// when applied to [parameters]. - void _verifyArguments( - int positional, - Map named, - ParameterList parameters, - AstNode nodeWithSpan, - ) => _addExceptionSpan( - nodeWithSpan, - () => parameters.verify(positional, MapKeySet(named)), - ); + void _verifyArguments(int positional, Map named, + ParameterList parameters, AstNode nodeWithSpan) => + _addExceptionSpan( + nodeWithSpan, () => parameters.verify(positional, MapKeySet(named))); Value visitSelectorExpression(SelectorExpression node) => _styleRuleIgnoringAtRoot?.originalSelector.asSassList ?? sassNull; @@ -3812,19 +3310,18 @@ final class _EvaluateVisitor var oldInSupportsDeclaration = _inSupportsDeclaration; _inSupportsDeclaration = false; var result = SassString( - [ - for (var value in node.text.contents) - switch (value) { - String() => value, - Expression() => switch (value.accept(this)) { - SassString(:var text) => text, - var result => _serialize(result, value, quote: false), - }, - _ => throw UnsupportedError("Unknown interpolation value $value"), - }, - ].join(), - quotes: node.hasQuotes, - ); + [ + for (var value in node.text.contents) + switch (value) { + String() => value, + Expression() => switch (value.accept(this)) { + SassString(:var text) => text, + var result => _serialize(result, value, quote: false) + }, + _ => throw UnsupportedError("Unknown interpolation value $value") + } + ].join(), + quotes: node.hasQuotes); _inSupportsDeclaration = oldInSupportsDeclaration; return result; } @@ -3849,20 +3346,12 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "At-rules may not be used within nested declarations.", - node.span, - ); + "At-rules may not be used within nested declarations.", node.span); } if (node.isChildless) { - _parent.addChild( - ModifiableCssAtRule( - node.name, - node.span, - childless: true, - value: node.value, - ), - ); + _parent.addChild(ModifiableCssAtRule(node.name, node.span, + childless: true, value: node.value)); return; } @@ -3874,19 +3363,15 @@ final class _EvaluateVisitor _inUnknownAtRule = true; } - _withParent( - ModifiableCssAtRule(node.name, node.span, value: node.value), - () { - // We don't have to check for an unknown at-rule in a style rule here, - // because the previous compilation has already bubbled the at-rule to the - // root. - for (var child in node.children) { - child.accept(this); - } - }, - through: (node) => node is CssStyleRule, - scopeWhen: false, - ); + _withParent(ModifiableCssAtRule(node.name, node.span, value: node.value), + () { + // We don't have to check for an unknown at-rule in a style rule here, + // because the previous compilation has already bubbled the at-rule to the + // root. + for (var child in node.children) { + child.accept(this); + } + }, through: (node) => node is CssStyleRule, scopeWhen: false); _inUnknownAtRule = wasInUnknownAtRule; _inKeyframes = wasInKeyframes; @@ -3905,26 +3390,17 @@ final class _EvaluateVisitor } void visitCssDeclaration(CssDeclaration node) { - _parent.addChild( - ModifiableCssDeclaration( - node.name, - node.value, - node.span, + _parent.addChild(ModifiableCssDeclaration(node.name, node.value, node.span, parsedAsCustomProperty: node.parsedAsCustomProperty, - valueSpanForMap: node.valueSpanForMap, - ), - ); + valueSpanForMap: node.valueSpanForMap)); } void visitCssImport(CssImport node) { // NOTE: this logic is largely duplicated in [_visitStaticImport]. Most // changes here should be mirrored there. - var modifiableNode = ModifiableCssImport( - node.url, - node.span, - modifiers: node.modifiers, - ); + var modifiableNode = + ModifiableCssImport(node.url, node.span, modifiers: node.modifiers); if (_parent != _root) { _parent.addChild(modifiableNode); } else if (_endOfImports == _root.children.length) { @@ -3940,16 +3416,11 @@ final class _EvaluateVisitor // here should be mirrored there. var rule = ModifiableCssKeyframeBlock(node.selector, node.span); - _withParent( - rule, - () { - for (var child in node.children) { - child.accept(this); - } - }, - through: (node) => node is CssStyleRule, - scopeWhen: false, - ); + _withParent(rule, () { + for (var child in node.children) { + child.accept(this); + } + }, through: (node) => node is CssStyleRule, scopeWhen: false); } void visitCssMediaRule(CssMediaRule node) { @@ -3958,51 +3429,44 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "Media rules may not be used within nested declarations.", - node.span, - ); + "Media rules may not be used within nested declarations.", node.span); } var mergedQueries = _mediaQueries.andThen( - (mediaQueries) => _mergeMediaQueries(mediaQueries, node.queries), - ); + (mediaQueries) => _mergeMediaQueries(mediaQueries, node.queries)); if (mergedQueries != null && mergedQueries.isEmpty) return; - var mergedSources = - mergedQueries == null - ? const {} - : {..._mediaQuerySources!, ..._mediaQueries!, ...node.queries}; + var mergedSources = mergedQueries == null + ? const {} + : {..._mediaQuerySources!, ..._mediaQueries!, ...node.queries}; _withParent( - ModifiableCssMediaRule(mergedQueries ?? node.queries, node.span), - () { - _withMediaQueries(mergedQueries ?? node.queries, mergedSources, () { - if (_styleRule case var styleRule?) { - // If we're in a style rule, copy it into the media query so that - // declarations immediately inside @media have somewhere to go. - // - // For example, "a {@media screen {b: c}}" should produce - // "@media screen {a {b: c}}". - _withParent(styleRule.copyWithoutChildren(), () { - for (var child in node.children) { - child.accept(this); - } - }, scopeWhen: false); - } else { + ModifiableCssMediaRule(mergedQueries ?? node.queries, node.span), () { + _withMediaQueries(mergedQueries ?? node.queries, mergedSources, () { + if (_styleRule case var styleRule?) { + // If we're in a style rule, copy it into the media query so that + // declarations immediately inside @media have somewhere to go. + // + // For example, "a {@media screen {b: c}}" should produce + // "@media screen {a {b: c}}". + _withParent(styleRule.copyWithoutChildren(), () { for (var child in node.children) { child.accept(this); } + }, scopeWhen: false); + } else { + for (var child in node.children) { + child.accept(this); } - }); - }, - through: - (node) => - node is CssStyleRule || - (mergedSources.isNotEmpty && - node is CssMediaRule && - node.queries.every(mergedSources.contains)), - scopeWhen: false, - ); + } + }); + }, + through: (node) => + node is CssStyleRule || + (mergedSources.isNotEmpty && + node is CssMediaRule && + node.queries.every(mergedSources.contains)), + scopeWhen: false); } void visitCssStyleRule(CssStyleRule node) { @@ -4011,47 +3475,31 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "Style rules may not be used within nested declarations.", - node.span, - ); + "Style rules may not be used within nested declarations.", node.span); } else if (_inKeyframes && _parent is CssKeyframeBlock) { throw _exception( - "Style rules may not be used within keyframe blocks.", - node.span, - ); + "Style rules may not be used within keyframe blocks.", node.span); } var styleRule = _styleRule; var nest = !(_styleRule?.fromPlainCss ?? false); - var originalSelector = - nest - ? node.selector.nestWithin( - styleRule?.originalSelector, - implicitParent: !_atRootExcludingStyleRule, - preserveParentSelectors: node.fromPlainCss, - ) - : node.selector; + var originalSelector = nest + ? node.selector.nestWithin(styleRule?.originalSelector, + implicitParent: !_atRootExcludingStyleRule, + preserveParentSelectors: node.fromPlainCss) + : node.selector; var selector = _extensionStore.addSelector(originalSelector, _mediaQueries); - var rule = ModifiableCssStyleRule( - selector, - node.span, - originalSelector: originalSelector, - fromPlainCss: node.fromPlainCss, - ); + var rule = ModifiableCssStyleRule(selector, node.span, + originalSelector: originalSelector, fromPlainCss: node.fromPlainCss); var oldAtRootExcludingStyleRule = _atRootExcludingStyleRule; _atRootExcludingStyleRule = false; - _withParent( - rule, - () { - _withStyleRule(rule, () { - for (var child in node.children) { - child.accept(this); - } - }); - }, - through: nest ? (node) => node is CssStyleRule : null, - scopeWhen: false, - ); + _withParent(rule, () { + _withStyleRule(rule, () { + for (var child in node.children) { + child.accept(this); + } + }); + }, through: nest ? (node) => node is CssStyleRule : null, scopeWhen: false); _atRootExcludingStyleRule = oldAtRootExcludingStyleRule; if (_parent.children case [..., var lastChild] when styleRule == null) { @@ -4071,34 +3519,28 @@ final class _EvaluateVisitor if (_declarationName != null) { throw _exception( - "Supports rules may not be used within nested declarations.", - node.span, - ); - } - - _withParent( - ModifiableCssSupportsRule(node.condition, node.span), - () { - if (_styleRule case var styleRule?) { - // If we're in a style rule, copy it into the supports rule so that - // declarations immediately inside @supports have somewhere to go. - // - // For example, "a {@supports (a: b) {b: c}}" should produce "@supports - // (a: b) {a {b: c}}". - _withParent(styleRule.copyWithoutChildren(), () { - for (var child in node.children) { - child.accept(this); - } - }); - } else { + "Supports rules may not be used within nested declarations.", + node.span); + } + + _withParent(ModifiableCssSupportsRule(node.condition, node.span), () { + if (_styleRule case var styleRule?) { + // If we're in a style rule, copy it into the supports rule so that + // declarations immediately inside @supports have somewhere to go. + // + // For example, "a {@supports (a: b) {b: c}}" should produce "@supports + // (a: b) {a {b: c}}". + _withParent(styleRule.copyWithoutChildren(), () { for (var child in node.children) { child.accept(this); } + }); + } else { + for (var child in node.children) { + child.accept(this); } - }, - through: (node) => node is CssStyleRule, - scopeWhen: false, - ); + } + }, through: (node) => node is CssStyleRule, scopeWhen: false); } // ## Utilities @@ -4128,34 +3570,22 @@ final class _EvaluateVisitor /// If [trim] is `true`, removes whitespace around the result. If /// [warnForColor] is `true`, this will emit a warning for any named color /// values passed into the interpolation. - CssValue _interpolationToValue( - Interpolation interpolation, { - bool trim = false, - bool warnForColor = false, - }) { - var result = _performInterpolation( - interpolation, - warnForColor: warnForColor, - ); - return CssValue( - trim ? trimAscii(result, excludeEscape: true) : result, - interpolation.span, - ); + CssValue _interpolationToValue(Interpolation interpolation, + {bool trim = false, bool warnForColor = false}) { + var result = + _performInterpolation(interpolation, warnForColor: warnForColor); + return CssValue(trim ? trimAscii(result, excludeEscape: true) : result, + interpolation.span); } /// Evaluates [interpolation]. /// /// If [warnForColor] is `true`, this will emit a warning for any named color /// values passed into the interpolation. - String _performInterpolation( - Interpolation interpolation, { - bool warnForColor = false, - }) { - var (result, _) = _performInterpolationHelper( - interpolation, - sourceMap: false, - warnForColor: warnForColor, - ); + String _performInterpolation(Interpolation interpolation, + {bool warnForColor = false}) { + var (result, _) = _performInterpolationHelper(interpolation, + sourceMap: false, warnForColor: warnForColor); return result; } @@ -4163,24 +3593,19 @@ final class _EvaluateVisitor /// can map spans from the resulting string back to the original /// [interpolation]. (String, InterpolationMap) _performInterpolationWithMap( - Interpolation interpolation, { - bool warnForColor = false, - }) { - var (result, map) = _performInterpolationHelper( - interpolation, - sourceMap: true, - warnForColor: warnForColor, - ); + Interpolation interpolation, + {bool warnForColor = false}) { + var (result, map) = _performInterpolationHelper(interpolation, + sourceMap: true, warnForColor: warnForColor); return (result, map!); } /// A helper that implements the core logic of both [_performInterpolation] /// and [_performInterpolationWithMap]. (String, InterpolationMap?) _performInterpolationHelper( - Interpolation interpolation, { - required bool sourceMap, - bool warnForColor = false, - }) { + Interpolation interpolation, + {required bool sourceMap, + bool warnForColor = false}) { var targetLocations = sourceMap ? [] : null; var oldInSupportsDeclaration = _inSupportsDeclaration; _inSupportsDeclaration = false; @@ -4200,23 +3625,19 @@ final class _EvaluateVisitor if (warnForColor && namesByColor.containsKey(result)) { var alternative = BinaryOperationExpression( - BinaryOperator.plus, - StringExpression( - Interpolation.plain("", interpolation.span), - quotes: true, - ), - expression, - ); + BinaryOperator.plus, + StringExpression(Interpolation.plain("", interpolation.span), + quotes: true), + expression); _warn( - "You probably don't mean to use the color value " - "${namesByColor[result]} in interpolation here.\n" - "It may end up represented as $result, which will likely produce " - "invalid CSS.\n" - "Always quote color names when using them as strings or map keys " - '(for example, "${namesByColor[result]}").\n' - "If you really want to use the color value here, use '$alternative'.", - expression.span, - ); + "You probably don't mean to use the color value " + "${namesByColor[result]} in interpolation here.\n" + "It may end up represented as $result, which will likely produce " + "invalid CSS.\n" + "Always quote color names when using them as strings or map keys " + '(for example, "${namesByColor[result]}").\n' + "If you really want to use the color value here, use '$alternative'.", + expression.span); } buffer.write(_serialize(result, expression, quote: false)); @@ -4226,8 +3647,7 @@ final class _EvaluateVisitor return ( buffer.toString(), targetLocations.andThen( - (targetLocations) => InterpolationMap(interpolation, targetLocations), - ), + (targetLocations) => InterpolationMap(interpolation, targetLocations)) ); } @@ -4262,12 +3682,9 @@ final class _EvaluateVisitor if (expression is VariableExpression) { return _addExceptionSpan( - expression, - () => _environment.getVariableNode( - expression.name, - namespace: expression.namespace, - ), - ) ?? + expression, + () => _environment.getVariableNode(expression.name, + namespace: expression.namespace)) ?? expression; } else { return expression; @@ -4282,12 +3699,8 @@ final class _EvaluateVisitor /// lattermost child of its parent. /// /// Runs [callback] in a new environment scope unless [scopeWhen] is false. - T _withParent( - S node, - T callback(), { - bool through(CssNode node)?, - bool scopeWhen = true, - }) { + T _withParent(S node, T callback(), + {bool through(CssNode node)?, bool scopeWhen = true}) { _addChild(node, through: through); var oldParent = _parent; @@ -4312,8 +3725,7 @@ final class _EvaluateVisitor parent = grandparent; } else { throw ArgumentError( - "through() must return false for at least one parent of $node.", - ); + "through() must return false for at least one parent of $node."); } } @@ -4352,10 +3764,7 @@ final class _EvaluateVisitor /// merged together to create [queries]. This is used to determine when it's /// safe to bubble one query through another. T _withMediaQueries( - List? queries, - Set? sources, - T callback(), - ) { + List? queries, Set? sources, T callback()) { var oldMediaQueries = _mediaQueries; var oldSources = _mediaQuerySources; _mediaQueries = queries; @@ -4389,22 +3798,21 @@ final class _EvaluateVisitor Value _withoutSlash(Value value, AstNode nodeForSpan) { if (value case SassNumber(asSlash: _?)) { String recommendation(SassNumber number) => switch (number.asSlash) { - (var before, var after) => - "math.div(${recommendation(before)}, ${recommendation(after)})", - _ => number.toString(), - }; + (var before, var after) => + "math.div(${recommendation(before)}, ${recommendation(after)})", + _ => number.toString() + }; _warn( - "Using / for division is deprecated and will be removed in Dart Sass " - "2.0.0.\n" - "\n" - "Recommendation: ${recommendation(value)}\n" - "\n" - "More info and automated migrator: " - "https://sass-lang.com/d/slash-div", - nodeForSpan.span, - Deprecation.slashDiv, - ); + "Using / for division is deprecated and will be removed in Dart Sass " + "2.0.0.\n" + "\n" + "Recommendation: ${recommendation(value)}\n" + "\n" + "More info and automated migrator: " + "https://sass-lang.com/d/slash-div", + nodeForSpan.span, + Deprecation.slashDiv); } return value.withoutSlash(); @@ -4412,11 +3820,8 @@ final 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. /// @@ -4425,7 +3830,7 @@ final class _EvaluateVisitor var frames = [ for (var (member, nodeWithSpan) in _stack) _stackFrame(member, nodeWithSpan.span), - if (span != null) _stackFrame(_member, span), + if (span != null) _stackFrame(_member, span) ]; return Trace(frames.reversed); } @@ -4442,12 +3847,8 @@ final class _EvaluateVisitor if (deprecation == null) { _logger.warn(message, span: span, trace: trace); } else { - _logger.warnForDeprecation( - deprecation, - message, - span: span, - trace: trace, - ); + _logger.warnForDeprecation(deprecation, message, + span: span, trace: trace); } } @@ -4456,26 +3857,16 @@ final class _EvaluateVisitor /// If [span] is passed, it's used for the innermost stack frame. SassRuntimeException _exception(String message, [FileSpan? span]) => SassRuntimeException( - message, - span ?? _stack.last.$2.span, - _stackTrace(span), - ); + message, span ?? _stack.last.$2.span, _stackTrace(span)); /// Returns a [MultiSpanSassRuntimeException] with the given [message], /// [primaryLabel], and [secondaryLabels]. /// /// The primary span is taken from the current stack trace span. - SassRuntimeException _multiSpanException( - String message, - String primaryLabel, - Map secondaryLabels, - ) => MultiSpanSassRuntimeException( - message, - _stack.last.$2.span, - primaryLabel, - secondaryLabels, - _stackTrace(), - ); + SassRuntimeException _multiSpanException(String message, String primaryLabel, + Map secondaryLabels) => + MultiSpanSassRuntimeException(message, _stack.last.$2.span, primaryLabel, + secondaryLabels, _stackTrace()); /// Runs [callback], and converts any [SassScriptException]s it throws to /// [SassRuntimeException]s with [nodeWithSpan]'s source span. @@ -4486,21 +3877,17 @@ final class _EvaluateVisitor /// /// If [addStackFrame] is true (the default), this will add an innermost stack /// frame for [nodeWithSpan]. Otherwise, it will use the existing stack as-is. - T _addExceptionSpan( - AstNode nodeWithSpan, - T callback(), { - bool addStackFrame = true, - }) { + T _addExceptionSpan(AstNode nodeWithSpan, T callback(), + {bool addStackFrame = true}) { try { return callback(); } on SassScriptException catch (error, stackTrace) { throwWithTrace( - error - .withSpan(nodeWithSpan.span) - .withTrace(_stackTrace(addStackFrame ? nodeWithSpan.span : null)), - error, - stackTrace, - ); + error + .withSpan(nodeWithSpan.span) + .withTrace(_stackTrace(addStackFrame ? nodeWithSpan.span : null)), + error, + stackTrace); } } @@ -4514,10 +3901,7 @@ final class _EvaluateVisitor rethrow; } on SassException catch (error, stackTrace) { throwWithTrace( - error.withTrace(_stackTrace(error.span)), - error, - stackTrace, - ); + error.withTrace(_stackTrace(error.span)), error, stackTrace); } } @@ -4530,10 +3914,9 @@ final class _EvaluateVisitor } on SassRuntimeException catch (error, stackTrace) { if (!error.span.text.startsWith("@error")) rethrow; throwWithTrace( - SassRuntimeException(error.message, nodeWithSpan.span, _stackTrace()), - error, - stackTrace, - ); + SassRuntimeException(error.message, nodeWithSpan.span, _stackTrace()), + error, + stackTrace); } } @@ -4568,10 +3951,8 @@ final class _ImportedCssVisitor implements ModifiableCssVisitor { _ImportedCssVisitor(this._visitor); void visitCssAtRule(ModifiableCssAtRule node) { - _visitor._addChild( - node, - through: node.isChildless ? null : (node) => node is CssStyleRule, - ); + _visitor._addChild(node, + through: node.isChildless ? null : (node) => node is CssStyleRule); } void visitCssComment(ModifiableCssComment node) => _visitor._addChild(node); @@ -4600,16 +3981,12 @@ final class _ImportedCssVisitor implements ModifiableCssVisitor { // has been merged, merging again is a no-op; if it hasn't been merged, // merging again will fail. var mediaQueries = _visitor._mediaQueries; - var hasBeenMerged = - mediaQueries == null || + var hasBeenMerged = mediaQueries == null || _visitor._mergeMediaQueries(mediaQueries, node.queries) != null; - _visitor._addChild( - node, - through: - (node) => - node is CssStyleRule || (hasBeenMerged && node is CssMediaRule), - ); + _visitor._addChild(node, + through: (node) => + node is CssStyleRule || (hasBeenMerged && node is CssMediaRule)); } void visitCssStyleRule(ModifiableCssStyleRule node) => @@ -4644,56 +4021,52 @@ final class _EvaluationContext implements EvaluationContext { void warn(String message, [Deprecation? deprecation]) { _visitor._warn( - message, - _visitor._importSpan ?? - _visitor._callableNode?.span ?? - _defaultWarnNodeWithSpan.span, - deprecation, - ); + message, + _visitor._importSpan ?? + _visitor._callableNode?.span ?? + _defaultWarnNodeWithSpan.span, + deprecation); } } /// The result of evaluating arguments to a function or mixin. -typedef _ArgumentResults = - ({ - /// Arguments passed by position. - List positional, - - /// The [AstNode]s that hold the spans for each [positional] argument. - /// - /// 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. - List positionalNodes, - - /// Arguments passed by name. - Map named, - - /// The [AstNode]s that hold the spans for each [named] argument. - /// - /// 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. - Map namedNodes, - - /// The separator used for the rest argument list, if any. - ListSeparator separator, - }); +typedef _ArgumentResults = ({ + /// Arguments passed by position. + List positional, + + /// The [AstNode]s that hold the spans for each [positional] argument. + /// + /// 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. + List positionalNodes, + + /// Arguments passed by name. + Map named, + + /// The [AstNode]s that hold the spans for each [named] argument. + /// + /// 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. + Map namedNodes, + + /// The separator used for the rest argument list, if any. + ListSeparator separator +}); /// The result of loading a stylesheet via [Evaluator._loadStylesheet]. -typedef _LoadedStylesheet = - ( - /// The stylesheet itself. - Stylesheet stylesheet, { - - /// The importer that was used to load the stylesheet. - /// - /// This is `null` when running in Node Sass compatibility mode. - Importer? importer, +typedef _LoadedStylesheet = ( + /// The stylesheet itself. + Stylesheet stylesheet, { + /// The importer that was used to load the stylesheet. + /// + /// This is `null` when running in Node Sass compatibility mode. + Importer? importer, - /// Whether this load counts as a dependency. - /// - /// That is, whether this was (transitively) loaded through a load path or - /// importer rather than relative to the entrypoint. - bool isDependency, - }); + /// Whether this load counts as a dependency. + /// + /// That is, whether this was (transitively) loaded through a load path or + /// importer rather than relative to the entrypoint. + bool isDependency +}); diff --git a/tool/grind/generate_deprecations.dart b/tool/grind/generate_deprecations.dart index 71d128dde..5590f465c 100644 --- a/tool/grind/generate_deprecations.dart +++ b/tool/grind/generate_deprecations.dart @@ -8,6 +8,7 @@ import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:dart_style/dart_style.dart'; import 'package:grinder/grinder.dart'; +import 'package:pub_semver/pub_semver.dart'; import 'package:yaml/yaml.dart'; import 'utils.dart'; @@ -78,7 +79,7 @@ void deprecations() { fail("Couldn't find block for generated code in lib/src/deprecation.dart"); } var newCode = dartText.replaceFirst(_blockRegex, buffer.toString()); - dartFile.writeAsStringSync( - DartFormatter(languageVersion: DartFormatter.latestLanguageVersion) - .format(newCode)); + dartFile.writeAsStringSync(DartFormatter( + languageVersion: Version.parse(Platform.version.split(' ').first)) + .format(newCode)); } diff --git a/tool/grind/synchronize.dart b/tool/grind/synchronize.dart index efdca7fac..8581eccc8 100644 --- a/tool/grind/synchronize.dart +++ b/tool/grind/synchronize.dart @@ -16,6 +16,7 @@ import 'package:crypto/crypto.dart'; import 'package:dart_style/dart_style.dart'; import 'package:grinder/grinder.dart'; import 'package:path/path.dart' as p; +import 'package:pub_semver/pub_semver.dart'; import 'package:source_span/source_span.dart'; import 'package:sass/src/util/nullable.dart'; @@ -58,7 +59,8 @@ String synchronizeFile(String source) { parseFile(path: source, featureSet: FeatureSet.latestLanguageVersion()) .unit .accept(visitor); - return DartFormatter(languageVersion: DartFormatter.latestLanguageVersion) + return DartFormatter( + languageVersion: Version.parse(Platform.version.split(' ').first)) .format(visitor.result); }