diff --git a/moor/test/data/tables/custom_tables.g.dart b/moor/test/data/tables/custom_tables.g.dart index 89a6a2c4..8d5c1d84 100644 --- a/moor/test/data/tables/custom_tables.g.dart +++ b/moor/test/data/tables/custom_tables.g.dart @@ -1203,7 +1203,7 @@ abstract class _$CustomTablesDb extends GeneratedDatabase { Selectable multiple(Expression predicate) { final generatedpredicate = $write(predicate, hasMultipleTables: true); return customSelect( - 'SELECT d.*, "c.a" AS "nested_0.a", "c.b" AS "nested_0.b", "c.c" AS "nested_0.c" FROM with_constraints c\n INNER JOIN with_defaults d\n ON d.a = c.a AND d.b = c.b\n WHERE ${generatedpredicate.sql}', + 'SELECT d.*, "c"."a" AS "nested_0.a", "c"."b" AS "nested_0.b", "c"."c" AS "nested_0.c" FROM with_constraints c\n INNER JOIN with_defaults d\n ON d.a = c.a AND d.b = c.b\n WHERE ${generatedpredicate.sql}', variables: [...generatedpredicate.introducedVariables], readsFrom: {withConstraints, withDefaults}).map(_rowToMultipleResult); } diff --git a/moor_generator/lib/src/backends/build/preprocess_builder.dart b/moor_generator/lib/src/backends/build/preprocess_builder.dart index 0f1f6a7a..02180319 100644 --- a/moor_generator/lib/src/backends/build/preprocess_builder.dart +++ b/moor_generator/lib/src/backends/build/preprocess_builder.dart @@ -38,26 +38,51 @@ class PreprocessBuilder extends Builder { final moorFileContent = await buildStep.readAsString(input); final engine = SqlEngine(EngineOptions(useMoorExtensions: true)); - final parsed = engine.parseMoorFile(moorFileContent); + final parsedInput = engine.parseMoorFile(moorFileContent); - final dartLexemes = parsed.tokens + final dartLexemes = parsedInput.tokens .whereType() .map((token) => token.dartCode) .toList(); if (dartLexemes.isEmpty) return; // nothing to do, no Dart in this moor file - final importedFiles = parsed.rootNode.allDescendants - .whereType() - .map((stmt) => stmt.importedFile) - .where((import) => import.endsWith('.dart')); + // Crawl through transitive imports and find all Dart libraries + final seenFiles = {}; + final queue = [input]; + + while (queue.isNotEmpty) { + final asset = queue.removeLast(); + + if (!seenFiles.contains(asset)) { + seenFiles.add(asset); + + if (asset.extension == '.moor') { + final parsed = asset == input + ? parsedInput + : engine.parseMoorFile(await buildStep.readAsString(asset)); + + parsed.rootNode.allDescendants + .whereType() + .map((stmt) => AssetId.resolve(stmt.importedFile, from: asset)) + .where((importedId) => + !seenFiles.contains(importedId) && + !queue.contains(importedId)) + .forEach(queue.add); + } + } + } + + final importedDartFiles = + seenFiles.where((asset) => asset.extension == '.dart'); // to analyze the expressions, generate a fake Dart file that declares each // expression in a `var`, we can then read the static type. final dartBuffer = StringBuffer(); - for (final import in importedFiles) { - dartBuffer.write('import ${asDartLiteral(import)};\n'); + for (final import in importedDartFiles) { + final importUri = import.uri.toString(); + dartBuffer.write('import ${asDartLiteral(importUri)};\n'); } for (var i = 0; i < dartLexemes.length; i++) { diff --git a/moor_generator/test/backends/build/preprocess_builder_tests.dart b/moor_generator/test/backends/build/preprocess_builder_tests.dart index 067789f1..47b43e5b 100644 --- a/moor_generator/test/backends/build/preprocess_builder_tests.dart +++ b/moor_generator/test/backends/build/preprocess_builder_tests.dart @@ -51,4 +51,52 @@ class MyConverter extends TypeConverter { 'type_args': [], }); }); + + test('finds dart files over transitive imports', () async { + final writer = InMemoryAssetWriter(); + final reader = await PackageAssetReader.currentIsolate(); + + await testBuilder( + PreprocessBuilder(), + { + 'foo|main.moor': ''' +import 'indirection.moor'; + +CREATE TABLE foo ( + id INT NOT NULL MAPPED BY `const MyConverter()` +); + ''', + 'foo|indirection.moor': ''' +import 'converter.dart'; + ''', + 'foo|converter.dart': ''' +import 'package:moor/moor.dart'; + +class MyConverter extends TypeConverter { + const MyConverter(); + + int mapToSql(DateTime time) => time?.millisecondsSinceEpoch; + + DateTime mapToDart(int fromSql) { + if (fromSql == null) return null; + return DateTime.fromMillisecondsSinceEpoch(fromSql); + } +} + ''', + }, + writer: writer, + reader: reader, + ); + + final output = + utf8.decode(writer.assets[AssetId.parse('foo|main.dart_in_moor')]); + final serialized = json.decode(output); + + expect(serialized['const MyConverter()'], { + 'type': 'interface', + 'library': 'asset:foo/converter.dart', + 'class_name': 'MyConverter', + 'type_args': [], + }); + }); }