blob: 3e2f65b813dd2daabfbec7218ffca889ed589fdd [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library linter.src.rules.implementation_imports;
import 'package:analyzer/dart/ast/ast.dart' show AstVisitor, ImportDirective;
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:linter/src/linter.dart';
const desc = "Don't import implementation files from another package.";
const details = '''
**DON'T** import implementation files from another package.
From the the [pub package layout doc]
(https://www.dartlang.org/tools/pub/package-layout.html#implementation-files):
The libraries inside `lib` are publicly visible: other packages are free to
import them. But much of a package's code is internal implementation libraries
that should only be imported and used by the package itself. Those go inside a
subdirectory of `lib` called `src`. You can create subdirectories in there if
it helps you organize things.
You are free to import libraries that live in `lib/src` from within other Dart
code in the same package (like other libraries in `lib`, scripts in `bin`,
and tests) but you should never import from another package's `lib/src`
directory. Those files are not part of the package’s public API, and they
might change in ways that could break your code.
**Bad:**
```
// In 'road_runner'
import 'package:acme/lib/src/internals.dart;
```
''';
bool isImplementation(Uri uri) {
List<String> segments = uri?.pathSegments ?? const [];
if (segments.length > 2) {
if (segments[1] == 'src') {
return true;
}
}
return false;
}
bool isPackage(Uri uri) => uri?.scheme == 'package';
bool samePackage(Uri uri1, Uri uri2) {
var segments1 = uri1.pathSegments;
var segments2 = uri2.pathSegments;
if (segments1.length < 1 || segments2.length < 1) {
return false;
}
return segments1[0] == segments2[0];
}
class ImplementationImports extends LintRule {
ImplementationImports()
: super(
name: 'implementation_imports',
description: desc,
details: details,
group: Group.style);
@override
AstVisitor getVisitor() => new Visitor(this);
}
class Visitor extends SimpleAstVisitor {
final LintRule rule;
Visitor(this.rule);
@override
visitImportDirective(ImportDirective node) {
Uri importUri = node?.source?.uri;
Uri sourceUri = node?.element?.source?.uri;
// Test for 'package:*/src/'.
if (!isImplementation(importUri)) {
return;
}
// If the source URI is not a `package` URI bail out.
if (!isPackage(sourceUri)) {
return;
}
if (!samePackage(importUri, sourceUri)) {
rule.reportLint(node.uri);
}
}
}