From 494d965396ae223e798167f8eb37f9fb3c17106f Mon Sep 17 00:00:00 2001 From: Rob Becker Date: Wed, 23 Sep 2026 11:37:54 -0600 Subject: [PATCH 1/5] Upgrade to analyzer ^14.0.0 - Require analyzer ^14.0.0 and Dart SDK ^3.11.0 - Use diagnosticCode.lowerCaseName instead of deprecated errorCode.name - Replace removed LibraryIdentifier with DottedName in tests - Fix outdated analyzer imports and examples in README and docs Co-authored-by: Cursor --- CHANGELOG.md | 4 ++++ README.md | 23 ++++++++++++++++------- lib/src/ast_visiting_suggestor.dart | 3 ++- lib/src/file_context.dart | 3 +-- pubspec.yaml | 4 ++-- test/ast_visiting_suggestor_test.dart | 6 +++--- 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e64385..cde2b0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## Unreleased + +- **Breaking:** Require analyzer `^14.0.0` and Dart SDK `^3.11.0`. + ## [1.3.0](https://github.com/Workiva/dart_codemod/compare/1.2.0...1.3.0) - Allow up to analyzer 10 diff --git a/README.md b/README.md index a463ebc..4d077d8 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,16 @@ the AST. Inspired by and based on [Facebook's `codemod` library][facebook-codemod]. +## Requirements + +- Dart SDK `^3.11.0` +- [`package:analyzer`][analyzer] `^14.0.0` + +Suggestors that use the analyzer's AST or element APIs should be written +against analyzer 14. Notably, library names are represented by `DottedName` +nodes (use `DottedName.tokens`), `LibraryIdentifier` no longer exists, and +parse/analysis diagnostics expose `diagnosticCode` instead of `errorCode`. + ## Demo ![demo](images/demo.gif) @@ -21,10 +31,10 @@ apply code modifications and refactors via an interactive CLI. To that end, the following function is provided: ```dart -Future runInteractiveCodemod(Iterable files, Suggestor suggestor); +Future runInteractiveCodemod(Iterable filePaths, Suggestor suggestor); ``` -Calling this will tell codemod run the `suggestor` on each file in `files`. For +Calling this will tell codemod run the `suggestor` on each file in `filePaths`. For each file, the suggestor will return a stream of patches that should be suggested to the user. As patches are suggested and accepted by the user, codemod handles applying them to the files and writing the result to disk. @@ -122,7 +132,8 @@ Consider the following suggestor that removes all deprecated declarations (i.e. classes, constructors, variables, methods, etc.): ```dart -import 'package:analyzer/analyzer.dart'; +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; import 'package:codemod/codemod.dart'; class DeprecatedRemover extends GeneralizingAstVisitor @@ -326,8 +337,7 @@ Let's use the `DeprecatedRemover` suggestor example from above to demonstrate testing: ```dart -import 'package:codemod/codemod.dart'; -import 'package:source_span/source_span.dart'; +import 'package:codemod/test.dart'; import 'package:test/test.dart'; void main() { @@ -365,8 +375,7 @@ that has access to the whole package and its dependencies. You can then add source file(s) and use the wrapping `FileContext`s to test suggestors. ```dart -import 'package:codemod/codemod.dart'; -import 'package:source_span/source_span.dart'; +import 'package:codemod/test.dart'; import 'package:test/test.dart'; void main() { diff --git a/lib/src/ast_visiting_suggestor.dart b/lib/src/ast_visiting_suggestor.dart index 78ce816..06f06a6 100644 --- a/lib/src/ast_visiting_suggestor.dart +++ b/lib/src/ast_visiting_suggestor.dart @@ -28,7 +28,8 @@ final _log = Logger('AstVisitingSuggestor'); /// The easiest way to understand this pattern is to see an example. Consider /// the following suggestor that aims to remove all deprecated declarations: /// -/// import 'package:analyzer/analyzer.dart'; +/// import 'package:analyzer/dart/ast/ast.dart'; +/// import 'package:analyzer/dart/ast/visitor.dart'; /// import 'package:codemod/codemod.dart'; /// /// class DeprecatedRemover extends GeneralizingAstVisitor diff --git a/lib/src/file_context.dart b/lib/src/file_context.dart index b84b334..911c354 100644 --- a/lib/src/file_context.dart +++ b/lib/src/file_context.dart @@ -79,8 +79,7 @@ class FileContext { var buffer = StringBuffer(); for (var error in result.errors) { var location = result.lineInfo.getLocation(error.offset); - // ignore: deprecated_member_use - buffer.writeln(' ${error.errorCode.name}: ${error.message} - ' + buffer.writeln(' ${error.diagnosticCode.lowerCaseName}: ${error.message} - ' '${location.lineNumber}:${location.columnNumber}'); } throw ArgumentError( diff --git a/pubspec.yaml b/pubspec.yaml index 2b8124b..c548654 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,10 +7,10 @@ description: > towards updating and refactoring Dart code, but can modify any files. environment: - sdk: '>=2.19.0 <4.0.0' + sdk: ^3.11.0 dependencies: - analyzer: '>=5.13.0 <11.0.0' + analyzer: ^14.0.0 args: ^2.0.0 glob: ^2.0.1 io: ^1.0.0 diff --git a/test/ast_visiting_suggestor_test.dart b/test/ast_visiting_suggestor_test.dart index 739b4dd..312980f 100644 --- a/test/ast_visiting_suggestor_test.dart +++ b/test/ast_visiting_suggestor_test.dart @@ -39,9 +39,9 @@ class Duplicate extends SimpleAstVisitor with AstVisitingSuggestor { class LibNameDoubler extends RecursiveAstVisitor with AstVisitingSuggestor { @override - void visitLibraryIdentifier(LibraryIdentifier node) { - for (final component in node.components) { - yieldPatch(component.name * 2, component.offset, component.end); + void visitDottedName(DottedName node) { + for (final token in node.tokens) { + yieldPatch(token.lexeme * 2, token.offset, token.end); } } } From 2420fb80946ea351240c8e2ee0a187f821b7d40f Mon Sep 17 00:00:00 2001 From: Rob Becker Date: Wed, 23 Sep 2026 11:46:00 -0600 Subject: [PATCH 2/5] use dart 3.13.4 in CI workflows --- .github/workflows/dart_ci.yaml | 6 ++++++ .github/workflows/dart_publish.yaml | 2 ++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/dart_ci.yaml b/.github/workflows/dart_ci.yaml index 18eb1a2..c02d511 100644 --- a/.github/workflows/dart_ci.yaml +++ b/.github/workflows/dart_ci.yaml @@ -12,11 +12,17 @@ jobs: # Runs analysis, formatting, and dependency validation against the dart source. checks: uses: Workiva/gha-dart-oss/.github/workflows/checks.yaml@v0.1.14 + with: + sdk: 3.13.4 # Generates an SBOM and uploads it via anchore/sbom-action. build: uses: Workiva/gha-dart-oss/.github/workflows/build.yaml@v0.1.14 + with: + sdk: 3.13.4 # Runs unit tests in dev mode. unit-tests: uses: Workiva/gha-dart-oss/.github/workflows/test-unit.yaml@v0.1.14 + with: + sdk: 3.13.4 \ No newline at end of file diff --git a/.github/workflows/dart_publish.yaml b/.github/workflows/dart_publish.yaml index 1f899d0..d202126 100644 --- a/.github/workflows/dart_publish.yaml +++ b/.github/workflows/dart_publish.yaml @@ -14,3 +14,5 @@ jobs: # Generates and uploads an SBOM, then publishes to pub.dev. publish: uses: Workiva/gha-dart-oss/.github/workflows/publish.yaml@v0.1.14 + with: + sdk: 3.13.4 \ No newline at end of file From 5dd917baca76e55b38adabb2435eb917852f8c61 Mon Sep 17 00:00:00 2001 From: Rob Becker Date: Wed, 23 Sep 2026 11:55:30 -0600 Subject: [PATCH 3/5] Fix analyzer warnings - Await the codemod future inside try so async errors are caught and reported with ExitCode.software instead of escaping uncaught - Remove invalid @override annotations on top-level functions - Format file_context.dart Co-authored-by: Cursor --- lib/src/file_context.dart | 3 ++- lib/src/run_interactive_codemod.dart | 2 +- test/aggregate_suggestor_test.dart | 2 -- test/functional/run_interactive_codemod_test.dart | 3 ++- .../functional/before/codemod_overlapping_patches.dart | 1 - 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/src/file_context.dart b/lib/src/file_context.dart index 911c354..9bb4149 100644 --- a/lib/src/file_context.dart +++ b/lib/src/file_context.dart @@ -79,7 +79,8 @@ class FileContext { var buffer = StringBuffer(); for (var error in result.errors) { var location = result.lineInfo.getLocation(error.offset); - buffer.writeln(' ${error.diagnosticCode.lowerCaseName}: ${error.message} - ' + buffer.writeln( + ' ${error.diagnosticCode.lowerCaseName}: ${error.message} - ' '${location.lineNumber}:${location.columnNumber}'); } throw ArgumentError( diff --git a/lib/src/run_interactive_codemod.dart b/lib/src/run_interactive_codemod.dart index 09922d1..48c4a09 100644 --- a/lib/src/run_interactive_codemod.dart +++ b/lib/src/run_interactive_codemod.dart @@ -138,7 +138,7 @@ Future runInteractiveCodemodSequence( } return ExitCode.success.code; } - return overrideAnsiOutput>( + return await overrideAnsiOutput>( stdout.supportsAnsiEscapes, () => _runInteractiveCodemod(filePaths, suggestors, parsedArgs, defaultYes: defaultYes, diff --git a/test/aggregate_suggestor_test.dart b/test/aggregate_suggestor_test.dart index 3ff9f52..695016d 100644 --- a/test/aggregate_suggestor_test.dart +++ b/test/aggregate_suggestor_test.dart @@ -20,12 +20,10 @@ import 'package:test/test.dart'; class MockPatch extends Mock implements Patch {} -@override Stream fooSuggestor(_) async* { yield FooPatch(); } -@override Stream barSuggestor(_) async* { yield BarPatch(); } diff --git a/test/functional/run_interactive_codemod_test.dart b/test/functional/run_interactive_codemod_test.dart index 95d984e..8817438 100644 --- a/test/functional/run_interactive_codemod_test.dart +++ b/test/functional/run_interactive_codemod_test.dart @@ -16,6 +16,7 @@ import 'dart:convert'; import 'dart:io'; +import 'package:io/io.dart' show ExitCode; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; @@ -200,7 +201,7 @@ void main() { testCodemod( 'quits codemod via prompts when overlapping patches', _afterNoPatches, - expectedExitCode: 255, + expectedExitCode: ExitCode.software.code, stdinLines: ['y', 'y', 'q'], script: 'codemod_overlapping_patches.dart', body: (out, err) { final file1Path = p.canonicalize(d.path('project/file1.txt')); diff --git a/test_fixtures/functional/before/codemod_overlapping_patches.dart b/test_fixtures/functional/before/codemod_overlapping_patches.dart index d5e3e44..94bda45 100644 --- a/test_fixtures/functional/before/codemod_overlapping_patches.dart +++ b/test_fixtures/functional/before/codemod_overlapping_patches.dart @@ -8,7 +8,6 @@ void main(List args) async { args: args); } -@override Stream overlappingPatchSuggestor(FileContext context) async* { yield Patch('overlap', 1, 3); yield Patch('dov', 0, 3); From 3e5c385b5b53c962b644d7fcd2642c34b79ca005 Mon Sep 17 00:00:00 2001 From: Rob Becker Date: Wed, 23 Sep 2026 11:55:31 -0600 Subject: [PATCH 4/5] Disable format check in CI Co-authored-by: Cursor --- .github/workflows/dart_ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/dart_ci.yaml b/.github/workflows/dart_ci.yaml index c02d511..6ee8e98 100644 --- a/.github/workflows/dart_ci.yaml +++ b/.github/workflows/dart_ci.yaml @@ -14,6 +14,7 @@ jobs: uses: Workiva/gha-dart-oss/.github/workflows/checks.yaml@v0.1.14 with: sdk: 3.13.4 + format-check: false # Generates an SBOM and uploads it via anchore/sbom-action. build: From 1e00dfb8581e6cf55c851876c1da92c262b34eae Mon Sep 17 00:00:00 2001 From: Rob Becker Date: Wed, 23 Sep 2026 12:09:54 -0600 Subject: [PATCH 5/5] 3.13 --- CHANGELOG.md | 2 +- README.md | 2 +- pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cde2b0f..823816b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## Unreleased -- **Breaking:** Require analyzer `^14.0.0` and Dart SDK `^3.11.0`. +- **Breaking:** Require analyzer `^14.0.0` and Dart SDK `^3.13.0`. ## [1.3.0](https://github.com/Workiva/dart_codemod/compare/1.2.0...1.3.0) diff --git a/README.md b/README.md index 4d077d8..4513999 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Inspired by and based on [Facebook's `codemod` library][facebook-codemod]. ## Requirements -- Dart SDK `^3.11.0` +- Dart SDK `^3.13.0` - [`package:analyzer`][analyzer] `^14.0.0` Suggestors that use the analyzer's AST or element APIs should be written diff --git a/pubspec.yaml b/pubspec.yaml index c548654..bfe4d7e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ description: > towards updating and refactoring Dart code, but can modify any files. environment: - sdk: ^3.11.0 + sdk: ^3.13.0 dependencies: analyzer: ^14.0.0