Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS interop support for ES6 modules #53783

Open
eyebrowsoffire opened this issue Oct 17, 2023 · 6 comments
Open

JS interop support for ES6 modules #53783

eyebrowsoffire opened this issue Oct 17, 2023 · 6 comments
Labels
area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. web-js-interop Issues that impact all js interop

Comments

@eyebrowsoffire
Copy link
Contributor

Some sort of first-class support for ES modules in JS interop would be very useful for both JS backends and dart2wasm. Currently, there doesn't appear to be a way to do dynamic or static importing of ES modules through JS interop, so here are a few ideas:

  • Declare a module's interface via a static interop declaration, and have an @staticImport() annotation with a const string url parameter, which transforms into a static import of an ES module:
@JS()
@staticInterop
class MyModule {}

extension MyModuleExtension extends MyModule {
  external JSAny foo(JSAny param);
}

@JS()
@staticImport('relative/path/to/module.js')
external MyModule get myModule;
  • In dart:js_interop, expose a builtin Future<ModuleExports> import<ModuleExports implements JSAny>(String url) function for dynamic imports. Currently trying to call import via static interop doesn't actually work because it's not a real function, it's a browser builtin.
@lrhn lrhn added the area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. label Oct 20, 2023
@Markzipan Markzipan added the web-js-interop Issues that impact all js interop label Oct 23, 2023
@srujzs
Copy link
Contributor

srujzs commented Oct 23, 2023

Related request regarding idea 2: #52852

@srujzs
Copy link
Contributor

srujzs commented Jan 3, 2024

FYI - I've just landed a member to address the second request. Since the result is a promise that resolves to an object, you can use an extension type/@staticInterop class to interface with the resulting JS object.

@pattobrien
Copy link

pattobrien commented Feb 24, 2024

In case its useful, I wanted to make a note on usability issues found using a real example use case when implementing a node_interop package.

@JS('fs')
library fs;

import 'dart:js_interop';

// getter used for importing the `fs` module
@JS()
Future<FS> get fs async => FS._(await importModule('fs').toDart);

// defines all of the members on `fs`
extension type FS._(JSObject _) implements JSObject {
  external JSAny readFileSync(
    JSAny file, [
    JSAny? options,
  ]);
  ...
}

// USAGE (i.e. users of `package:node_interop`
Future<void> main() async {
  final thisDir = '/Users/pattobrien/dev/pattobrien/dart3_node_interop';
  final files = (await fs).readdirSync(thisDir.toJS);
  for (final file in files.toDart) {
    console.log(file);
  }
}

A couple things to point out:

  • As @eyebrowsoffire mentioned, the syntax for declaring a module is verbose; I feel as if it would be ideal if the intent could be inferred from the annotation on the library directive declaration
  • nesting every single member of the fs module within a FS or FSModule extension type feels unintuitive and non-Dart like, even though it does accurately reflect what's going on behind the scenes
  • I don't love the effect that async imports cause when accessing the module's members (i.e. the(await fs) statement, especially since this is a static import included with node. Ideally there would be a way to import a module synchronously, based on the use case.

@srujzs
Copy link
Contributor

srujzs commented Feb 26, 2024

Great feedback, I agree with all your points!

I can imagine an annotation on the library that would signal to the compiler to import a module as a namespace that can then be used in lowering the interop calls. That way, top-level interop declarations would correspond to top-levels in that module, linking the Dart library to the JS module. We don't have support for interop to tell any of the web compilers to statically import a module, but it might be possible without too much effort - TBD.

@pattobrien
Copy link

pattobrien commented Feb 27, 2024

I can imagine an annotation on the library that would signal to the compiler to import a module as a namespace that can then be used in lowering the interop calls.

That would be amazing! Ideally inferring js semantics from how users are already using Dart would be the way to go. If I import a dart fs library into my entrypoint, then the generated JS code should import the respective js fs module.

The translation from dart code to js code would then look something like the following:

Dart JS Transpiled code
import 'package:node/fs.dart'; import * as fs from 'fs';
import 'package:node/fs.dart' as foo; import * as foo from 'fs';
import 'package:node/fs.dart' show readdirSync; import { readdirSync } from 'fs';

I've also opened a related but separate issue regarding accessing the top-level module object in non-browser environments. There are IMO a couple other small things that would need to change with how Dart handles js compilation today to better support ESM, so not sure if this issue should exist as a catchall for ESM support or specifically an issue about the module/namespace definition semantics.

@srujzs
Copy link
Contributor

srujzs commented Feb 27, 2024

It would be much harder to convert import statements into something specific for interop as that's likely a language change, but we can leverage some annotation for that syntax instead.

Better support for ESM is likely an intersection between interop and the compilers. We should create a meta issue linking all the relevant issues together here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. web-js-interop Issues that impact all js interop
Projects
Status: No status
Development

No branches or pull requests

5 participants