Skip to content

Commit 70b9205

Browse files
committed
Adapt the tutorials to use @scala-js/vite-plugin-scalajs.
1 parent 1c90444 commit 70b9205

File tree

3 files changed

+56
-114
lines changed

3 files changed

+56
-114
lines changed

doc/tutorial/laminar.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ We will elaborate on this point later.
2626
To start off, we add a dependency on Laminar in our `build.sbt`:
2727

2828
{% highlight diff %}
29-
libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.2.0",
29+
libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.4.0",
3030
+
3131
+ // Depend on Laminar
3232
+ libraryDependencies += "com.raquo" %%% "laminar" % "0.14.2",

doc/tutorial/scalablytyped.md

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ In `package.json`, we add dependencies on Chart.js, its TypeScript type definiti
3838
+ "chart.js": "2.9.4"
3939
+ },
4040
"devDependencies": {
41+
"@scala-js/vite-plugin-scalajs": "1.0.0",
4142
- "vite": "^3.2.3"
4243
+ "vite": "^3.2.3",
4344
+ "typescript": "4.6.2",

doc/tutorial/scalajs-vite.md

+54-113
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,17 @@ We bootstrap our setup using the vanilla Vite template.
2222
Navigate to a directory where you store projects, and run the command
2323

2424
{% highlight shell %}
25-
$ npm create vite@3.2.1
25+
$ npm create vite@4.1.0
2626
{% endhighlight %}
2727

2828
Choose a project name (we choose `livechart`).
2929
Select the "Vanilla" framework and the "JavaScript" variant.
3030
Our output gives:
3131

3232
{% highlight shell %}
33-
$ npm create vite@3.2.1
33+
$ npm create vite@4.1.0
3434
Need to install the following packages:
35-
create-vite@3.2.1
35+
create-vite@4.1.0
3636
Ok to proceed? (y)
3737
✔ Project name: … livechart
3838
✔ Select a framework: › Vanilla
@@ -55,10 +55,11 @@ $ npm install
5555
[...]
5656
$ npm run dev
5757

58-
VITE v3.2.3 ready in 135 ms
58+
VITE v4.1.4 ready in 156 ms
5959

60-
➜ Local: http://127.0.0.1:5173/
60+
➜ Local: http://localhost:5173/
6161
➜ Network: use --host to expose
62+
➜ press h to show help
6263
{% endhighlight %}
6364

6465
Open the provided URL to see the running JavaScript-based hello world.
@@ -67,7 +68,7 @@ Open the provided URL to see the running JavaScript-based hello world.
6768

6869
In the generated folder, we find the following relevant files:
6970

70-
* `index.html`: the main web page; it contains a `<script type=module src="main.js">` referencing the main JavaScript entry point.
71+
* `index.html`: the main web page; it contains a `<script type=module src="/main.js">` referencing the main JavaScript entry point.
7172
* `main.js`: the main JavaScript entry point; it sets up some DOM elements, and sets up a counter for a button.
7273
* `counter.js`: it implements a counter functionality for a button.
7374
* `package.json`: the config file for `npm`, the JavaScript package manager and build orchestrator.
@@ -109,7 +110,7 @@ sbt.version=1.8.0
109110
* `project/plugins.sbt`: declare sbt plugins; in this case, only sbt-scalajs
110111

111112
{% highlight scala %}
112-
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.12.0")
113+
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.0")
113114
{% endhighlight %}
114115

115116
At the root of our `livechart/` project, we add one file: `build.sbt`.
@@ -122,7 +123,7 @@ import org.scalajs.linker.interface.ModuleSplitStyle
122123
lazy val livechart = project.in(file("."))
123124
.enablePlugins(ScalaJSPlugin) // Enable the Scala.js plugin in this project
124125
.settings(
125-
scalaVersion := "3.2.1",
126+
scalaVersion := "3.2.2",
126127

127128
// Tell Scala.js that this is an application with a main method
128129
scalaJSUseMainModuleInitializer := true,
@@ -143,7 +144,7 @@ lazy val livechart = project.in(file("."))
143144
/* Depend on the scalajs-dom library.
144145
* It provides static types for the browser DOM APIs.
145146
*/
146-
libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.2.0",
147+
libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.4.0",
147148
)
148149
{% endhighlight %}
149150

@@ -244,14 +245,33 @@ The `fastLinkJS` task produces the `.js` outputs from the Scala.js command.
244245
The `~` prefix instructs sbt to re-run that task every time a source file changes.
245246

246247
There is one thing left to change: replace the hand-written JavaScript code with our Scala.js application.
247-
Open the file `main.js`, remove almost everything to leave only the following two lines:
248+
We use the `@scala-js/vite-plugin-scalajs` plugin to link Vite and Scala.js with minimal configuration.
249+
We install it in the dev-dependencies with:
250+
251+
{% highlight shell %}
252+
$ npm install -D @scala-js/vite-plugin-scalajs@1.0.0
253+
{% endhighlight %}
254+
255+
and instruct Vite to use it with the following configuration in a new file `vite.config.js`:
256+
257+
{% highlight javascript %}
258+
import { defineConfig } from "vite";
259+
import scalaJSPlugin from "@scala-js/vite-plugin-scalajs";
260+
261+
export default defineConfig({
262+
plugins: [scalaJSPlugin()],
263+
});
264+
{% endhighlight %}
265+
266+
Finally, open the file `main.js`, remove almost everything to leave only the following two lines:
248267

249268
{% highlight javascript %}
250269
import './style.css'
251-
import './target/scala-3.2.1/livechart-fastopt/main.js'
270+
import 'scalajs:main.js'
252271
{% endhighlight %}
253272

254-
If you had not stopped the `npm run dev` process with Vite, Vite will immediately pick up the changes and refresh the browser with our updated "Hello Scala.js!" message.
273+
You may have to stop and restart the `npm run dev` process, so that Vite picks up the newly created configuration file.
274+
Vite will refresh the browser with our updated "Hello Scala.js!" message.
255275

256276
## Live changes with Scala.js
257277

@@ -288,129 +308,50 @@ This ensures that the development cycle remains as short as possible.
288308
## Production build
289309

290310
The `fastLinkJS` task of sbt and the `npm run dev` task of Vite are optimized for incremental development.
291-
For production, we want to perform more optimizations on the Scala.js side with `fullLinkJS`, and bundle minimized files with `npm run build`.
292-
293-
For that to work well, we need to conditionally wire the output of `fastLinkJS` (resp. `fullLinkJS`) to the input of Vite when it is in development mode (resp. production mode).
294-
This requires some configuration in Vite.
295-
296-
We create the file `vite.config.js` at the root of our project, with the following content:
297-
298-
{% highlight javascript %}
299-
import { spawnSync } from "child_process";
300-
import { defineConfig } from "vite";
301-
302-
// Detect whether Vite runs in development or production mode
303-
function isDev() {
304-
return process.env.NODE_ENV !== "production";
305-
}
306-
307-
// Utility to invoke a given sbt task and fetch its output
308-
function printSbtTask(task) {
309-
const args = ["--error", "--batch", `print ${task}`];
310-
const options = {
311-
stdio: [
312-
"pipe", // StdIn
313-
"pipe", // StdOut
314-
"inherit", // StdErr
315-
],
316-
};
317-
const result = process.platform === 'win32'
318-
? spawnSync("sbt.bat", args.map(x => `"${x}"`), {shell: true, ...options})
319-
: spawnSync("sbt", args, options);
320-
321-
if (result.error)
322-
throw result.error;
323-
if (result.status !== 0)
324-
throw new Error(`sbt process failed with exit code ${result.status}`);
325-
return result.stdout.toString('utf8').trim();
326-
}
327-
328-
// Get the output of fastLinkJS or fullLinkJS depending on isDev()
329-
const scalaJSOutputTask = isDev() ? "fastLinkJSOutput" : "fullLinkJSOutput";
330-
const scalaJSOutput = printSbtTask(scalaJSOutputTask);
331-
332-
// Tell Vite to replace references to `@scalaJSOutput` by the path computed above
333-
export default defineConfig({
334-
resolve: {
335-
alias: [
336-
{
337-
find: "@scalaJSOutput",
338-
replacement: scalaJSOutput,
339-
},
340-
],
341-
},
342-
});
343-
{% endhighlight %}
344-
345-
While this may look scary, most of the complexity is concentrated into `printSbtTask`.
346-
That utility invokes a third-party process (sbt) in a platform-independent way (in particular, for Windows) and retrieves its `stdout` output.
347-
Other than that, we are doing two things:
348-
349-
1. Depending on `process.env.NODE_ENV`, we retrieve the output of the sbt task `fastLinkJSOutput` or `fullLinkJSOutput`.
350-
These tasks return the output directory of `fastLinkJS` and `fullLinkJS`, respectively.
351-
2. We tell Vite to replace references to `@scalaJSOutput` by the retrieved path.
352-
353-
We then amend the top-level `main.js` file to use that new `@scalaJSOutput` variable:
354-
355-
{% highlight javascript %}
356-
import './style.css'
357-
import '@scalaJSOutput/main.js'
358-
{% endhighlight %}
359-
360-
If the `npm run dev` process is still running, Vite will automatically pick up the new configuration and restart its server.
361-
362-
Now, nothing visible has changed, since we still render the development mode.
363-
To build to the production version of our website, we stop Vite with `Ctrl+C` and launch the following instead:
311+
For production, we want to perform more optimizations on the Scala.js side and bundle minimized files with `npm run build`.
312+
We stop Vite with `Ctrl+C` and launch the following instead:
364313

365314
{% highlight shell %}
366315
$ npm run build
367316

368-
> livechart2@0.0.0 build
317+
> livechart@0.0.0 build
369318
> vite build
370319
371-
vite v3.2.3 building for production...
320+
vite v4.1.4 building for production...
321+
[info] welcome to sbt 1.8.0 (Temurin Java 1.8.0_362)
322+
[...]
323+
[info] Full optimizing .../livechart/target/scala-3.2.2/livechart-opt
324+
.../livechart/target/scala-3.2.2/livechart-opt
372325
✓ 11 modules transformed.
373-
dist/assets/javascript.8dac5379.svg 0.97 KiB
374-
dist/index.html 0.44 KiB
375-
dist/assets/index.d0964974.css 1.19 KiB / gzip: 0.62 KiB
376-
dist/assets/index.b5e141cb.js 28.67 KiB / gzip: 6.95 KiB
326+
dist/index.html 0.45 kB
327+
dist/assets/javascript-8dac5379.svg 1.00 kB
328+
dist/assets/index-48a8825f.css 1.24 kB │ gzip: 0.65 kB
329+
dist/assets/index-3c83baa6.js 28.84 kB │ gzip: 6.97 kB
377330
{% endhighlight %}
378331

379332
Since the built website uses an ECMAScript module, we need to serve it through an HTTP server to visualize it.
380-
We use the npm application `http-server` for that purpose, as we can run it without any additional dependency:
333+
We can use Vite's `preview` mode for that purpose, as we can run it without any additional dependency:
381334

382335
{% highlight shell %}
383-
$ cd dist
384-
$ npx http-server
385-
Starting up http-server, serving ./
386-
387-
http-server version: 14.1.1
388-
389-
http-server settings:
390-
CORS: disabled
391-
Cache: 3600 seconds
392-
Connection Timeout: 120 seconds
393-
Directory Listings: visible
394-
AutoIndex: visible
395-
Serve GZIP Files: false
396-
Serve Brotli Files: false
397-
Default File Extension: none
398-
399-
Available on:
400-
http://127.0.0.1:8080
401-
Hit CTRL-C to stop the server
336+
$ npm run preview
337+
338+
339+
> vite preview
340+
341+
➜ Local: http://localhost:4173/
342+
➜ Network: use --host to expose
402343
{% endhighlight %}
403344

404345
Navigate to the mentioned URL to see your website.
405346

406347
## Conclusion
407348

408-
In this tutorial, we saw how to configure Scala.js with Vite from the ground up.
349+
In this tutorial, we saw how to configure Scala.js with Vite from the ground up using `@scala-js/vite-plugin-scalajs`.
409350
We used sbt as our build tool, but the same effect can be achieved with any other Scala build tool, such as [Mill](https://com-lihaoyi.github.io/mill/) or [scala-cli](https://scala-cli.virtuslab.org/).
410351

411352
Our setup features the following properties:
412353

413354
* Development mode with live reloading: changing Scala source files automatically triggers recompilation and browser refresh.
414-
* Production mode, wired to automatically take the `fullLinkJS` output of Scala.js, and producing a unique `.js` file.
355+
* Production mode, wired to automatically take the fully optimized output of Scala.js, and producing a unique `.js` file.
415356

416357
In our [next tutorial about Laminar](./laminar.html), we will learn how to write UIs in idiomatic Scala code.

0 commit comments

Comments
 (0)