Version 3.14.0-110.0.dev

Merge 0b79bdfb86dbbb3e18c232b45fa462a7293e7db4 into dev
diff --git a/build/toolchain/win/setup_toolchain.py b/build/toolchain/win/setup_toolchain.py
index 434ca9f..dd11516 100644
--- a/build/toolchain/win/setup_toolchain.py
+++ b/build/toolchain/win/setup_toolchain.py
@@ -22,7 +22,7 @@
 import gn_helpers
 
 SCRIPT_DIR = os.path.dirname(__file__)
-SDK_VERSION = '10.0.26100.0'
+SDK_VERSION = '10.0.28000.0'
 
 
 def _ExtractImportantEnvironment(output_of_set):
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index ee033b3..1830c72 100644
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -3,6 +3,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+# For Dart/Flutter developers:
+# This file keeps the MSVC toolchain up-to-date for Google developers.
+# It is copied from Chromium:
+#   https://cs.chromium.org/chromium/src/build/vs_toolchain.py
+# with modifications that update paths, and remove dependencies on gyp.
+# To update to a new MSVC toolchain, copy the updated script from the Chromium
+# tree, and edit to make it work in the Dart tree by updating paths in the original script.
+
 
 import collections
 import glob
@@ -17,7 +25,7 @@
 
 from gn_helpers import ToGNString
 
-# VS 2022 17.13.4 with 10.0.26100.3323 SDK with ARM64 libraries and UWP support.
+# VS 2022 17.13.4 with 10.0.26100.4654 SDK with ARM64 libraries and UWP support.
 # See go/win-toolchain-reference for instructions about how to update the
 # toolchain.
 #
@@ -59,7 +67,7 @@
 # * docs/windows_build_instructions.md
 #   Make sure any version numbers in the documentation match the code.
 #
-TOOLCHAIN_HASH = '076960eda6'
+TOOLCHAIN_HASH = 'e4305f407e'
 SDK_VERSION = '10.0.26100.0'
 
 # Visual Studio versions are listed in descending order of priority.
@@ -304,7 +312,7 @@
   list_of_str_versions.sort(key=to_number_sequence, reverse=True)
 
 
-def _CopyUCRTRuntime(target_dir, source_dir, target_cpu, suffix):
+def _CopyUCRTRuntime(target_dir, source_dir, target_cpu, debug):
   """Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't
   exist, but the target directory does exist."""
   if target_cpu == 'arm64':
@@ -312,7 +320,7 @@
     # {x.y.z}/[debug_nonredist/]arm64/Microsoft.VC14x.CRT/.
     # Select VC toolset directory based on Visual Studio version
     vc_redist_root = FindVCRedistRoot()
