mirror of https://github.com/AMT-Cheif/drift.git
Serialize and restore file dependencies too
This commit is contained in:
parent
e51bb0976e
commit
a6549425ef
|
@ -1,6 +1,7 @@
|
||||||
## 2.7.0-dev
|
## 2.7.0-dev
|
||||||
|
|
||||||
- Make `validateDatabaseSchema()` work in migration tests.
|
- Make `validateDatabaseSchema()` work in migration tests.
|
||||||
|
- Fix elements from transitive imports in drift files not being added reliably.
|
||||||
|
|
||||||
## 2.6.0
|
## 2.6.0
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'state.dart';
|
||||||
///
|
///
|
||||||
/// At the moment, the cache is not set up to handle changing files.
|
/// At the moment, the cache is not set up to handle changing files.
|
||||||
class DriftAnalysisCache {
|
class DriftAnalysisCache {
|
||||||
final Map<Uri, Map<String, Object?>> serializedElements = {};
|
final Map<Uri, CachedSerializationResult> serializationCache = {};
|
||||||
final Map<Uri, FileState> knownFiles = {};
|
final Map<Uri, FileState> knownFiles = {};
|
||||||
final Map<DriftElementId, DiscoveredElement> discoveredElements = {};
|
final Map<DriftElementId, DiscoveredElement> discoveredElements = {};
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ class DriftAnalysisCache {
|
||||||
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);
|
serializationCache.remove(uri);
|
||||||
|
|
||||||
return knownFiles.putIfAbsent(uri, () => FileState(uri))
|
return knownFiles.putIfAbsent(uri, () => FileState(uri))
|
||||||
..errorsDuringDiscovery.clear()
|
..errorsDuringDiscovery.clear()
|
||||||
|
@ -62,8 +62,7 @@ class DriftAnalysisCache {
|
||||||
final found = pending.removeLast();
|
final found = pending.removeLast();
|
||||||
yield found;
|
yield found;
|
||||||
|
|
||||||
for (final imported
|
for (final imported in found.imports ?? const <Uri>[]) {
|
||||||
in found.discovery?.importDependencies ?? const <Uri>[]) {
|
|
||||||
if (seenUris.add(imported)) {
|
if (seenUris.add(imported)) {
|
||||||
pending.add(knownFiles[imported]!);
|
pending.add(knownFiles[imported]!);
|
||||||
}
|
}
|
||||||
|
@ -71,3 +70,10 @@ class DriftAnalysisCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CachedSerializationResult {
|
||||||
|
final List<Uri> cachedImports;
|
||||||
|
final Map<String, Map<String, Object?>> cachedElements;
|
||||||
|
|
||||||
|
CachedSerializationResult(this.cachedImports, this.cachedElements);
|
||||||
|
}
|
||||||
|
|
|
@ -98,8 +98,8 @@ class DriftAnalysisDriver {
|
||||||
///
|
///
|
||||||
/// Returns non-null if analysis results were found and successfully restored.
|
/// Returns non-null if analysis results were found and successfully restored.
|
||||||
Future<Map<String, Object?>?> readStoredAnalysisResult(Uri uri) async {
|
Future<Map<String, Object?>?> readStoredAnalysisResult(Uri uri) async {
|
||||||
final cached = cache.serializedElements[uri];
|
final cached = cache.serializationCache[uri];
|
||||||
if (cached != null) return cached;
|
if (cached != null) return cached.cachedElements;
|
||||||
|
|
||||||
// Not available in in-memory cache, so let's read it from the file system.
|
// Not available in in-memory cache, so let's read it from the file system.
|
||||||
final reader = cacheReader;
|
final reader = cacheReader;
|
||||||
|
@ -109,7 +109,15 @@ class DriftAnalysisDriver {
|
||||||
if (found == null) return null;
|
if (found == null) return null;
|
||||||
|
|
||||||
final parsed = json.decode(found) as Map<String, Object?>;
|
final parsed = json.decode(found) as Map<String, Object?>;
|
||||||
return cache.serializedElements[uri] = parsed;
|
final data = CachedSerializationResult(
|
||||||
|
[
|
||||||
|
for (final entry in parsed['imports'] as List)
|
||||||
|
Uri.parse(entry as String)
|
||||||
|
],
|
||||||
|
(parsed['elements'] as Map<String, Object?>).cast(),
|
||||||
|
);
|
||||||
|
cache.serializationCache[uri] = data;
|
||||||
|
return data.cachedElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _recoverFromCache(FileState state) async {
|
Future<bool> _recoverFromCache(FileState state) async {
|
||||||
|
@ -128,6 +136,21 @@ class DriftAnalysisDriver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final cachedImports = cache.serializationCache[state.ownUri]?.cachedImports;
|
||||||
|
if (cachedImports != null && state.discovery == null) {
|
||||||
|
state.cachedImports = cachedImports;
|
||||||
|
for (final import in cachedImports) {
|
||||||
|
final found = cache.stateForUri(import);
|
||||||
|
|
||||||
|
if (found.imports == null) {
|
||||||
|
// Attempt to recover this file as well to make sure we know the
|
||||||
|
// imports for every file transitively reachable from the sources
|
||||||
|
// analyzed.
|
||||||
|
await _recoverFromCache(found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return allRecovered;
|
return allRecovered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,6 +254,24 @@ class DriftAnalysisDriver {
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Serializes imports and locally-defined elements of the file.
|
||||||
|
///
|
||||||
|
/// Serialized data can later be recovered if a [cacheReader] is set on this
|
||||||
|
/// driver, which avoids running duplicate analysis runs across build steps.
|
||||||
|
SerializedElements serializeState(FileState state) {
|
||||||
|
final data = ElementSerializer.serialize(
|
||||||
|
state.analysis.values.map((e) => e.result).whereType());
|
||||||
|
|
||||||
|
final imports = state.discovery?.importDependencies;
|
||||||
|
if (imports != null) {
|
||||||
|
data.serializedData['imports'] = [
|
||||||
|
for (final import in imports) import.toString()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads serialized data and a generated Dart helper file used to serialize
|
/// Reads serialized data and a generated Dart helper file used to serialize
|
||||||
|
|
|
@ -15,6 +15,8 @@ class FileState {
|
||||||
final Uri ownUri;
|
final Uri ownUri;
|
||||||
|
|
||||||
DiscoveredFileState? discovery;
|
DiscoveredFileState? discovery;
|
||||||
|
List<Uri>? cachedImports;
|
||||||
|
|
||||||
final List<DriftAnalysisError> errorsDuringDiscovery = [];
|
final List<DriftAnalysisError> errorsDuringDiscovery = [];
|
||||||
|
|
||||||
final Map<DriftElementId, ElementAnalysisState> analysis = {};
|
final Map<DriftElementId, ElementAnalysisState> analysis = {};
|
||||||
|
@ -24,6 +26,8 @@ class FileState {
|
||||||
|
|
||||||
FileState(this.ownUri);
|
FileState(this.ownUri);
|
||||||
|
|
||||||
|
Iterable<Uri>? get imports => discovery?.importDependencies ?? cachedImports;
|
||||||
|
|
||||||
String get extension => url.extension(ownUri.path);
|
String get extension => url.extension(ownUri.path);
|
||||||
|
|
||||||
/// Whether this file contains a drift database or a drift accessor / DAO.
|
/// Whether this file contains a drift database or a drift accessor / DAO.
|
||||||
|
|
|
@ -10,9 +10,13 @@ import 'results/results.dart';
|
||||||
|
|
||||||
class SerializedElements {
|
class SerializedElements {
|
||||||
final List<AnnotatedDartCode> dartTypes;
|
final List<AnnotatedDartCode> dartTypes;
|
||||||
final Map<String, Object?> serializedElements;
|
final Map<String, Object?> serializedData;
|
||||||
|
final Map<String, Object?> _serializedElements;
|
||||||
|
|
||||||
SerializedElements(this.dartTypes, this.serializedElements);
|
SerializedElements(
|
||||||
|
this.dartTypes, this.serializedData, this._serializedElements) {
|
||||||
|
serializedData['elements'] = _serializedElements;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes [DriftElement]s to JSON.
|
/// Serializes [DriftElement]s to JSON.
|
||||||
|
@ -22,13 +26,13 @@ class SerializedElements {
|
||||||
/// a single file changes). However, it means that we have to serialize analysis
|
/// a single file changes). However, it means that we have to serialize analysis
|
||||||
/// results to read them back in in a later build step.
|
/// results to read them back in in a later build step.
|
||||||
class ElementSerializer {
|
class ElementSerializer {
|
||||||
final SerializedElements _result = SerializedElements([], {});
|
final SerializedElements _result = SerializedElements([], {}, {});
|
||||||
|
|
||||||
ElementSerializer._();
|
ElementSerializer._();
|
||||||
|
|
||||||
void _serializeElements(Iterable<DriftElement> elements) {
|
void _serializeElements(Iterable<DriftElement> elements) {
|
||||||
for (final element in elements) {
|
for (final element in elements) {
|
||||||
_result.serializedElements[element.id.name] = _serialize(element);
|
_result._serializedElements[element.id.name] = _serialize(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ import 'dart:convert';
|
||||||
import 'package:build/build.dart';
|
import 'package:build/build.dart';
|
||||||
|
|
||||||
import '../../analysis/driver/driver.dart';
|
import '../../analysis/driver/driver.dart';
|
||||||
import '../../analysis/serializer.dart';
|
|
||||||
import '../../analysis/options.dart';
|
import '../../analysis/options.dart';
|
||||||
import '../../writer/import_manager.dart';
|
import '../../writer/import_manager.dart';
|
||||||
import '../../writer/writer.dart';
|
import '../../writer/writer.dart';
|
||||||
|
@ -45,10 +44,9 @@ class DriftAnalyzer extends Builder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final serialized = ElementSerializer.serialize(
|
final serialized = driver.serializeState(results);
|
||||||
results.analysis.values.map((e) => e.result).whereType());
|
|
||||||
final asJson =
|
final asJson =
|
||||||
JsonUtf8Encoder(' ' * 2).convert(serialized.serializedElements);
|
JsonUtf8Encoder(' ' * 2).convert(serialized.serializedData);
|
||||||
|
|
||||||
final jsonOutput = buildStep.inputId.addExtension('.drift_module.json');
|
final jsonOutput = buildStep.inputId.addExtension('.drift_module.json');
|
||||||
final typesOutput = buildStep.inputId.addExtension('.types.temp.dart');
|
final typesOutput = buildStep.inputId.addExtension('.types.temp.dart');
|
||||||
|
|
|
@ -347,4 +347,37 @@ class Database extends $Database {}
|
||||||
'a|lib/db.drift.dart': decodedMatches(contains(r'.$drift0];'))
|
'a|lib/db.drift.dart': decodedMatches(contains(r'.$drift0];'))
|
||||||
}, result.dartOutputs, result);
|
}, result.dartOutputs, result);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('writes query from transitive import', () async {
|
||||||
|
final result = await emulateDriftBuild(
|
||||||
|
inputs: {
|
||||||
|
'a|lib/main.dart': '''
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@DriftDatabase(include: {'a.drift'})
|
||||||
|
class MyDatabase {}
|
||||||
|
''',
|
||||||
|
'a|lib/a.drift': '''
|
||||||
|
import 'b.drift';
|
||||||
|
|
||||||
|
CREATE TABLE foo (bar INTEGER);
|
||||||
|
''',
|
||||||
|
'a|lib/b.drift': '''
|
||||||
|
import 'c.drift';
|
||||||
|
|
||||||
|
CREATE TABLE foo2 (bar INTEGER);
|
||||||
|
''',
|
||||||
|
'a|lib/c.drift': '''
|
||||||
|
q: SELECT 1;
|
||||||
|
''',
|
||||||
|
},
|
||||||
|
logger: loggerThat(neverEmits(anything)),
|
||||||
|
);
|
||||||
|
|
||||||
|
checkOutputs({
|
||||||
|
'a|lib/main.drift.dart': decodedMatches(
|
||||||
|
contains(r'Selectable<int> q()'),
|
||||||
|
)
|
||||||
|
}, result.dartOutputs, result);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ abstract class $Database extends i0.GeneratedDatabase {
|
||||||
likes,
|
likes,
|
||||||
follows,
|
follows,
|
||||||
popularUsers,
|
popularUsers,
|
||||||
|
i1.usersName,
|
||||||
i4.$drift0
|
i4.$drift0
|
||||||
];
|
];
|
||||||
@override
|
@override
|
||||||
|
|
Loading…
Reference in New Issue