Dart build_runner 只能扫描/读/写web/目录下的文件吗?

Edm*_*Tam 5 dart flutter

我正在开发一个 Flutter 应用程序,并且想编写一个构建脚本来将某种原始文件(CSV 格式)转换为格式化的 JSON 文件以作为 Flutter 资产包含在内。

通过使用像json_serializablejaguar_serializer我了解的库build_runner,所以看起来我自己编写Builder并通过调用它build_runner是一种明智的方式。

由于有关编写我们自己的构建脚本的资源非常有限,我首先修改了此处找到的示例。但是我在尝试更改输入输出文件的路径时卡住了:当我运行时flutter pub pub run build_runner build,结果发现Dart只搜索[project_dir]/web目录中匹配的文件,并且只允许我将文件写入该web目录。所以这段代码

buildStep.writeAsString(new AssetId(buildStep.inputId.package, 'assets/resources/foo.json'), '[]');
Run Code Online (Sandbox Code Playgroud)

将产生以下异常:

UnexpectedOutputException: myapp|assets/resources/foo.json
Expected only: {myapp|web/.json}
[SEVERE] Failed after 24.4s
Run Code Online (Sandbox Code Playgroud)

json_serializable并且jaguar_serializer可以在任何地方自由生成代码这一事实似乎表明我的配置有问题。但是我web在我的代码和build.yaml文件中的任何地方都找不到这个东西,所以这真的很令人费解。

FWIW,这是我的builder.yaml文件的内容:

builders:
  jsonBuilder:
    import: "package:myapp/builder.dart"
    builder_factories: ["jsonFileBuilder"]
    build_extensions: {"source.csv": [".json"]}
    build_to: source
    auto_apply: root_package
Run Code Online (Sandbox Code Playgroud)

这是builder.dart文件

import 'dart:async';
import 'package:build/build.dart';

Builder jsonFileBuilder(BuilderOptions options) => new JsonFileBuilder();

class JsonFileBuilder implements Builder {
  @override
  Future build(BuildStep buildStep) async {
    await buildStep.writeAsString(
        new AssetId(buildStep.inputId.package, 'assets/resources/foo.json'),
        'hello');
  }

  @override
  final buildExtensions = const {
    'source.csv': const ['.json']
  };
}
Run Code Online (Sandbox Code Playgroud)

提前致谢!

Edm*_*Tam 11

好吧,看来这个问题实际上是双重的。

关于输入

我的问题是,如果我将源 CSV 文件放在某个任意目录(如offline)下,由于找不到匹配的文件,我的构建脚本将无法运行。但是当我将文件放在web(如示例中)下时,脚本会被调用。这给了我一种错觉,即文件扫描仅限于web/. 事实是,有一个硬编码文件/目录白名单列表

const List<String> _defaultRootPackageWhitelist = const [
  'benchmark/**',
  'bin/**',
  'example/**',
  'lib/**',
  'test/**',
  'tool/**',
  'web/**',
  'pubspec.yaml',
  'pubspec.lock',
];
Run Code Online (Sandbox Code Playgroud)

如果我将 CSV 文件放在lib/.

要将我的非白名单文件附加到列表中,我必须将以下部分添加到build.yaml.

targets:
  $default:
    sources: 
    # Need to replicate the default whitelist here or other build will break:
    - "benchmark/**"
    - "bin/**"
    - "example/**"
    - "lib/**"
    - "test/**"
    - "tool/**"
    - "web/**"
    - "pubspec.yaml"
    - "pubspec.lock"
    # My new source
    - "offline/source.csv"
Run Code Online (Sandbox Code Playgroud)

关于输出

我的问题是我无法写入任意目录,构建系统一直抱怨我只能写入web/. 所以我认为文件写入被锁定到web/. 同样,这并不完全正确,因为文件写入实际上被锁定在输入文件的目录中。这意味着我只能offline/在构建运行程序成功识别此目录中的文件后才能写入。

文件写入权限的检查由以下方法build_step_impl.dart控制:

  void _checkOutput(AssetId id) {
    if (!_expectedOutputs.contains(id)) {
      throw new UnexpectedOutputException(id, expected: _expectedOutputs);
    }
  }
Run Code Online (Sandbox Code Playgroud)

_expectedOutputsbuildExtensions我的构建器类的地图的展平值。所以如果我真的想写信给assets/resources/foo.json我,我必须这样写:

  @override
  final buildExtensions = const {
    'source.csv': const ['/../../assets/resources/foo.json']
  };
Run Code Online (Sandbox Code Playgroud)

build.yaml必须进行类似的调整:

build_extensions: {".csv": ["/../../assets/resources/foo.json"]}
Run Code Online (Sandbox Code Playgroud)

这基本上解决了我的大部分问题,但仍有一些问题:

首先,我计划编写一个构建脚本来读取单个文件并写入多个文件。文件的数量及其文件名取决于源文件的内容。鉴于检查代码,这似乎不是受支持的功能。不是拦截器,但有点不方便。

第二个是关于输出文件路径。您可以看到我的源文件重新加载[projectDir]/offline并且我想写入[projectDir]/assets,但输出文件名读取/../../assets/...,这是两个级别。这是因为原始文件名(没有扩展名)被添加到输出路径中,这就是为什么会.g.dart在生成 Dart 代码的构建器配置中看到的原因。我需要这样写/../../assets,我的文件的完整输出路径将变成[projectDir]/offline/source/../../assets使其解析为所需的输出文件路径。但是默认行为给我的印象是构建系统不希望我的脚本写入当前文件目录之外的任何地方,而我正在做的是对系统的黑客攻击或滥用。