Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ jobs:
uses: Workiva/gha-dart-oss/.github/workflows/checks.yaml@v0.1.12
with:
sdk: 3.12.2
format-check: false
additional-checks: |
no_entrypoint_imports

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Unreleased
- Upgrade to Dart 3 SDK (`>=3.12.0 <4.0.0`)
- Remove `null_safety_required_props` codemod and related fixtures (null-safety migration tooling no longer needed under Dart 3)
- Run `dart format` and re-enable format check in CI

## 2.38.0
- Add mui_system_props_migration codemod to migrate from system props to sx
Expand Down
29 changes: 11 additions & 18 deletions lib/src/boilerplate_suggestors/boilerplate_utilities.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import 'package:analyzer/dart/ast/ast.dart';
import 'package:logging/logging.dart';
import 'package:over_react_codemod/src/util.dart';

typedef YieldPatch = void Function(String replacement, int startingOffset,
[int? endingOffset]);
typedef YieldPatch =
void Function(String replacement, int startingOffset, [int? endingOffset]);

const semverReportNotAvailable =
'Semver report not available; this class is assumed to be public and thus will not be updated.';
Expand All @@ -36,8 +36,10 @@ final logger = Logger('over_react_codemod.boilerplate_upgrade.semver_helper');
/// If [shouldTreatAllComponentsAsPrivate] is true, the returned [SemverHelper]
/// assumes all classes passed to [getPublicExportLocations] are private
/// (see: [SemverHelper.alwaysPrivate] constructor).
SemverHelper getSemverHelper(String path,
{bool shouldTreatAllComponentsAsPrivate = false}) {
SemverHelper getSemverHelper(
String path, {
bool shouldTreatAllComponentsAsPrivate = false,
}) {
if (shouldTreatAllComponentsAsPrivate) {
return SemverHelper.alwaysPrivate();
} else {
Expand Down Expand Up @@ -75,11 +77,7 @@ SemverHelper getSemverHelper(String path,
}

/// Returns whether or not [node] is publicly exported.
bool isPublic(
ClassDeclaration node,
SemverHelper semverHelper,
String path,
) {
bool isPublic(ClassDeclaration node, SemverHelper semverHelper, String path) {
return semverHelper.getPublicExportLocations(node, path).isNotEmpty;
}

Expand All @@ -94,23 +92,18 @@ class SemverHelper {

/// Used to ensure [getPublicExportLocations] always returns an empty list,
/// treating all components as private.
SemverHelper.alwaysPrivate()
: _exportList = null,
_isAlwaysPrivate = true;
SemverHelper.alwaysPrivate() : _exportList = null, _isAlwaysPrivate = true;

/// Used to ensure [getPublicExportLocations] always returns a non-empty list,
/// treating all components as public.
SemverHelper.alwaysPublic(this.warning)
: _exportList = null,
_isAlwaysPrivate = false;
: _exportList = null,
_isAlwaysPrivate = false;

/// Returns a list of locations where [node] is publicly exported.
///
/// If [node] is not publicly exported, returns an empty list.
List<String> getPublicExportLocations(
ClassDeclaration node,
String path,
) {
List<String> getPublicExportLocations(ClassDeclaration node, String path) {
final className = stripPrivateGeneratedPrefix(node.name.lexeme);

if (!path.startsWith('lib/')) {
Expand Down
24 changes: 8 additions & 16 deletions lib/src/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,7 @@ const List<String> overReactPropsStateNonMixinAnnotationNames = [
];

/// Annotation names for over_react's props and state mixins.
const List<String> overReactMixinAnnotationNames = [
'PropsMixin',
'StateMixin',
];
const List<String> overReactMixinAnnotationNames = ['PropsMixin', 'StateMixin'];

/// A list of the names of the core component classes that can be upgraded to a "v2" version.
const List<String> upgradableV1ComponentClassNames = [
Expand Down Expand Up @@ -101,16 +98,14 @@ const String stateMetaType = 'StateMeta';
const String temporaryCompanionClassComment =
'This will be removed once the transition to Dart 2 is complete.';

RegExp getDependencyRegExp(String packageName) => RegExp(
packageName + r''':\s*(["']?)(.+)\1\s*$''',
multiLine: true,
);
RegExp getDependencyRegExp(String packageName) =>
RegExp(packageName + r''':\s*(["']?)(.+)\1\s*$''', multiLine: true);

RegExp getHostedDependencyRegExp(String packageName) => RegExp(
packageName +
r''':\n\s+hosted:\n\s+name:.*\n\s+url:.*\n\s+version:\s*(["']?)(.+)\1\s*$''',
multiLine: true,
);
packageName +
r''':\n\s+hosted:\n\s+name:.*\n\s+url:.*\n\s+version:\s*(["']?)(.+)\1\s*$''',
multiLine: true,
);

/// Regex to find a react dependency.
final RegExp reactDependencyRegExp = getDependencyRegExp('react');
Expand All @@ -119,10 +114,7 @@ final RegExp reactDependencyRegExp = getDependencyRegExp('react');
final RegExp overReactDependencyRegExp = getDependencyRegExp('over_react');

/// Regex to find the dependency pubspec.yaml key.
final RegExp dependencyRegExp = RegExp(
r'^dependencies:\s*$',
multiLine: true,
);
final RegExp dependencyRegExp = RegExp(r'^dependencies:\s*$', multiLine: true);

/// Regex to find the dependency pubspec.yaml key.
final RegExp devDependencyRegExp = RegExp(
Expand Down
95 changes: 54 additions & 41 deletions lib/src/creator_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ class DartTempProjectCreator {
late List<PubspecCreator> pubspecCreators;
late String mainDartContents;

DartTempProjectCreator(
{PubspecCreator? pubspecCreator,
List<PubspecCreator>? pubspecCreators,
String? mainDartContents}) {
DartTempProjectCreator({
PubspecCreator? pubspecCreator,
List<PubspecCreator>? pubspecCreators,
String? mainDartContents,
}) {
if (pubspecCreator != null && pubspecCreators != null) {
throw ArgumentError(
'Cannot specify both pubspecCreator and pubspecCreators');
'Cannot specify both pubspecCreator and pubspecCreators',
);
}

this.pubspecCreators =
Expand All @@ -24,8 +26,9 @@ class DartTempProjectCreator {
for (var pubspecCreator in pubspecCreators!) {
pubspecCreator.create(dir.path);
}
File(p.join(dir.path, 'main.dart'))
.writeAsStringSync(this.mainDartContents);
File(
p.join(dir.path, 'main.dart'),
).writeAsStringSync(this.mainDartContents);
}
}

Expand All @@ -37,13 +40,14 @@ class PubspecCreator {
final List<DependencyCreator> dependencies;
final String path;

PubspecCreator(
{this.name = 'fake_package',
this.version = '0.0.0',
this.isPrivate = true,
this.sdkVersion = '">=2.4.0 <3.0.0"',
this.dependencies = const [],
this.path = ''});
PubspecCreator({
this.name = 'fake_package',
this.version = '0.0.0',
this.isPrivate = true,
this.sdkVersion = '">=2.4.0 <3.0.0"',
this.dependencies = const [],
this.path = '',
});

void create(String rootDir) {
final parentDir = p.join(rootDir, path);
Expand All @@ -59,10 +63,12 @@ class PubspecCreator {
dependencies.addAll(new_dependencies);
}

void addDependency(String name,
{String version = 'any',
bool asDev = false,
bool Function()? shouldAdd}) {
void addDependency(
String name, {
String version = 'any',
bool asDev = false,
bool Function()? shouldAdd,
}) {
if (shouldAdd?.call() ?? true) {
dependencies.add(DependencyCreator(name, version: version, asDev: asDev));
}
Expand All @@ -81,17 +87,17 @@ class PubspecCreator {
' sdk: $sdkVersion\n' +
(dependencies.isNotEmpty
? '\ndependencies: \n' +
dependencies
.where((dep) => !(dep.asDev || dep.asOverride))
.join('\n')
dependencies
.where((dep) => !(dep.asDev || dep.asOverride))
.join('\n')
: '') +
(dependencies.any((dep) => dep.asDev)
? '\ndev_dependencies: \n' +
dependencies.where((dep) => dep.asDev).join('\n')
dependencies.where((dep) => dep.asDev).join('\n')
: '') +
(dependencies.any((dep) => dep.asOverride)
? '\ndependency_overrides: \n' +
dependencies.where((dep) => dep.asOverride).join('\n')
dependencies.where((dep) => dep.asOverride).join('\n')
: '') +
'\n';
}
Expand All @@ -117,12 +123,14 @@ class DependencyCreator {
}) : version = _versionWithQuotes(version) {
if (pathOverride.isNotEmpty && gitOverride.isNotEmpty) {
throw ArgumentError(
'Cannot provide both git and path overrides on single dep.');
'Cannot provide both git and path overrides on single dep.',
);
}
}

factory DependencyCreator.fromOverrideConfig(
DependencyOverrideConfig config) {
DependencyOverrideConfig config,
) {
switch (config.type) {
case ConfigType.simple:
return DependencyCreator(
Expand Down Expand Up @@ -195,11 +203,12 @@ class DartProjectCreatorTestConfig {
this.mainDartContents,
List<PubspecCreator>? pubspecCreators,
this.shouldRunCodemod = false,
}) : _testName = testName,
expectedExitCode = expectedExitCode ?? (shouldRunCodemod ? 1 : 0) {
}) : _testName = testName,
expectedExitCode = expectedExitCode ?? (shouldRunCodemod ? 1 : 0) {
if (pubspecCreators != null && dependencies != null) {
throw ArgumentError(
'Cannot specify both pubspecCreators and dependencies');
'Cannot specify both pubspecCreators and dependencies',
);
}
this.pubspecCreators =
pubspecCreators ?? [PubspecCreator(dependencies: dependencies ?? [])];
Expand All @@ -212,19 +221,23 @@ class DartProjectCreatorTestConfig {
if (pubspecCreators.isEmpty) {
name += 'no pubspecs';
} else {
name += pubspecCreators.map((creator) {
// Make it so that test names aren't multiline, trim/consolidate whitespace.
final humanReadableDependencies = creator.dependencies
.map((dep) => dep
.toString()
.trim()
.replaceAll('\n', '\\n')
.replaceAll(RegExp(r' +'), ' '))
.toList();
return 'pubspec'
' at ${creator.path.isEmpty ? 'root' : 'path ${creator.path}/pubspec.yaml'}'
' with dependencies: ${humanReadableDependencies}';
}).join(', ');
name += pubspecCreators
.map((creator) {
// Make it so that test names aren't multiline, trim/consolidate whitespace.
final humanReadableDependencies = creator.dependencies
.map(
(dep) => dep
.toString()
.trim()
.replaceAll('\n', '\\n')
.replaceAll(RegExp(r' +'), ' '),
)
.toList();
return 'pubspec'
' at ${creator.path.isEmpty ? 'root' : 'path ${creator.path}/pubspec.yaml'}'
' with dependencies: ${humanReadableDependencies}';
})
.join(', ');
}
return name;
}
Expand Down
20 changes: 13 additions & 7 deletions lib/src/dart2_9_suggestors/dart2_9_utilities.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ SimpleIdentifier? getGeneratedFactory(TopLevelVariableDeclaration node) {
final generatedName = r'_$' + name;
if (initializer is SimpleIdentifier && initializer.name == generatedName)
return initializer;
return allDescendantsOfType<SimpleIdentifier>(initializer)
.firstWhereOrNull((identifier) => identifier.name == generatedName);
return allDescendantsOfType<SimpleIdentifier>(
initializer,
).firstWhereOrNull((identifier) => identifier.name == generatedName);
}
}

Expand All @@ -80,8 +81,9 @@ bool isClassOrConnectedComponentFactory(TopLevelVariableDeclaration node) =>

/// Returns whether or not [node] is in the legacy boilerplate syntax.
bool isLegacyFactoryDecl(TopLevelVariableDeclaration node) {
final annotation = node.metadata
.firstWhereOrNull((m) => m.toSource().startsWith('@Factory'));
final annotation = node.metadata.firstWhereOrNull(
(m) => m.toSource().startsWith('@Factory'),
);
return isClassOrConnectedComponentFactory(node) && annotation != null;
}

Expand Down Expand Up @@ -110,14 +112,18 @@ bool isLegacyFactoryDecl(TopLevelVariableDeclaration node) {
///
/// `// ignore: invalid_assignment` => `// ignore: invalid_assignment`
void removeIgnoreComment(
Token? comment, String ignoreToRemove, YieldPatch yieldPatch) {
Token? comment,
String ignoreToRemove,
YieldPatch yieldPatch,
) {
ArgumentError.checkNotNull(ignoreToRemove, 'ignoreToRemove');
if (comment == null) return;

final lexeme = comment.lexeme.replaceAll(' ', '').toLowerCase();
if (lexeme.startsWith('//ignore:')) {
final ignoreList =
lexeme.replaceFirst(RegExp('\/\/ignore\:'), '').split(',');
final ignoreList = lexeme
.replaceFirst(RegExp('\/\/ignore\:'), '')
.split(',');
if (ignoreList.contains(ignoreToRemove) && ignoreList.length == 1) {
yieldPatch('', comment.previous?.end ?? comment.offset, comment.end);
} else if (ignoreList.contains(ignoreToRemove)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,27 @@ class FactoryAndConfigIgnoreCommentRemover extends RecursiveAstVisitor
}

removeIgnoreComment(
node.beginToken.precedingComments, ignoreToRemove, yieldPatch);
node.beginToken.precedingComments,
ignoreToRemove,
yieldPatch,
);
}
}

@override
bool shouldSkip(FileContext context) => hasParseErrors(context.sourceText);

Iterable<Token> _findPossibleIgnoreComments(
SimpleIdentifier generatedFactoryNode) sync* {
SimpleIdentifier generatedFactoryNode,
) sync* {
final lineNumber = context.sourceFile.getLine(generatedFactoryNode.offset);
for (var comment in allComments(generatedFactoryNode.root.beginToken)) {
final commentLineNumber = context.sourceFile.getLine(comment.offset);
final commentAppliesToNode =
// EOL comments
commentLineNumber == lineNumber ||
// Comments on the previous line
commentLineNumber == lineNumber - 1;
// Comments on the previous line
commentLineNumber == lineNumber - 1;
if (commentAppliesToNode) {
yield comment;
}
Expand Down
7 changes: 5 additions & 2 deletions lib/src/dart2_9_suggestors/generated_factory_migrator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ class GeneratedFactoryMigrator extends RecursiveAstVisitor
return;
}

yieldPatch('$castFunctionName(', generatedFactory!.offset,
generatedFactory.offset);
yieldPatch(
'$castFunctionName(',
generatedFactory!.offset,
generatedFactory.offset,
);
yieldPatch(')', generatedFactory.end, generatedFactory.end);
}
}
Expand Down
6 changes: 1 addition & 5 deletions lib/src/dart2_suggestors/orcm_ignore_remover.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ final _orcmIgnore = RegExp(r'[\n]?[ ]*//[ ]*orcm_ignore[ ]*');
/// Suggestor that removes every instance of a `// orcm_ignore` comment.
Stream<Patch> orcmIgnoreRemover(FileContext context) async* {
for (final match in _orcmIgnore.allMatches(context.sourceText)) {
yield Patch(
'',
match.start,
match.end,
);
yield Patch('', match.start, match.end);
}
}
Loading
Loading