1
- // Copyright (c) 2016 , the Dart project authors. Please see the AUTHORS file
1
+ // Copyright (c) 2025 , the Dart project authors. Please see the AUTHORS file
2
2
// for details. All rights reserved. Use of this source code is governed by a
3
3
// BSD-style license that can be found in the LICENSE file.
4
4
import 'dart:async' ;
@@ -15,43 +15,63 @@ import 'package:path/path.dart' as path;
15
15
import '../package_graph/package_graph.dart' ;
16
16
import 'writer.dart' ;
17
17
18
- /// Basic [AssetReader] which uses a [PackageGraph] to look up where to read
19
- /// files from disk.
20
- class FileBasedAssetReader extends AssetReader implements AssetReaderState {
18
+ /// Pluggable [AssetReader] and [AssetWriter] .
19
+ class ReaderWriter extends AssetReader
20
+ implements AssetReaderState , RunnerAssetWriter {
21
+ /// The package the generator is running for.
22
+ ///
23
+ /// Deletes are only allowed within this package.
24
+ final String rootPackage;
25
+
21
26
@override
22
27
final AssetPathProvider assetPathProvider;
23
28
@override
24
29
final Filesystem filesystem;
25
30
@override
26
- late final AssetFinder assetFinder = FunctionAssetFinder (_findAssets) ;
31
+ final AssetFinder assetFinder;
27
32
@override
28
33
final FilesystemCache cache;
34
+ @override
35
+ final InputTracker ? inputTracker;
36
+
37
+ /// A [ReaderWriter] suitable for real builds.
38
+ ///
39
+ /// [packageGraph] is used for mapping paths and finding assets. The
40
+ /// `dart-io` filesystem is used with no cache.
41
+ ///
42
+ /// Use [copyWith] to change settings such as caching.
43
+ factory ReaderWriter (PackageGraph packageGraph) => ReaderWriter .using (
44
+ rootPackage: packageGraph.root.name,
45
+ assetFinder: PackageGraphAssetFinder (packageGraph),
46
+ assetPathProvider: packageGraph,
47
+ filesystem: IoFilesystem (),
48
+ cache: const PassthroughFilesystemCache (),
49
+ // In real builds input tracking is done per generator.
50
+ inputTracker: null ,
51
+ );
29
52
30
- final PackageGraph packageGraph;
31
-
32
- FileBasedAssetReader (
33
- this .packageGraph, {
34
- AssetPathProvider ? assetPathProvider,
35
- Filesystem ? filesystem,
36
- FilesystemCache ? cache,
37
- }) : filesystem = filesystem ?? IoFilesystem (),
38
- cache = cache ?? const PassthroughFilesystemCache (),
39
- assetPathProvider = assetPathProvider ?? packageGraph;
53
+ ReaderWriter .using ({
54
+ required this .rootPackage,
55
+ required this .assetFinder,
56
+ required this .assetPathProvider,
57
+ required this .filesystem,
58
+ required this .cache,
59
+ required this .inputTracker,
60
+ });
40
61
41
62
@override
42
- FileBasedAssetReader copyWith ({
63
+ ReaderWriter copyWith ({
43
64
AssetPathProvider ? assetPathProvider,
44
65
FilesystemCache ? cache,
45
- }) => FileBasedAssetReader (
46
- packageGraph,
66
+ }) => ReaderWriter .using (
67
+ rootPackage: rootPackage,
68
+ assetFinder: assetFinder,
47
69
assetPathProvider: assetPathProvider ?? this .assetPathProvider,
48
70
filesystem: filesystem,
49
71
cache: cache ?? this .cache,
72
+ inputTracker: inputTracker,
50
73
);
51
74
52
- @override
53
- InputTracker ? get inputTracker => null ;
54
-
55
75
@override
56
76
Future <bool > canRead (AssetId id) {
57
77
return cache.exists (
@@ -96,44 +116,11 @@ class FileBasedAssetReader extends AssetReader implements AssetReaderState {
96
116
@override
97
117
Stream <AssetId > findAssets (Glob glob) => throw UnimplementedError ();
98
118
99
- Stream <AssetId > _findAssets (Glob glob, String ? package) {
100
- var packageNode =
101
- package == null ? packageGraph.root : packageGraph[package];
102
- if (packageNode == null ) {
103
- throw ArgumentError (
104
- "Could not find package '$package ' which was listed as "
105
- 'an input. Please ensure you have that package in your deps, or '
106
- 'remove it from your input sets.' ,
107
- );
108
- }
109
- // TODO(davidmorgan): make this read via `filesystem`, currently it
110
- // reads directly via `dart:io`.
111
- return glob
112
- .list (followLinks: true , root: packageNode.path)
113
- .where ((e) => e is File && ! path.basename (e.path).startsWith ('._' ))
114
- .cast <File >()
115
- .map ((file) => _fileToAssetId (file, packageNode));
116
- }
117
-
118
- /// Creates an [AssetId] for [file] , which is a part of [packageNode] .
119
- AssetId _fileToAssetId (File file, PackageNode packageNode) {
120
- var filePath = path.normalize (file.absolute.path);
121
- var relativePath = path.relative (filePath, from: packageNode.path);
122
- return AssetId (packageNode.name, relativePath);
123
- }
124
- }
125
-
126
- /// Basic [AssetWriter] which uses a [PackageGraph] to look up where to write
127
- /// files to disk.
128
- class FileBasedAssetWriter implements RunnerAssetWriter {
129
- final PackageGraph packageGraph;
130
- final Filesystem filesystem;
131
-
132
- FileBasedAssetWriter (this .packageGraph) : filesystem = IoFilesystem ();
119
+ // [AssetWriter] methods.
133
120
134
121
@override
135
122
Future <void > writeAsBytes (AssetId id, List <int > bytes) async {
136
- final path = packageGraph .pathFor (id);
123
+ final path = assetPathProvider .pathFor (id);
137
124
await filesystem.writeAsBytes (path, bytes);
138
125
}
139
126
@@ -143,19 +130,19 @@ class FileBasedAssetWriter implements RunnerAssetWriter {
143
130
String contents, {
144
131
Encoding encoding = utf8,
145
132
}) async {
146
- final path = packageGraph .pathFor (id);
133
+ final path = assetPathProvider .pathFor (id);
147
134
await filesystem.writeAsString (path, contents, encoding: encoding);
148
135
}
149
136
150
137
@override
151
138
Future delete (AssetId id) async {
152
- if (id.package != packageGraph.root.name ) {
139
+ if (id.package != rootPackage ) {
153
140
throw InvalidOutputException (
154
141
id,
155
- 'Should not delete assets outside of ${ packageGraph . root . name } ' ,
142
+ 'Should not delete assets outside of $rootPackage ' ,
156
143
);
157
144
}
158
- final path = packageGraph .pathFor (id);
145
+ final path = assetPathProvider .pathFor (id);
159
146
await filesystem.delete (path);
160
147
}
161
148
@@ -164,3 +151,38 @@ class FileBasedAssetWriter implements RunnerAssetWriter {
164
151
// TODO(davidmorgan): add back write caching, "batching".
165
152
}
166
153
}
154
+
155
+ /// [AssetFinder] that uses [PackageGraph] to map packages to paths, then
156
+ /// uses the `dart:io` filesystem to find files.
157
+ ///
158
+ /// TODO(davidmorgan): read via `Filesystem` instead of `dart-io` .
159
+ class PackageGraphAssetFinder implements AssetFinder {
160
+ final PackageGraph packageGraph;
161
+
162
+ PackageGraphAssetFinder (this .packageGraph);
163
+
164
+ @override
165
+ Stream <AssetId > find (Glob glob, {String ? package}) {
166
+ var packageNode =
167
+ package == null ? packageGraph.root : packageGraph[package];
168
+ if (packageNode == null ) {
169
+ throw ArgumentError (
170
+ "Could not find package '$package ' which was listed as "
171
+ 'an input. Please ensure you have that package in your deps, or '
172
+ 'remove it from your input sets.' ,
173
+ );
174
+ }
175
+ return glob
176
+ .list (followLinks: true , root: packageNode.path)
177
+ .where ((e) => e is File && ! path.basename (e.path).startsWith ('._' ))
178
+ .cast <File >()
179
+ .map ((file) => _fileToAssetId (file, packageNode));
180
+ }
181
+
182
+ /// Creates an [AssetId] for [file] , which is a part of [packageNode] .
183
+ static AssetId _fileToAssetId (File file, PackageNode packageNode) {
184
+ var filePath = path.normalize (file.absolute.path);
185
+ var relativePath = path.relative (filePath, from: packageNode.path);
186
+ return AssetId (packageNode.name, relativePath);
187
+ }
188
+ }
0 commit comments