In Scala.js facades, why does the @js.native annotation preclude the @JSExport one?

145 views Asked by At

Please consider a Scala.js library with a native dependency implemented as pure JavaScript CommonJS module.

The library includes a facade for the JavaScript dependency. As expected, the facade includes a lot of code like:

@JSImport("com", "Foo") @js.native
class Foo extends js.Object { ... }

Unfortunately, the ScalaJS-Bundler bundles Foo in a way that hides it from the global scope. The obvious fix involves adding the @JSExport annotation to the other two, but that results in a compiler error.

Why isn't js.native compatible with JSExport? What would it take to add support for @JSExport on facades?

Is any work-around available now?

1

There are 1 answers

0
sjrd On BEST ANSWER

@JSExport on top-level classes and objects was deprecated in Scala.js 0.6.15. What you are after is actually @JSExportTopLevel.

There is no fundamental reason that @JSExportTopLevel is not compatible with @JSImport/@JSGlobal. It isn't because of the 3 following things:

  • supporting it means more work in the entire compiler toolchain to support it,
  • it felt like a rare use case, and
  • there is another way to achieve the same result.

The other way to achieve the result is simply to export a val storing the result of the import, as follows:

@js.native
@JSImport("com", "Foo")
class Foo extends js.Object { ... }

// 'private' not to pollute the Scala API with this object
private object Reexports {
  @JSExportTopLevel("Foo") // or another name
  val Foo = js.constructorOf[Foo]
}

It sure is a bit more verbose if you only re-export one such import, but you can bundle as many as you want in the unique object Reexports.