How to use jQuery + Bootstrap.js in Scala.js with scalajs bundler? - scala.js

1. Loading JQuery
I have the following defined to interface with JQuery:
#js.native
sealed trait JQuery extends js.Any {
def hide(): js.Any
}
#js.native
#JSImport("jquery/dist/jquery.slim.min.js", "jQuery")
object JQuery extends js.Object {
def apply(x: String): JQuery = js.native
}
I confirmed that the #JSimport seems to work, in that the bundler js file increases by an amount approximately equal to the size of jquery/dist/jquery.slim.min.js when I use #JSimport instead of JSGlobal.
However, once I run my application, when I get to the code that does JQuery("p").hide() I get an error:
Codebook.scala:42 Uncaught TypeError: (0 , $i_jquery$002fdist$002fjquery$002emin$002ejs.jQuery) is not a function
at Codebook.scala:42
at $c_sjsr_AnonFunction1.apply__O__O (AnonFunctions.scala:15)
at HTMLAnchorElement.<anonymous> (mount.scala:106)
(anonymous) # Codebook.scala:42
$c_sjsr_AnonFunction1.apply__O__O # AnonFunctions.scala:15
(anonymous) # mount.scala:106
2. Loading JQuery before Bootstrap.js
After the above definitions, if I add the following code to interface with Bootstrap (and actually use it somewhere so it isn't "optimized away"), I will get a runtime error immediately on page load:
#js.native
sealed trait JQueryBootstrap extends JQuery {
def collapse(arg: String): js.Any
}
#js.native
#JSImport("bootstrap/dist/js/bootstrap.min.js", "jQuery")
object JQueryBootstrap extends js.Object {
def apply(x: String): JQueryBootstrap = js.native
//TODO not the complete API yet, should be string || "Options":
//TODO https://bootstrapdocs.com/v3.3.6/docs/javascript/#collapse-methods
def collapse(arg: String): js.Any = js.native
}
implicit class JQueryBootstrapExtra(val jq: JQueryBootstrap) extends AnyVal {
def collapseToggle = jq.collapse("toggle")
}
The error on pageload is:
bootstrap.min.js:6 Uncaught Error: Bootstrap's JavaScript requires jQuery
at Object.<anonymous> (bootstrap.min.js:6)
at __webpack_require__ (bootstrap abeaa4cf57d5fe91d588:19)
at Object.<anonymous> (Utils.scala:35)
at Object.<anonymous> (ced2ar3-view-fastopt-bundle.js:102442)
at __webpack_require__ (bootstrap abeaa4cf57d5fe91d588:19)
at Object.$env (bootstrap abeaa4cf57d5fe91d588:62)
at __webpack_require__ (bootstrap abeaa4cf57d5fe91d588:19)
at bootstrap abeaa4cf57d5fe91d588:62
at bootstrap abeaa4cf57d5fe91d588:62
(anonymous) # bootstrap.min.js:6
__webpack_require__ # bootstrap abeaa4cf57d5fe91d588:19
(anonymous) # Utils.scala:35
(anonymous) # ced2ar3-view-fastopt-bundle.js:102442
__webpack_require__ # bootstrap abeaa4cf57d5fe91d588:19
$env # bootstrap abeaa4cf57d5fe91d588:62
__webpack_require__ # bootstrap abeaa4cf57d5fe91d588:19
(anonymous) # bootstrap abeaa4cf57d5fe91d588:62
(anonymous) # bootstrap abeaa4cf57d5fe91d588:62
I have confirmed my code works if I load the js files manually via dynamically inserted <script> tags, but one issue with that (other than messiness and code-size optimization) is that I haven' been able to easily control the load order, but I digress...

Related

importing static javascript functions into scala.js

In a third party library react-native-fbsdk-next I have a file FBAccessToken.ts
The class below has a few ordinary members and a static member function.
class FBAccessToken {
accessToken: string;
permissions: Array<string>;
static getCurrentAccessToken() {
return new Promise<FBAccessToken | null>((resolve) => {
...
}
}
}
export default FBAccessToken;
I need to import both the class members as well as the static function.
My strategy is to use a scala class and a companion object like so
#native
#JSImport("react-native-fbsdk-next", "FBAccessToken")
class FBAccessToken extends js.Object {
def accessToken: String = native
def permissions: Array[String] = native
}
#native
#JSImport("react-native-fbsdk-next", "FBAccessToken")
object FBAccessToken extends js.Object {
def getCurrentAccessToken: Promise[FBAccessToken | Null] = native
}
But the static function to companion object import seems to not work.
sbt fastOptJs works but on running the app in a simulator I get
TypeError: undefined is not an object (evaluating '_$$_REQUIRE(_dependencyMap[4], "react-native-fbsdk-next").FBAccessToken.getCurrentAccessToken')
What am I doing wrong?
FBAccessToken is exported under the default export in the JavaScript library:
export default FBAccessToken;
and not under its own name "FBAccessToken".
Therefore, when importing it from Scala.js, you have to use JSImport.Default as the second argument ot #JSImport:
#JSImport("react-native-fbsdk-next", JSImport.Default)
This applies both to the class and to the object

scala-js "#JSGlobalScope" error when migrating to scala-js 1.1.1

I had a fallowing snippet of code in scala-js(0.6.33)
object Main2 extends App {
val js = for {
jsTest <- JSTest.js1.toOption
} yield jsTest
println(JSTest.js1)
}
import scala.scalajs.js
import scala.scalajs.js.annotation.JSGlobalScope
#js.native
#JSGlobalScope
object JSTest extends js.Object {
def js1: js.UndefOr[JS2] = js.native
}
#js.native
trait JS1 extends js.Object {
def js1: js.UndefOr[JS2] = js.native
}
#js.native
trait JS2 extends js.Object {
def js2: js.UndefOr[Int] = js.native
}
And I was migrating the project to use scala-js(1.1.1)
when I compile the same code in scala-js(1.1.1), I am getting this error: -
const value = js1;
^
ReferenceError: js1 is not defined
Can anyone help me achieve the same functionality with scala-js(1.1.1)?
configuration: -
scala -> 2.13.3, sbt -> 1.3.13, jvm -> 14
As the release notes of Scala.js 1.0.0 explain, trying to even read a member of an #JSGlobalScope object that does not actually exist in the global scope will throw a ReferenceError, whereas Scala.js 0.6.x would give undefined.
The same section of the release notes explains that, if the purpose is to check whether it is defined, it is now necessary to use an explicit test with js.typeOf. In your specific example:
object Main2 extends App {
val js = for {
jsTest <- JSTest.js1.toOption
} yield jsTest
println(JSTest.js1)
}
That would mean that you can't unconditionally access JSTest.js1 like that anymore. You first have to make sure it exists using a js.typeOf test:
object Main2 extends App {
val safeJS1 =
if (js.typeOf(JSTest.js1) == "undefined") None
else JSTest.js1.toOption
val js = for {
jsTest <- safeJS1
} yield jsTest
println(safeJS1)
}

How to combine the Module Dependencies with the Dependencies of its parent Module with Mill

I want to combine the Module Dependencies with the ones from the parent Module.
I have the following in my build.sc:
trait ExampleModule extends ModuleWithTests {
override def moduleDeps = Seq(camunda, cli)
}
object twitter extends ExampleModule {
override def moduleDeps = super.moduleDeps ++ Seq(twitterApi)
}
This gives me:
build.sc:222: type mismatch;
found : Seq[build.this.ModuleWithTests]
required: Seq[build.this.ModuleWithTests{def moduleDeps: Seq[build.this.ModuleWithTests]}]
override def moduleDeps = super.moduleDeps ++ Seq(twitterApi)
^
build.sc:222: `T.command` definitions must have 1 parameter list
override def moduleDeps = super.moduleDeps ++ Seq(twitterApi)
^
Compilation Failed
Is there a way to achieve this, or do I have to list the module dependencies in each child module?
Your example is almost right, but because you omitted the explicit return type of ExampleModule.moduleDeps it seems the compiler inferred a more specific type of Seq[ModuleWithTests] in this case. Looks like all your modules camunda and cli also implement this trait. Whereas twitterApi seems to not implement it.
To fix the compile error, you might try to add the explicit return type Seq[JavaModule] to ExampleModule.moduleDeps.

sbt could not find implicit value for parameter valName

I'm using sbt to build some of the riscv boom from the source code, but the sbt complains that it "could not find implicit value for parameter valName: : freechips.rocketchip.diplomacy.ValName". The detailed error message are as below:
[error] F:\hiMCU\my_proj\src\main\scala\freechips\rocketchip\tile\BaseTile.scala:170:42: could not find implicit value for parameter valName: freechips.rocketchip.diplomacy.ValName
[error] Error occurred in an application involving default arguments.
[error] protected val tlMasterXbar = LazyModule(new TLXbar)
The code where sbt complains is as below:
abstract class BaseTile private (val crossing: ClockCrossingType, q: Parameters)
extends LazyModule()(q)
with CrossesToOnlyOneClockDomain
with HasNonDiplomaticTileParameters
{
// Public constructor alters Parameters to supply some legacy compatibility keys
def this(tileParams: TileParams, crossing: ClockCrossingType, lookup: LookupByHartIdImpl, p: Parameters) = {
this(crossing, p.alterMap(Map(
TileKey -> tileParams,
TileVisibilityNodeKey -> TLEphemeralNode()(ValName("tile_master")),
LookupByHartId -> lookup
)))
}
def module: BaseTileModuleImp[BaseTile]
def masterNode: TLOutwardNode
def slaveNode: TLInwardNode
def intInwardNode: IntInwardNode // Interrupts to the core from external devices
def intOutwardNode: IntOutwardNode // Interrupts from tile-internal devices (e.g. BEU)
def haltNode: IntOutwardNode // Unrecoverable error has occurred; suggest reset
def ceaseNode: IntOutwardNode // Tile has ceased to retire instructions
def wfiNode: IntOutwardNode // Tile is waiting for an interrupt
protected val tlOtherMastersNode = TLIdentityNode()
protected val tlSlaveXbar = LazyModule(new TLXbar)
protected val tlMasterXbar = LazyModule(new TLXbar)
protected val intXbar = LazyModule(new IntXbar)
....
}
The LazyModule object code is as below:
object LazyModule
{
protected[diplomacy] var scope: Option[LazyModule] = None
private var index = 0
def apply[T <: LazyModule](bc: T)(implicit valName: ValName, sourceInfo: SourceInfo): T = {
// Make sure the user put LazyModule around modules in the correct order
// If this require fails, probably some grandchild was missing a LazyModule
// ... or you applied LazyModule twice
require (scope.isDefined, s"LazyModule() applied to ${bc.name} twice ${sourceLine(sourceInfo)}")
require (scope.get eq bc, s"LazyModule() applied to ${bc.name} before ${scope.get.name} ${sourceLine(sourceInfo)}")
scope = bc.parent
bc.info = sourceInfo
if (!bc.suggestedNameVar.isDefined) bc.suggestName(valName.name)
bc
}
}
I think the sbt should find some val of type freechips.rocketchip.diplomacy.ValName, but it didn't find such kind of val.
You need to have an object of type ValName in the scope where your LazyModules are instantiated:
implicit val valName = ValName("MyXbars")
For more details on Scala's implicit please see https://docs.scala-lang.org/tutorials/tour/implicit-parameters.html.html
You generally shouldn't need to manually create a ValName, the Scala compiler can materialize them automatically based on the name of the val you're assigning the LazyModule to. You didn't include your imports in your example, but can you try importing ValName?
import freechips.rocketchip.diplomacy.ValName
In most of rocket-chip code, this is imported via wildcard importing everything in the diplomacy package
import freechips.rocketchip.diplomacy._

How do I use Scala-Meta Parse an object?

I am trying to use Scala Meta to write an annotation so I can generate another case class from an existing object.
But when I try to do this:
MyObject.parse[Source].show[Structure]
I got this error:
Error:(5, 20) not enough arguments for method parse: (implicit convert: scala.meta.common.Convert[domain.MyObject.type,scala.meta.inputs.Input], implicit parse: scala.meta.parsers.Parse[scala.meta.Source], implicit dialect: scala.meta.Dialect)scala.meta.parsers.Parsed[scala.meta.Source].
Unspecified value parameters convert, parse, dialect.
MyObject.parse[Source].show[Structure];}
^
I am very confused because based on their tutorial, that's what I need to start with
http://scalameta.org/tutorial/#.parse[T]
How can I reflect this object to loop through all properties?
Thanks
parse[Source] parses text. You may try the following
import scala.meta._
"object MyObject".parse[Source].get.show[Syntax]
If you are creating annotation then it might look like:
#MyAnnotation
object MyObject
And in another module:
import scala.meta._
class MyAnnotation extends StaticAnnotation {
inline def apply(defn: Any): Any = meta {
defn.show[Syntax]
defn
}
}