loadWebModule static method

Future<NitroWasmModule> loadWebModule(
  1. String libName, {
  2. String? jsUrl,
  3. String? assetPackage,
})

Loads the Emscripten glue script for libName and instantiates its module. Idempotent: concurrent and repeated calls share one load.

jsUrl is the URL of the MODULARIZE glue .js (the .wasm is fetched relative to it). Defaults to the Flutter asset path assets/packages/<package>/assets/web/<libName>.js when assetPackage is given, else assets/web/<libName>.js relative to the page.

Implementation

static Future<NitroWasmModule> loadWebModule(
  String libName, {
  String? jsUrl,
  String? assetPackage,
}) {
  final cached = _moduleCache[libName];
  if (cached != null) {
    _libRefCount[libName] = (_libRefCount[libName] ?? 0) + 1;
    return Future.value(cached);
  }
  // Every awaiter of a shared in-flight load must count as a holder, and a
  // FAILED load must not stay cached — otherwise a transient network error
  // poisons the module forever, and N concurrent callers share one refcount
  // so the first releaseLib evicts a module the others still hold.
  final inFlight = _loading[libName];
  if (inFlight != null) {
    // Count only on success, so a shared FAILED load leaks no reference.
    return inFlight.then((m) {
      _libRefCount[libName] = (_libRefCount[libName] ?? 0) + 1;
      return m;
    });
  }
  return _loading.putIfAbsent(libName, () async {
    final url = jsUrl ?? (assetPackage != null ? 'assets/packages/$assetPackage/assets/web/$libName.js' : 'assets/web/$libName.js');
    _log(NitroLogLevel.verbose, 'loadWebModule', 'Loading WASM module: $libName from $url');
    final sw = Stopwatch()..start();

    await _injectScript(url, libName);

    final factoryName = nitroWebExportName(libName);
    final factory = globalContext.getProperty(factoryName.toJS);
    if (factory == null || !factory.typeofEquals('function')) {
      throw StateError(
        '$libName: $url loaded but did not define $factoryName(). Build the '
        'module with -sMODULARIZE=1 -sEXPORT_NAME=$factoryName '
        '(web/build_web.sh does this).',
      );
    }
    final promise = (factory as JSFunction).callAsFunction(null, JSObject())! as JSPromise;
    final raw = await promise.toDart;
    final module = NitroWasmModule(libName, EmscriptenModule(raw! as JSObject));

    _registerPostFn(module);

    sw.stop();
    _log(NitroLogLevel.verbose, 'loadWebModule', 'Loaded: $libName in ${sw.elapsedMicroseconds} µs');
    _moduleCache[libName] = module;
    _libRefCount[libName] = (_libRefCount[libName] ?? 0) + 1;
    return module;
  }).whenComplete(() => _loading.remove(libName));
}