in build_runner_core/lib/src/asset_graph/graph.dart [259:370]
Future Function(AssetId id) delete,
AssetReader digestReader) async {
var newIds = <AssetId>{};
var modifyIds = <AssetId>{};
var removeIds = <AssetId>{};
updates.forEach((id, changeType) {
if (changeType != ChangeType.ADD && get(id) == null) return;
switch (changeType) {
case ChangeType.ADD:
newIds.add(id);
break;
case ChangeType.MODIFY:
modifyIds.add(id);
break;
case ChangeType.REMOVE:
removeIds.add(id);
break;
}
});
var newAndModifiedNodes = [
for (var id in modifyIds) get(id)!,
..._addSources(newIds),
];
// Pre-emptively compute digests for the new and modified nodes we know have
// outputs.
await _setLastKnownDigests(
newAndModifiedNodes.where((node) =>
node.isValidInput &&
(node.outputs.isNotEmpty ||
node.primaryOutputs.isNotEmpty ||
node.lastKnownDigest != null)),
digestReader);
// Collects the set of all transitive ids to be removed from the graph,
// based on the removed `SourceAssetNode`s by following the
// `primaryOutputs`.
var transitiveRemovedIds = <AssetId>{};
void addTransitivePrimaryOutputs(AssetId id) {
transitiveRemovedIds.add(id);
get(id)!.primaryOutputs.forEach(addTransitivePrimaryOutputs);
}
removeIds
.where((id) => get(id) is SourceAssetNode)
.forEach(addTransitivePrimaryOutputs);
// The generated nodes to actually delete from the file system.
var idsToDelete = Set<AssetId>.from(transitiveRemovedIds)
..removeAll(removeIds);
// We definitely need to update manually deleted outputs.
for (var deletedOutput
in removeIds.map(get).whereType<GeneratedAssetNode>()) {
deletedOutput.state = NodeState.definitelyNeedsUpdate;
}
// Transitively invalidates all assets. This needs to happen after the
// structure of the graph has been updated.
var invalidatedIds = <AssetId>{};
var newGeneratedOutputs =
_addOutputsForSources(buildPhases, newIds, rootPackage);
var allNewAndDeletedIds = {...newGeneratedOutputs, ...transitiveRemovedIds};
void invalidateNodeAndDeps(AssetId id) {
var node = get(id);
if (node == null) return;
if (!invalidatedIds.add(id)) return;
if (node is NodeWithInputs && node.state == NodeState.upToDate) {
node.state = NodeState.mayNeedUpdate;
}
// Update all outputs of this asset as well.
for (var output in node.outputs) {
invalidateNodeAndDeps(output);
}
}
for (var changed in updates.keys.followedBy(newGeneratedOutputs)) {
invalidateNodeAndDeps(changed);
}
// For all new or deleted assets, check if they match any glob nodes and
// invalidate those.
for (var id in allNewAndDeletedIds) {
var samePackageGlobNodes = packageNodes(id.package)
.whereType<GlobAssetNode>()
.where((n) => n.state == NodeState.upToDate);
for (final node in samePackageGlobNodes) {
if (node.glob.matches(id.path)) {
invalidateNodeAndDeps(node.id);
node.state = NodeState.mayNeedUpdate;
}
}
}
// Delete all the invalidated assets, then remove them from the graph. This
// order is important because some `AssetWriter`s throw if the id is not in
// the graph.
await Future.wait(idsToDelete.map(delete));
// Remove all deleted source assets from the graph, which also recursively
// removes all their primary outputs.
for (var id in removeIds.where((id) => get(id) is SourceAssetNode)) {
invalidateNodeAndDeps(id);
_removeRecursive(id);
}
return invalidatedIds;
}