make hot reload reflect changes to asset transformer configurations (#144660)

In service of https://github.com/flutter/flutter/issues/143348

This PR makes hot reloads reflect changes to transformer configurations under the `assets` section in pubspec.yaml.
This PR is optimized for ease of implementation, and should not be merged as-is. If it is merged as-is, seriously consider creating a tech debt issue and assigning it to someone to make sure it gets resolved.
This commit is contained in:
Andrew Kolos 2024-03-24 21:13:11 -07:00 committed by GitHub
parent a5cf4191dc
commit cd785544fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 84 additions and 6 deletions

View file

@ -14,6 +14,7 @@ import 'base/deferred_component.dart';
import 'base/file_system.dart';
import 'base/logger.dart';
import 'base/platform.dart';
import 'base/utils.dart';
import 'build_info.dart';
import 'cache.dart';
import 'convert.dart';
@ -84,9 +85,8 @@ enum AssetKind {
/// Contains all information about an asset needed by tool the to prepare and
/// copy an asset file to the build output.
@immutable
final class AssetBundleEntry {
const AssetBundleEntry(this.content, {
AssetBundleEntry(this.content, {
required this.kind,
required this.transformers,
});
@ -96,6 +96,10 @@ final class AssetBundleEntry {
final List<AssetTransformerEntry> transformers;
Future<List<int>> contentsAsBytes() => content.contentsAsBytes();
bool hasEquivalentConfigurationWith(AssetBundleEntry other) {
return listEquals(transformers, other.transformers);
}
}
abstract class AssetBundle {
@ -425,11 +429,11 @@ class ManifestAssetBundle implements AssetBundle {
final File variantFile = variant.lookupAssetFile(_fileSystem);
inputFiles.add(variantFile);
assert(variantFile.existsSync());
entries[variant.entryUri.path] ??= AssetBundleEntry(
_setIfConfigurationChanged(entries, variant.entryUri.path, AssetBundleEntry(
DevFSFileContent(variantFile),
kind: variant.kind,
transformers: variant.transformers,
);
));
}
}
// Save the contents of each deferred component image, image variant, and font
@ -459,11 +463,11 @@ class ManifestAssetBundle implements AssetBundle {
for (final _Asset variant in assetsMap[asset]!) {
final File variantFile = variant.lookupAssetFile(_fileSystem);
assert(variantFile.existsSync());
deferredComponentsEntries[componentName]![variant.entryUri.path] ??= AssetBundleEntry(
_setIfConfigurationChanged(deferredComponentsEntries[componentName]!, variant.entryUri.path, AssetBundleEntry(
DevFSFileContent(variantFile),
kind: AssetKind.regular,
transformers: variant.transformers,
);
));
}
}
}
@ -563,6 +567,13 @@ class ManifestAssetBundle implements AssetBundle {
return true;
}
void _setIfConfigurationChanged(Map<String, AssetBundleEntry> entryMap, String key, AssetBundleEntry entry,) {
final AssetBundleEntry? existingEntry = entryMap[key];
if (existingEntry == null || !entry.hasEquivalentConfigurationWith(existingEntry)) {
entryMap[key] = entry;
}
}
void _setLicenseIfChanged(
String combinedLicenses,
TargetPlatform? targetPlatform,

View file

@ -428,6 +428,73 @@ flutter:
),
);
});
testWithoutContext("AssetBundleEntry::content::isModified is true when an asset's transformers change in between builds", () async {
final FileSystem fileSystem = MemoryFileSystem.test();
fileSystem.file('my-asset.txt').createSync();
final BufferLogger logger = BufferLogger.test();
final FakePlatform platform = FakePlatform();
fileSystem.file('.packages').createSync();
fileSystem.file('pubspec.yaml')
..createSync()
..writeAsStringSync(r'''
name: example
flutter:
assets:
- path: my-asset.txt
transformers:
- package: my-transformer-one
''');
final ManifestAssetBundle bundle = ManifestAssetBundle(
logger: logger,
fileSystem: fileSystem,
platform: platform,
flutterRoot: Cache.defaultFlutterRoot(
platform: platform,
fileSystem: fileSystem,
userMessages: UserMessages(),
),
);
await bundle.build(
packagesPath: '.packages',
flutterProject: FlutterProject.fromDirectoryTest(
fileSystem.currentDirectory,
),
);
expect(bundle.entries['my-asset.txt']!.content.isModified, isTrue);
await bundle.build(
packagesPath: '.packages',
flutterProject: FlutterProject.fromDirectoryTest(
fileSystem.currentDirectory,
),
);
expect(bundle.entries['my-asset.txt']!.content.isModified, isFalse);
fileSystem.file('pubspec.yaml').writeAsStringSync(r'''
name: example
flutter:
assets:
- path: my-asset.txt
transformers:
- package: my-transformer-one
- package: my-transformer-two
''');
await bundle.build(
packagesPath: '.packages',
flutterProject: FlutterProject.fromDirectoryTest(
fileSystem.currentDirectory,
),
);
expect(bundle.entries['my-asset.txt']!.content.isModified, isTrue);
});
});
group('AssetBundle.build (web builds)', () {