Scala-js: Importing native js modules for side effects only

Created on 21 Aug 2020  路  6Comments  路  Source: scala-js/scala-js

There does not appear to be a way to do the JS equivalent of import 'foo'; in Scala.js. Our workaround is this pattern:

  @js.native
  @JSImport("@material/mwc-button", JSImport.Default)
  object RawMwcButton extends js.Object

  private val dummy = RawMwcButton

If we don't have the dummy variable, object gets optimized out and import never happens.

Is there / should there be a better way to do this?

language wontfix

Most helpful comment

You don't necessarily need a dummy variable. It's OK to only refer to the object as a statement, for example in your main or in the constructor body of a class/object that relies on that functionality. In other words, wherever you put the

private val dummy = RawMwcButton

you can equally write just

RawMwcButton

There is no better way to do this. Since Scala.js only reaches external imports for things that are themselves reachable, you need to make sure that the object is somehow reachable.

AFAICT, it's also not really possible to change Scala.js to make this any different. Much of the specification relies on having no real entry points besides main methods and exports. We do have the notion of static initializers, which are "out of the blue" entry points, so there is precedent for adding other kinds of entry points. But adding a different kind @JSImport that would automatically reach its target module if it is on the classpath would behave very differently from the other @JSImports, and for that there is no precedent.

All 6 comments

You don't necessarily need a dummy variable. It's OK to only refer to the object as a statement, for example in your main or in the constructor body of a class/object that relies on that functionality. In other words, wherever you put the

private val dummy = RawMwcButton

you can equally write just

RawMwcButton

There is no better way to do this. Since Scala.js only reaches external imports for things that are themselves reachable, you need to make sure that the object is somehow reachable.

AFAICT, it's also not really possible to change Scala.js to make this any different. Much of the specification relies on having no real entry points besides main methods and exports. We do have the notion of static initializers, which are "out of the blue" entry points, so there is precedent for adding other kinds of entry points. But adding a different kind @JSImport that would automatically reach its target module if it is on the classpath would behave very differently from the other @JSImports, and for that there is no precedent.

Could we allow local native objects/classes so that this can be hidden away? (maybe even with a macro library)?

def loadMwc(): Unit = {
   @js.native
   @JSImport("@material/mwc-button", JSImport.Default)
   object RawMwcButton extends js.Object

  RawMwcButton
}

It can already be hidden away by making it private in a companion object or something like that. It doesn't have to leak into the public API.

Ah, fair enough. Although that wouldn't work with a def macro. But not sure that is actually necessary.

Shall we close this as wontfix then? @uosis is that OK with you?

Yep, the "object as a statement" is a reasonable workaround, and is probably a better approach than classpath scanning for imports anyway.

Thanks for clarifying this!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fdietze picture fdietze  路  5Comments

sjrd picture sjrd  路  3Comments

rajeevprasanna picture rajeevprasanna  路  8Comments

lihaoyi picture lihaoyi  路  8Comments

japgolly picture japgolly  路  7Comments