compileSources static method

JsModuleBundle compileSources({
  1. required String entry,
  2. required Map<String, String> sources,
  3. bool stripSource = true,
  4. bool verify = true,
})

Compile sources (module name → source text) into a bundle.

Compilation runs in a scratch engine so the peak parser cost is not charged to the engine that later runs the code. When verify is set the import graph is linked (but not executed) and a missing module fails here instead of at the first import at runtime.

stripSource drops function source text, which shrinks both the bytecode and the runtime heap at the cost of Function.prototype.toString.

Implementation

static JsModuleBundle compileSources({
  required String entry,
  required Map<String, String> sources,
  bool stripSource = true,
  bool verify = true,
}) {
  if (!sources.containsKey(entry)) {
    throw ArgumentError.value(
      entry,
      'entry',
      'entry module is not present in sources',
    );
  }
  final modules = <String, Uint8List>{};
  final missing = <String>{};
  // QuickJS resolves a module's imports while compiling it, so the scratch
  // engine has to be able to reach every dependency. Serving them from
  // [sources] is also what turns an incomplete graph into an error here
  // instead of at the first import at runtime.
  final scratch = QuickJsRuntime2(
    memoryLimit: 0,
    webApis: const JsWebApis.none(),
    moduleHandler: (name) {
      final source = sources[name];
      if (source == null) {
        missing.add(name);
        throw JSError('module "$name" is not in sources');
      }
      return source;
    },
  );
  try {
    sources.forEach((name, source) {
      missing.clear();
      try {
        modules[name] = scratch.compile(
          source,
          name,
          stripSource: stripSource,
          asModule: true,
        );
      } on JSError catch (error) {
        if (missing.isEmpty) rethrow;
        throw JSError(
          '$name imports ${missing.join(', ')}, which '
          '${missing.length == 1 ? 'is' : 'are'} not in sources',
          error.stack,
        );
      }
    });
  } finally {
    scratch.dispose();
  }
  final bundle = JsModuleBundle(entry: entry, modules: modules);
  if (verify) bundle.verify();
  return bundle;
}