diff --git a/CHANGELOG.md b/CHANGELOG.md index 93e8bb37..0cd1ec1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ only be considered configured when the configuring declaration is upstream of the `!default` declaration. +* When namespacing a negated variable, adds parentheses around it to prevent the + `-` from being parsed as part of the namespace. + +* Fix a bug in the migrating of removed color functions when the amount is a + variable being namespaced. + ## 1.0.0 * Initial release. diff --git a/lib/src/migrators/module.dart b/lib/src/migrators/module.dart index 81d9809d..2c55a6b1 100644 --- a/lib/src/migrators/module.dart +++ b/lib/src/migrators/module.dart @@ -522,8 +522,10 @@ class _ModuleMigrationVisitor extends MigrationVisitor { /// Adds a namespace to any function call that requires it. @override void visitFunctionExpression(FunctionExpression node) { - super.visitFunctionExpression(node); - if (node.namespace != null) return; + if (node.namespace != null) { + super.visitFunctionExpression(node); + return; + } if (references.sources.containsKey(node)) { var declaration = references.functions[node]; _unreferencable.check(declaration, node); @@ -561,6 +563,7 @@ class _ModuleMigrationVisitor extends MigrationVisitor { ', \$module: "$namespace"')); }, getFunctionCall: true); } + super.visitFunctionExpression(node); } /// Calls [patchNamespace] when the function [node] requires a namespace. @@ -635,8 +638,13 @@ class _ModuleMigrationVisitor extends MigrationVisitor { void _patchRemovedColorFunction(String name, Expression arg, {FileSpan existingArgName}) { var parameter = removedColorFunctions[name]; - var needsParens = - parameter.endsWith('-') && arg is BinaryOperationExpression; + // Surround the argument in parens if negated to avoid `-` being parsed + // as part of the namespace. + var needsParens = parameter.endsWith('-') && + (arg is BinaryOperationExpression || + arg is FunctionExpression || + (arg is VariableExpression && + references.variables[arg]?.sourceUrl != currentUrl)); var leftParen = needsParens ? '(' : ''; if (existingArgName == null) { addPatch(patchBefore(arg, '$parameter$leftParen')); @@ -927,7 +935,12 @@ class _ModuleMigrationVisitor extends MigrationVisitor { _renameReference(nameSpan(node), declaration); var namespace = _namespaceForDeclaration(declaration); if (namespace != null) { + // Surround the variable in parens if negated to avoid `-` being parsed + // as part of the namespace. + var negated = matchesBeforeSpan(node.span, '-'); + if (negated) addPatch(patchBefore(node, '(')); addPatch(patchBefore(node, '$namespace.')); + if (negated) addPatch(patchAfter(node, ')')); } } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 3cb97e55..fed0bf51 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -85,6 +85,13 @@ FileSpan extendBackward(FileSpan span, String text) { return span.file.span(start - text.length, span.end.offset); } +/// Returns true if [span] is preceded by exactly [text]. +bool matchesBeforeSpan(FileSpan span, String text) { + var start = span.start.offset; + if (start - text.length < 0) return false; + return span.file.getText(start - text.length, start) == text; +} + /// Returns whether [character] is whitespace, according to Sass's definition. bool isWhitespace(int character) => character == $space || diff --git a/test/migrators/module/namespace_negation.hrx b/test/migrators/module/namespace_negation.hrx new file mode 100644 index 00000000..b6623cdb --- /dev/null +++ b/test/migrators/module/namespace_negation.hrx @@ -0,0 +1,23 @@ +<==> arguments +--migrate-deps + +<==> input/entrypoint.scss +@import "library"; +a { + b: -$amount; + color: darken($color, $amount); + background: darken($color, fn()); +} + +<==> input/_library.scss +$color: blue; +$amount: 10%; + +<==> output/entrypoint.scss +@use "sass:color"; +@use "library"; +a { + b: -(library.$amount); + color: color.adjust(library.$color, $lightness: -(library.$amount)); + background: color.adjust(library.$color, $lightness: -(fn())); +}