Skip to content

Commit

Permalink
Merge pull request #241 from Workiva/INTL-1332
Browse files Browse the repository at this point in the history
INTL-1332: Add import smarts to codemod
  • Loading branch information
rmconsole7-wk authored Nov 1, 2023
2 parents 60ae76b + a926209 commit dde6403
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 25 deletions.
55 changes: 42 additions & 13 deletions lib/src/intl_suggestors/intl_importer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import 'dart:io';

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:codemod/codemod.dart';
import 'package:collection/collection.dart';
import 'package:path/path.dart' as path;

Stream<Patch> intlImporter(
FileContext context, String projectName, String className) async* {
Expand All @@ -38,27 +41,43 @@ Stream<Patch> intlImporter(

if (!needsIntlImport) return;

final intlUri = 'package:${projectName}/src/intl/${projectName}_intl.dart';

final intlFilePath = '/src/intl/${projectName}_intl.dart';
final intlUri = 'package:${projectName}' + intlFilePath;
final intlDirectory = path.join(context.root, intlFilePath);
final relativePathToIntlDir =
path.relative(intlDirectory, from: Directory.current.path);
final insertInfo = _insertionLocationForPackageImport(
intlUri, mainLibraryUnitResult.unit, mainLibraryUnitResult.lineInfo);
yield Patch(
insertInfo.leadingNewlines +
"import '$intlUri';" +
insertInfo.trailingNewlines,
insertInfo.offset,
insertInfo.offset);

final importStatement = insertInfo.usePackageImports
? packageImport(intlUri, insertInfo)
: relativeImport(relativePathToIntlDir, insertInfo);

yield Patch(importStatement, insertInfo.offset, insertInfo.offset);
}

String packageImport(String intlUri, _InsertionLocation insertInfo) =>
insertInfo.leadingNewlines +
"import '$intlUri';" +
insertInfo.trailingNewlines;

String relativeImport(
String relativeImportPath, _InsertionLocation insertInfo) =>
insertInfo.leadingNewlines +
"import '$relativeImportPath';" +
insertInfo.trailingNewlines;

class _InsertionLocation {
final int offset;
final int leadingNewlineCount;
final int trailingNewlineCount;
final bool usePackageImports;

_InsertionLocation(
this.offset, {
this.leadingNewlineCount = 0,
this.trailingNewlineCount = 0,
this.usePackageImports = false,
});

String get leadingNewlines => '\n' * leadingNewlineCount;
Expand Down Expand Up @@ -94,6 +113,7 @@ _InsertionLocation _insertionLocationForPackageImport(
final AstNode relativeNode;
final bool insertAfter;
final bool inOwnSection;
bool hasOnlyPackageImports;
if (firstPackageImportSortedAfterNewImport != null) {
relativeNode = firstPackageImportSortedAfterNewImport;
insertAfter = false;
Expand Down Expand Up @@ -121,12 +141,21 @@ _InsertionLocation _insertionLocationForPackageImport(
// No directive to insert relative to; insert before the first member or
// at the beginning of the file.
return _InsertionLocation(unit.declarations.firstOrNull?.offset ?? 0,
trailingNewlineCount: 2);
trailingNewlineCount: 2, usePackageImports: true);
}

hasOnlyPackageImports = !imports.any((importDirective) {
final uriContent = importDirective.uri.stringValue;
if (uriContent != null) {
final uri = Uri.parse(uriContent);
return uri != null && uri.scheme != 'package' && uri.scheme != 'dart';
}
return true;
});

return _InsertionLocation(
insertAfter ? relativeNode.end : relativeNode.offset,
leadingNewlineCount: insertAfter ? (inOwnSection ? 2 : 1) : 0,
trailingNewlineCount: !insertAfter ? (inOwnSection ? 2 : 1) : 0,
);
insertAfter ? relativeNode.end : relativeNode.offset,
leadingNewlineCount: insertAfter ? (inOwnSection ? 2 : 1) : 0,
trailingNewlineCount: !insertAfter ? (inOwnSection ? 2 : 1) : 0,
usePackageImports: hasOnlyPackageImports);
}
48 changes: 36 additions & 12 deletions test/intl_suggestors/intl_importer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ void main() {
expectedOutput: /*language=dart*/ '''
import 'package:over_react/over_react.dart';
import 'package:test_project/src/intl/test_project_intl.dart';
content() => TestProjectIntl.testString;
''',
);
Expand All @@ -75,15 +74,13 @@ void main() {
await testSuggestor(
input: /*language=dart*/ '''
import 'package:z_fake_package/z_fake_package.dart';
content() => TestProjectIntl.testString;
''',
isExpectedError: (e) =>
isUndefinedIntlError(e) || isFakeUriError(e),
expectedOutput: /*language=dart*/ '''
import 'package:test_project/src/intl/test_project_intl.dart';
import 'package:z_fake_package/z_fake_package.dart';
content() => TestProjectIntl.testString;
''',
);
Expand All @@ -94,7 +91,6 @@ void main() {
input: /*language=dart*/ '''
import 'package:over_react/over_react.dart';
import 'package:z_fake_package/z_fake_package.dart';
content() => TestProjectIntl.testString;
''',
isExpectedError: (e) =>
Expand All @@ -103,7 +99,6 @@ void main() {
import 'package:over_react/over_react.dart';
import 'package:test_project/src/intl/test_project_intl.dart';
import 'package:z_fake_package/z_fake_package.dart';
content() => TestProjectIntl.testString;
''',
);
Expand All @@ -117,7 +112,6 @@ void main() {
import 'package:over_react/components.dart';
import 'package:z_fake_package/z_fake_package_1.dart';
import 'package:z_fake_package/z_fake_package_2.dart';
content() => TestProjectIntl.testString;
''',
isExpectedError: (e) =>
Expand All @@ -128,7 +122,6 @@ void main() {
import 'package:test_project/src/intl/test_project_intl.dart';
import 'package:z_fake_package/z_fake_package_1.dart';
import 'package:z_fake_package/z_fake_package_2.dart';
content() => TestProjectIntl.testString;
''',
);
Expand All @@ -139,19 +132,15 @@ void main() {
await testSuggestor(
input: /*language=dart*/ '''
import 'package:over_react/over_react.dart';
import 'a/fake_relative_file.dart';
content() => TestProjectIntl.testString;
''',
isExpectedError: (e) =>
isUndefinedIntlError(e) || isFakeUriError(e),
expectedOutput: /*language=dart*/ '''
import 'package:over_react/over_react.dart';
import 'package:test_project/src/intl/test_project_intl.dart';
import '../../../../../src/intl/test_project_intl.dart';
import 'a/fake_relative_file.dart';
content() => TestProjectIntl.testString;
''',
);
Expand All @@ -163,6 +152,7 @@ void main() {
import 'dart:html';
content() => TestProjectIntl.testString;
''',
isExpectedError: (e) =>
isUndefinedIntlError(e) || isFakeUriError(e),
Expand All @@ -177,6 +167,40 @@ void main() {
});
});

test('when there are Dart and Package', () async {
await testSuggestor(
input: /*language=dart*/ '''
import 'dart:html';
import 'package:over_react/over_react.dart';
content() => TestProjectIntl.testString;
''',
isExpectedError: (e) => isUndefinedIntlError(e) || isFakeUriError(e),
expectedOutput: /*language=dart*/ '''
import 'dart:html';
import 'package:over_react/over_react.dart';
import 'package:test_project/src/intl/test_project_intl.dart';
content() => TestProjectIntl.testString;
''',
);
});
test('package import in the same package should produce relative import',
() async {
await testSuggestor(
input: /*language=dart*/ '''
content() => TestProjectIntl.testString;
''',
isExpectedError: (e) => isUndefinedIntlError(e) || isFakeUriError(e),
expectedOutput: /*language=dart*/ '''
import 'package:test_project/src/intl/test_project_intl.dart';
content() => TestProjectIntl.testString;
''',
);
});

test('when there is just a library declaration', () async {
await testSuggestor(
input: /*language=dart*/ '''
Expand Down

0 comments on commit dde6403

Please sign in to comment.