Announcing Scala.js 1.0.0-M8
Jun 3, 2019.
We are very excited to announce the release of Scala.js 1.0.0-M8!
This development release is mostly intended for testing purposes, and as a synchronization point with library authors so that they can start upgrading in preparation for the final release.
As the change in “major” version number witnesses, this release is not binary compatible with 0.6.x, nor with the previous milestones of the 1.x series. Libraries need to be recompiled and republished using this milestone to be compatible.
Moreover, this release is not entirely source compatible with 0.6.x either. We expect, however, that further milestones for 1.0.0 will stay backward source compatible with this milestone.
These release notes contain cumulative changes with respect to 0.6.28. Compared to 1.0.0-M7, the following changes are noteworthy:
- Add support for Scala 2.13.0-RC3, and hopefully for Scala 2.13.0 final
- Overall bring up-to-date with improvements and bug fixes available in 0.6.28
- Scala.js now emits ECMAScript 2015 code by default
JSEnvAPI has been refactored to use
java.nio.file.Paths instead of custom virtual files
- The linker API is now fully asynchronous; clean separation into different compatibility layers is pending (#3656)
We would also like to remind readers of the following important change that happened in 1.0.0-M5 and 1.0.0-M7, respectively:
- Drop compatibility with sbt-crossproject v0.4.x and earlier (v0.5.0 or later is required)
- With the default module kind
vars, rather than assigned as properties of the global object
Please report any issues on GitHub.
Preparations before upgrading from 0.6.x
Before upgrading to 1.0.0-M8, we strongly recommend that you upgrade to Scala.js 0.6.28 or later, and address all deprecation warnings. Since Scala.js 1.0.0-M8 removes support for all the deprecated features in 0.6.x, it is easier to see the deprecation messages guiding you to the proper replacements.
In particular, make sure that you explicitly use
sbt-crossproject instead of the default
crossProject is deprecated in 0.6.28, but it is easy to overlook deprecations in the build itself.
Additionally to the explicitly deprecated things, make sure to use
scalaJSLinkerConfig instead of the following sbt settings:
Upgrade to 1.0.0-M8 from 0.6.28 or later
As a first approximation, all you need to do is to update the version number in
In addition, if you use some of the components that have been moved to separate repositories, you will need to add some more dependencies in
If you use
- Change its version number to
"1.0.0-RC1"(irrespective of the version of Scala.js itself, and hence of the
If you use
jsDependencies (or rely on the
jsDependencies of your transitive dependencies):
addSbtPlugin("org.scala-js" % "sbt-jsdependencies" % "1.0.0-M8")in
If you use the Node.js with jsdom environment:
libraryDependencies += "org.scala-js" %% "scalajs-env-jsdom-nodejs" % "1.0.0-M8"in
If you use the PhantomJS environment:
addSbtPlugin("org.scala-js" % "sbt-scalajs-env-phantomjs" % "1.0.0-M8")in
Finally, if your build has
you will need to remove it (Scala.js 1.x always behaves as if
sjsDefinedByDefault were present).
This should get your build up to speed to Scala.js 1.0.0-M8. From there, you should be able to test whether things go smoothly, or whether you are affected by the breaking changes detailed below.
This section discusses the backward incompatible changes, which might affect your project.
Scala 2.10.x is not supported anymore, nor building on JDK 6 and 7
The title says it all: you cannot use Scala.js with
anymore. Note that you can still use the sbt plugin with sbt 0.13.17+, even though it runs itself on 2.10.x. Only the Scala.js code itself (not the build) cannot use Scala 2.10.x.
In addition, building Scala.js code on top of JDK 6 or 7 is not supported anymore either.
Finally, a severe regression in Scala 2.12.0 upstream, affecting
js.UndefOr, forced us to drop support for Scala 2.12.0 (see #3024).
Scala 2.12.1+ is supported.
Access to the global scope instead of the global object
This is the only major breaking change at the language level.
In Scala.js 1.x,
Concretely, this has three consequences, which we outline below.
Further information can be found in the documentation about the global scope in Scala.js.
For example, the following is valid:
but the following variant, where the name
Math is only known at run-time, is not valid anymore:
Global scope objects cannot be stored in a separate
For example, the following is invalid and will cause a compile error:
as well as:
This follows from the previous rule. If the above two snippets were allowed, we could not check that we only access members with statically known names.
The first snippet can be advantageously replaced by a renaming import:
Accessing a member that is not declared causes a
ReferenceError to be thrown
This is a run-time behavior change, and in our experience the larger source of breakages in actual code.
Previously, reading a non-existent member of the global object, such as
would evaluate to
In Scala.js 1.x, this throws a
Similarly, writing to a non-existent member, such as
would previously create said global variable.
In Scala.js 1.x, it also throws a
A typical use case of the previous behavior was to test whether a global variable was defined or not, e.g.,
This idiom is broken in Scala.js 1.x, and needs to be replaced by an explicit use of
js.typeOf “method” is magical when its argument is a member of a global scope object.
Scala.js emits ECMAScript 2015 code by default
Now that ES 2015 has been supported by major JS engines for a while, it was time to emit ES 2015 code by default. The ES 2015 output has several advantages over the older ES 5.1 strict mode output:
Error, have an
[[ErrorData]]internal slot, and therefore receive proper debugging info in JS engines, allowing better display of stack traces and error messages in interactive debuggers.
- Static fields and methods in JS classes are properly inherited. See https://github.com/scala-js/scala-js/issues/2771.
- The generated code is shorter.
To revert to emitting ES 5.1 strict mode code, use the following sbt setting:
With the default module kind
NoModule, top-level exports are exported as top-level
In Scala.js 0.6.x, top-level exports such as
vars instead, as if by:
However, it also means that the generated .js file must be interpreted as a proper script for the variables to be visible by other scripts. This may have compatibility consequences.
js.UndefOr[A] is now an alias for
A | Unit
Instead of defining
js.UndefOr[+A] as its own type, it is now a simple type alias for
A | Unit:
Option-like API is of course preserved.
We do not expect this to cause any significant issue, but it may impact type inference in subtle ways that can cause compile errors for previously valid code.
You may have to adjust some uses of
js.UndefOr due to these changes.
testHtml replaces both
The separation of
testHtmlFullOpt, which were independent of the value of
scalaJSStage, caused significant unfixable issues in 0.6.x.
In Scala.js 1.x, both are replaced by a single task,
It is equivalent to the old
testHtmlFastOpt if the value of
FastOptStage (the default), and to
testHtmlFullOpt if it is
This makes it more consistent with other tasks such as
Java system properties are not loaded from
__ScalaJSEnv nor sbt’s
In 0.6.x, the sbt plugin automatically extracted
-D options in
javaOptions, and transferred them to the Java system properties API inside Scala.js, using a generated .js file filling in the magic variable
Scala.js 1.x does not support
__ScalaJSEnv anymore, therefore
-D options in
javaOptions are now ignored.
Use your own mechanism to transfer data from the build to your Scala.js code, for example source code generation.
A unique, simple sbt setting to control what JS files are
By default, only the .js file generated by
fullOptJS is given to the selected
jsEnv to be
In 0.6.x, there were several non-obvious task keys to modify this behavior:
loadedJSEnv and related.
JSEnvs decided on their own, based on unclear heuristics, whether to treat the files as modules or not, and as what kind of module.
Scala.js 1.x consolidates all of that into one simple task key
jsEnvInput of type
org.scalajs.jsenv.Input, which is an ADT with the following possible alternatives:
Input.ScriptsToLoad(scripts): a list of .js files to load as scripts
Input.ESModulesToLoad(modules): a list of files to load as ECMAScript modules (some JS envs may require a specific extension such as
.mjsfor this to work, due to limitations of the underlying engines)
Input.CommonJSModulesToLoad(modules): a list of .js files to load as CommonJS modules
JSEnvs support all kinds of
The default value of
jsEnvInput is an
Input whose type is derived from the
ModuleKind, and which contains a single file, namely the output of
scalajs-javalib-ex was removed
scalajs-javalib-ex is removed in 1.x.
It only contained a partial implementation of
If you were using it, we recommend that you integrate a copy of its source code from Scala.js 0.6.x into your project.
js.use(x).as[T] was removed
The use cases for
js.use(x).as[T] have been dramatically reduced by non-native JS classes (previously known as Scala.js-defined JS classes).
This API seems virtually unused on the visible Web.
Moreover, it was the only macro in the Scala.js standard library.
We have therefore removed it from the standard library, and it is not provided anymore. On demand, we can republish it as a separate library, if you need it.
The Tools API has been split into 2 artifacts and its packages reorganized
This only concerns consumers of the Tools API, i.e., tools that build on top of the Scala.js linker, such as ScalaFiddle.
In Scala.js 0.6.x, all the tools were in one artifact
This artifact has been split in two in Scala.js 1.x:
scalajs-logging: tiny logging API
scalajs-linker: the linker API
In addition, the packages have been reorganized as follows:
org.scalajs.core.tools.io-> gone (replaced by standard
java.nio.file.Path-based APIs, and some abstractions in
Additionally, the linker API has been refactored to be fully asynchronous in nature.
There are very few enhancements in Scala.js 1.0.0-M8. Scala.js 1.0.0 is focused on simplifying Scala.js, not on adding new features. Nevertheless, here are a few enhancements.
Scala.js can access
require and other magical “global” variables of special JS environments
require in Node.js, are now visible to Scala.js.
For example, it is possible to dynamically call
require as follows in Scala.js 1.x:
We still recommend to use
CommonJSModule for statically known imports.
Declaring inner classes in native JS classes
allowing use sites to instantiate them as
In Scala.js 0.6.x, it is very awkward to define a facade type for
OuterClass, as illustrated in issue #2398.
Scala.js 1.x now allows to declare them very easily as inner JS classes:
which in turns allows for the following call site:
It is now possible to declare non-native JS classes inside outer
classes or inside
defs, and use their
js.constructorOf in a meaningful way.
one could call it from Scala.js as:
resulting in the following output:
In Scala.js 0.6.x, the above code would compile but produce incoherent results at run-time, because
js.constructorOf was meaningless for nested classes.
Amongst others, the following bugs have been fixed since 0.6.28:
- #2800 Global
classes cannot be accessed by Scala.js
- #2382 Name clash for
$outerpointers of two different nesting levels (fixed for Scala 2.10 and 2.11; 2.12 did not suffer from the bug in 0.6.x)
- #3085 Linking error after the optimizer for
Cross-building for Scala.js 0.6.x and 1.x
If you want to cross-compile your libraries for Scala.js 0.6.x and 1.x (which you definitely should), here are a couple tips.
Dynamically load a custom version of Scala.js
Since the version of Scala.js is not decided by an sbt setting in
build.sbt, but by the version of the sbt plugin in
project/plugins.sbt, standard cross-building setups based on
++ cannot be applied.
We recommend that you load the version of Scala.js from an environment variable.
For example, you can do this in your
You can then launch
from your command line to start up your build with Scala.js 1.0.0-M8.
Extra dependencies for JS environments
You can further build on the above
val scalaJSVersion to dynamically add dependencies on
scalajs-env-jsdom-nodejs if you use them:
In both cases, you can then use the source-compatible API in
build.sbt to select your JS environment of choice.
Extra dependencies for
Similarly, you can conditionally depend on
jsDependencies as follows:
In that case, you should unconditionally keep the
on the relevant projects.
Scala.js 0.6.20 and later define a no-op
JSDependenciesPlugin to allow for this scenario.
Conditional application of
In Scala.js 1.x, the flag
-P:scalajs:sjsDefinedByDefault has been removed.
However, if you have non-native JS types in your codebase, you need this flag in Scala.js 0.6.x.
Add the following setting to your
build.sbt to conditionally enable that flag: