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;
|
import 'package:drift_dev/src/cli/cli.dart' as cli;
|
||||||
|
|
||||||
Future main(List<String> args) {
|
Future main(List<String> args) {
|
||||||
|
|
|
@ -104,6 +104,10 @@ class NoSuchFile extends DiscoveredFileState {
|
||||||
NoSuchFile() : super(const []);
|
NoSuchFile() : super(const []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class UnknownFile extends DiscoveredFileState {
|
||||||
|
UnknownFile() : super(const []);
|
||||||
|
}
|
||||||
|
|
||||||
abstract class DiscoveredElement {
|
abstract class DiscoveredElement {
|
||||||
final DriftElementId ownId;
|
final DriftElementId ownId;
|
||||||
|
|
||||||
|
|
|
@ -300,7 +300,7 @@ class DartTableResolver extends LocalElementResolver<DiscoveredDartTable> {
|
||||||
Future<Iterable<PendingColumnInformation>> _parseColumns(
|
Future<Iterable<PendingColumnInformation>> _parseColumns(
|
||||||
ClassElement element) async {
|
ClassElement element) async {
|
||||||
final columnNames = element.allSupertypes
|
final columnNames = element.allSupertypes
|
||||||
.map((t) => t.element2)
|
.map((t) => t.element)
|
||||||
.followedBy([element])
|
.followedBy([element])
|
||||||
.expand((e) => e.fields)
|
.expand((e) => e.fields)
|
||||||
.where((field) =>
|
.where((field) =>
|
||||||
|
|
|
@ -45,7 +45,7 @@ class DartViewResolver extends LocalElementResolver<DiscoveredDartView> {
|
||||||
|
|
||||||
Future<List<TableReferenceInDartView>> _parseStaticReferences() async {
|
Future<List<TableReferenceInDartView>> _parseStaticReferences() async {
|
||||||
return await Stream.fromIterable(discovered.dartElement.allSupertypes
|
return await Stream.fromIterable(discovered.dartElement.allSupertypes
|
||||||
.map((t) => t.element2)
|
.map((t) => t.element)
|
||||||
.followedBy([discovered.dartElement]).expand((e) => e.fields))
|
.followedBy([discovered.dartElement]).expand((e) => e.fields))
|
||||||
.asyncMap((field) => _getStaticReference(field))
|
.asyncMap((field) => _getStaticReference(field))
|
||||||
.where((ref) => ref != null)
|
.where((ref) => ref != null)
|
||||||
|
@ -68,13 +68,13 @@ class DartViewResolver extends LocalElementResolver<DiscoveredDartView> {
|
||||||
await resolver.driver.backend.loadElementDeclaration(field.getter!);
|
await resolver.driver.backend.loadElementDeclaration(field.getter!);
|
||||||
if (node is MethodDeclaration && node.body is EmptyFunctionBody) {
|
if (node is MethodDeclaration && node.body is EmptyFunctionBody) {
|
||||||
final table = await resolveDartReferenceOrReportError<DriftTable>(
|
final table = await resolveDartReferenceOrReportError<DriftTable>(
|
||||||
type.element2, (msg) {
|
type.element, (msg) {
|
||||||
return DriftAnalysisError.inDartAst(
|
return DriftAnalysisError.inDartAst(
|
||||||
field, node.returnType ?? node.name, msg);
|
field, node.returnType ?? node.name, msg);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (table != null) {
|
if (table != null) {
|
||||||
final name = node.name2.lexeme;
|
final name = node.name.lexeme;
|
||||||
return TableReferenceInDartView(table, name);
|
return TableReferenceInDartView(table, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ class DiscoverStep {
|
||||||
|
|
||||||
Future<void> discover() async {
|
Future<void> discover() async {
|
||||||
final extension = _file.extension;
|
final extension = _file.extension;
|
||||||
|
_file.discovery = UnknownFile();
|
||||||
|
|
||||||
switch (extension) {
|
switch (extension) {
|
||||||
case '.dart':
|
case '.dart':
|
||||||
|
@ -170,7 +171,10 @@ class _FindDartElements extends RecursiveElementVisitor<void> {
|
||||||
// implementations as tables.
|
// implementations as tables.
|
||||||
return _isTable.isAssignableFrom(element) &&
|
return _isTable.isAssignableFrom(element) &&
|
||||||
!_isTable.isExactly(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) {
|
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:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:analyzer/file_system/physical_file_system.dart';
|
|
||||||
import 'package:args/command_runner.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:drift_dev/src/cli/project.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
import '../backends/analyzer_context_backend.dart';
|
||||||
import 'commands/analyze.dart';
|
import 'commands/analyze.dart';
|
||||||
import 'commands/identify_databases.dart';
|
import 'commands/identify_databases.dart';
|
||||||
import 'commands/migrate.dart';
|
//import 'commands/migrate.dart';
|
||||||
import 'commands/schema.dart';
|
//import 'commands/schema.dart';
|
||||||
import 'logging.dart';
|
import 'logging.dart';
|
||||||
|
|
||||||
Future run(List<String> args) async {
|
Future run(List<String> args) async {
|
||||||
|
@ -31,14 +30,14 @@ class MoorCli {
|
||||||
|
|
||||||
MoorCli() {
|
MoorCli() {
|
||||||
_runner = CommandRunner(
|
_runner = CommandRunner(
|
||||||
'pub run moor_generator',
|
'dart run drift_dev',
|
||||||
'CLI utilities for the moor package, currently in an experimental state.',
|
'CLI utilities for the drift package, currently in an experimental state.',
|
||||||
usageLineLength: 80,
|
usageLineLength: 80,
|
||||||
)
|
)
|
||||||
..addCommand(AnalyzeCommand(this))
|
..addCommand(AnalyzeCommand(this))
|
||||||
..addCommand(IdentifyDatabases(this))
|
..addCommand(IdentifyDatabases(this));
|
||||||
..addCommand(SchemaCommand(this))
|
// ..addCommand(SchemaCommand(this))
|
||||||
..addCommand(MigrateCommand(this));
|
// ..addCommand(MigrateCommand(this));
|
||||||
|
|
||||||
_runner.argParser
|
_runner.argParser
|
||||||
.addFlag('verbose', abbr: 'v', defaultsTo: false, negatable: false);
|
.addFlag('verbose', abbr: 'v', defaultsTo: false, negatable: false);
|
||||||
|
@ -50,9 +49,11 @@ class MoorCli {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<MoorDriver> createMoorDriver() async {
|
Future<PhysicalDriftDriver> createMoorDriver() async {
|
||||||
return MoorDriver(PhysicalResourceProvider.INSTANCE,
|
return AnalysisContextBackend.createDriver(
|
||||||
options: project.moorOptions, contextRoot: project.directory.path);
|
options: project.moorOptions,
|
||||||
|
projectDirectory: project.directory.path,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> run(Iterable<String> args) async {
|
Future<void> run(Iterable<String> args) async {
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:path/path.dart' as p;
|
|
||||||
|
|
||||||
import '../cli.dart';
|
import '../cli.dart';
|
||||||
|
|
||||||
class AnalyzeCommand extends MoorCommand {
|
class AnalyzeCommand extends MoorCommand {
|
||||||
AnalyzeCommand(MoorCli cli) : super(cli);
|
AnalyzeCommand(MoorCli cli) : super(cli);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get description => 'Analyze and lint moor files';
|
String get description => 'Analyze and lint drift database code';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get name => 'analyze';
|
String get name => 'analyze';
|
||||||
|
@ -20,18 +18,14 @@ class AnalyzeCommand extends MoorCommand {
|
||||||
var errorCount = 0;
|
var errorCount = 0;
|
||||||
|
|
||||||
await for (final file in cli.project.sourceFiles) {
|
await for (final file in cli.project.sourceFiles) {
|
||||||
if (p.extension(file.path) != '.moor') continue;
|
|
||||||
|
|
||||||
cli.logger.fine('Analyzing $file');
|
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) {
|
for (final error in results.allErrors) {
|
||||||
cli.logger.warning('For file ${p.relative(file.path)}:');
|
cli.logger.warning(error.toString());
|
||||||
for (final error in parsed.errors.errors) {
|
errorCount++;
|
||||||
error.writeDescription(cli.logger.warning);
|
|
||||||
errorCount++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:drift_dev/src/analyzer/runner/results.dart';
|
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
|
||||||
|
import '../../analysis/results/results.dart';
|
||||||
import '../cli.dart';
|
import '../cli.dart';
|
||||||
|
|
||||||
class IdentifyDatabases extends MoorCommand {
|
class IdentifyDatabases extends MoorCommand {
|
||||||
|
@ -11,7 +11,7 @@ class IdentifyDatabases extends MoorCommand {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get description =>
|
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
|
@override
|
||||||
String get name => 'identify-databases';
|
String get name => 'identify-databases';
|
||||||
|
@ -28,20 +28,24 @@ class IdentifyDatabases extends MoorCommand {
|
||||||
|
|
||||||
cli.logger.fine('Scanning $file');
|
cli.logger.fine('Scanning $file');
|
||||||
|
|
||||||
final parsed = (await driver.waitFileParsed(file.path))!;
|
final result = await driver.analyzeElementsForPath(file.path);
|
||||||
final result = parsed.currentResult;
|
for (final analyzedElement in result.analysis.values) {
|
||||||
|
final element = analyzedElement.result;
|
||||||
|
|
||||||
// might be a `part of` file...
|
if (element is BaseDriftAccessor) {
|
||||||
if (result is! ParsedDartFile) continue;
|
final message = StringBuffer(
|
||||||
|
'Found ${element.id.name} in ${element.id.libraryUri}!');
|
||||||
|
|
||||||
if (result.dbAccessors.isNotEmpty) {
|
if (element is DriftDatabase) {
|
||||||
final displayName = p.relative(file.path, from: directory.path);
|
final daos =
|
||||||
|
element.accessorTypes.map((e) => e.toString()).join(', ');
|
||||||
|
message
|
||||||
|
..writeln()
|
||||||
|
..write('Schema version: ${element.schemaVersion}, daos: $daos');
|
||||||
|
}
|
||||||
|
|
||||||
final names = result.dbAccessors
|
cli.logger.info(message);
|
||||||
.map((t) => t.declaration!.fromClass.name)
|
}
|
||||||
.join(', ');
|
|
||||||
|
|
||||||
cli.logger.info('$displayName has moor databases or daos: $names');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,16 +38,14 @@ class DumpSchemaCommand extends Command {
|
||||||
final driver = await cli.createMoorDriver();
|
final driver = await cli.createMoorDriver();
|
||||||
|
|
||||||
final absolute = File(rest[0]).absolute.path;
|
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');
|
cli.exit('Unexpected error: The input file could not be analyzed');
|
||||||
}
|
}
|
||||||
|
|
||||||
final result = input.currentResult;
|
final result = input.fileAnalysis;
|
||||||
if (result is! ParsedDartFile) {
|
|
||||||
cli.exit('Input file is not a Dart file');
|
|
||||||
}
|
|
||||||
|
|
||||||
final db = result.declaredDatabases.single;
|
final db = result.declaredDatabases.single;
|
||||||
final writer = SchemaWriter(db, options: cli.project.moorOptions);
|
final writer = SchemaWriter(db, options: cli.project.moorOptions);
|
||||||
|
|
Loading…
Reference in New Issue