jodinathan / typings

Apache License 2.0
39 stars 9 forks source link

Migrate to `dart:js_interop` and Extension Types #26

Open pattobrien opened 3 months ago

pattobrien commented 3 months ago

Are there any plans to use the new js_interop features? I've been working on some experiment ts2dart converters (e.g. to create a VSCode Extension using Dart), and would love to work together to migrate to the new conventions.

Some of the topics I've been working on revolve around the following:

I'd love to begin discussing ways we could collaborate with the community to migrate to js_interop!

Blimster commented 3 months ago

Hi @pattobrien,

I just closed my issue #25. I opened it an hour before you, but you put much more effort into your description.

I'm also interested to work together on that topic. My final goal is to provide (almost) complete bindings for BabylonJS. I wrote a transpiler for that some years ago (see https://github.com/Blimster/babylon_dart/tree/main/generator). But it only supports maybe 40% of the BabylonJS API.

Next to your 3 bullets, I think 2 more things must be considered:

pattobrien commented 3 months ago

@Blimster ah amazing! what a nice coincidence :)

Sanitize (e.g. method overrides with less parameters) Configuration to change the behavior of the transpiler

Good additional points.. Here's a little bit of insight into how I believe a very good DevX would go for library developers (e.g. BabylonDart), which allows configuration of generated code for both build_runner as well as macros.

  1. Convert BabylonJS .d.ts files to JSON Schema representation (fairly self-explanatory).
  2. Configure Typings base macro with the Babylon schema.
// babylon_dart/lib/macro.dart

class BabylonType extends TypingsMacro {
    const BabylonType(
      JsonPointer super.pointer,
    );

    @override
    TypescriptSchema get metaSchema => TypescriptSchema(
       defs: {
          'Engine': /* Engine type schema goes here */,
          'FreeCamera': /* FreeCamera type schema */,
          ...
        },
    );
}
  1. Use the above Macro to build the types:
// babylon_dart/lib/types/engine.dart

@BabylonType('#/defs/Engine') // here we tell the macro which type we're declaring
extension type Engine(JSObject value) implements JSObject {
  /* members are automatically generated */

  // alternatively we can override members of a type
  // by simply inlining a new method definition
  void resize() { /* custom resize definition */ }
}

Of course, we could also use a cli app to generate most of the above code for us, so that you wouldn't have to manually create declarations for the Engine type if you don't need to override any of its members.

Importantly, this kind of workflow would allow library developers to very easily work alongside the code generators, to modify the JS/TS types in ways that make sense for Dart developers. No additional code-gen configurations required.

What do you think?

jodinathan commented 3 months ago

I am not sure what kind of features in the new js_interop we coud use in Typings.

I've created a plugin for VSCode using Typings, you can check it here: https://github.com/jodinathan/element_tree_viewer/tree/main/plugins/element_tree_vscode

pattobrien commented 3 months ago

I am not sure what kind of features in the new js_interop we coud use in Typings.

It's not so much about new features, its that js_interop is the recommended mechanism for interop going forward. dart:js and dart:html are now considered legacy packages.

You can see some additional benefits of the migration here: https://dart.dev/interop/js-interop/past-js-interop

I've created a plugin for VSCode using Typings, you can check it here: https://github.com/jodinathan/element_tree_viewer/tree/main/plugins/element_tree_vscode

Appreciate this a lot! This will be great reference.

jodinathan commented 3 months ago

I am not sure what kind of features in the new js_interop we coud use in Typings.

It's not so much about new features, its that js_interop is the recommended mechanism for interop going forward. dart:js and dart:html are now considered legacy packages.

You can see some additional benefits of the migration here: https://dart.dev/interop/js-interop/past-js-interop

So, the new package:web from the Dart team is based on my old js_bindings. Typings is already a new replacement for it.

So actually, you can ditch package:web (which I think there are too much boilerplate) and use Typings instead.

Check the wiki to know more about how Typings work and feel free to make any questions

pattobrien commented 3 months ago

Check the wiki to know more about how Typings work and feel free to make any questions

I'm familiar with how Typings and the rest of the packages work - it doesn't change the fact that dart:js, package:js, dart:html, and dart:js_utils (packages which Typings relies on) are now considered legacy packages in favor of dart:js_interop and extension types. These features were introduced very recently in Dart v3.3, and though support for the legacy packages will likely still remain for the forseeable future, they won't be getting updates as much as dart:js_interop will.

So actually, you can ditch package:web (which I think there are too much boilerplate) and use Typings instead.

You can probably understand that first-party Dart SDK support will almost always be preferred over a third party package - so it's likely that the JS- and browser-specific bindings in package:web and dart:js_interop will be preferred by the community over the equivalent bindings that exist in Typings. Typings would then focus on bindings from packages on npm.

But there's still a lot that can be re-used regarding the code-generation portion of this repository (i.e. ts2dart and ts_ast) - this code could be ported over to use the new js_interop library and Extension Types, so that users can generate Typings packages using the newer APIs that are now recommended by the Dart team.

Does that make sense?

jodinathan commented 3 months ago

Does that make sense?

it does to a degree.
Typings doesn't rely on dart:html and IIRC, the dart:js_interop uses the package:js and package:js_util.
So I may be mistaken, and probably am, but there is a chance that Typings is already on pair with the new interop stuff.

but, it has been quite some time that I've read about js_interop, so things should have changed.

Can you add some before->after of what the generated code would be?

Blimster commented 3 months ago

@jodinathan, you should really read the docs for the new js interop (https://dart.dev/interop/js-interop/past-js-interop). Starting with Dart 3.3, the preferred way for JS interop is to use Dart extension types together with types from dart:js_interop.

From the docs linked above a Dart binding for a JS class should now look like that:

extension type Time._(JSObject _) implements JSObject {
  external Time(int hours, int minutes);
  external factory Time.onlyHours(int hours);

  external static Time dinnerTime;
  external static Time getTimeDifference(Time t1, Time t2);

  external int hours;
  external int minutes;
  external bool isDinnerTime();

  bool isMidnight() => hours == 0 && minutes == 0;
}

That said, Typings have to be changed to generate Dart sources to use the new way for JS interop. The package:web is provided by the Dart team for browser APIs. It already uses the new JS interop.