mirror of https://github.com/AMT-Cheif/drift.git
Start generating code from new analysis results
This commit is contained in:
parent
a6caae9d8f
commit
5947956109
|
@ -1,4 +1,5 @@
|
||||||
// This field is analyzed by drift_dev to easily obtain common types.
|
// This field is analyzed by drift_dev to easily obtain common types.
|
||||||
export 'runtime/types/converters.dart' show TypeConverter, JsonTypeConverter;
|
export 'runtime/types/converters.dart' show TypeConverter, JsonTypeConverter;
|
||||||
|
export 'runtime/query_builder/query_builder.dart' show TableInfo;
|
||||||
|
|
||||||
export 'dsl/dsl.dart' show Table, DriftDatabase, DriftAccessor;
|
export 'dsl/dsl.dart' show Table, DriftDatabase, DriftAccessor;
|
||||||
|
|
|
@ -7,10 +7,11 @@ Builder preparingBuilder(BuilderOptions options) => PreprocessBuilder();
|
||||||
|
|
||||||
Builder analyzer(BuilderOptions options) => DriftAnalyzer(options);
|
Builder analyzer(BuilderOptions options) => DriftAnalyzer(options);
|
||||||
|
|
||||||
Builder driftBuilder(BuilderOptions options) => DriftSharedPartBuilder(options);
|
Builder driftBuilder(BuilderOptions options) =>
|
||||||
|
DriftBuilder(DriftGenerationMode.monolithicSharedPart, options);
|
||||||
|
|
||||||
Builder driftBuilderNotShared(BuilderOptions options) =>
|
Builder driftBuilderNotShared(BuilderOptions options) =>
|
||||||
DriftPartBuilder(options);
|
DriftBuilder(DriftGenerationMode.monolithicPart, options);
|
||||||
|
|
||||||
PostProcessBuilder driftCleanup(BuilderOptions options) {
|
PostProcessBuilder driftCleanup(BuilderOptions options) {
|
||||||
return const FileDeletingBuilder(['.temp.dart']);
|
return const FileDeletingBuilder(['.temp.dart']);
|
||||||
|
|
|
@ -2,12 +2,18 @@ import '../results/element.dart';
|
||||||
import 'state.dart';
|
import 'state.dart';
|
||||||
|
|
||||||
class DriftAnalysisCache {
|
class DriftAnalysisCache {
|
||||||
|
final Map<Uri, Map<String, Object?>> serializedElements = {};
|
||||||
final Map<Uri, FileState> knownFiles = {};
|
final Map<Uri, FileState> knownFiles = {};
|
||||||
final Map<DriftElementId, DiscoveredElement> discoveredElements = {};
|
final Map<DriftElementId, DiscoveredElement> discoveredElements = {};
|
||||||
|
|
||||||
|
FileState stateForUri(Uri uri) {
|
||||||
|
return knownFiles[uri] ?? notifyFileChanged(uri);
|
||||||
|
}
|
||||||
|
|
||||||
FileState notifyFileChanged(Uri uri) {
|
FileState notifyFileChanged(Uri uri) {
|
||||||
// todo: Mark references for files that import this one as stale.
|
// todo: Mark references for files that import this one as stale.
|
||||||
// todo: Mark elements that reference an element in this file as stale.
|
// todo: Mark elements that reference an element in this file as stale.
|
||||||
|
serializedElements.remove(uri);
|
||||||
|
|
||||||
return knownFiles.putIfAbsent(uri, () => FileState(uri))
|
return knownFiles.putIfAbsent(uri, () => FileState(uri))
|
||||||
..errorsDuringDiscovery.clear()
|
..errorsDuringDiscovery.clear()
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:sqlparser/sqlparser.dart';
|
import 'package:sqlparser/sqlparser.dart';
|
||||||
|
|
||||||
import '../../analyzer/options.dart';
|
import '../../analyzer/options.dart';
|
||||||
|
@ -7,6 +9,8 @@ import '../resolver/dart/helper.dart';
|
||||||
import '../resolver/discover.dart';
|
import '../resolver/discover.dart';
|
||||||
import '../resolver/drift/sqlparser/mapping.dart';
|
import '../resolver/drift/sqlparser/mapping.dart';
|
||||||
import '../resolver/resolver.dart';
|
import '../resolver/resolver.dart';
|
||||||
|
import '../results/results.dart';
|
||||||
|
import '../serializer.dart';
|
||||||
import 'cache.dart';
|
import 'cache.dart';
|
||||||
import 'error.dart';
|
import 'error.dart';
|
||||||
import 'state.dart';
|
import 'state.dart';
|
||||||
|
@ -17,6 +21,9 @@ class DriftAnalysisDriver {
|
||||||
final DriftOptions options;
|
final DriftOptions options;
|
||||||
|
|
||||||
late final TypeMapping typeMapping = TypeMapping(options);
|
late final TypeMapping typeMapping = TypeMapping(options);
|
||||||
|
late final ElementDeserializer deserializer = ElementDeserializer(this);
|
||||||
|
|
||||||
|
AnalysisResultCacheReader? cacheReader;
|
||||||
|
|
||||||
KnownDriftTypes? _knownTypes;
|
KnownDriftTypes? _knownTypes;
|
||||||
|
|
||||||
|
@ -43,10 +50,45 @@ class DriftAnalysisDriver {
|
||||||
return _knownTypes ??= await KnownDriftTypes.resolve(this);
|
return _knownTypes ??= await KnownDriftTypes.resolve(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<FileState> prepareFileForAnalysis(Uri uri) async {
|
Future<Map<String, Object?>?> readStoredAnalysisResult(Uri uri) async {
|
||||||
|
final cached = cache.serializedElements[uri];
|
||||||
|
if (cached != null) return cached;
|
||||||
|
|
||||||
|
// Not available in in-memory cache, so let's read it from the file system.
|
||||||
|
final reader = cacheReader;
|
||||||
|
if (reader == null) return null;
|
||||||
|
|
||||||
|
final found = await reader.readCacheFor(uri);
|
||||||
|
if (found == null) return null;
|
||||||
|
|
||||||
|
final parsed = json.decode(found) as Map<String, Object?>;
|
||||||
|
return cache.serializedElements[uri] = parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _recoverFromCache(FileState state) async {
|
||||||
|
final stored = await readStoredAnalysisResult(state.ownUri);
|
||||||
|
if (stored == null) return false;
|
||||||
|
|
||||||
|
var allRecovered = true;
|
||||||
|
|
||||||
|
for (final local in stored.keys) {
|
||||||
|
final id = DriftElementId(state.ownUri, local);
|
||||||
|
try {
|
||||||
|
await deserializer.readDriftElement(id);
|
||||||
|
} on CouldNotDeserializeException catch (e, s) {
|
||||||
|
backend.log.fine('Could not deserialize $id', e, s);
|
||||||
|
allRecovered = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allRecovered;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<FileState> prepareFileForAnalysis(Uri uri,
|
||||||
|
{bool needsDiscovery = true}) async {
|
||||||
var known = cache.knownFiles[uri] ?? cache.notifyFileChanged(uri);
|
var known = cache.knownFiles[uri] ?? cache.notifyFileChanged(uri);
|
||||||
|
|
||||||
if (known.discovery == null) {
|
if (known.discovery == null && needsDiscovery) {
|
||||||
await DiscoverStep(this, known).discover();
|
await DiscoverStep(this, known).discover();
|
||||||
cache.postFileDiscoveryResults(known);
|
cache.postFileDiscoveryResults(known);
|
||||||
|
|
||||||
|
@ -93,14 +135,30 @@ class DriftAnalysisDriver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<FileState> fullyAnalyze(Uri uri) async {
|
Future<FileState> resolveElements(Uri uri) async {
|
||||||
var known = cache.knownFiles[uri];
|
var known = cache.stateForUri(uri);
|
||||||
|
await prepareFileForAnalysis(uri, needsDiscovery: false);
|
||||||
|
|
||||||
if (known == null || known.discovery == null) {
|
if (known.isFullyAnalyzed) {
|
||||||
known = await prepareFileForAnalysis(uri);
|
// Well, there's nothing to do now.
|
||||||
|
return known;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final allRecoveredFromCache = await _recoverFromCache(known);
|
||||||
|
if (allRecoveredFromCache) {
|
||||||
|
// We were able to read all elements from cache, so we don't have to
|
||||||
|
// run any analysis now.
|
||||||
|
return known;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We couldn't recover all analyzed elements. Let's run an analysis run
|
||||||
|
// now then.
|
||||||
|
await prepareFileForAnalysis(uri, needsDiscovery: true);
|
||||||
await _analyzePrepared(known);
|
await _analyzePrepared(known);
|
||||||
return known;
|
return known;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class AnalysisResultCacheReader {
|
||||||
|
Future<String?> readCacheFor(Uri uri);
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,12 @@ class FileState {
|
||||||
|
|
||||||
String get extension => url.extension(ownUri.path);
|
String get extension => url.extension(ownUri.path);
|
||||||
|
|
||||||
|
bool get isFullyAnalyzed {
|
||||||
|
return discovery != null &&
|
||||||
|
discovery!.locallyDefinedElements
|
||||||
|
.every((e) => elementIsAnalyzed(e.ownId));
|
||||||
|
}
|
||||||
|
|
||||||
bool elementIsAnalyzed(DriftElementId id) {
|
bool elementIsAnalyzed(DriftElementId id) {
|
||||||
return analysis[id]?.isUpToDate == true;
|
return analysis[id]?.isUpToDate == true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,16 +7,20 @@ import 'package:collection/collection.dart';
|
||||||
import '../../driver/driver.dart';
|
import '../../driver/driver.dart';
|
||||||
|
|
||||||
class KnownDriftTypes {
|
class KnownDriftTypes {
|
||||||
|
final LibraryElement helperLibrary;
|
||||||
final ClassElement tableElement;
|
final ClassElement tableElement;
|
||||||
final InterfaceType tableType;
|
final InterfaceType tableType;
|
||||||
|
final InterfaceType tableInfoType;
|
||||||
final InterfaceType driftDatabase;
|
final InterfaceType driftDatabase;
|
||||||
final InterfaceType driftAccessor;
|
final InterfaceType driftAccessor;
|
||||||
final InterfaceElement typeConverter;
|
final InterfaceElement typeConverter;
|
||||||
final InterfaceElement jsonTypeConverter;
|
final InterfaceElement jsonTypeConverter;
|
||||||
|
|
||||||
KnownDriftTypes._(
|
KnownDriftTypes._(
|
||||||
|
this.helperLibrary,
|
||||||
this.tableElement,
|
this.tableElement,
|
||||||
this.tableType,
|
this.tableType,
|
||||||
|
this.tableInfoType,
|
||||||
this.typeConverter,
|
this.typeConverter,
|
||||||
this.jsonTypeConverter,
|
this.jsonTypeConverter,
|
||||||
this.driftDatabase,
|
this.driftDatabase,
|
||||||
|
@ -32,8 +36,10 @@ class KnownDriftTypes {
|
||||||
final daoElement = exportNamespace.get('DriftAccessor') as ClassElement;
|
final daoElement = exportNamespace.get('DriftAccessor') as ClassElement;
|
||||||
|
|
||||||
return KnownDriftTypes._(
|
return KnownDriftTypes._(
|
||||||
|
helper,
|
||||||
tableElement,
|
tableElement,
|
||||||
tableElement.defaultInstantiation,
|
tableElement.defaultInstantiation,
|
||||||
|
(exportNamespace.get('TableInfo') as InterfaceElement).thisType,
|
||||||
exportNamespace.get('TypeConverter') as InterfaceElement,
|
exportNamespace.get('TypeConverter') as InterfaceElement,
|
||||||
exportNamespace.get('JsonTypeConverter') as InterfaceElement,
|
exportNamespace.get('JsonTypeConverter') as InterfaceElement,
|
||||||
dbElement.defaultInstantiation,
|
dbElement.defaultInstantiation,
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:analyzer/dart/ast/ast.dart' as dart;
|
import 'package:analyzer/dart/ast/ast.dart' as dart;
|
||||||
import 'package:analyzer/dart/element/element.dart';
|
import 'package:analyzer/dart/element/element.dart';
|
||||||
import 'package:analyzer/dart/element/visitor.dart';
|
import 'package:analyzer/dart/element/visitor.dart';
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
import 'package:recase/recase.dart';
|
import 'package:recase/recase.dart';
|
||||||
import 'package:source_gen/source_gen.dart';
|
import 'package:source_gen/source_gen.dart';
|
||||||
import 'package:sqlparser/sqlparser.dart' hide AnalysisError;
|
import 'package:sqlparser/sqlparser.dart' hide AnalysisError;
|
||||||
|
@ -52,22 +53,24 @@ class DiscoverStep {
|
||||||
Future<void> discover() async {
|
Future<void> discover() async {
|
||||||
final extension = _file.extension;
|
final extension = _file.extension;
|
||||||
|
|
||||||
|
debugger(when: _file.ownUri.path.endsWith('todos.dart'));
|
||||||
|
|
||||||
switch (extension) {
|
switch (extension) {
|
||||||
case '.dart':
|
case '.dart':
|
||||||
|
LibraryElement library;
|
||||||
try {
|
try {
|
||||||
final library = await _driver.backend.readDart(_file.ownUri);
|
library = await _driver.backend.readDart(_file.ownUri);
|
||||||
final finder =
|
} catch (e) {
|
||||||
_FindDartElements(this, library, await _driver.loadKnownTypes());
|
|
||||||
await finder.find();
|
|
||||||
|
|
||||||
_file.errorsDuringDiscovery.addAll(finder.errors);
|
|
||||||
_file.discovery =
|
|
||||||
DiscoveredDartLibrary(library, _checkForDuplicates(finder.found));
|
|
||||||
} catch (e, s) {
|
|
||||||
_driver.backend.log
|
|
||||||
.fine('Could not read Dart library from ${_file.ownUri}', e, s);
|
|
||||||
_file.discovery = NotADartLibrary();
|
_file.discovery = NotADartLibrary();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
final finder =
|
||||||
|
_FindDartElements(this, library, await _driver.loadKnownTypes());
|
||||||
|
await finder.find();
|
||||||
|
|
||||||
|
_file.errorsDuringDiscovery.addAll(finder.errors);
|
||||||
|
_file.discovery =
|
||||||
|
DiscoveredDartLibrary(library, _checkForDuplicates(finder.found));
|
||||||
break;
|
break;
|
||||||
case '.drift':
|
case '.drift':
|
||||||
final engine = _driver.newSqlEngine();
|
final engine = _driver.newSqlEngine();
|
||||||
|
@ -141,7 +144,7 @@ class _FindDartElements extends RecursiveElementVisitor<void> {
|
||||||
final DiscoverStep _discoverStep;
|
final DiscoverStep _discoverStep;
|
||||||
final LibraryElement _library;
|
final LibraryElement _library;
|
||||||
|
|
||||||
final TypeChecker _isTable, _isDatabase, _isDao;
|
final TypeChecker _isTable, _isTableInfo, _isDatabase, _isDao;
|
||||||
|
|
||||||
final List<Future<void>> _pendingWork = [];
|
final List<Future<void>> _pendingWork = [];
|
||||||
|
|
||||||
|
@ -151,6 +154,7 @@ class _FindDartElements extends RecursiveElementVisitor<void> {
|
||||||
_FindDartElements(
|
_FindDartElements(
|
||||||
this._discoverStep, this._library, KnownDriftTypes knownTypes)
|
this._discoverStep, this._library, KnownDriftTypes knownTypes)
|
||||||
: _isTable = TypeChecker.fromStatic(knownTypes.tableType),
|
: _isTable = TypeChecker.fromStatic(knownTypes.tableType),
|
||||||
|
_isTableInfo = TypeChecker.fromStatic(knownTypes.tableInfoType),
|
||||||
_isDatabase = TypeChecker.fromStatic(knownTypes.driftDatabase),
|
_isDatabase = TypeChecker.fromStatic(knownTypes.driftDatabase),
|
||||||
_isDao = TypeChecker.fromStatic(knownTypes.driftAccessor);
|
_isDao = TypeChecker.fromStatic(knownTypes.driftAccessor);
|
||||||
|
|
||||||
|
@ -159,9 +163,20 @@ class _FindDartElements extends RecursiveElementVisitor<void> {
|
||||||
await Future.wait(_pendingWork);
|
await Future.wait(_pendingWork);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isDslTable(ClassElement element) {
|
||||||
|
// check if the table inherits from the drift table class. The !isExactly
|
||||||
|
// check is here because we run this generator on drift itself and we get
|
||||||
|
// weird errors for the Table class itself. In weird cases where we iterate
|
||||||
|
// over generated code (standalone tool), don't report existing
|
||||||
|
// implementations as tables.
|
||||||
|
return _isTable.isAssignableFrom(element) &&
|
||||||
|
!_isTable.isExactly(element) &&
|
||||||
|
!_isTableInfo.isAssignableFrom(element);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void visitClassElement(ClassElement element) {
|
void visitClassElement(ClassElement element) {
|
||||||
if (_isTable.isAssignableFrom(element)) {
|
if (_isDslTable(element)) {
|
||||||
_pendingWork.add(Future.sync(() async {
|
_pendingWork.add(Future.sync(() async {
|
||||||
final name = await _sqlNameOfTable(element);
|
final name = await _sqlNameOfTable(element);
|
||||||
final id = _discoverStep._id(name);
|
final id = _discoverStep._id(name);
|
||||||
|
|
|
@ -41,7 +41,7 @@ class AnnotatedDartCode {
|
||||||
|
|
||||||
return AnnotatedDartCode([
|
return AnnotatedDartCode([
|
||||||
for (final part in serializedElements)
|
for (final part in serializedElements)
|
||||||
if (part is Map) DartTopLevelSymbol.fromJson(json) else part as String
|
if (part is Map) DartTopLevelSymbol.fromJson(part) else part as String
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import 'dart:convert' as convert;
|
|
||||||
|
|
||||||
import 'package:analyzer/dart/element/element.dart';
|
import 'package:analyzer/dart/element/element.dart';
|
||||||
import 'package:analyzer/dart/element/nullability_suffix.dart';
|
import 'package:analyzer/dart/element/nullability_suffix.dart';
|
||||||
import 'package:analyzer/dart/element/type.dart';
|
import 'package:analyzer/dart/element/type.dart';
|
||||||
import 'package:analyzer/dart/element/type_provider.dart';
|
|
||||||
import 'package:analyzer/dart/element/type_system.dart';
|
|
||||||
import 'package:analyzer/dart/element/type_visitor.dart';
|
import 'package:analyzer/dart/element/type_visitor.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:drift/drift.dart' show DriftSqlType, UpdateKind;
|
import 'package:drift/drift.dart' show DriftSqlType, UpdateKind;
|
||||||
import 'package:sqlparser/sqlparser.dart' show ReferenceAction;
|
import 'package:sqlparser/sqlparser.dart' show ReferenceAction;
|
||||||
|
|
||||||
|
import 'driver/driver.dart';
|
||||||
|
import 'driver/state.dart';
|
||||||
import 'results/results.dart';
|
import 'results/results.dart';
|
||||||
|
|
||||||
class ElementSerializer {
|
class ElementSerializer {
|
||||||
|
@ -260,38 +258,32 @@ class _DartTypeSerializer extends TypeVisitor<Map<String, Object?>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ElementDeserializer {
|
class ElementDeserializer {
|
||||||
final Map<Uri, Map<String, Object?>> _loadedJson = {};
|
|
||||||
final Map<DriftElementId, DriftElement> _deserializedElements = {};
|
|
||||||
final Map<Uri, LibraryElement> _loadedLibraries = {};
|
final Map<Uri, LibraryElement> _loadedLibraries = {};
|
||||||
|
|
||||||
final TypeProvider defaultTypeProvider;
|
final DriftAnalysisDriver driver;
|
||||||
final TypeSystem typeSystem;
|
|
||||||
|
|
||||||
ElementDeserializer(this.defaultTypeProvider, this.typeSystem);
|
ElementDeserializer(this.driver);
|
||||||
|
|
||||||
/// Loads the serialized definitions of all elements with a
|
|
||||||
/// [DriftElementId.libraryUri] matching the [uri].
|
|
||||||
Future<String> loadStateForUri(Uri uri);
|
|
||||||
|
|
||||||
Future<LibraryElement> loadDartLibrary(Uri uri);
|
|
||||||
|
|
||||||
Future<DartType> _readDartType(Map json) async {
|
Future<DartType> _readDartType(Map json) async {
|
||||||
final suffix = NullabilitySuffix.values.byName(json['suffix'] as String);
|
final suffix = NullabilitySuffix.values.byName(json['suffix'] as String);
|
||||||
|
final helper = await driver.loadKnownTypes();
|
||||||
|
|
||||||
|
final typeProvider = helper.helperLibrary.typeProvider;
|
||||||
|
|
||||||
switch (json['kind'] as String) {
|
switch (json['kind'] as String) {
|
||||||
case 'dynamic':
|
case 'dynamic':
|
||||||
return defaultTypeProvider.dynamicType;
|
return typeProvider.dynamicType;
|
||||||
case 'Never':
|
case 'Never':
|
||||||
return suffix == NullabilitySuffix.none
|
return suffix == NullabilitySuffix.none
|
||||||
? defaultTypeProvider.neverType
|
? typeProvider.neverType
|
||||||
: defaultTypeProvider.nullType;
|
: typeProvider.nullType;
|
||||||
case 'void':
|
case 'void':
|
||||||
return defaultTypeProvider.voidType;
|
return typeProvider.voidType;
|
||||||
case 'interface':
|
case 'interface':
|
||||||
final libraryUri = Uri.parse(json['library'] as String);
|
final libraryUri = Uri.parse(json['library'] as String);
|
||||||
final lib =
|
final lib = _loadedLibraries[libraryUri] ??=
|
||||||
_loadedLibraries[libraryUri] ??= await loadDartLibrary(libraryUri);
|
await driver.backend.readDart(libraryUri);
|
||||||
final element = lib.exportNamespace.get(json['element'] as String)
|
final element = lib.exportNamespace.get(json['element'] as String)
|
||||||
as InterfaceElement;
|
as InterfaceElement;
|
||||||
final instantiation = [
|
final instantiation = [
|
||||||
|
@ -306,14 +298,33 @@ abstract class ElementDeserializer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<DriftElement> _readElementReference(Map json) async {
|
Future<DriftElement> _readElementReference(Map json) {
|
||||||
final id = DriftElementId.fromJson(json);
|
return readDriftElement(DriftElementId.fromJson(json));
|
||||||
|
}
|
||||||
|
|
||||||
final data = _loadedJson[id.libraryUri] ??= convert.json
|
Future<DriftElement> readDriftElement(DriftElementId id) async {
|
||||||
.decode(await loadStateForUri(id.libraryUri)) as Map<String, Object?>;
|
final state = driver.cache.stateForUri(id.libraryUri).analysis[id] ??=
|
||||||
|
ElementAnalysisState(id);
|
||||||
|
if (state.result != null && state.isUpToDate) {
|
||||||
|
return state.result!;
|
||||||
|
}
|
||||||
|
|
||||||
return _deserializedElements[id] ??=
|
final data = await driver.readStoredAnalysisResult(id.libraryUri);
|
||||||
await _readDriftElement(data[id.name] as Map);
|
if (data == null) {
|
||||||
|
throw CouldNotDeserializeException(
|
||||||
|
'Analysis data for ${id..libraryUri} not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = await _readDriftElement(data[id.name] as Map);
|
||||||
|
state
|
||||||
|
..result = result
|
||||||
|
..isUpToDate = true;
|
||||||
|
return result;
|
||||||
|
} catch (e, s) {
|
||||||
|
throw CouldNotDeserializeException(
|
||||||
|
'Internal error while deserializing $id: $e at \n$s');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<DriftColumn> _readDriftColumnReference(Map json) async {
|
Future<DriftColumn> _readDriftColumnReference(Map json) async {
|
||||||
|
@ -439,7 +450,54 @@ abstract class ElementDeserializer {
|
||||||
: null,
|
: null,
|
||||||
source: source,
|
source: source,
|
||||||
);
|
);
|
||||||
|
case 'database':
|
||||||
|
case 'dao':
|
||||||
|
final referenceById = {
|
||||||
|
for (final reference in references) reference.id: reference,
|
||||||
|
};
|
||||||
|
|
||||||
|
final tables = [
|
||||||
|
for (final tableId in json['tables'])
|
||||||
|
referenceById[DriftElementId.fromJson(tableId as Map)] as DriftTable
|
||||||
|
];
|
||||||
|
final views = [
|
||||||
|
for (final tableId in json['views'])
|
||||||
|
referenceById[DriftElementId.fromJson(tableId as Map)] as DriftView
|
||||||
|
];
|
||||||
|
final includes =
|
||||||
|
(json['includes'] as List).cast<String>().map(Uri.parse).toList();
|
||||||
|
final queries = (json['views'] as List)
|
||||||
|
.cast<Map>()
|
||||||
|
.map(QueryOnAccessor.fromJson)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (type == 'database') {
|
||||||
|
return DriftDatabase(
|
||||||
|
id: id,
|
||||||
|
declaration: declaration,
|
||||||
|
declaredTables: tables,
|
||||||
|
declaredViews: views,
|
||||||
|
declaredIncludes: includes,
|
||||||
|
declaredQueries: queries,
|
||||||
|
schemaVersion: json['schema_version'] as int,
|
||||||
|
accessorTypes: [
|
||||||
|
for (final dao in json['daos'])
|
||||||
|
AnnotatedDartCode.fromJson(dao as Map)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
assert(type == 'dao');
|
||||||
|
|
||||||
|
return DatabaseAccessor(
|
||||||
|
id: id,
|
||||||
|
declaration: declaration,
|
||||||
|
declaredTables: tables,
|
||||||
|
declaredViews: views,
|
||||||
|
declaredIncludes: includes,
|
||||||
|
declaredQueries: queries,
|
||||||
|
databaseClass: AnnotatedDartCode.fromJson(json['database'] as Map),
|
||||||
|
);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw UnimplementedError('Unsupported element type: $type');
|
throw UnimplementedError('Unsupported element type: $type');
|
||||||
}
|
}
|
||||||
|
@ -512,3 +570,12 @@ abstract class ElementDeserializer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CouldNotDeserializeException implements Exception {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
const CouldNotDeserializeException(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => message;
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'package:build/build.dart';
|
||||||
import '../../analysis/driver/driver.dart';
|
import '../../analysis/driver/driver.dart';
|
||||||
import '../../analysis/serializer.dart';
|
import '../../analysis/serializer.dart';
|
||||||
import '../../analyzer/options.dart';
|
import '../../analyzer/options.dart';
|
||||||
import 'new_backend.dart';
|
import 'backend.dart';
|
||||||
|
|
||||||
class DriftAnalyzer extends Builder {
|
class DriftAnalyzer extends Builder {
|
||||||
final DriftOptions options;
|
final DriftOptions options;
|
||||||
|
@ -24,13 +24,25 @@ class DriftAnalyzer extends Builder {
|
||||||
final backend = DriftBuildBackend(buildStep);
|
final backend = DriftBuildBackend(buildStep);
|
||||||
final driver = DriftAnalysisDriver(backend, options);
|
final driver = DriftAnalysisDriver(backend, options);
|
||||||
|
|
||||||
final results = await driver.fullyAnalyze(buildStep.inputId.uri);
|
final results = await driver.resolveElements(buildStep.inputId.uri);
|
||||||
|
|
||||||
final serializer = ElementSerializer();
|
for (final parseError in results.errorsDuringDiscovery) {
|
||||||
final asJson = serializer.serializeElements(
|
log.warning(parseError.toString());
|
||||||
results.analysis.values.map((e) => e.result).whereType());
|
}
|
||||||
final serialized = JsonUtf8Encoder(' ' * 2).convert(asJson);
|
|
||||||
|
|
||||||
await buildStep.writeAsBytes(buildStep.allowedOutputs.single, serialized);
|
if (results.analysis.isNotEmpty) {
|
||||||
|
for (final result in results.analysis.values) {
|
||||||
|
for (final error in result.errorsDuringAnalysis) {
|
||||||
|
log.warning(error.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final serializer = ElementSerializer();
|
||||||
|
final asJson = serializer.serializeElements(
|
||||||
|
results.analysis.values.map((e) => e.result).whereType());
|
||||||
|
final serialized = JsonUtf8Encoder(' ' * 2).convert(asJson);
|
||||||
|
|
||||||
|
await buildStep.writeAsBytes(buildStep.allowedOutputs.single, serialized);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:build/build.dart';
|
||||||
import 'package:build/build.dart' as build;
|
import 'package:build/build.dart' as build;
|
||||||
|
|
||||||
import '../../analysis/backend.dart';
|
import '../../analysis/backend.dart';
|
||||||
|
import '../../analysis/driver/driver.dart';
|
||||||
|
|
||||||
class DriftBuildBackend extends DriftBackend {
|
class DriftBuildBackend extends DriftBackend {
|
||||||
final BuildStep _buildStep;
|
final BuildStep _buildStep;
|
||||||
|
@ -41,3 +42,20 @@ class DriftBuildBackend extends DriftBackend {
|
||||||
return _buildStep.resolver.astNodeFor(element, resolve: true);
|
return _buildStep.resolver.astNodeFor(element, resolve: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class BuildCacheReader implements AnalysisResultCacheReader {
|
||||||
|
final BuildStep _buildStep;
|
||||||
|
|
||||||
|
BuildCacheReader(this._buildStep);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> readCacheFor(Uri uri) async {
|
||||||
|
// These files are generated by the `DriftAnalyzer` builder
|
||||||
|
final assetId = AssetId.resolve(uri).addExtension('.drift_module.json');
|
||||||
|
if (await _buildStep.canRead(assetId)) {
|
||||||
|
return _buildStep.readAsString(assetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,108 +0,0 @@
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:analyzer/dart/ast/ast.dart';
|
|
||||||
import 'package:analyzer/dart/element/element.dart';
|
|
||||||
import 'package:build/build.dart' hide log;
|
|
||||||
import 'package:build/build.dart' as build show log;
|
|
||||||
import 'package:drift_dev/src/analyzer/options.dart';
|
|
||||||
import 'package:drift_dev/src/backends/backend.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
|
|
||||||
import '../../analysis/preprocess_drift.dart';
|
|
||||||
|
|
||||||
class BuildBackend extends Backend {
|
|
||||||
final DriftOptions options;
|
|
||||||
|
|
||||||
BuildBackend([this.options = const DriftOptions.defaults()]);
|
|
||||||
|
|
||||||
BuildBackendTask createTask(BuildStep step) {
|
|
||||||
return BuildBackendTask(step, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Uri resolve(Uri base, String import) {
|
|
||||||
final from = AssetId.resolve(base);
|
|
||||||
return AssetId.resolve(Uri.parse(import), from: from).uri;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BuildBackendTask extends BackendTask {
|
|
||||||
final BuildStep step;
|
|
||||||
final BuildBackend backend;
|
|
||||||
|
|
||||||
BuildBackendTask(this.step, this.backend);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Uri get entrypoint => step.inputId.uri;
|
|
||||||
|
|
||||||
AssetId _resolve(Uri uri) {
|
|
||||||
return AssetId.resolve(uri, from: step.inputId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<AstNode?> loadElementDeclaration(Element element) async {
|
|
||||||
return await step.resolver.astNodeFor(element, resolve: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String> readMoor(Uri uri) {
|
|
||||||
return step.readAsString(_resolve(uri));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<LibraryElement> resolveDart(Uri uri) async {
|
|
||||||
try {
|
|
||||||
final asset = _resolve(uri);
|
|
||||||
return await step.resolver.libraryFor(asset);
|
|
||||||
} on NonLibraryAssetException catch (_) {
|
|
||||||
throw NotALibraryException(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Logger get log => build.log;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> exists(Uri uri) {
|
|
||||||
return step.canRead(_resolve(uri));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Expression> resolveExpression(
|
|
||||||
Uri context, String dartExpression, Iterable<String> imports) async {
|
|
||||||
// we try to detect all calls of resolveTypeOf in an earlier builder and
|
|
||||||
// prepare the result. See PreprocessBuilder for details
|
|
||||||
final preparedHelperFile =
|
|
||||||
_resolve(context).changeExtension('.drift_prep.json');
|
|
||||||
final temporaryFile = _resolve(context).changeExtension('.temp.dart');
|
|
||||||
|
|
||||||
if (!await step.canRead(preparedHelperFile)) {
|
|
||||||
throw CannotReadExpressionException('Generated helper file not found. '
|
|
||||||
'Check the build log for earlier errors.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: Cache this step?
|
|
||||||
final content = await step.readAsString(preparedHelperFile);
|
|
||||||
final json = DriftPreprocessorResult.fromJson(
|
|
||||||
jsonDecode(content) as Map<String, Object?>);
|
|
||||||
|
|
||||||
final fieldName = json.inlineDartExpressionsToHelperField[dartExpression];
|
|
||||||
if (fieldName == null) {
|
|
||||||
throw CannotReadExpressionException(
|
|
||||||
'Generated helper file does not contain '
|
|
||||||
'$dartExpression!');
|
|
||||||
}
|
|
||||||
|
|
||||||
final library = await step.resolver.libraryFor(temporaryFile);
|
|
||||||
final field = library.units.first.topLevelVariables
|
|
||||||
.firstWhere((element) => element.name == fieldName);
|
|
||||||
final fieldAst = await step.resolver.astNodeFor(field, resolve: true);
|
|
||||||
|
|
||||||
final initializer = (fieldAst as VariableDeclaration).initializer;
|
|
||||||
if (initializer == null) {
|
|
||||||
throw CannotReadExpressionException(
|
|
||||||
'Malformed helper file, this should never happen');
|
|
||||||
}
|
|
||||||
return initializer;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +1,8 @@
|
||||||
import 'package:build/build.dart';
|
import 'package:build/build.dart';
|
||||||
import 'package:drift_dev/src/analyzer/options.dart';
|
|
||||||
import 'package:drift_dev/src/analyzer/runner/file_graph.dart';
|
import '../../analysis/driver/driver.dart';
|
||||||
import 'package:drift_dev/src/analyzer/runner/results.dart';
|
import '../../analyzer/options.dart';
|
||||||
import 'package:drift_dev/src/analyzer/runner/task.dart';
|
import 'backend.dart';
|
||||||
import 'package:drift_dev/src/analyzer/session.dart';
|
|
||||||
import 'package:drift_dev/src/backends/build/build_backend.dart';
|
|
||||||
import 'package:drift_dev/src/backends/build/generators/dao_generator.dart';
|
|
||||||
import 'package:drift_dev/src/backends/build/generators/database_generator.dart';
|
|
||||||
import 'package:drift_dev/writer.dart';
|
|
||||||
import 'package:source_gen/source_gen.dart';
|
|
||||||
|
|
||||||
class _BuilderFlags {
|
class _BuilderFlags {
|
||||||
bool didWarnAboutDeprecatedOptions = false;
|
bool didWarnAboutDeprecatedOptions = false;
|
||||||
|
@ -16,68 +10,48 @@ class _BuilderFlags {
|
||||||
|
|
||||||
final _flags = Resource(() => _BuilderFlags());
|
final _flags = Resource(() => _BuilderFlags());
|
||||||
|
|
||||||
mixin DriftBuilder on Builder {
|
enum DriftGenerationMode {
|
||||||
DriftOptions get options;
|
/// Generate a shart part file which `source_gen:combining_builder` will then
|
||||||
|
/// pick up to generate a part for the input file.
|
||||||
|
///
|
||||||
|
/// Drift will generate a single part file for the main database file and each
|
||||||
|
/// DAO-defining file.
|
||||||
|
monolithicSharedPart,
|
||||||
|
|
||||||
Writer createWriter() {
|
/// Like [monolithicSharedPart], except that drift will generate a single
|
||||||
return Writer(options);
|
/// part file on its own instead of generating a part file for `source_gen`
|
||||||
}
|
/// to process later.
|
||||||
|
monolithicPart,
|
||||||
Future<ParsedDartFile> analyzeDartFile(BuildStep step) async {
|
|
||||||
Task? task;
|
|
||||||
FoundFile input;
|
|
||||||
try {
|
|
||||||
final backend = BuildBackend(options);
|
|
||||||
final backendTask = backend.createTask(step);
|
|
||||||
final session = MoorSession(backend, options: options);
|
|
||||||
|
|
||||||
input = session.registerFile(step.inputId.uri);
|
|
||||||
task = session.startTask(backendTask);
|
|
||||||
await task.runTask();
|
|
||||||
} finally {
|
|
||||||
task?.printErrors();
|
|
||||||
}
|
|
||||||
|
|
||||||
return input.currentResult as ParsedDartFile;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
T _createBuilder<T extends DriftBuilder>(
|
class DriftBuilder extends Builder {
|
||||||
BuilderOptions options,
|
|
||||||
T Function(List<Generator> generators, DriftOptions parsedOptions) creator,
|
|
||||||
) {
|
|
||||||
final parsedOptions = DriftOptions.fromJson(options.config);
|
|
||||||
|
|
||||||
final generators = <Generator>[
|
|
||||||
DriftDatabaseGenerator(),
|
|
||||||
DaoGenerator(),
|
|
||||||
];
|
|
||||||
|
|
||||||
final builder = creator(generators, parsedOptions);
|
|
||||||
|
|
||||||
for (final generator in generators.cast<BaseGenerator>()) {
|
|
||||||
generator.builder = builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
class DriftSharedPartBuilder extends SharedPartBuilder with DriftBuilder {
|
|
||||||
@override
|
|
||||||
final DriftOptions options;
|
final DriftOptions options;
|
||||||
|
final DriftGenerationMode generationMode;
|
||||||
|
|
||||||
DriftSharedPartBuilder._(
|
DriftBuilder._(this.options, this.generationMode);
|
||||||
List<Generator> generators, String name, this.options)
|
|
||||||
: super(generators, name);
|
|
||||||
|
|
||||||
factory DriftSharedPartBuilder(BuilderOptions options) {
|
factory DriftBuilder(
|
||||||
return _createBuilder(options, (generators, parsedOptions) {
|
DriftGenerationMode generationMode, BuilderOptions options) {
|
||||||
return DriftSharedPartBuilder._(generators, 'drift', parsedOptions);
|
final parsedOptions = DriftOptions.fromJson(options.config);
|
||||||
});
|
return DriftBuilder._(parsedOptions, generationMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future build(BuildStep buildStep) async {
|
Map<String, List<String>> get buildExtensions {
|
||||||
|
switch (generationMode) {
|
||||||
|
case DriftGenerationMode.monolithicSharedPart:
|
||||||
|
return {
|
||||||
|
'.dart': ['.drift.g.part']
|
||||||
|
};
|
||||||
|
case DriftGenerationMode.monolithicPart:
|
||||||
|
return {
|
||||||
|
'.dart': ['.drift.dart']
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> build(BuildStep buildStep) async {
|
||||||
final flags = await buildStep.fetchResource(_flags);
|
final flags = await buildStep.fetchResource(_flags);
|
||||||
if (!flags.didWarnAboutDeprecatedOptions &&
|
if (!flags.didWarnAboutDeprecatedOptions &&
|
||||||
options.enabledDeprecatedOption) {
|
options.enabledDeprecatedOption) {
|
||||||
|
@ -87,24 +61,26 @@ class DriftSharedPartBuilder extends SharedPartBuilder with DriftBuilder {
|
||||||
flags.didWarnAboutDeprecatedOptions = true;
|
flags.didWarnAboutDeprecatedOptions = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.build(buildStep);
|
final driver = DriftAnalysisDriver(DriftBuildBackend(buildStep), options)
|
||||||
|
..cacheReader = BuildCacheReader(buildStep);
|
||||||
|
|
||||||
|
final fromCache =
|
||||||
|
await driver.readStoredAnalysisResult(buildStep.inputId.uri);
|
||||||
|
|
||||||
|
if (fromCache == null) {
|
||||||
|
// Don't do anything! There are no analysis results for this file, so
|
||||||
|
// there's nothing for drift to generate code for.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final result = await driver.resolveElements(buildStep.inputId.uri);
|
||||||
|
|
||||||
|
final buffer = StringBuffer();
|
||||||
|
for (final element in result.analysis.values) {
|
||||||
|
buffer.writeln('// ${element.ownId}');
|
||||||
|
}
|
||||||
|
|
||||||
|
await buildStep.writeAsString(
|
||||||
|
buildStep.allowedOutputs.single, buffer.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DriftPartBuilder extends PartBuilder with DriftBuilder {
|
|
||||||
@override
|
|
||||||
final DriftOptions options;
|
|
||||||
|
|
||||||
DriftPartBuilder._(List<Generator> generators, String extension, this.options)
|
|
||||||
: super(generators, extension);
|
|
||||||
|
|
||||||
factory DriftPartBuilder(BuilderOptions options) {
|
|
||||||
return _createBuilder(options, (generators, parsedOptions) {
|
|
||||||
return DriftPartBuilder._(generators, '.drift.dart', parsedOptions);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class BaseGenerator {
|
|
||||||
late DriftBuilder builder;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
import 'package:build/build.dart';
|
|
||||||
import 'package:drift_dev/src/backends/build/drift_builder.dart';
|
|
||||||
import 'package:drift_dev/src/utils/type_utils.dart';
|
|
||||||
import 'package:drift_dev/writer.dart';
|
|
||||||
import 'package:source_gen/source_gen.dart';
|
|
||||||
|
|
||||||
import '../../../model/base_entity.dart';
|
|
||||||
|
|
||||||
class DaoGenerator extends Generator implements BaseGenerator {
|
|
||||||
@override
|
|
||||||
late DriftBuilder builder;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String> generate(LibraryReader library, BuildStep buildStep) async {
|
|
||||||
final parsed = await builder.analyzeDartFile(buildStep);
|
|
||||||
final writer = builder.createWriter();
|
|
||||||
|
|
||||||
for (final dao in parsed.declaredDaos) {
|
|
||||||
final classScope = writer.child();
|
|
||||||
final element = dao.fromClass;
|
|
||||||
|
|
||||||
final daoName = element!.displayName;
|
|
||||||
|
|
||||||
final dbTypeName = dao.dbClass.codeString();
|
|
||||||
classScope.leaf().write('mixin _\$${daoName}Mixin on '
|
|
||||||
'DatabaseAccessor<$dbTypeName> {\n');
|
|
||||||
|
|
||||||
for (final entity in dao.entities.whereType<DriftEntityWithResultSet>()) {
|
|
||||||
final infoType = entity.entityInfoName;
|
|
||||||
final getterName = entity.dbGetterName;
|
|
||||||
classScope.leaf().write(
|
|
||||||
'$infoType get $getterName => attachedDatabase.$getterName;\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
dao.queries
|
|
||||||
?.forEach((query) => QueryWriter(classScope.child()).write(query));
|
|
||||||
|
|
||||||
classScope.leaf().write('}');
|
|
||||||
}
|
|
||||||
|
|
||||||
return writer.writeGenerated();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
import 'package:build/build.dart';
|
|
||||||
import 'package:drift_dev/src/backends/build/drift_builder.dart';
|
|
||||||
import 'package:drift_dev/writer.dart';
|
|
||||||
import 'package:pub_semver/pub_semver.dart';
|
|
||||||
import 'package:source_gen/source_gen.dart';
|
|
||||||
|
|
||||||
final _minLanguageVersion = Version(2, 12, 0);
|
|
||||||
|
|
||||||
class DriftDatabaseGenerator extends Generator implements BaseGenerator {
|
|
||||||
@override
|
|
||||||
late DriftBuilder builder;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String> generate(LibraryReader library, BuildStep buildStep) async {
|
|
||||||
final parsed = await builder.analyzeDartFile(buildStep);
|
|
||||||
final writer = builder.createWriter();
|
|
||||||
|
|
||||||
if (parsed.declaredDatabases.isNotEmpty) {
|
|
||||||
const ignore = '// ignore_for_file: type=lint';
|
|
||||||
writer.leaf().writeln(ignore);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final db in parsed.declaredDatabases) {
|
|
||||||
DatabaseWriter(db, writer.child()).write();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsed.declaredDatabases.isNotEmpty) {
|
|
||||||
// Warn if the project uses an SDK version that is incompatible with what
|
|
||||||
// drift generates.
|
|
||||||
final version = library.element.languageVersion.effective;
|
|
||||||
final major = version.major;
|
|
||||||
final minor = version.minor;
|
|
||||||
|
|
||||||
if (version < _minLanguageVersion) {
|
|
||||||
log.warning(
|
|
||||||
'The language version of this file is Dart $major.$minor. '
|
|
||||||
'Drift generates code for Dart $_minLanguageVersion or later. Please '
|
|
||||||
'consider raising the minimum SDK version in your pubspec.yaml to at '
|
|
||||||
'least $_minLanguageVersion.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return writer.writeGenerated();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ import 'dart:convert';
|
||||||
import 'package:build/build.dart';
|
import 'package:build/build.dart';
|
||||||
|
|
||||||
import '../../analysis/preprocess_drift.dart';
|
import '../../analysis/preprocess_drift.dart';
|
||||||
import 'new_backend.dart';
|
import 'backend.dart';
|
||||||
|
|
||||||
/// A support builder that runs before the main generator to parse and resolve
|
/// A support builder that runs before the main generator to parse and resolve
|
||||||
/// inline Dart resources in a `.drift` file.
|
/// inline Dart resources in a `.drift` file.
|
||||||
|
|
|
@ -17,8 +17,8 @@ class Foo extends Table {
|
||||||
'''
|
'''
|
||||||
});
|
});
|
||||||
|
|
||||||
final file =
|
final file = await backend.driver
|
||||||
await backend.driver.fullyAnalyze(Uri.parse('package:a/main.dart'));
|
.resolveElements(Uri.parse('package:a/main.dart'));
|
||||||
expect(file.errorsDuringDiscovery, isEmpty);
|
expect(file.errorsDuringDiscovery, isEmpty);
|
||||||
|
|
||||||
final result = file.analysis.values.single;
|
final result = file.analysis.values.single;
|
||||||
|
@ -41,8 +41,8 @@ class Foo extends Table {
|
||||||
'''
|
'''
|
||||||
});
|
});
|
||||||
|
|
||||||
final file =
|
final file = await backend.driver
|
||||||
await backend.driver.fullyAnalyze(Uri.parse('package:a/main.dart'));
|
.resolveElements(Uri.parse('package:a/main.dart'));
|
||||||
expect(file.errorsDuringDiscovery, isEmpty);
|
expect(file.errorsDuringDiscovery, isEmpty);
|
||||||
|
|
||||||
final result = file.analysis.values.single;
|
final result = file.analysis.values.single;
|
||||||
|
@ -68,8 +68,8 @@ class Foo extends Table {
|
||||||
'''
|
'''
|
||||||
});
|
});
|
||||||
|
|
||||||
final file =
|
final file = await backend.driver
|
||||||
await backend.driver.fullyAnalyze(Uri.parse('package:a/main.dart'));
|
.resolveElements(Uri.parse('package:a/main.dart'));
|
||||||
expect(file.errorsDuringDiscovery, isEmpty);
|
expect(file.errorsDuringDiscovery, isEmpty);
|
||||||
|
|
||||||
final result = file.analysis.values.single;
|
final result = file.analysis.values.single;
|
||||||
|
@ -98,7 +98,7 @@ class Foo extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
final uri = Uri.parse('package:a/main.dart');
|
final uri = Uri.parse('package:a/main.dart');
|
||||||
final file = await backend.driver.fullyAnalyze(uri);
|
final file = await backend.driver.resolveElements(uri);
|
||||||
final otherTable =
|
final otherTable =
|
||||||
file.analysis[DriftElementId(uri, 'other_table')]!.result as DriftTable;
|
file.analysis[DriftElementId(uri, 'other_table')]!.result as DriftTable;
|
||||||
final foo = file.analysis[DriftElementId(uri, 'foo')]!.result as DriftTable;
|
final foo = file.analysis[DriftElementId(uri, 'foo')]!.result as DriftTable;
|
||||||
|
@ -127,7 +127,7 @@ class Foo extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
final file =
|
final file =
|
||||||
await backend.driver.fullyAnalyze(Uri.parse('package:a/main.dart'));
|
await backend.driver.resolveElements(Uri.parse('package:a/main.dart'));
|
||||||
final table = file.analysis.values.single.result as DriftTable;
|
final table = file.analysis.values.single.result as DriftTable;
|
||||||
|
|
||||||
expect(table.references, isEmpty);
|
expect(table.references, isEmpty);
|
||||||
|
|
|
@ -102,7 +102,7 @@ class InvalidConstraints extends Table {
|
||||||
final uri = Uri.parse('package:a/main.dart');
|
final uri = Uri.parse('package:a/main.dart');
|
||||||
|
|
||||||
Future<ElementAnalysisState?> findTable(String dartName) async {
|
Future<ElementAnalysisState?> findTable(String dartName) async {
|
||||||
final state = await backend.driver.fullyAnalyze(uri);
|
final state = await backend.driver.resolveElements(uri);
|
||||||
|
|
||||||
return state.analysis.values.firstWhereOrNull((e) {
|
return state.analysis.values.firstWhereOrNull((e) {
|
||||||
final result = e.result;
|
final result = e.result;
|
||||||
|
@ -133,7 +133,7 @@ class InvalidConstraints extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('reports discovery error for table with wrong name', () async {
|
test('reports discovery error for table with wrong name', () async {
|
||||||
final state = await backend.driver.fullyAnalyze(uri);
|
final state = await backend.driver.resolveElements(uri);
|
||||||
expect(state.errorsDuringDiscovery, [
|
expect(state.errorsDuringDiscovery, [
|
||||||
isDriftError(
|
isDriftError(
|
||||||
contains('This getter must directly return a string literal'),
|
contains('This getter must directly return a string literal'),
|
||||||
|
|
|
@ -21,7 +21,7 @@ CREATE TABLE b (
|
||||||
});
|
});
|
||||||
|
|
||||||
final state =
|
final state =
|
||||||
await backend.driver.fullyAnalyze(Uri.parse('package:a/a.drift'));
|
await backend.driver.resolveElements(Uri.parse('package:a/a.drift'));
|
||||||
|
|
||||||
expect(state, hasNoErrors);
|
expect(state, hasNoErrors);
|
||||||
final results = state.analysis.values.toList();
|
final results = state.analysis.values.toList();
|
||||||
|
|
|
@ -20,7 +20,7 @@ CREATE TABLE b (
|
||||||
});
|
});
|
||||||
|
|
||||||
final state =
|
final state =
|
||||||
await backend.driver.fullyAnalyze(Uri.parse('package:a/a.drift'));
|
await backend.driver.resolveElements(Uri.parse('package:a/a.drift'));
|
||||||
|
|
||||||
expect(state, hasNoErrors);
|
expect(state, hasNoErrors);
|
||||||
final results = state.analysis.values.toList();
|
final results = state.analysis.values.toList();
|
||||||
|
@ -51,7 +51,7 @@ CREATE TABLE a (
|
||||||
});
|
});
|
||||||
|
|
||||||
final state =
|
final state =
|
||||||
await backend.driver.fullyAnalyze(Uri.parse('package:a/a.drift'));
|
await backend.driver.resolveElements(Uri.parse('package:a/a.drift'));
|
||||||
|
|
||||||
expect(state, hasNoErrors);
|
expect(state, hasNoErrors);
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ CREATE TABLE b (
|
||||||
});
|
});
|
||||||
|
|
||||||
final stateA =
|
final stateA =
|
||||||
await backend.driver.fullyAnalyze(Uri.parse('package:a/a.drift'));
|
await backend.driver.resolveElements(Uri.parse('package:a/a.drift'));
|
||||||
expect(stateA, hasNoErrors);
|
expect(stateA, hasNoErrors);
|
||||||
|
|
||||||
// Check that `b` has been analyzed and is in cache.
|
// Check that `b` has been analyzed and is in cache.
|
||||||
|
@ -102,7 +102,7 @@ CREATE TABLE a (
|
||||||
});
|
});
|
||||||
|
|
||||||
final state =
|
final state =
|
||||||
await backend.driver.fullyAnalyze(Uri.parse('package:a/a.drift'));
|
await backend.driver.resolveElements(Uri.parse('package:a/a.drift'));
|
||||||
expect(state.errorsDuringDiscovery, isEmpty);
|
expect(state.errorsDuringDiscovery, isEmpty);
|
||||||
|
|
||||||
final resultA = state.analysis.values.single;
|
final resultA = state.analysis.values.single;
|
||||||
|
|
Loading…
Reference in New Issue