mirror of https://github.com/AMT-Cheif/drift.git
Migrate simple CLI tools to new analyzer
This commit is contained in:
parent
c5504237d5
commit
c5a800d4c4
|
@ -1,4 +1,3 @@
|
|||
// @dart=2.9
|
||||
import 'package:drift_dev/src/cli/cli.dart' as cli;
|
||||
|
||||
Future main(List<String> args) {
|
||||
|
|
|
@ -104,6 +104,10 @@ class NoSuchFile extends DiscoveredFileState {
|
|||
NoSuchFile() : super(const []);
|
||||
}
|
||||
|
||||
class UnknownFile extends DiscoveredFileState {
|
||||
UnknownFile() : super(const []);
|
||||
}
|
||||
|
||||
abstract class DiscoveredElement {
|
||||
final DriftElementId ownId;
|
||||
|
||||
|
|
|
@ -300,7 +300,7 @@ class DartTableResolver extends LocalElementResolver<DiscoveredDartTable> {
|
|||
Future<Iterable<PendingColumnInformation>> _parseColumns(
|
||||
ClassElement element) async {
|
||||
final columnNames = element.allSupertypes
|
||||
.map((t) => t.element2)
|
||||
.map((t) => t.element)
|
||||
.followedBy([element])
|
||||
.expand((e) => e.fields)
|
||||
.where((field) =>
|
||||
|
|
|
@ -45,7 +45,7 @@ class DartViewResolver extends LocalElementResolver<DiscoveredDartView> {
|
|||
|
||||
Future<List<TableReferenceInDartView>> _parseStaticReferences() async {
|
||||
return await Stream.fromIterable(discovered.dartElement.allSupertypes
|
||||
.map((t) => t.element2)
|
||||
.map((t) => t.element)
|
||||
.followedBy([discovered.dartElement]).expand((e) => e.fields))
|
||||
.asyncMap((field) => _getStaticReference(field))
|
||||
.where((ref) => ref != null)
|
||||
|
@ -68,13 +68,13 @@ class DartViewResolver extends LocalElementResolver<DiscoveredDartView> {
|
|||
await resolver.driver.backend.loadElementDeclaration(field.getter!);
|
||||
if (node is MethodDeclaration && node.body is EmptyFunctionBody) {
|
||||
final table = await resolveDartReferenceOrReportError<DriftTable>(
|
||||
type.element2, (msg) {
|
||||
type.element, (msg) {
|
||||
return DriftAnalysisError.inDartAst(
|
||||
field, node.returnType ?? node.name, msg);
|
||||
});
|
||||
|
||||
if (table != null) {
|
||||
final name = node.name2.lexeme;
|
||||
final name = node.name.lexeme;
|
||||
return TableReferenceInDartView(table, name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ class DiscoverStep {
|
|||
|
||||
Future<void> discover() async {
|
||||
final extension = _file.extension;
|
||||
_file.discovery = UnknownFile();
|
||||
|
||||
switch (extension) {
|
||||
case '.dart':
|
||||
|
@ -170,7 +171,10 @@ class _FindDartElements extends RecursiveElementVisitor<void> {
|
|||
// implementations as tables.
|
||||
return _isTable.isAssignableFrom(element) &&
|
||||
!_isTable.isExactly(element) &&
|
||||
!_isTableInfo.isAssignableFrom(element);
|
||||
!_isTableInfo.isAssignableFrom(element) &&
|
||||
// Temporary workaround until https://github.com/dart-lang/source_gen/pull/628
|
||||
// gets merged.
|
||||
!element.mixins.any((e) => e.nameIfInterfaceType == 'TableInfo');
|
||||
}
|
||||
|
||||
bool _isDslView(ClassElement element) {
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
import 'package:analyzer/dart/analysis/analysis_context.dart';
|
||||
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
|
||||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/file_system/overlay_file_system.dart';
|
||||
import 'package:analyzer/file_system/physical_file_system.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import '../analysis/backend.dart';
|
||||
import '../analysis/driver/driver.dart';
|
||||
import '../analysis/driver/state.dart';
|
||||
import '../analyzer/options.dart';
|
||||
|
||||
class PhysicalDriftDriver {
|
||||
final DriftAnalysisDriver driver;
|
||||
final AnalysisContextBackend _backend;
|
||||
|
||||
PhysicalDriftDriver(this.driver, this._backend);
|
||||
|
||||
Uri uriFromPath(String path) => _backend.provider.pathContext.toUri(path);
|
||||
|
||||
Future<FileState> analyzeElementsForPath(String path) {
|
||||
return driver.resolveElements(uriFromPath(path));
|
||||
}
|
||||
}
|
||||
|
||||
/// A drift analysis backend deferring Dart analysis to the given [context].
|
||||
class AnalysisContextBackend extends DriftBackend {
|
||||
@override
|
||||
final Logger log = Logger('drift.analysis');
|
||||
|
||||
final AnalysisContext context;
|
||||
|
||||
/// An overlay resource provider, which must also be used by the [context].
|
||||
///
|
||||
/// This will be used to create artificial files used to resolve the type of
|
||||
/// Dart expressions.
|
||||
final OverlayResourceProvider provider;
|
||||
|
||||
AnalysisContextBackend(this.context, this.provider);
|
||||
|
||||
static PhysicalDriftDriver createDriver({
|
||||
DriftOptions options = const DriftOptions.defaults(),
|
||||
ResourceProvider? resourceProvider,
|
||||
required String projectDirectory,
|
||||
}) {
|
||||
final underlyingProvider =
|
||||
resourceProvider ?? PhysicalResourceProvider.INSTANCE;
|
||||
final provider = OverlayResourceProvider(underlyingProvider);
|
||||
|
||||
final contextCollection = AnalysisContextCollection(
|
||||
includedPaths: [projectDirectory],
|
||||
resourceProvider: provider,
|
||||
);
|
||||
final context = contextCollection.contextFor(projectDirectory);
|
||||
|
||||
final backend = AnalysisContextBackend(context, provider);
|
||||
final driver = DriftAnalysisDriver(backend, options);
|
||||
return PhysicalDriftDriver(driver, backend);
|
||||
}
|
||||
|
||||
String? _pathOfUri(Uri uri) {
|
||||
final currentSession = context.currentSession;
|
||||
final path = currentSession.uriConverter.uriToPath(uri);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<AstNode?> loadElementDeclaration(Element element) async {
|
||||
final library = element.library;
|
||||
if (library == null) return null;
|
||||
|
||||
final info =
|
||||
await context.currentSession.getResolvedLibraryByElement(library);
|
||||
if (info is ResolvedLibraryResult) {
|
||||
return info.getElementDeclaration(element)?.node;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> readAsString(Uri uri) {
|
||||
final path = _pathOfUri(uri);
|
||||
if (path == null) return Future.error('Uri $uri could not be resolved');
|
||||
final resourceProvider = context.currentSession.resourceProvider;
|
||||
|
||||
return Future.value(resourceProvider.getFile(path).readAsStringSync());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LibraryElement> readDart(Uri uri) async {
|
||||
final result = await context.currentSession.getLibraryByUri(uri.toString());
|
||||
if (result is LibraryElementResult) {
|
||||
return result.element;
|
||||
}
|
||||
|
||||
throw NotALibraryException(uri);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Expression> resolveExpression(
|
||||
Uri context, String dartExpression, Iterable<String> imports) async {
|
||||
// Create a fake file next to the content
|
||||
final path = _pathOfUri(context)!;
|
||||
final pathContext = provider.pathContext;
|
||||
final pathForTemp = pathContext.join(
|
||||
pathContext.dirname(path), 'moor_temp_${dartExpression.hashCode}.dart');
|
||||
|
||||
final content = StringBuffer();
|
||||
for (final import in imports) {
|
||||
content.writeln('import "$import";');
|
||||
}
|
||||
content.writeln('var e = $dartExpression;');
|
||||
|
||||
provider.setOverlay(
|
||||
pathForTemp,
|
||||
content: content.toString(),
|
||||
modificationStamp: DateTime.now().millisecondsSinceEpoch,
|
||||
);
|
||||
|
||||
try {
|
||||
final result =
|
||||
await this.context.currentSession.getResolvedLibrary(pathForTemp);
|
||||
|
||||
if (result is! ResolvedLibraryResult) {
|
||||
throw CannotReadExpressionException(
|
||||
'Could not resolve temporary helper file');
|
||||
}
|
||||
|
||||
final compilationUnit = result.units.first.unit;
|
||||
|
||||
for (final member in compilationUnit.declarations) {
|
||||
if (member is TopLevelVariableDeclaration) {
|
||||
return member.variables.variables.first.initializer!;
|
||||
}
|
||||
}
|
||||
|
||||
throw CannotReadExpressionException(
|
||||
'Temporary helper file contains no field.');
|
||||
} finally {
|
||||
provider.removeOverlay(pathForTemp);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Uri resolveUri(Uri base, String uriString) {
|
||||
final resolved = base.resolve(uriString);
|
||||
final uriConverter = context.currentSession.uriConverter;
|
||||
|
||||
// Try to make uris consistent by going to path and back
|
||||
final path = uriConverter.uriToPath(resolved);
|
||||
if (path == null) return resolved;
|
||||
|
||||
return uriConverter.pathToUri(path) ?? resolved;
|
||||
}
|
||||
}
|
|
@ -1,16 +1,15 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:analyzer/file_system/physical_file_system.dart';
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:drift_dev/src/backends/common/driver.dart';
|
||||
import 'package:drift_dev/src/cli/project.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import '../backends/analyzer_context_backend.dart';
|
||||
import 'commands/analyze.dart';
|
||||
import 'commands/identify_databases.dart';
|
||||
import 'commands/migrate.dart';
|
||||
import 'commands/schema.dart';
|
||||
//import 'commands/migrate.dart';
|
||||
//import 'commands/schema.dart';
|
||||
import 'logging.dart';
|
||||
|
||||
Future run(List<String> args) async {
|
||||
|
@ -31,14 +30,14 @@ class MoorCli {
|
|||
|
||||
MoorCli() {
|
||||
_runner = CommandRunner(
|
||||
'pub run moor_generator',
|
||||
'CLI utilities for the moor package, currently in an experimental state.',
|
||||
'dart run drift_dev',
|
||||
'CLI utilities for the drift package, currently in an experimental state.',
|
||||
usageLineLength: 80,
|
||||
)
|
||||
..addCommand(AnalyzeCommand(this))
|
||||
..addCommand(IdentifyDatabases(this))
|
||||
..addCommand(SchemaCommand(this))
|
||||
..addCommand(MigrateCommand(this));
|
||||
..addCommand(IdentifyDatabases(this));
|
||||
// ..addCommand(SchemaCommand(this))
|
||||
// ..addCommand(MigrateCommand(this));
|
||||
|
||||
_runner.argParser
|
||||
.addFlag('verbose', abbr: 'v', defaultsTo: false, negatable: false);
|
||||
|
@ -50,9 +49,11 @@ class MoorCli {
|
|||
);
|
||||
}
|
||||
|
||||
Future<MoorDriver> createMoorDriver() async {
|
||||
return MoorDriver(PhysicalResourceProvider.INSTANCE,
|
||||
options: project.moorOptions, contextRoot: project.directory.path);
|
||||
Future<PhysicalDriftDriver> createMoorDriver() async {
|
||||
return AnalysisContextBackend.createDriver(
|
||||
options: project.moorOptions,
|
||||
projectDirectory: project.directory.path,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> run(Iterable<String> args) async {
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../cli.dart';
|
||||
|
||||
class AnalyzeCommand extends MoorCommand {
|
||||
AnalyzeCommand(MoorCli cli) : super(cli);
|
||||
|
||||
@override
|
||||
String get description => 'Analyze and lint moor files';
|
||||
String get description => 'Analyze and lint drift database code';
|
||||
|
||||
@override
|
||||
String get name => 'analyze';
|
||||
|
@ -20,18 +18,14 @@ class AnalyzeCommand extends MoorCommand {
|
|||
var errorCount = 0;
|
||||
|
||||
await for (final file in cli.project.sourceFiles) {
|
||||
if (p.extension(file.path) != '.moor') continue;
|
||||
|
||||
cli.logger.fine('Analyzing $file');
|
||||
|
||||
final parsed = (await driver.waitFileParsed(file.path))!;
|
||||
final results =
|
||||
await driver.driver.fullyAnalyze(driver.uriFromPath(file.path));
|
||||
|
||||
if (parsed.errors.errors.isNotEmpty) {
|
||||
cli.logger.warning('For file ${p.relative(file.path)}:');
|
||||
for (final error in parsed.errors.errors) {
|
||||
error.writeDescription(cli.logger.warning);
|
||||
errorCount++;
|
||||
}
|
||||
for (final error in results.allErrors) {
|
||||
cli.logger.warning(error.toString());
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:drift_dev/src/analyzer/runner/results.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../../analysis/results/results.dart';
|
||||
import '../cli.dart';
|
||||
|
||||
class IdentifyDatabases extends MoorCommand {
|
||||
|
@ -11,7 +11,7 @@ class IdentifyDatabases extends MoorCommand {
|
|||
|
||||
@override
|
||||
String get description =>
|
||||
'Test for the analyzer - list all moor databases in a project';
|
||||
'Test for the analyzer - list all drift databases in a project';
|
||||
|
||||
@override
|
||||
String get name => 'identify-databases';
|
||||
|
@ -28,20 +28,24 @@ class IdentifyDatabases extends MoorCommand {
|
|||
|
||||
cli.logger.fine('Scanning $file');
|
||||
|
||||
final parsed = (await driver.waitFileParsed(file.path))!;
|
||||
final result = parsed.currentResult;
|
||||
final result = await driver.analyzeElementsForPath(file.path);
|
||||
for (final analyzedElement in result.analysis.values) {
|
||||
final element = analyzedElement.result;
|
||||
|
||||
// might be a `part of` file...
|
||||
if (result is! ParsedDartFile) continue;
|
||||
if (element is BaseDriftAccessor) {
|
||||
final message = StringBuffer(
|
||||
'Found ${element.id.name} in ${element.id.libraryUri}!');
|
||||
|
||||
if (result.dbAccessors.isNotEmpty) {
|
||||
final displayName = p.relative(file.path, from: directory.path);
|
||||
if (element is DriftDatabase) {
|
||||
final daos =
|
||||
element.accessorTypes.map((e) => e.toString()).join(', ');
|
||||
message
|
||||
..writeln()
|
||||
..write('Schema version: ${element.schemaVersion}, daos: $daos');
|
||||
}
|
||||
|
||||
final names = result.dbAccessors
|
||||
.map((t) => t.declaration!.fromClass.name)
|
||||
.join(', ');
|
||||
|
||||
cli.logger.info('$displayName has moor databases or daos: $names');
|
||||
cli.logger.info(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,16 +38,14 @@ class DumpSchemaCommand extends Command {
|
|||
final driver = await cli.createMoorDriver();
|
||||
|
||||
final absolute = File(rest[0]).absolute.path;
|
||||
final input = await driver.waitFileParsed(absolute);
|
||||
final input =
|
||||
await driver.driver.fullyAnalyze(driver.uriFromPath(absolute));
|
||||
|
||||
if (input == null || !input.isAnalyzed) {
|
||||
if (!input.isFullyAnalyzed) {
|
||||
cli.exit('Unexpected error: The input file could not be analyzed');
|
||||
}
|
||||
|
||||
final result = input.currentResult;
|
||||
if (result is! ParsedDartFile) {
|
||||
cli.exit('Input file is not a Dart file');
|
||||
}
|
||||
final result = input.fileAnalysis;
|
||||
|
||||
final db = result.declaredDatabases.single;
|
||||
final writer = SchemaWriter(db, options: cli.project.moorOptions);
|
||||
|
|
Loading…
Reference in New Issue