The goal of this project is to provide an easy and typesafe way to create Chrome apps and extensions in Scala using the Scala.js project.
DISCLAIMER: As of today (2020-03-30), I decided to fork the plugin so that I can keep applying updates and releasing new versions, the main reason to do this is to migrate to scalajs 1.0.0, which support is still experimental, if you use an older scalajs version, use the original plugin instead.
The bindings provide access to the Chrome app and extension APIs. There are two levels for each API. One that provides the raw JavaScript bindings and a second one which wraps the raw API in a more Scala idiomatic way.
The package structure is similar to the original JavaScript API.
// original JavaScript
chrome.system.cpu.getInfo(function(info){
if (chrome.runtime.lastError === undefined) {
console.log(info);
} else {
console.log("ohoh something went wrong!");
}
});
// raw bindings
chrome.system.cpu.bindings.CPU.getInfo((info: CPUInfo) => {
if (chrome.runtime.bindings.Runtime.lastError.isEmpty) {
println(info)
} else {
println("ohoh something went wrong!")
}
})
// Scala idiomatic way using Future
chrome.system.cpu.CPU.getInfo.onComplete {
case Success(info) => println(info)
case Failure(error) => println("ohoh something went wrong!")
}
The Scala idiomatic binding provides the following general changes:
- Futures instead of callbacks
- Error handling using types like
Future
/Try
instead of global error variable. - Using
Option
for things that may or may not be defined.
The job of the SBT plugin is to help with common tasks for developing Chrome apps/extensions. It also provides a way to configure your app/extension in your SBT file and automatically generate the manifest file.
chromePackage
will create a ZIP file you can upload to the Chrome Web Store.chromeUnpackedOpt
(orchromeUnpackedFast
) will build your projects with (or without) optimizations enabled. The output will be intarget/chrome/unpacked-opt
(ortarget/chrome/unpacked-fast
) and can be loaded by Chrome as an unpacked extension/app.
Add this to your project/plugins.sbt
:
addSbtPlugin("com.alexitc" % "sbt-chrome-plugin" % "0.6.1")
Add this to your project dependencies:
"com.alexitc" %%% "scala-js-chrome" % "0.6.1"
If you need the latest version, clone the repository and run sbt version
on it, replace the current version with that value.
You may need to add this line to resolve the snapshot version: resolvers += Resolver.sonatypeRepo("snapshots")
with scalajs-bundler
to use the <project-name>-f{ast,ull}opt-bundle.js
generated by scalajs-bundler add the following to your build.sbt
:
fastOptJsLib := Attributed.blank((webpack in (Compile, fastOptJS)).value.head)
fullOptJsLib := Attributed.blank((webpack in (Compile, fullOptJS)).value.head)
NOTE: if code seems to be executing duplicate times unintentionally, try removing these lines from the project's build.sbt
scalaJSUseMainModuleInitializer := true
scalaJSUseMainModuleInitializer in Test := false
import chrome.app.runtime.bindings.LaunchData
import chrome.app.window.Window
import utils.ChromeApp
import scalajs.concurrent.JSExecutionContext.Implicits.queue
object ChromeAppExample extends ChromeApp {
override def onLaunched(launchData: LaunchData): Unit = {
println("hello world from scala!")
Window.create("assets/html/App.html").foreach { window =>
/**
Access to the document of the newly created window.
From here you can change the HTML of the window with whatever
library you want to use.
*/
window.contentWindow.document
}
}
}
For a more complete example see chrome-system-monitor and scala-js-chrome examples.
There are already multiple libraries to manipulate HTML and build your UI available for Scala.js:
- scala-js-dom For simple dom access
- scalatags Scala DSL for creating HTML
- scalajs-react Bindings for using react.js
- scalacss Typesafe way to write CSS with Scala
- Binding.scala Reactive data-binding for Scala
In Chrome apps and extensions there are multiple places where you can run JavaScript. Normally you split your logic into different files and load them into whatever context they need to run. Since Scala.js compiles your whole project into one big file all contexts need to load this big file with all the logic even if they only need a small subset. This can cause your app you use more memory then it need to. In some cases this can be worked around for example the a background page can manipulate the DOM of an App window so you don't need any JavaScript at all in the window itself.