Skip to content

Commit 8132054

Browse files
authored
[native_assets_cli] BuildOutput extension: addDataAssetDirectories (#2097)
1 parent 543902c commit 8132054

File tree

5 files changed

+178
-25
lines changed

5 files changed

+178
-25
lines changed
Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,11 @@
11
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
4-
import 'dart:io';
54

65
import 'package:native_assets_cli/data_assets.dart';
76

87
void main(List<String> args) async {
98
await build(args, (input, output) async {
10-
final assetDirectory = Directory.fromUri(
11-
input.packageRoot.resolve('assets/'),
12-
);
13-
// If assets are added, rerun hook.
14-
output.addDependency(assetDirectory.uri);
15-
16-
await for (final dataAsset in assetDirectory.list()) {
17-
if (dataAsset is! File) {
18-
continue;
19-
}
20-
21-
// The file path relative to the package root, with forward slashes.
22-
final name = dataAsset.uri
23-
.toFilePath(windows: false)
24-
.substring(input.packageRoot.toFilePath(windows: false).length);
25-
26-
output.assets.data.add(
27-
DataAsset(package: input.packageName, name: name, file: dataAsset.uri),
28-
);
29-
// TODO(https://github.com/dart-lang/native/issues/1208): Report
30-
// dependency on asset.
31-
output.addDependency(dataAsset.uri);
32-
}
9+
await output.addDataAssetDirectories(['assets'], input: input);
3310
});
3411
}

pkgs/native_assets_cli/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- Added validation that all URLs in the `Input` and `Output` of hooks are
44
absolute.
5+
- Added `addDataAssetDirectories` extension method on `BuildOutputBuilder`.
56

67
## 0.11.0
78

pkgs/native_assets_cli/lib/data_assets.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export 'native_assets_cli.dart'
1212
EncodedAssetLinkOutputBuilder;
1313
export 'src/data_assets/config.dart'
1414
show
15+
AddDataAssetsDirectory,
1516
DataAssetBuildOutputBuilder,
1617
DataAssetBuildOutputBuilderAdd,
1718
DataAssetLinkInput,

pkgs/native_assets_cli/lib/src/data_assets/config.dart

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,82 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'dart:io';
6+
57
import '../config.dart';
68

79
import 'data_asset.dart';
810

11+
extension AddDataAssetsDirectory on BuildOutputBuilder {
12+
/// Extension on [BuildOutput] to handle data asset directories and files.
13+
///
14+
/// This extension provides a convenient way for build hooks to add
15+
/// [DataAsset] dependencies from one or more directories or individual files.
16+
///
17+
/// If any specified path does not exist, a [FileSystemException] is thrown.
18+
/// Any error during the directory listing is caught and rethrown with
19+
/// additional context.
20+
///
21+
/// When recursive is set to true, the method will also add all subdirectories
22+
/// and their files as dependencies.
23+
Future<void> addDataAssetDirectories(
24+
List<String> paths, {
25+
required BuildInput input,
26+
bool recursive = false,
27+
}) async {
28+
String assetName(Uri assetUri) => assetUri
29+
.toFilePath(windows: false)
30+
.substring(input.packageRoot.toFilePath().length);
31+
32+
for (final path in paths) {
33+
final resolvedUri = input.packageRoot.resolve(path);
34+
final directory = Directory.fromUri(resolvedUri);
35+
final file = File.fromUri(resolvedUri);
36+
37+
if (await directory.exists()) {
38+
try {
39+
addDependency(directory.uri);
40+
await for (final entity in directory.list(
41+
recursive: recursive,
42+
followLinks: false,
43+
)) {
44+
if (entity is File) {
45+
assets.data.add(
46+
DataAsset(
47+
package: input.packageName,
48+
name: assetName(entity.uri),
49+
file: entity.uri,
50+
),
51+
);
52+
}
53+
addDependency(entity.uri);
54+
}
55+
} on FileSystemException catch (e) {
56+
throw FileSystemException(
57+
'Error reading directory "$path": ${e.message}',
58+
directory.path,
59+
e.osError,
60+
);
61+
}
62+
} else if (await file.exists()) {
63+
assets.data.add(
64+
DataAsset(
65+
package: input.packageName,
66+
name: assetName(file.uri),
67+
file: file.uri,
68+
),
69+
);
70+
addDependency(file.uri);
71+
} else {
72+
throw FileSystemException(
73+
'Path does not exist',
74+
resolvedUri.toFilePath(windows: Platform.isWindows),
75+
);
76+
}
77+
}
78+
}
79+
}
80+
981
/// Extension to the [HookConfig] providing access to configuration specific
1082
/// to data assets.
1183
extension CodeAssetHookConfig on HookConfig {

pkgs/native_assets_cli/test/data_assets/validation_test.dart

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ void main() {
3535
BuildInputBuilder()
3636
..setupShared(
3737
packageName: packageName,
38-
packageRoot: tempUri,
38+
packageRoot: tempUri.resolve('$packageName/'),
3939
outputFile: tempUri.resolve('output.json'),
4040
outputDirectory: outDirUri,
4141
outputDirectoryShared: outDirSharedUri,
@@ -108,4 +108,106 @@ void main() {
108108
);
109109
expect(errors, contains(contains('More than one')));
110110
});
111+
112+
test('addDataAssetDirectories processes multiple directories', () async {
113+
final input = makeDataBuildInput();
114+
final outputBuilder = BuildOutputBuilder();
115+
116+
final assetsDir1Uri = packageRootUri.resolve('assets1');
117+
final assetsDir1 = Directory.fromUri(assetsDir1Uri);
118+
await assetsDir1.create(recursive: true);
119+
120+
final assetsDir2Uri = packageRootUri.resolve('assets2');
121+
final assetsDir2 = Directory.fromUri(assetsDir2Uri);
122+
await assetsDir2.create(recursive: true);
123+
124+
// Create a file in assets1.
125+
final file1Uri = assetsDir1.uri.resolve('file1.txt');
126+
final file1 = File.fromUri(file1Uri);
127+
await file1.writeAsString('Hello World');
128+
129+
// Create a file in assets2.
130+
final file2Uri = assetsDir2.uri.resolve('file2.txt');
131+
final file2 = File.fromUri(file2Uri);
132+
await file2.writeAsString('Hello Dart');
133+
134+
final output = BuildOutput(outputBuilder.json);
135+
await outputBuilder.addDataAssetDirectories([
136+
'assets1',
137+
'assets2',
138+
], input: input);
139+
140+
// Check that the files in both directories were added as dependencies.
141+
expect(output.dependencies, contains(file1Uri));
142+
expect(output.dependencies, contains(file2Uri));
143+
});
144+
145+
test('addDataAssetDirectories processes one file', () async {
146+
final input = makeDataBuildInput();
147+
final outputBuilder = BuildOutputBuilder();
148+
149+
final assetsDirUri = packageRootUri.resolve('single_assets');
150+
final assetsDir = Directory.fromUri(assetsDirUri);
151+
await assetsDir.create(recursive: true);
152+
153+
// Create a file in the single_assets directory.
154+
final fileUri = assetsDir.uri.resolve('single_file.txt');
155+
final file = File.fromUri(fileUri)..createSync();
156+
await file.writeAsString('Test content');
157+
158+
final output = BuildOutput(outputBuilder.json);
159+
await outputBuilder.addDataAssetDirectories([
160+
'single_assets/single_file.txt',
161+
], input: input);
162+
163+
// Check that the file in the directory was added as a dependency.
164+
expect(output.dependencies, contains(fileUri));
165+
});
166+
167+
test('addDataAssetDirectories processes nested directories', () async {
168+
final input = makeDataBuildInput();
169+
final outputBuilder = BuildOutputBuilder();
170+
171+
// Create top-level assets directory.
172+
final assetsDirUri = packageRootUri.resolve('assets3');
173+
final assetsDir = Directory.fromUri(assetsDirUri);
174+
await assetsDir.create(recursive: true);
175+
176+
// Create nested subdirectory.
177+
final nestedDirUri = assetsDir.uri.resolve('subdir');
178+
final nestedDir = Directory.fromUri(nestedDirUri);
179+
await nestedDir.create(recursive: true);
180+
181+
final nestedDir2Uri = nestedDir.uri.resolve('subdir2');
182+
final nestedDir2 = Directory.fromUri(nestedDir2Uri);
183+
await nestedDir2.create(recursive: true);
184+
185+
// Create a file in the top-level assets directory.
186+
final fileTopUri = assetsDir.uri.resolve('top_file.txt');
187+
final fileTop = File.fromUri(fileTopUri);
188+
await fileTop.writeAsString('Top level file');
189+
190+
// Create a file in the nested subdirectory.
191+
final nestedFileUri = nestedDir.uri.resolve('nested_file.txt');
192+
final nestedFile = File.fromUri(nestedFileUri);
193+
await nestedFile.writeAsString('Nested file');
194+
195+
// Create a file in the nested subdirectory.
196+
final nestedFile2Uri = nestedDir2.uri.resolve('nested_file2.txt');
197+
final nestedFile2 = File.fromUri(nestedFile2Uri);
198+
await nestedFile2.writeAsString('Nested file 2');
199+
200+
final output = BuildOutput(outputBuilder.json);
201+
await outputBuilder.addDataAssetDirectories(
202+
['assets3'],
203+
input: input,
204+
recursive: true,
205+
);
206+
207+
// Verify that the top-level directory, nested directory, and both files are
208+
// added.
209+
expect(output.dependencies, contains(fileTopUri));
210+
expect(output.dependencies, contains(nestedFileUri));
211+
expect(output.dependencies, contains(nestedFile2Uri));
212+
});
111213
}

0 commit comments

Comments
 (0)