readSnapshot function
Reads a constant frozen schema without executing the Dart input file.
Accepts the generated frozenSchema or one unambiguous top-level constant
SchemaSnapshot, including a private constant in a historical migration.
The file must be an independent library. Only literal scalar defaults are
supported, matching the model generator; application code is never loaded.
Implementation
Future<SchemaSnapshot> readSnapshot(String sourcePath) async {
final file = p.normalize(p.absolute(sourcePath));
final collection = AnalysisContextCollection(
includedPaths: [file],
sdkPath: p.dirname(p.dirname(Platform.resolvedExecutable)),
);
try {
final result = await collection
.contextFor(file)
.currentSession
.getResolvedLibrary(file);
if (result is! ResolvedLibraryResult || result.units.length != 1) {
throw FormatException('$file: expected one independent Dart library.');
}
final errors = result.units.single.diagnostics.where(
(error) => error.severity.name == 'error',
);
if (errors.isNotEmpty) {
throw FormatException('$file: ${errors.join('\n')}');
}
final snapshots = result.element.topLevelVariables
.where((variable) => variable.isConst)
.map((variable) => variable.computeConstantValue())
.whereType<DartObject>()
.where((value) => _isSchemaType(value, 'SchemaSnapshot'))
.toList();
final preferred = snapshots.where(
(value) => value.variable?.name == 'frozenSchema',
);
final snapshot = preferred.singleOrNull ?? snapshots.singleOrNull;
if (snapshot == null) {
throw FormatException(
'$file: declare const frozenSchema, or exactly one const SchemaSnapshot.',
);
}
final schema = SchemaSnapshot(
engine: Engine.values[_enumIndex(_field(snapshot, 'engine'))],
tables: [
for (final table in _field(snapshot, 'tables').toListValue()!)
TableDefinition(_field(table, 'name').toStringValue()!, [
for (final column in _field(table, 'columns').toListValue()!)
ColumnDefinition(
name: _field(column, 'name').toStringValue()!,
field: _field(column, 'field').toStringValue()!,
type: ScalarType.values[_enumIndex(_field(column, 'type'))],
nullable: _field(column, 'nullable').toBoolValue()!,
primaryKey: _field(column, 'primaryKey').toBoolValue()!,
identity: _field(column, 'identity').toBoolValue()!,
unique: _field(column, 'unique').toBoolValue()!,
defaultValue: _literalDefault(_field(column, 'defaultValue')),
references: _reference(_field(column, 'references')),
),
]),
],
);
// Validates cross-table constraints and freezes the analyzed collections.
return MigrationPlan(
engine: schema.engine,
steps: const [],
snapshot: schema,
).snapshot;
} finally {
await collection.dispose();
}
}