[dart:js_interop] Add support for dynamically importing modules

Closes https://github.com/dart-lang/sdk/issues/52852

Allows importing of modules using the JS "import()" expression.
The result is a promise that resolves to a module, from which users
can then access exported members from.

CoreLibraryReviewExempt: Backend-specific library.
Change-Id: I4c1caae689be55d1dbb038448e3243bcb80ac8b5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/343683
Commit-Queue: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
Srujan Gaddam 2024-01-03 01:00:19 +00:00 committed by Commit Queue
parent 3f275d858f
commit 4804d54792
6 changed files with 49 additions and 2 deletions

View file

@ -448,3 +448,8 @@ extension JSAnyOperatorExtension on JSAny? {
@pragma('dart2js:prefer-inline')
bool get isTruthy => js_util.isTruthy(this);
}
@patch
@pragma('dart2js:prefer-inline')
JSPromise<JSObject> importModule(String moduleName) =>
foreign_helper.JS('', 'import(#)', moduleName);

View file

@ -526,15 +526,20 @@ extension JSAnyOperatorExtension on JSAny? {
@patch
bool get not => _boxNonNullable<JSBoolean>(
js_helper.JS<WasmExternRef?>('(o) => !o', this.toExternRef))
js_helper.JS<WasmExternRef?>('(o) => !o', this.toExternRef))
.toDart;
@patch
bool get isTruthy => _boxNonNullable<JSBoolean>(
js_helper.JS<WasmExternRef?>('(o) => !!o', this.toExternRef))
js_helper.JS<WasmExternRef?>('(o) => !!o', this.toExternRef))
.toDart;
}
@patch
JSPromise<JSObject> importModule(String moduleName) =>
_boxNonNullable<JSPromise<JSObject>>(js_helper.JS<WasmExternRef?>(
'(m) => import(m)', moduleName.toJS.toExternRef));
@JS('Array')
@staticInterop
class _Array {

View file

@ -622,6 +622,8 @@ extension JSAnyOperatorExtension on JSAny? {
external bool get isTruthy;
}
// Top-levels.
/// Given a Dart object that is marked "exportable", creates a JS object that
/// wraps the given Dart object. Look at the `@JSExport` annotation to determine
/// what constitutes "exportable" for a Dart class. The object literal
@ -667,3 +669,13 @@ external JSObject createJSInteropWrapper<T extends Object>(T dartObject);
// `createStaticInteropMock`, we avoid introducing this method until later.
// external T createJSInteropMock<T extends JSObject, U extends Object>(
// U dartMock, [JSObject? proto = null]);
/// Call to dynamically import a JS module with the given [moduleName] using the
/// JS `import()` syntax.
///
/// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
/// for more details.
///
/// Returns a [JSPromise] that resolves to a [JSObject] that's the module
/// namespace object.
external JSPromise<JSObject> importModule(String moduleName);

View file

@ -0,0 +1,21 @@
// Copyright (c) 2024, 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:js_interop';
import 'package:async_helper/async_helper.dart';
import 'package:expect/minitest.dart';
extension type Module(JSObject o) implements JSObject {
external String testModuleFunction();
}
void main() {
asyncTest(() async {
final module = Module(await importModule(
'/root_dart/tests/lib/js/static_interop_test/import/module.mjs')
.toDart);
expect(module.testModuleFunction(), 'success');
});
}

View file

@ -0,0 +1,3 @@
export function testModuleFunction() {
return 'success';
}

View file

@ -33,6 +33,7 @@ js/export/static_interop_mock/proto_test: SkipByDesign # Uses dart:html.
js/js_util/javascriptobject_extensions_test: SkipByDesign # Uses dart:html.
js/static_interop_test/constants_test: SkipByDesign # Uses dart:html.
js/static_interop_test/futurevaluetype_test: SkipByDesign # Uses dart:html.
js/static_interop_test/import/import_test: SkipByDesign # TODO(srujzs): This test uses the file system to load a module. Since the test runner doesn't start an HTTP server for d8, I don't think this is supported.
js/static_interop_test/supertype_transform_test: SkipByDesign # Uses dart:html.
[ $runtime == dart_precompiled ]