dart-lang / native

Dart packages related to FFI and native assets bundling.
BSD 3-Clause "New" or "Revised" License
154 stars 43 forks source link

[native_assets_cli] Helper to add all files in a directory as `DataAsset`s #1346

Open dcharkes opened 3 months ago

dcharkes commented 3 months ago

Adding all files in a directory as data assets seems to be a common use case.

In Flutter, all files in a directory can be added as assets easily:

https://docs.flutter.dev/ui/assets/assets-and-images#specifying-assets

flutter:
  assets:
    - directory/
    - directory/subdirectory/

Using these assets in Flutter is via the full file path:

https://docs.flutter.dev/ui/assets/assets-and-images#loading-text-assets

await rootBundle.loadString('assets/config.json')

We should be able to support this type of use case:

void main(List<String> args) async {
  await build(args, (config, output) async {
    await output.addDataAssetsDirectory('directory/', config);
    await output.addDataAssetsDirectory('directory/subdirectory/', config);
  });
}

Implementation sketch:

extension on BuildOutput {
  /// Adds all the files in [directory] as [DataAsset]s to this [BuildOutput].
  ///
  /// The [directory] must end with a slash and be relative to
  /// [BuildConfig.packageRoot].
  Future<void> addDataAssetsDirectory(
    String directory, {
    required BuildConfig config,
    String? linkInPackage,
  }) async {
    final packageName = config.packageName;
    final packageRoot = config.packageRoot;
    final assetDirectory = Directory.fromUri(packageRoot.resolve('assets/'));
    addDependency(assetDirectory.uri);

    await for (final assetFile in assetDirectory.list()) {
      if (assetFile is! File) {
        continue;
      }
      final assetUri = assetFile.uri;

      // The file path relative to the package root, with forward slashes.
      final name = assetUri
          .toFilePath(windows: false)
          .substring(packageRoot.toFilePath(windows: false).length);

      addAsset(
        DataAsset(
          package: packageName,
          name: name,
          file: assetUri,
        ),
        linkInPackage: linkInPackage,
      );
      addDependency(assetUri);
    }
  }
}

With such an implementation we'd also establish a convention that the a data asset in assets/foo.json will be available at runtime as package:my_package/assets/foo.json which aligns with the Flutter assets approach of making assets available via their full path.

Inspired by https://github.com/dart-lang/native/pull/1345#discussion_r1679054714.

Open to alternative suggestions, WDYT @mosuem?

mosuem commented 3 months ago

As discussed earlier:

In would be even nicer if adding all assets was enabled by default. This requires us to land automatic treeshaking of assets first, and needs special care because of existing asset folders.

dcharkes commented 3 months ago

And we could add bool recursive param to the helper function.