0

There are some objects in my Scala.js project I would like to access dynamically. I think I might be able to get this working using Dynamic, however currently the fastOptJS / fullOptJS step is discarding them. Is there some way (annotation?) I could mark them as required, never to be discarded during optimization?

I do not want to list them anywhere, as they are hundreds of them in my app and they are often being added / removed.

Example:

trait Res {
  def value: String
}
object A {
  def value = "A..."
}
object B {
  def value = "B..."
}

def loadAB(name: String): String {
  scala.scalajs.js.Dynamic.global.selectDynamic(name).asInstanceOf[Res].value
}

2 Answers 2

3

You cannot access objects defined in Scala.js through js.Dynamic.global, because by default Scala.js does not put anything in global. It only puts there things exported with @JSExportTopLevel.

@Simon Groenewolt suggested a solution based on @JSExportTopLevel, but that is a hack with suboptimal properties in your case. Indeed, it will force every single one of those objects to be exposed in the JavaScript global scope, and that is not good, as they could be tampered with by JavaScript (or tamper with other JavaScript libraries).

Besides, that hack will cease to function if you (or your dependents) emit CommonJS modules. It will also not work as such anymore in Scala.js 1.x, since dynamic selection from global will not be permitted anymore (you would have to dynamically find the global object yourself beforehand instead).

A better solution for your use case seems to be the Reflect API. In particular, it looks like you want to be able to load objects that extend Res given their name, which is exactly what Reflect gives you, assuming you annotate Res with @EnableReflectiveInstantiation:

package foo

import scala.scalajs.reflect.Reflect
import scala.scalajs.reflect.annotation.EnableReflectiveInstantiation

@EnableReflectiveInstantiation
trait Res {def value;}

object A {def value = "A...";}
object B {def value = "B...";}

def loadAB(name: String): String {
  Reflect
    .lookupLoadableModuleClass(name + "$")
    .getOrElse(throw new Exception("hum, that as not found")
    .loadModule()
    .asInstanceOf[Res]
    .value
}

println(loadAB("foo.A"))

Note that you need to use the fully qualified name of the objects.

Sign up to request clarification or add additional context in comments.

1 Comment

A much better solution than mine! (And a good point about it not working with javascript modules)
1

Maybe annotating them with @JSExportTopLevel(<identifier>) will have the desired effect? This is meant for exporting to JavaScript, but will have the side-effect of preventing the code from being discarded when bundling.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.