diff --git a/CHANGELOG.md b/CHANGELOG.md index 83a1263..e30f8c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.6.0 + +### Strict Unary Migrator + +* Add a new migrator for eliminating ambiguous syntax for the `+` and `-` + operators that will soon be deprecated. + ## 1.5.6 * No user-visible changes. diff --git a/lib/src/migrators/strict_unary.dart b/lib/src/migrators/strict_unary.dart new file mode 100644 index 0000000..2dc73a0 --- /dev/null +++ b/lib/src/migrators/strict_unary.dart @@ -0,0 +1,47 @@ +// Copyright 2022 Google LLC +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:sass_api/sass_api.dart'; + +import '../migration_visitor.dart'; +import '../migrator.dart'; +import '../patch.dart'; + +/// Migrates deprecated `$a -$b` construct to unambiguous `$a - $b`. +class StrictUnaryMigrator extends Migrator { + final name = "strict-unary"; + final description = r"Migrates deprecated `$a -$b` syntax (and similar) to " + r"unambiguous `$a - $b`"; + + @override + Map migrateFile( + ImportCache importCache, Stylesheet stylesheet, Importer importer) { + var visitor = _UnaryMigrationVisitor(importCache, migrateDependencies); + var result = visitor.run(stylesheet, importer); + missingDependencies.addAll(visitor.missingDependencies); + return result; + } +} + +class _UnaryMigrationVisitor extends MigrationVisitor { + _UnaryMigrationVisitor(ImportCache importCache, bool migrateDependencies) + : super(importCache, migrateDependencies); + + @override + void visitBinaryOperationExpression(BinaryOperationExpression node) { + if (node.operator == BinaryOperator.plus || + node.operator == BinaryOperator.minus) { + var betweenOperands = node.span.file + .span(node.left.span.end.offset, node.right.span.start.offset) + .text; + if (betweenOperands.startsWith(RegExp(r'\s')) && + betweenOperands.endsWith(node.operator.operator)) { + addPatch(Patch.insert(node.right.span.start, ' ')); + } + } + super.visitBinaryOperationExpression(node); + } +} diff --git a/lib/src/runner.dart b/lib/src/runner.dart index 8d0528b..f602db0 100644 --- a/lib/src/runner.dart +++ b/lib/src/runner.dart @@ -16,6 +16,7 @@ import 'io.dart'; import 'migrators/division.dart'; import 'migrators/module.dart'; import 'migrators/namespace.dart'; +import 'migrators/strict_unary.dart'; import 'exception.dart'; /// A command runner that runs a migrator based on provided arguments. @@ -55,6 +56,7 @@ class MigratorRunner extends CommandRunner> { addCommand(DivisionMigrator()); addCommand(ModuleMigrator()); addCommand(NamespaceMigrator()); + addCommand(StrictUnaryMigrator()); } /// Runs a migrator and then writes the migrated files to disk unless diff --git a/pubspec.yaml b/pubspec.yaml index b42892e..6c32ce6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass_migrator -version: 1.5.7-dev +version: 1.6.0 description: A tool for running migrations on Sass files homepage: https://github.com/sass/migrator @@ -10,13 +10,12 @@ dependencies: args: ^2.1.0 charcode: ^1.2.0 collection: ^1.16.0 - # Workaround until pulyaevskiy/node-interop#110 is resolved - file: '>=6.1.0 <6.1.2' + file: ^6.1.4 glob: ^2.0.1 js: ^0.6.3 meta: ^1.3.0 node_interop: ^2.0.2 - node_io: ^2.1.0 + node_io: ^2.2.0 path: ^1.8.0 sass_api: ^3.0.0 source_span: ^1.8.1 diff --git a/test/migrators/namespace_node_test.dart b/test/migrators/namespace_node_test.dart index 9d77f50..4ca1e00 100644 --- a/test/migrators/namespace_node_test.dart +++ b/test/migrators/namespace_node_test.dart @@ -12,5 +12,5 @@ import '../utils.dart'; main() { runNodeTests = true; - testMigrator("division"); + testMigrator("namespace"); } diff --git a/test/migrators/strict_unary/add_whitespace.hrx b/test/migrators/strict_unary/add_whitespace.hrx new file mode 100644 index 0000000..1f89007 --- /dev/null +++ b/test/migrators/strict_unary/add_whitespace.hrx @@ -0,0 +1,31 @@ +<==> input/entrypoint.scss +$x: 1; +$y: 2; + +plus { + a: $x +$y; + b: ($x) +($y); + c: $x +$y +$y; +} + +minus { + a: $x -$y; + b: ($x) -($y); + c: $x -$y -$y; +} + +<==> output/entrypoint.scss +$x: 1; +$y: 2; + +plus { + a: $x + $y; + b: ($x) + ($y); + c: $x + $y + $y; +} + +minus { + a: $x - $y; + b: ($x) - ($y); + c: $x - $y - $y; +} diff --git a/test/migrators/strict_unary/no_change.hrx b/test/migrators/strict_unary/no_change.hrx new file mode 100644 index 0000000..b5fe1c7 --- /dev/null +++ b/test/migrators/strict_unary/no_change.hrx @@ -0,0 +1,20 @@ +<==> input/entrypoint.scss +$x: 1; +$y: 2; + +plus { + a: $x+$y; + b: $x + $y; + c: $x (+$y); + d: $x+ $y; +} + +minus { + a: ($x)-$y; + b: $x - $y; + c: $x (-$y); + d: ($x)- $y; +} + +<==> log.txt +Nothing to migrate! diff --git a/test/migrators/strict_unary_dart_test.dart b/test/migrators/strict_unary_dart_test.dart new file mode 100644 index 0000000..d593850 --- /dev/null +++ b/test/migrators/strict_unary_dart_test.dart @@ -0,0 +1,11 @@ +// Copyright 2022 Google LLC +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import '../utils.dart'; + +main() { + testMigrator("strict_unary"); +} diff --git a/test/migrators/strict_unary_node_test.dart b/test/migrators/strict_unary_node_test.dart new file mode 100644 index 0000000..4fe46b3 --- /dev/null +++ b/test/migrators/strict_unary_node_test.dart @@ -0,0 +1,16 @@ +// Copyright 2022 Google LLC +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +@Tags(["node"]) + +import 'package:test/test.dart'; + +import '../utils.dart'; + +main() { + runNodeTests = true; + testMigrator("strict_unary"); +} diff --git a/test/utils.dart b/test/utils.dart index a84ca7e..e774ff1 100644 --- a/test/utils.dart +++ b/test/utils.dart @@ -56,7 +56,7 @@ Future _testHrx(File hrxFile, String migrator) async { await files.unpack(); var process = await runMigrator([ - migrator, + migrator.replaceAll('_', '-'), '--no-unicode', ...files.arguments, for (var path in files.input.keys)