Skip to content

Commit a1a8766

Browse files
authored
Merge pull request #518 from sjrd/scalajs-1.3.0
Announcing Scala.js 1.3.0.
2 parents 7bf200e + 96c6a14 commit a1a8766

File tree

6 files changed

+352
-1
lines changed

6 files changed

+352
-1
lines changed

_config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ colors: #in hex code if not noted else
5757

5858
### VERSIONS ###
5959
versions:
60-
scalaJS: 1.2.0
60+
scalaJS: 1.3.0
6161
scalaJSBinary: 1
6262
scalaJS06x: 0.6.33
6363
scalaJS06xBinary: 0.6

_data/library/versions.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@
2323
- 1.0.0
2424
- 1.1.0
2525
- 1.2.0
26+
- 1.3.0
Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
---
2+
layout: post
3+
title: Announcing Scala.js 1.3.0
4+
category: news
5+
tags: [releases]
6+
permalink: /news/2020/10/16/announcing-scalajs-1.3.0/
7+
---
8+
9+
10+
We are excited to announce the release of Scala.js 1.3.0!
11+
12+
This release brings one of the most awaited features for Scala.js: module splitting support!
13+
It is now possible to split the generated .js file into multiple modules, to optimize download size in multi-page applications or speed up incremental bundling.
14+
15+
In addition, this release contains a number of new methods and classes in the JDK implementation, among which the higher-order methods of the `java.util` collections, and the locale-sensitive overloads of `String.toLowerCase`, `toUpperCase` and `format`.
16+
17+
Read on for more details.
18+
19+
<!--more-->
20+
21+
## Getting started
22+
23+
If you are new to Scala.js, head over to [the tutorial]({{ BASE_PATH }}/tutorial/).
24+
25+
If you need help with anything related to Scala.js, you may find our community [on Gitter](https://gitter.im/scala-js/scala-js) and [on Stack Overflow](https://stackoverflow.com/questions/tagged/scala.js).
26+
27+
Bug reports can be filed [on GitHub](https://github.com/scala-js/scala-js/issues).
28+
29+
## Release notes
30+
31+
If upgrading from Scala.js 0.6.x, make sure to read [the release notes of Scala.js 1.0.0]({{ BASE_PATH }}/news/2020/02/25/announcing-scalajs-1.0.0/) first, as they contain a host of important information, including breaking changes.
32+
33+
This is a **minor** release:
34+
35+
* It is backward binary compatible with all earlier versions in the 1.x series: libraries compiled with 1.0.x through 1.2.x can be used with 1.3.0 without change.
36+
* It is *not* forward binary compatible with 1.2.x: libraries compiled with 1.3.0 cannot be used with 1.2.x or earlier.
37+
* It is *not* entirely backward source compatible: it is not guaranteed that a codebase will compile *as is* when upgrading from 1.2.x (in particular in the presence of `-Xfatal-warnings`).
38+
39+
As a reminder, libraries compiled with 0.6.x cannot be used with Scala.js 1.x; they must be republished with 1.x first.
40+
41+
## Known source breaking changes
42+
43+
### `js.Promise.then`
44+
45+
The result type of `js.Promise.then` was changed from `js.Thenable` to `js.Promise`.
46+
This is unlikely to cause any issue in most cases, since `js.Promise` extends `js.Thenable`.
47+
It might cause compilation errors in some rare cases due to type inference, or if you declare a subclass of `js.Promise`.
48+
49+
## Module splitting
50+
51+
### Quickstart
52+
53+
First, instead of `fastOptJS` / `fullOptJS`, use `fastLinkJS` / `fullLinkJS`.
54+
The outputs of those commands will be in a subdirectory like `project/target/scala-2.13/project-fastopt/`, instead of as a single file `.../scala-2.13/project-fastopt.js`.
55+
You can then create different entry points and/or generate as many small modules as possible using the following setups.
56+
57+
#### For different entry points
58+
59+
Set the `moduleID` for your top-level exports and/or module initializers explicitly (a module initializer is basically a main method for the module).
60+
The default `moduleID` is `"main"`.
61+
62+
{% highlight scala %}
63+
@JSExportTopLevel(name = "startAdmin", moduleID = "admin")
64+
def startAdmin(): Unit = ???
65+
{% endhighlight %}
66+
67+
{% highlight scala %}
68+
import org.scalajs.linker.interface.ModuleInitializer
69+
scalaJSModuleInitializers in Compile += {
70+
ModuleInitializer.mainMethod("my.app.admin.Main", "main")
71+
.withModuleID("admin")
72+
}
73+
{% endhighlight %}
74+
75+
Everything with the same `moduleID` will go into the same entry point module.
76+
77+
#### For many small modules
78+
79+
By default, module splitting genereates as few modules as possible.
80+
In some cases, generates as many modules as small as possible is preferable, which can be configured with:
81+
82+
{% highlight scala %}
83+
import org.scalajs.linker.interface.ModuleSplitStyle
84+
scalaJSLinkerConfig ~= (_.withModuleSplitStyle(ModuleSplitStyle.SmallestModules))
85+
{% endhighlight %}
86+
87+
### What is Module Splitting?
88+
89+
With module splitting, the Scala.js linker splits its output into multiple JavaScript modules (i.e. files).
90+
Both ES6 modules (`ModuleKind.ESModule`) and CommonJS modules (`ModuleKind.CommonJSModule`) are supported.
91+
92+
There are several reasons to split JavaScript output into multiple files:
93+
94+
* Share code between different parts of an application (e.g. user/admin interfaces).
95+
* Create smaller files to minimize changes for incremental downstream tooling.
96+
* Load parts of a large app progressively (not supported yet, see [#4201](https://github.com/scala-js/scala-js/issues/4201)).
97+
98+
The Scala.js linker can split a full Scala.js application automatically based on:
99+
100+
* The entry points (top-level exports and module initializers)
101+
* The split style (fewest modules or smallest modules)
102+
103+
### Entry Points
104+
105+
Scala.js-generated code has two different kinds of entry points:
106+
107+
* Top level exports: Definitions to be called from external JS code.
108+
* Module initializers: Code that gets executed when a module is imported (i.e., main methods).
109+
110+
The Scala.js linker determines how to group entry points into different (public) modules by using their assigned `moduleID`.
111+
The default `moduleID` is `"main"`.
112+
113+
The `moduleID` of a top-level export can be specified using the `moduleID` parameter.
114+
The `moduleID` of a `ModuleInitializer` can be specified by the `withModuleID` method.
115+
116+
**Example**:
117+
118+
Say you have the following `App.scala` and `build.sbt`:
119+
120+
{% highlight scala %}
121+
package my.app
122+
123+
import scala.collection.mutable
124+
import scala.scalajs.js.annotation._
125+
126+
// Separate objects to allow for splitting.
127+
128+
object AppA {
129+
@JSExportTopLevel(name = "start", moduleID = "a")
130+
def a(): Unit = println("hello from a")
131+
}
132+
133+
object AppB {
134+
private val x = mutable.Set.empty[String]
135+
136+
@JSExportTopLevel(name = "start", moduleID = "b")
137+
def b(): Unit = {
138+
println("hello from b")
139+
println(x)
140+
}
141+
142+
def main(): Unit = x.add("something")
143+
}
144+
{% endhighlight %}
145+
146+
{% highlight scala %}
147+
import org.scalajs.linker.interface.ModuleInitializer
148+
149+
scalaJSModuleInitializers in Compile += {
150+
ModuleInitializer.mainMethod("my.app.AppB", "main").withModuleID("b")
151+
}
152+
{% endhighlight %}
153+
154+
This would generate two public modules `a.js` / `b.js`.
155+
`a.js` will export a method named `start` that calls `AppA.a`.
156+
`b.js` will export a method named `start` that calls `AppB.b`.
157+
Further, importing `b.js` will call `AppB.main`.
158+
159+
Note that there is no public module `main.js`, because there is no entry point using the default `moduleID`.
160+
161+
### Module Split Styles
162+
163+
So far, we have seen how public modules can be configured.
164+
Based on the public modules, the Scala.js linker generates internal modules for the shared code between the public modules.
165+
Unlike public modules, internal modules may not be imported by user code.
166+
Doing so is undefined behavior and subject to change at any time.
167+
168+
The linker generates internal modules automatically based on the dependency graph of the code and `moduleSplitStyle`.
169+
You can change it as follows:
170+
171+
{% highlight scala %}
172+
import org.scalajs.linker.interface.ModuleSplitStyle
173+
scalaJSLinkerConfig ~= (_.withModuleSplitStyle(ModuleSplitStyle.SmallestModules))
174+
{% endhighlight %}
175+
176+
There are currently two module split styles: `FewestModules` and `SmallestModules`.
177+
178+
#### `FewestModules`
179+
180+
Create as few modules as possible without including unnecessary code.
181+
This is the default.
182+
183+
In the example above, this would generate:
184+
185+
* `a.js`: public module, containing `AppA` and the export of `start`.
186+
* `b.js`: public module, containing `AppB`, `mutable.Set`, the export of `start` and the call to `AppB.main`
187+
* `a-b.js`: internal module, Scala.js core and the implementation of `println`.
188+
189+
This also works for more than two public modules, creating intermediate shared (internal) modules as necessary.
190+
191+
#### `SmallestModules`
192+
193+
Create modules that are as small as possible.
194+
The smallest unit of splitting is a Scala class.
195+
196+
Using this mode typically results in an internal module per class with the exception of classes that have circular dependencies: these are put into the same module to avoid a circular module dependency graph.
197+
198+
In the example above, this would generate:
199+
200+
* `a.js`: public module, containing the export of `start`.
201+
* `b.js`: public module, containing the export of `start` and the call to `AppB.main`
202+
* many internal small modules (~50 for this example), approximately one per class.
203+
204+
Generating many small modules can be useful if the output of Scala.js is further processed by downstream JavaScript bundling tools.
205+
In incremental builds, they will not need to reprocess the entire Scala.js-generated .js file, but instead only the small modules that have changed.
206+
207+
### Linker Output
208+
209+
With module splitting, the set of files created by the linker is not known at invocation time.
210+
To support this new requirement, the linker output is configured as follows:
211+
212+
* A directory where all files go: `scalaJSLinkerOutputDirectory`
213+
* Patterns for output file names: `outputPatterns` on `scalaJSLinkerConfig`.
214+
215+
Both of these have reasonable defaults and usually do not need to be changed.
216+
The exception is file extensions.
217+
If you need to produce `*.mjs` files for Node.js, use:
218+
219+
{% highlight scala %}
220+
import org.scalajs.linker.interface.OutputPatterns
221+
scalaJSLinkerConfig ~= (_.withOutputPatterns(OutputPatterns.fromJSFile("%s.mjs")))
222+
{% endhighlight %}
223+
224+
In order to make sense of the files in the directory, linking returns a `Report` listing the public modules and their file names.
225+
226+
### sbt backwards compatibility
227+
228+
Since the `fastOptJS` / `fullOptJS` keys/tasks assume that linking will produce a single file, we had to introduce two new keys/tasks for linking: `fastLinkJS` / `fullLinkJS`.
229+
These tasks return the `Report` instead of an individual `File`.
230+
231+
In order to ensure backwards compatibility, the `fastOptJS` / `fullOptJS` tasks now invoke `fastLinkJS` / `fullLinkJS` respectively and copy the produced files to their target location.
232+
However, this only works if the linker produced a single public module.
233+
So with actual module splitting, `fastOptJS` / `fullOptJS` will fail.
234+
235+
The `run` and `test` tasks now depend on `fastLinkJS` / `fullLinkJS` (depending on the `scalaJSStage`) and load the public module with `moduleID="main"` (they fail if no such module exists).
236+
This does not change their behavior for existing builds but allows running and testing with module splitting enabled.
237+
238+
## Miscellaneous
239+
240+
### New JDK APIs
241+
242+
This release contains a significant amount of additions in the JDK APIs that we support, notably thanks to contributions by [@er1c](https://github.com/er1c), [@ekrich](https://github.com/ekrich) and [@exoego](https://github.com/exoego).
243+
244+
New interface definitions in `java.util.function.*`:
245+
246+
* `BiConsumer`, `Supplier`, `Function`, `BiFunction`, `UnaryOperator`, `BinaryOperator` and `BiPredicate`
247+
* Specializations of `Supplier`, and `Predicate`
248+
249+
New classes:
250+
251+
* `java.lang.Character.UnicodeBlock`
252+
* `java.util.StringTokenizer`
253+
* `java.io.CharArrayWriter`
254+
* `java.io.CharArrayReader`
255+
256+
Methods with fixed behavior to comply with the JDK specification:
257+
258+
* In `java.lang.Character`:
259+
* `toLowerCase(Char)` and `toUpperCase(Char)`
260+
* In `java.lang.String`:
261+
* `compareTo`
262+
* `equalsIgnoreCase`
263+
* `compareToIgnoreCase`
264+
265+
New methods in existing classes and interfaces (some are only available when compiling on a recent enough JDK):
266+
267+
* In `java.lang.String`:
268+
* `repeat` (JDK 11+)
269+
* In `java.lang.Character`:
270+
* `toLowerCase(codePoint: Int)` and `toUpperCase(codePoint: Int)`
271+
* `toTitleCase(ch: Char)` and `toTitleCase(codePoint: Int)`
272+
* `highSurrogate` and `lowSurrogate`
273+
* `hashCode(ch: Char)`
274+
* `reverseBytes(ch: Char)`
275+
* `toString(codePoint: Int)` (JDK 11+)
276+
* Default methods in `java.util.Iterator`:
277+
* `remove`
278+
* `forEachRemaining`
279+
* Default methods in `java.util.List`:
280+
* `sort`
281+
* `replaceAll`
282+
* Default methods in `java.util.Map`:
283+
* `getOrDefault`
284+
* `forEach`
285+
* `replaceAll`
286+
* `putIfAbsent`
287+
* `remove(key, value)`
288+
* `replace(key, oldValue, newValue)`
289+
* `replace(key, value)`
290+
* `computeIfAbsent`
291+
* `computeIfPresent`
292+
* `compute`
293+
* `merge`
294+
* In `java.util.Optional`:
295+
* `isEmpty` (JDK 11+)
296+
* `ifPresent`
297+
* `ifPresentOrElse` (JDK 9+)
298+
* `filter`
299+
* `map`
300+
* `flatMap`
301+
* `or` (JDK 9+)
302+
* `orElse`
303+
* `orElseGet`
304+
* `orElseThrow(Supplier)`
305+
* `orElseThrow()` (JDK 10+)
306+
* In `java.util.Properties`:
307+
* `load`
308+
* `save`
309+
* `store`
310+
* `list`
311+
312+
Finally, the following `Locale`-sensitive methods have been added, although they will only transitively link if support for `java.util.Locale` APIs is enabled using [scala-java-locales](https://github.com/cquiroz/scala-java-locales):
313+
314+
* In `java.lang.String`:
315+
* `toLowerCase(Locale)` and `toUpperCase(Locale)`
316+
* `format(Locale, ...)`
317+
* In `java.util.Formatter`:
318+
* constructors with a `Locale` parameter
319+
* `format(Locale, ...)`
320+
321+
Speaking of locales, we have slightly changed the definition of the default locale of Scala.js.
322+
Previously, it was specified as `en-US`.
323+
Starting from Scala.js 1.3.0, it is specified as `Locale.ROOT`.
324+
This change makes no difference in terms of behavior (only in terms of "spirit"), since all the methods that were previously implemented in Scala.js have the same behavior for `ROOT` than for `en-US`.
325+
326+
Note that it is not possible to change the default locale, as methods that do not take `Locale` arguments are hard-coded for the behavior of `Locale.ROOT` (even when `scala-java-locales` is used).
327+
To get locale-sensitive behavior, the overloads taking explicit `Locale` arguments must be used.
328+
329+
## Bug fixes
330+
331+
Among others, the following bugs have been fixed in 1.3.0:
332+
333+
* [#4195](https://github.com/scala-js/scala-js/issues/4195) `LinkedHashMap` iteration not empty after `clear`
334+
* [#4188](https://github.com/scala-js/scala-js/issues/4188) Make `js.Promise.then` return `js.Promise` instead of `js.Thenable`
335+
* [#4203](https://github.com/scala-js/scala-js/issues/4203) `Matcher.region` not mutating the matcher
336+
* [#4204](https://github.com/scala-js/scala-js/issues/4204) `Matcher.start()/end()` give incorrect results
337+
* [#4210](https://github.com/scala-js/scala-js/issues/4210) `java.util.Date.from(Instant)` throws the wrong kind of exception
338+
339+
You can find the full list [on GitHub](https://github.com/scala-js/scala-js/issues?q=is%3Aissue+milestone%3Av1.3.0+is%3Aclosed).

assets/badges/scalajs-1.3.0.svg

Lines changed: 1 addition & 0 deletions
Loading

doc/all-api.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ title: All previous versions of the Scala.js API
55

66
## All previous versions of the API
77

8+
### Scala.js 1.3.0
9+
* [1.3.0 scalajs-library]({{ site.production_url }}/api/scalajs-library/1.3.0/scala/scalajs/js/index.html)
10+
* [1.3.0 scalajs-test-interface]({{ site.production_url }}/api/scalajs-test-interface/1.3.0/)
11+
* [1.3.0 scalajs-ir]({{ site.production_url }}/api/scalajs-ir/1.3.0/org/scalajs/ir/index.html)
12+
* [1.3.0 scalajs-linker-interface]({{ site.production_url }}/api/scalajs-linker-interface/1.3.0/org/scalajs/linker/interface/index.html) ([Scala.js version]({{ site.production_url }}/api/scalajs-linker-interface-js/1.3.0/org/scalajs/linker/interface/index.html))
13+
* [1.3.0 scalajs-linker]({{ site.production_url }}/api/scalajs-linker/1.3.0/org/scalajs/linker/index.html) ([Scala.js version]({{ site.production_url }}/api/scalajs-linker-js/1.3.0/org/scalajs/linker/index.html))
14+
* [1.3.0 scalajs-test-adapter]({{ site.production_url }}/api/scalajs-sbt-test-adapter/1.3.0/org/scalajs/testing/adapter/index.html)
15+
* [1.3.0 sbt-scalajs]({{ site.production_url }}/api/sbt-scalajs/1.3.0/#org.scalajs.sbtplugin.package)
16+
817
### Scala.js 1.2.0
918
* [1.2.0 scalajs-library]({{ site.production_url }}/api/scalajs-library/1.2.0/scala/scalajs/js/index.html)
1019
* [1.2.0 scalajs-test-interface]({{ site.production_url }}/api/scalajs-test-interface/1.2.0/)

doc/internals/version-history.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ title: Version history
55

66
## Version history of Scala.js
77

8+
- [1.3.0](/news/2020/10/16/announcing-scalajs-1.3.0/)
89
- [1.2.0](/news/2020/09/09/announcing-scalajs-1.2.0/)
910
- [1.1.1](/news/2020/07/02/announcing-scalajs-1.1.1/)
1011
- [1.1.0](/news/2020/05/18/announcing-scalajs-1.1.0/)

0 commit comments

Comments
 (0)