-    if suffix.startswith('.'):
+    if not debug:
       vc_toolset_dir = 'Microsoft.{}.CRT' \
          .format(MSVC_TOOLSET_VERSION[GetVisualStudioVersion()])
       source_dir = os.path.join(vc_redist_root,
@@ -322,17 +330,25 @@
          .format(MSVC_TOOLSET_VERSION[GetVisualStudioVersion()])
       source_dir = os.path.join(vc_redist_root, 'debug_nonredist',
                                 'arm64', vc_toolset_dir)
-  file_parts = ('msvcp140', 'vccorlib140', 'vcruntime140')
+
+  # The filepaths may have an additional 'd' depending on whether we are in
+  # debug mode.
+  def d(s):
+    return s + 'd' if debug else s
+
+  file_parts = (d('msvcp140'), d('msvcp140') + '_atomic_wait', d('vccorlib140'),
+                d('vcruntime140'))
   if target_cpu == 'x64' and GetVisualStudioVersion() != '2017':
-    file_parts = file_parts + ('vcruntime140_1', )
+    file_parts = file_parts + (d('vcruntime140_1'), )
   for file_part in file_parts:
-    dll = file_part + suffix
+    dll = file_part + '.dll'
     target = os.path.join(target_dir, dll)
     source = os.path.join(source_dir, dll)
     _CopyRuntimeImpl(target, source)
+
   # We must copy ucrtbased.dll for all CPU types. The rest of the Universal CRT
   # is installed as part of the OS in Windows 10 and beyond.
-  if not suffix.startswith('.'):
+  if debug:
     win_sdk_dir = os.path.normpath(
         os.environ.get(
             'WINDOWSSDKDIR',
@@ -352,8 +368,10 @@
       if not os.path.isdir(source_dir):
         continue
       break
-    _CopyRuntimeImpl(os.path.join(target_dir, 'ucrtbase' + suffix),
-                     os.path.join(source_dir, 'ucrtbase' + suffix))
+    _CopyRuntimeImpl(os.path.join(target_dir,
+                                  d('ucrtbase') + '.dll'),
+                     os.path.join(source_dir,
+                                  d('ucrtbase') + '.dll'))
 
 
 def FindVCComponentRoot(component):
@@ -389,9 +407,8 @@
 def _CopyRuntime(target_dir, source_dir, target_cpu, debug):
   """Copy the VS runtime DLLs, only if the target doesn't exist, but the target
   directory does exist. Handles VS 2015, 2017 and 2019."""
-  suffix = 'd.dll' if debug else '.dll'
   # VS 2015, 2017 and 2019 use the same CRT DLLs.
-  _CopyUCRTRuntime(target_dir, source_dir, target_cpu, suffix)
+  _CopyUCRTRuntime(target_dir, source_dir, target_cpu, debug)
 
 
 def CopyDlls(target_dir, configuration, target_cpu):
@@ -529,7 +546,11 @@
         # ciopfs not found in PATH; try the one downloaded from the DEPS hook.
         ciopfs = os.path.join(script_dir, 'ciopfs')
       if not os.path.isdir(toolchain_dir):
-        os.mkdir(toolchain_dir)
+        try:
+          os.mkdir(toolchain_dir)
+        except FileExistsError:
+          # ciopfsd died, but fuse is still mounted.
+          subprocess.check_call(["fusermount", "-u", toolchain_dir])
       if not os.path.isdir(toolchain_dir + '.ciopfs'):
         os.mkdir(toolchain_dir + '.ciopfs')
       # Without use_ino, clang's #pragma once and Wnonportable-include-path
diff --git a/pkg/dart2wasm/lib/compile.dart b/pkg/dart2wasm/lib/compile.dart
index 8da4f24..6c9601b 100644
--- a/pkg/dart2wasm/lib/compile.dart
+++ b/pkg/dart2wasm/lib/compile.dart
@@ -656,6 +656,7 @@
       translator.internalizedStringsForJSRuntime,
       translator.options.enableDeferredLoading ||
           translator.options.enableMultiModuleStressTestMode,
+      options.supportsES6Modules,
     );
 
     final supportJs = _generateSupportJs(options.translatorOptions);
diff --git a/pkg/dart2wasm/lib/compiler_options.dart b/pkg/dart2wasm/lib/compiler_options.dart
index 2ac9451..7bcaba6 100644
--- a/pkg/dart2wasm/lib/compiler_options.dart
+++ b/pkg/dart2wasm/lib/compiler_options.dart
@@ -61,6 +61,7 @@
   Set<int> moduleIdsToOptimize = const {};
   bool stripWasm = true;
   Uri? recordedUsesFile;
+  bool supportsES6Modules = true;
   List<CompilerPhase> phases = const [
     CompilerPhase.cfe,
     CompilerPhase.tfa,
diff --git a/pkg/dart2wasm/lib/dart2wasm.dart b/pkg/dart2wasm/lib/dart2wasm.dart
index ec6fc03..4b93893 100644
--- a/pkg/dart2wasm/lib/dart2wasm.dart
+++ b/pkg/dart2wasm/lib/dart2wasm.dart
@@ -218,6 +218,12 @@
     (o, value) => o.moduleIdsToOptimize = value.toSet(),
   ),
   UriOption("recorded-uses", (o, value) => o.recordedUsesFile = value),
+  Flag(
+    "supports-es6-modules",
+    (o, value) => o.supportsES6Modules = value,
+    defaultsTo: _d.supportsES6Modules,
+    negatable: true,
+  ),
 ];
 
 Map<fe.ExperimentalFlag, bool> processFeExperimentalFlags(
diff --git a/pkg/dart2wasm/lib/js/runtime_blob.dart b/pkg/dart2wasm/lib/js/runtime_blob.dart
index 47e3d5c..4294524 100644
--- a/pkg/dart2wasm/lib/js/runtime_blob.dart
+++ b/pkg/dart2wasm/lib/js/runtime_blob.dart
@@ -1,26 +1,54 @@
 // Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
+final nonEs6MjsTemplate = Template(r'''(function() {
+const exportObject = {};
 
-final jsRuntimeBlobTemplate = Template(r'''
 // Compiles a dart2wasm-generated main module from `source` which can then
 // be instantiated via the `instantiate` method.
 //
 // `source` needs to be a `Response` object (or promise thereof) e.g. created
 // via the `fetch()` JS API.
-export async function compileStreaming(source) {
-  const builtins = {<<BUILTINS_MAP_BODY>>};
-  return new CompiledApp(
-      await WebAssembly.compileStreaming(source, builtins), builtins);
-}
+exportObject.compileStreaming = <<COMPILE_STREAMING>>;
 
 // Compiles a dart2wasm-generated wasm module from `bytes` which is then
 // instantiable via the `instantiate` method.
-export async function compile(bytes) {
+exportObject.compile = <<COMPILE>>;
+
+<<REST>>
+
+return exportObject;
+})''');
+
+final es6MjsTemplate = Template(
+  r'''// Compiles a dart2wasm-generated main module from `source` which can then
+// be instantiated via the `instantiate` method.
+//
+// `source` needs to be a `Response` object (or promise thereof) e.g. created
+// via the `fetch()` JS API.
+export <<COMPILE_STREAMING>>
+
+// Compiles a dart2wasm-generated wasm module from `bytes` which is then
+// instantiable via the `instantiate` method.
+export <<COMPILE>>
+
+<<REST>>''',
+);
+
+final compileStreamingTemplate = Template(
+  r'''async function compileStreaming(source) {
+  const builtins = {<<BUILTINS_MAP_BODY>>};
+  return new CompiledApp(
+      await WebAssembly.compileStreaming(source, builtins), builtins);
+}''',
+);
+
+final compileTemplate = Template(r'''async function compile(bytes) {
   const builtins = {<<BUILTINS_MAP_BODY>>};
   return new CompiledApp(await WebAssembly.compile(bytes, builtins), builtins);
-}
+}''');
 
+final jsRuntimeBlobTemplate = Template(r'''
 class CompiledApp {
   constructor(module, builtins) {
     this.module = module;
diff --git a/pkg/dart2wasm/lib/js/runtime_generator.dart b/pkg/dart2wasm/lib/js/runtime_generator.dart
index 4ac9706..aa7173d 100644
--- a/pkg/dart2wasm/lib/js/runtime_generator.dart
+++ b/pkg/dart2wasm/lib/js/runtime_generator.dart
@@ -123,6 +123,7 @@
     Iterable<Procedure> translatedProcedures,
     List<String> constantStrings,
     bool supportsAdditionalModuleLoading,
+    bool supportsES6Modules,
   ) {
     final jsMethods = generateJsMethods(translatedProcedures);
 
@@ -148,18 +149,34 @@
             'MAIN_MODULE_NAME': mainModuleName,
           })
         : '';
+    final result = StringBuffer();
 
-    return jsRuntimeBlobTemplate.instantiate({
-      ...jsStringBuiltinPolyfillImportVars,
-      ...moduleLoadingImportVars,
-      'BUILTINS_MAP_BODY': builtins.join(', '),
-      'JS_METHODS': jsMethods,
-      'INTERNAL_IMPORTS_MODULE_NAME':
-          _interopMemberNamer.interopHelperModuleName,
-      'IMPORTED_JS_STRINGS_IN_MJS': internalizedStrings,
-      'JS_STRING_POLYFILL_METHODS': jsPolyFillMethods,
-      'DEFERRED_LIBRARY_HELPER_METHODS': moduleLoadingHelperMethods,
-    });
+    final moduleTemplate = supportsES6Modules
+        ? es6MjsTemplate
+        : nonEs6MjsTemplate;
+    result.write(
+      moduleTemplate.instantiate({
+        'COMPILE_STREAMING': compileStreamingTemplate.instantiate({
+          'BUILTINS_MAP_BODY': builtins.join(', '),
+        }),
+        'COMPILE': compileTemplate.instantiate({
+          'BUILTINS_MAP_BODY': builtins.join(', '),
+        }),
+        'REST': jsRuntimeBlobTemplate.instantiate({
+          ...jsStringBuiltinPolyfillImportVars,
+          ...moduleLoadingImportVars,
+          'BUILTINS_MAP_BODY': builtins.join(', '),
+          'JS_METHODS': jsMethods,
+          'INTERNAL_IMPORTS_MODULE_NAME':
+              _interopMemberNamer.interopHelperModuleName,
+          'IMPORTED_JS_STRINGS_IN_MJS': internalizedStrings,
+          'JS_STRING_POLYFILL_METHODS': jsPolyFillMethods,
+          'DEFERRED_LIBRARY_HELPER_METHODS': moduleLoadingHelperMethods,
+        }),
+      }),
+    );
+
+    return result.toString();
   }
 }
 
diff --git a/pkg/dart2wasm/test/mjs_format/main.dart b/pkg/dart2wasm/test/mjs_format/main.dart
new file mode 100644
index 0000000..2bfa7d3
--- /dev/null
+++ b/pkg/dart2wasm/test/mjs_format/main.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2026, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+void main() {
+  print('Hello, mjs!');
+}
diff --git a/pkg/dart2wasm/test/mjs_format/mjs_es6.mjs.golden b/pkg/dart2wasm/test/mjs_format/mjs_es6.mjs.golden
new file mode 100644
index 0000000..72a6a5f
--- /dev/null
+++ b/pkg/dart2wasm/test/mjs_format/mjs_es6.mjs.golden
@@ -0,0 +1,186 @@
+// Compiles a dart2wasm-generated main module from `source` which can then
+// be instantiated via the `instantiate` method.
+//
+// `source` needs to be a `Response` object (or promise thereof) e.g. created
+// via the `fetch()` JS API.
+export async function compileStreaming(source) {
+  const builtins = {builtins: ['js-string'], importedStringConstants: ''};
+  return new CompiledApp(
+      await WebAssembly.compileStreaming(source, builtins), builtins);
+}
+
+// Compiles a dart2wasm-generated wasm module from `bytes` which is then
+// instantiable via the `instantiate` method.
+export async function compile(bytes) {
+  const builtins = {builtins: ['js-string'], importedStringConstants: ''};
+  return new CompiledApp(await WebAssembly.compile(bytes, builtins), builtins);
+}
+
+class CompiledApp {
+  constructor(module, builtins) {
+    this.module = module;
+    this.builtins = builtins;
+  }
+
+  // The second argument is an options object containing:
+  // `loadDeferredModules` is a JS function that takes an array of module names
+  //   matching wasm files produced by the dart2wasm compiler. It also takes a
+  //   callback that should be invoked for each loaded module with 2 arguments:
+  //   (1) the module name, (2) the loaded module in a format supported by
+  //   `WebAssembly.compile` or `WebAssembly.compileStreaming`. The callback
+  //   returns a Promise that resolves when the module is instantiated.
+  //   loadDeferredModules should return a Promise that resolves when all the
+  //   modules have been loaded and the callback promises have resolved.
+  // `loadDeferredId` is a JS function that takes load ID produced by the
+  //   compiler when the `use-load-ids` option is passed. Each load ID maps to
+  //   one or more wasm files as specified in the emitted JSON file. It also
+  //   takes a callback that should be invoked for each loaded module with 2
+  //   arguments: (1) the module name, (2) the loaded module in a format
+  //   supported by `WebAssembly.compile` or `WebAssembly.compileStreaming`.
+  //   The callback returns a Promise that resolves when the module is
+  //   instantiated.
+  //   loadDeferredId should return a Promise that resolves when all the
+  //   modules have been loaded and the callback promises have resolved.
+  async instantiate(additionalImports, {loadDeferredModules, loadDeferredId} = {}) {
+    let dartInstance;
+
+    // Prints to the console
+    function printToConsole(value) {
+      if (typeof dartPrint == "function") {
+        dartPrint(value);
+        return;
+      }
+      if (typeof console == "object" && typeof console.log != "undefined") {
+        console.log(value);
+        return;
+      }
+      if (typeof print == "function") {
+        print(value);
+        return;
+      }
+
+      throw "Unable to print message: " + value;
+    }
+
+    // A special symbol attached to functions that wrap Dart functions.
+    const jsWrappedDartFunctionSymbol = Symbol("JSWrappedDartFunction");
+
+    function finalizeWrapper(dartFunction, wrapped) {
+      wrapped.dartFunction = dartFunction;
+      wrapped[jsWrappedDartFunctionSymbol] = true;
+      return wrapped;
+    }
+
+    // Imports
+    const dart2wasm = {
+            B: s => printToConsole(s),
+      C: Function.prototype.call.bind(Number.prototype.toString),
+      D: Function.prototype.call.bind(BigInt.prototype.toString),
+      E: (exn) => {
+        let stackString = exn.toString();
+        let frames = stackString.split('\n');
+        let drop = 4;
+        if (frames[0].startsWith('Error')) {
+            drop += 1;
+        }
+        return frames.slice(drop).join('\n');
+      },
+      F: () => new Error().stack,
+      G: s => JSON.stringify(s),
+      H: Function.prototype.call.bind(Number.prototype.toString),
+      I: Function.prototype.call.bind(String.prototype.indexOf),
+      J: (exn) => {
+        if (exn instanceof Error) {
+          return exn.stack;
+        } else {
+          return null;
+        }
+      },
+      K: o => String(o),
+      L: o => o === undefined,
+
+    };
+
+    const baseImports = {
+      dart2wasm: dart2wasm,
+      Math: Math,
+      Date: Date,
+      Object: Object,
+      Array: Array,
+      Reflect: Reflect,
+      WebAssembly: {
+        JSTag: WebAssembly.JSTag,
+      },
+      "": new Proxy({}, { get(_, prop) { return prop; } }),
+
+    };
+
+    const jsStringPolyfill = {
+      "charCodeAt": (s, i) => s.charCodeAt(i),
+      "compare": (s1, s2) => {
+        if (s1 < s2) return -1;
+        if (s1 > s2) return 1;
+        return 0;
+      },
+      "concat": (s1, s2) => s1 + s2,
+      "equals": (s1, s2) => s1 === s2,
+      "fromCharCode": (i) => String.fromCharCode(i),
+      "length": (s) => s.length,
+      "substring": (s, a, b) => s.substring(a, b),
+      "fromCharCodeArray": (a, start, end) => {
+        if (end <= start) return '';
+
+        const read = dartInstance.exports.$wasmI16ArrayGet;
+        let result = '';
+        let index = start;
+        const chunkLength = Math.min(end - index, 500);
+        let array = new Array(chunkLength);
+        while (index < end) {
+          const newChunkLength = Math.min(end - index, 500);
+          for (let i = 0; i < newChunkLength; i++) {
+            array[i] = read(a, index++);
+          }
+          if (newChunkLength < chunkLength) {
+            array = array.slice(0, newChunkLength);
+          }
+          result += String.fromCharCode(...array);
+        }
+        return result;
+      },
+      "intoCharCodeArray": (s, a, start) => {
+        if (s === '') return 0;
+
+        const write = dartInstance.exports.$wasmI16ArraySet;
+        for (var i = 0; i < s.length; ++i) {
+          write(a, start++, s.charCodeAt(i));
+        }
+        return s.length;
+      },
+      "test": (s) => typeof s == "string",
+    };
+
+
+    
+
+    dartInstance = await WebAssembly.instantiate(this.module, {
+      ...baseImports,
+      ...additionalImports,
+      
+      "wasm:js-string": jsStringPolyfill,
+    });
+
+    return new InstantiatedApp(this, dartInstance);
+  }
+}
+
+class InstantiatedApp {
+  constructor(compiledApp, instantiatedModule) {
+    this.compiledApp = compiledApp;
+    this.instantiatedModule = instantiatedModule;
+  }
+
+  // Call the main function with the given arguments.
+  invokeMain(...args) {
+    this.instantiatedModule.exports.$invokeMain(args);
+  }
+}
diff --git a/pkg/dart2wasm/test/mjs_format/mjs_no_es6.mjs.golden b/pkg/dart2wasm/test/mjs_format/mjs_no_es6.mjs.golden
new file mode 100644
index 0000000..410d03b
--- /dev/null
+++ b/pkg/dart2wasm/test/mjs_format/mjs_no_es6.mjs.golden
@@ -0,0 +1,193 @@
+(function() {
+const exportObject = {};
+
+// Compiles a dart2wasm-generated main module from `source` which can then
+// be instantiated via the `instantiate` method.
+//
+// `source` needs to be a `Response` object (or promise thereof) e.g. created
+// via the `fetch()` JS API.
+exportObject.compileStreaming = async function compileStreaming(source) {
+  const builtins = {builtins: ['js-string'], importedStringConstants: ''};
+  return new CompiledApp(
+      await WebAssembly.compileStreaming(source, builtins), builtins);
+};
+
+// Compiles a dart2wasm-generated wasm module from `bytes` which is then
+// instantiable via the `instantiate` method.
+exportObject.compile = async function compile(bytes) {
+  const builtins = {builtins: ['js-string'], importedStringConstants: ''};
+  return new CompiledApp(await WebAssembly.compile(bytes, builtins), builtins);
+};
+
+class CompiledApp {
+  constructor(module, builtins) {
+    this.module = module;
+    this.builtins = builtins;
+  }
+
+  // The second argument is an options object containing:
+  // `loadDeferredModules` is a JS function that takes an array of module names
+  //   matching wasm files produced by the dart2wasm compiler. It also takes a
+  //   callback that should be invoked for each loaded module with 2 arguments:
+  //   (1) the module name, (2) the loaded module in a format supported by
+  //   `WebAssembly.compile` or `WebAssembly.compileStreaming`. The callback
+  //   returns a Promise that resolves when the module is instantiated.
+  //   loadDeferredModules should return a Promise that resolves when all the
+  //   modules have been loaded and the callback promises have resolved.
+  // `loadDeferredId` is a JS function that takes load ID produced by the
+  //   compiler when the `use-load-ids` option is passed. Each load ID maps to
+  //   one or more wasm files as specified in the emitted JSON file. It also
+  //   takes a callback that should be invoked for each loaded module with 2
+  //   arguments: (1) the module name, (2) the loaded module in a format
+  //   supported by `WebAssembly.compile` or `WebAssembly.compileStreaming`.
+  //   The callback returns a Promise that resolves when the module is
+  //   instantiated.
+  //   loadDeferredId should return a Promise that resolves when all the
+  //   modules have been loaded and the callback promises have resolved.
+  async instantiate(additionalImports, {loadDeferredModules, loadDeferredId} = {}) {
+    let dartInstance;
+
+    // Prints to the console
+    function printToConsole(value) {
+      if (typeof dartPrint == "function") {
+        dartPrint(value);
+        return;
+      }
+      if (typeof console == "object" && typeof console.log != "undefined") {
+        console.log(value);
+        return;
+      }
+      if (typeof print == "function") {
+        print(value);
+        return;
+      }
+
+      throw "Unable to print message: " + value;
+    }
+
+    // A special symbol attached to functions that wrap Dart functions.
+    const jsWrappedDartFunctionSymbol = Symbol("JSWrappedDartFunction");
+
+    function finalizeWrapper(dartFunction, wrapped) {
+      wrapped.dartFunction = dartFunction;
+      wrapped[jsWrappedDartFunctionSymbol] = true;
+      return wrapped;
+    }
+
+    // Imports
+    const dart2wasm = {
+            B: s => printToConsole(s),
+      C: Function.prototype.call.bind(Number.prototype.toString),
+      D: Function.prototype.call.bind(BigInt.prototype.toString),
+      E: (exn) => {
+        let stackString = exn.toString();
+        let frames = stackString.split('\n');
+        let drop = 4;
+        if (frames[0].startsWith('Error')) {
+            drop += 1;
+        }
+        return frames.slice(drop).join('\n');
+      },
+      F: () => new Error().stack,
+      G: s => JSON.stringify(s),
+      H: Function.prototype.call.bind(Number.prototype.toString),
+      I: Function.prototype.call.bind(String.prototype.indexOf),
+      J: (exn) => {
+        if (exn instanceof Error) {
+          return exn.stack;
+        } else {
+          return null;
+        }
+      },
+      K: o => String(o),
+      L: o => o === undefined,
+
+    };
+
+    const baseImports = {
+      dart2wasm: dart2wasm,
+      Math: Math,
+      Date: Date,
+      Object: Object,
+      Array: Array,
+      Reflect: Reflect,
+      WebAssembly: {
+        JSTag: WebAssembly.JSTag,
+      },
+      "": new Proxy({}, { get(_, prop) { return prop; } }),
+
+    };
+
+    const jsStringPolyfill = {
+      "charCodeAt": (s, i) => s.charCodeAt(i),
+      "compare": (s1, s2) => {
+        if (s1 < s2) return -1;
+        if (s1 > s2) return 1;
+        return 0;
+      },
+      "concat": (s1, s2) => s1 + s2,
+      "equals": (s1, s2) => s1 === s2,
+      "fromCharCode": (i) => String.fromCharCode(i),
+      "length": (s) => s.length,
+      "substring": (s, a, b) => s.substring(a, b),
+      "fromCharCodeArray": (a, start, end) => {
+        if (end <= start) return '';
+
+        const read = dartInstance.exports.$wasmI16ArrayGet;
+        let result = '';
+        let index = start;
+        const chunkLength = Math.min(end - index, 500);
+        let array = new Array(chunkLength);
+        while (index < end) {
+          const newChunkLength = Math.min(end - index, 500);
+          for (let i = 0; i < newChunkLength; i++) {
+            array[i] = read(a, index++);
+          }
+          if (newChunkLength < chunkLength) {
+            array = array.slice(0, newChunkLength);
+          }
+          result += String.fromCharCode(...array);
+        }
+        return result;
+      },
+      "intoCharCodeArray": (s, a, start) => {
+        if (s === '') return 0;
+
+        const write = dartInstance.exports.$wasmI16ArraySet;
+        for (var i = 0; i < s.length; ++i) {
+          write(a, start++, s.charCodeAt(i));
+        }
+        return s.length;
+      },
+      "test": (s) => typeof s == "string",
+    };
+
+
+    
+
+    dartInstance = await WebAssembly.instantiate(this.module, {
+      ...baseImports,
+      ...additionalImports,
+      
+      "wasm:js-string": jsStringPolyfill,
+    });
+
+    return new InstantiatedApp(this, dartInstance);
+  }
+}
+
+class InstantiatedApp {
+  constructor(compiledApp, instantiatedModule) {
+    this.compiledApp = compiledApp;
+    this.instantiatedModule = instantiatedModule;
+  }
+
+  // Call the main function with the given arguments.
+  invokeMain(...args) {
+    this.instantiatedModule.exports.$invokeMain(args);
+  }
+}
+
+
+return exportObject;
+})
\ No newline at end of file
diff --git a/pkg/dart2wasm/test/mjs_test.dart b/pkg/dart2wasm/test/mjs_test.dart
new file mode 100644
index 0000000..aac3000
--- /dev/null
+++ b/pkg/dart2wasm/test/mjs_test.dart
@@ -0,0 +1,120 @@
+// Copyright (c) 2026, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:args/args.dart';
+import 'package:expect/expect.dart';
+import 'package:path/path.dart' as path;
+
+import 'util.dart';
+
+final String testDir = path.join(
+  path.dirname(Platform.script.toFilePath()),
+  'mjs_format',
+);
+final String mainDart = path.join(testDir, 'main.dart');
+final String es6GoldenPath = path.join(testDir, 'mjs_es6.mjs.golden');
+final String noEs6GoldenPath = path.join(testDir, 'mjs_no_es6.mjs.golden');
+
+Future<void> main(List<String> args) async {
+  if (!Platform.isLinux && !Platform.isMacOS) return;
+
+  final parser = ArgParser()
+    ..addFlag('update-golden', abbr: 'g', negatable: false);
+  final argsResult = parser.parse(args);
+  final bool updateGolden = argsResult.flag('update-golden');
+
+  await withTempDir((String tempDir) async {
+    final es6WasmPath = path.join(tempDir, 'out_es6.wasm');
+    final noEs6WasmPath = path.join(tempDir, 'out_no_es6.wasm');
+    final es6MjsPath = path.join(tempDir, 'out_es6.mjs');
+    final noEs6MjsPath = path.join(tempDir, 'out_no_es6.mjs');
+
+    // Compile with default (ES6 modules enabled)
+    await run([
+      dartAotExecutable,
+      dart2wasmSnapshot,
+      '--platform=$platformDill',
+      '-O0',
+      mainDart,
+      es6WasmPath,
+    ]);
+
+    // Compile with --no-supports-es6-modules
+    await run([
+      dartAotExecutable,
+      dart2wasmSnapshot,
+      '--platform=$platformDill',
+      '--no-supports-es6-modules',
+      '-O0',
+      mainDart,
+      noEs6WasmPath,
+    ]);
+
+    final es6Mjs = await File(es6MjsPath).readAsString();
+    final noEs6Mjs = await File(noEs6MjsPath).readAsString();
+
+    final es6GoldenFile = File(es6GoldenPath);
+    final noEs6GoldenFile = File(noEs6GoldenPath);
+
+    if (updateGolden) {
+      print('Updating golden files...');
+      await es6GoldenFile.writeAsString(es6Mjs);
+      await noEs6GoldenFile.writeAsString(noEs6Mjs);
+      return;
+    }
+
+    Expect.isTrue(
+      es6GoldenFile.existsSync(),
+      'Expected golden file $es6GoldenPath to exist.',
+    );
+    Expect.isTrue(
+      noEs6GoldenFile.existsSync(),
+      'Expected golden file $noEs6GoldenPath to exist.',
+    );
+
+    final expectedEs6Mjs = await es6GoldenFile.readAsString();
+    final expectedNoEs6Mjs = await noEs6GoldenFile.readAsString();
+
+    Expect.equals(expectedEs6Mjs, es6Mjs);
+    Expect.equals(expectedNoEs6Mjs, noEs6Mjs);
+
+    _verifyMjsDifferences(es6Mjs, noEs6Mjs);
+  });
+}
+
+void _verifyMjsDifferences(String es6Mjs, String noEs6Mjs) {
+  Expect.isTrue(
+    es6Mjs.contains('export async function compileStreaming('),
+    'ES6 mjs should contain export async function compileStreaming',
+  );
+  Expect.isTrue(
+    es6Mjs.contains('export async function compile('),
+    'ES6 mjs should contain export async function compile',
+  );
+  Expect.isFalse(
+    es6Mjs.startsWith('(function() {\nconst exportObject = {};'),
+    'ES6 mjs should not start with IIFE wrapper',
+  );
+
+  Expect.isTrue(
+    noEs6Mjs.startsWith('(function() {\nconst exportObject = {};'),
+    'Non-ES6 mjs should start with IIFE wrapper',
+  );
+  Expect.isTrue(
+    noEs6Mjs.endsWith('return exportObject;\n})'),
+    'Non-ES6 mjs should end with IIFE wrapper closing',
+  );
+  Expect.isTrue(
+    noEs6Mjs.contains(
+      'exportObject.compileStreaming = async function compileStreaming(',
+    ),
+    'Non-ES6 mjs should assign compileStreaming to exportObject',
+  );
+  Expect.isTrue(
+    noEs6Mjs.contains('exportObject.compile = async function compile('),
+    'Non-ES6 mjs should assign compile to exportObject',
+  );
+}
diff --git a/tools/VERSION b/tools/VERSION
index bf649ba..ada45d4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 3
 MINOR 14
 PATCH 0
-PRERELEASE 109
+PRERELEASE 110
 PRERELEASE_PATCH 0