Skip to content

Commit d71308b

Browse files
committed
.
1 parent 807fe9b commit d71308b

File tree

6 files changed

+248
-3
lines changed

6 files changed

+248
-3
lines changed

site/docs/_blog/_posts/2024-05-22-Viteless.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ Originall, I wanted to use module preloads. However, it's not possible to use mo
8585

8686
What we do intead, is to provide each module with a hash of it's content. When the module is loaded, we check the hash. If the hash is different, we reload the module. This is a very simple approach, but it works. If we configure middleware correctly, then wqhen the browser comes to reload, it can send the ETag and Validity of the existing resource. If we match, then we simply send back a 304 and the browser uses the cached resource.
8787

88-
So reloading? Fast. Very, fast. And the difficult module resolution problems? All dealt with by your friendly neighbourhood browser.
88+
So reloading? Fast. Very, fast. And the difficult module resolution problems? All dealt with by your friendly neighbourhood browser. Even better, we can take advantage of a little knowledge of scala-js to preload the fat `internal` dependancies!
89+
90+
This is a big win, because the fat dependancies are the slowest to load and appear at the end of the module graph. I believe this change makes us faster than vite for non-trivial projects.
8991

9092
To generate our `index.html`, our dev server monitors file changes, and updates a `MapRef[File, Hash]`. We use that `MapRef` to generate the `index.html` on demand. It appears natural, to request a page refresh (and a new `index.html`) when we detect linker success.
9193

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
title: On CDNs
3+
---
4+
5+
One of the objects which seems to crop up rather often when discussing this idea, is that
6+
7+
> My users are now dependant on a CDN!
8+
9+
This is true, although my invesigtaion makes this appear way less scary then it first looks. In fact on balance I've developed a strong preference for them...
10+
11+
The reliability concern is dealt with in two ways. Firstly according to jsdelivr [status](https://status.jsdelivr.com), it's uptime was 100% in 2023. So, that's not too bad.
12+
13+
And if you specifiy the explicit dependance of the module you are using... actually, it gets waaaay better ... because when the CDN responds to an explicitly versioned request, it includes a bunch of headers which say "This artifact is immutable. Don't bother me about this again. Ever".
14+
15+
And the browser wqill respect that - next time you go ask for your dependancy, it simply loads it out of it's cache. There _is no network request_. Dependancy load time : 0ms, according to the browser network tools.
16+
17+
It's tought to get faster than 0. Also, no request means no netork risk.
18+
19+
So, under "ideal" conditions:
20+
21+
- You are using a modern browser
22+
- You are making the request for a second+ time
23+
- You have explicitly specified the version of the dependancies you're using
24+
25+
Dependancy resolution is both very reliable and very fast. What's cool - that cache survives redeployment. So your app is slow for the user the first time... but the cache survives a redeployment. It's fast afterwards. We can reword the statment as follows;
26+
27+
> My users are now dependant on a CDN being available the first time they visit my site.
28+
29+
Which is less scary given the historical uptime!

site/docs/_docs/bundler.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
title: Ummm... no Bundler?
3+
---
4+
5+
Yup, no bundler.
6+
7+
Instead, consider using [ESModules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules), and resolving NPM dependancies out of a [CDN](https://www.jsdelivr.com).
8+
9+
It's less scary than it appears, and it feels _very_ good once it's up and going. An example that uses shoelace. [Sans Bundler](https://github.com/Quafadas/ShoelaceSansBundler).
10+
11+
# Scala-CLI
12+
13+
You'll need an `importmap.json` file.
14+
15+
```json
16+
{
17+
"imports": {
18+
"@shoelace-style/shoelace/dist/": "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.13.1/cdn/",
19+
}
20+
}
21+
```
22+
23+
and directives that tells scala-cli to use scala js, esmodules and where to find it.
24+
25+
```
26+
//> using platform js
27+
//> using jsModuleKind es
28+
//> using jsEsModuleImportMap importmap.json
29+
//> using jsModuleSplitStyleStr smallmodulesfor
30+
//> using jsSmallModuleForPackage frontend
31+
//> using dep com.raquo::laminar-shoelace::0.1.0
32+
```
33+
34+
# Mill (0.11.8+)
35+
36+
In your frontend module
37+
38+
```scala sc:nocompile
39+
40+
override def scalaJSImportMap = T {
41+
Seq(
42+
ESModuleImportMapping.Prefix("@shoelace-style/shoelace/dist/", "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.13.1/cdn/")
43+
)
44+
}
45+
```
46+
47+
Don't forget to depend on the facade too :-)...
48+
49+
```scala sc:nocompile
50+
def ivyDeps = Agg( ivy"com.raquo::laminar-shoelace::0.1.0" )
51+
```
52+
53+
# SBT
54+
55+
I haven't personally used, but it would be possible, with this plugin;
56+
57+
https://github.com/armanbilge/scalajs-importmap
58+
59+
# Misc
60+
61+
If you're walking on the bleeding edge with modules that aren't designed to be loaded out of a CDN (looking at you, SAP UI5 webcomponents), then things are not easy. You may need to give the browser some hints, on where it can resolve other parts of the module grap in your index.html;
62+
63+
64+
```json
65+
<script type="importmap">
66+
{
67+
"imports": {
68+
"@ui5/webcomponents-theming/": "https://cdn.jsdelivr.net/npm/@ui5/webcomponents-theming/",
69+
"@ui5/webcomponents-localization/": "https://cdn.jsdelivr.net/npm/@ui5/webcomponents-localization@1.24.7/",
70+
"@ui5/webcomponents/": "https://cdn.jsdelivr.net/npm/@ui5/webcomponents@1.24.7/",
71+
"@ui5/webcomponents-theming/": "https://cdn.jsdelivr.net/npm/@ui5/webcomponents-theming@1.24.7/",
72+
"@ui5/webcomponents-icons/": "https://cdn.jsdelivr.net/npm/@ui5/webcomponents-icons@1.24.7/",
73+
"@ui5/webcomponents-base/": "https://cdn.jsdelivr.net/npm/@ui5/webcomponents-base@1.24.7/",
74+
"@sap-theming/": "https://cdn.jsdelivr.net/npm/@sap-theming@1.24.7/",
75+
"@types/openui5/": "https://cdn.jsdelivr.net/npm/@types/openui5@1.24.7/",
76+
"@types/jquery/": "https://cdn.jsdelivr.net/npm/@types/jquery/",
77+
"lit-html": "https://cdn.jsdelivr.net/npm/lit-html",
78+
"lit-html/": "https://cdn.jsdelivr.net/npm/lit-html/"
79+
}
80+
}
81+
```
82+
This actually got 95% of the way there... but localisation doesn't work. That means (for example) the date picker doesn't work. I have no good answer for this. Given that project explicitly recommends not using a CDN, it's not an obvious move to attempt this.
83+
84+
The browser network tools are your friend.

site/docs/_docs/config.md

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# Config
22

3-
The server is a CLI. It has a number of flags that can be used to configure it. Here is the current list of flags and what they do.
3+
The server is a CLI. It has a number of flags that can be used to configure it. Here is the current list of flags and what they do. You can see these flags by running ` --help` in your terminal.
4+
5+
```
6+
cs launch io.github.quafadas:live-server-scala-cli-js_3:{{projectVersion}} -- --help
7+
8+
```
9+
410

511
```
612
Usage: LiveServer [--project-dir <string>] [--out-dir <string>] [--port <integer>] [--proxy-target-port <integer>] [--proxy-prefix-path <string>] [--log-level <string>] [--build-tool <string>] [--browse-on-open-at <string>] [--extra-build-args <string>]... [--mill-module-name <string>] [--path-to-index-html <string>] [--styles-dir <string>]
@@ -37,3 +43,104 @@ Options and flags:
3743
--styles-dir <string>
3844
A fully qualified path to your styles directory with LESS files in - e.g. c:/temp/helloScalaJS/styles
3945
```
46+
47+
# Examples;
48+
49+
## Scala-cli single module
50+
51+
Fire up a terminal in projectDir
52+
53+
```
54+
| projectDir
55+
├── hello.scala
56+
```
57+
58+
```sh
59+
cs launch io.github.quafadas::sjsls:{{projectVersion}}
60+
```
61+
This is the classic [viteless](https://github.com/Quafadas/viteless/tree/main) example
62+
63+
## Styles baby, make it look good!
64+
65+
With styles.
66+
67+
```
68+
| projectDir
69+
├── hello.scala
70+
├── styles
71+
├── ├── index.less
72+
```
73+
Run
74+
75+
```sh
76+
cs launch io.github.quafadas::sjsls:{{projectVersion}} -- --styles-dir --fully/qualified/dir/to/styles
77+
```
78+
79+
## Did I mention I want a full blown SPA?
80+
81+
With client side routing under `/app`?
82+
83+
```
84+
| projectDir
85+
├── hello.scala
86+
├── styles
87+
├── ├── index.less
88+
```
89+
Run
90+
91+
```sh
92+
cs launch io.github.quafadas::sjsls:{{projectVersion}} -- --client-routes-prefix app
93+
```
94+
95+
## Stop generating my HTML. I want to bring my own.
96+
97+
Okay.
98+
99+
```
100+
| projectDir
101+
├── hello.scala
102+
├── assets
103+
├── ├── index.less
104+
├── ├── index.html
105+
```
106+
With
107+
```sh
108+
cs launch io.github.quafadas::sjsls:{{projectVersion}} -- --path-to-index-html fully/qualified/path/to/assets
109+
```
110+
111+
Note: if you're brining your own html, drop the `--styles` flag - reference `index.less` from your html and read [docs](https://lesscss.org) to get it working in browser.
112+
113+
***
114+
You need to include this javascript script tag in the body html - otherwise no page refresh.
115+
116+
```
117+
<script>
118+
const sse = new EventSource("/refresh/v1/sse");
119+
sse.addEventListener("message", (e) => {
120+
const msg = JSON.parse(e.data);
121+
122+
if ("KeepAlive" in msg) console.log("KeepAlive");
123+
124+
if ("PageRefresh" in msg) location.reload();
125+
});
126+
</script>
127+
```
128+
***
129+
130+
## Full stack - need proxy to backend
131+
132+
With a backend running on `8080` and a frontend on `3000`, it is configured that requests beginning with `api` are proxied to localhost:8080.
133+
134+
Also, we're now using mill. We need to tell the cli the frontend module name and the directory the compiles JS ends up in.
135+
136+
```sh
137+
cs launch io.github.quafadas::sjsls:{{projectVersion}} -- \
138+
--path-to-index-html /Users/simon/Code/mill-full-stack/frontend/ui \
139+
--build-tool mill \
140+
--mill-module-name frontend \
141+
--port 3000 \
142+
--out-dir /Users/simon/Code/mill-full-stack/out/frontend/fastLinkJS.dest \
143+
--proxy-prefix-path /api \
144+
--proxy-target-port 8080 \
145+
146+
```

site/docs/_docs/deployment.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
title: Deployment
3+
---
4+
5+
This project targets the dev loop. When comes to deploy however, I always hit the same problem. From discord;
6+
7+
8+
> Ah, yeah, the classic SPA problem. While you develop, everything works because vite/react-scripts/... takes care of it. And when you deploy, everything seems to work fine, because navigation works via the history object and doesn't really send new requests. Only if you reload the page are you getting the 404. At least for modern SPAs and frameworks. There also was that brief period where most SPAs used the fragment for navigation, which didn't have that problem.
9+
>
10+
> The main thing complicating things is that - in most cases - you also need to serve resources, so neither "proxy /api, everything else to index.html" nor "everything /app to index html, rest proxied" work directly.
11+
>
12+
> What I've seen a few times is:
13+
14+
> - Everything at /api gets proxied
15+
> - Everything else is resolved as is
16+
> - index.html is the fallback document that you get instead of a 404.
17+
18+
This project provides a tiny helper library and an opinionated strategy to get over this hurdle, following exacltly the strategy laid out above.
19+
20+
21+
22+
23+

site/docs/_docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ scala-cli --version && \
1010
cs version && \
1111
git clone https://github.com/Quafadas/viteless.git && \
1212
cd viteless && \
13-
cs launch io.github.quafadas:live-server-scala-cli-js_3:0.1.1
13+
cs launch io.github.quafadas:live-server-scala-cli-js_3:{{projectVersion}}
1414
```
1515

1616
## It worked... okay... I have 20 more seconds

0 commit comments

Comments
 (0)