@jk2908/solas 0.16.1 → 0.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +16 -75
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Solas is experimental and under active development, so expect rough edges.
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
-
Solas requires **Vite 8** (which uses Rolldown as the bundler)
|
|
9
|
+
Solas requires **Vite 8** (which uses Rolldown as the bundler).
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
12
|
npm install @jk2908/solas react react-dom react-server-dom-webpack vite@^8
|
|
@@ -107,22 +107,9 @@ await router.go('/posts')
|
|
|
107
107
|
await router.go('/p/:id', { params: { id: 'post-1' } })
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
That gives you
|
|
110
|
+
That gives you autocomplete for known paths, required params for dynamic routes, and typed query and navigation options.
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
- required params for dynamic routes like `/p/:id`
|
|
114
|
-
- rejected params for static routes that do not accept them
|
|
115
|
-
- typed query and navigation options on `router.go(...)`
|
|
116
|
-
|
|
117
|
-
If you already have a concrete path, you can pass that directly instead of using route params:
|
|
118
|
-
|
|
119
|
-
```tsx
|
|
120
|
-
;<Link href={`/p/${post.id}`} />
|
|
121
|
-
|
|
122
|
-
await router.go(`/p/${post.id}`)
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
In that form the path is already resolved, so `params` are not used. Typed `Link` and `router.go(...)` only allow `params` when you pass a route pattern like `/p/:id`.
|
|
112
|
+
Pass a concrete path when it is already resolved. In that form `params` are not used; typed `Link` and `router.go(...)` only accept `params` with a route pattern like `/p/:id`.
|
|
126
113
|
|
|
127
114
|
If the target already contains a query string and you also pass `query`, Solas merges them. Existing query entries are kept unless you override the same key in `query`, and explicit `query` values win:
|
|
128
115
|
|
|
@@ -141,7 +128,7 @@ Use `Link` for same-origin app navigation. Prefetching is opt-in:
|
|
|
141
128
|
<Link href="/p/:id" params={{ id: 'post-1' }}>Typed params</Link>
|
|
142
129
|
```
|
|
143
130
|
|
|
144
|
-
`prefetch="none"` is the default.
|
|
131
|
+
`prefetch="none"` is the default. Nothing is prefetched unless you opt in with `Link` or call `router.prefetch(...)`.
|
|
145
132
|
|
|
146
133
|
Use `useRouter()` inside client components for programmatic navigation, prefetching, and refreshing the current route:
|
|
147
134
|
|
|
@@ -175,9 +162,7 @@ export function Controls() {
|
|
|
175
162
|
|
|
176
163
|
```tsx
|
|
177
164
|
const finalPath = await router.go('/posts')
|
|
178
|
-
```
|
|
179
165
|
|
|
180
|
-
```tsx
|
|
181
166
|
await router.refresh()
|
|
182
167
|
```
|
|
183
168
|
|
|
@@ -192,24 +177,7 @@ await router.go('/p/:id', {
|
|
|
192
177
|
})
|
|
193
178
|
```
|
|
194
179
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
```tsx
|
|
198
|
-
import type { Route, Solas } from '@jk2908/solas'
|
|
199
|
-
|
|
200
|
-
export const metadata: Route.Metadata<Solas.Routes['/writing/:slug']> = ({ params }) => {
|
|
201
|
-
const post = allPosts?.find(p => p.__mdsrc.slug === params?.slug)
|
|
202
|
-
|
|
203
|
-
return {
|
|
204
|
-
title: post?.title ?? 'Post not found',
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
export const staticParams: Route.StaticParams<Solas.Routes['/writing/:slug']> = () =>
|
|
209
|
-
allPosts?.map(p => ({ slug: p.__mdsrc.slug })) ?? []
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
That keeps your route params aligned across links, imperative navigation, metadata, and static params.
|
|
180
|
+
`Route.Metadata` and `Route.StaticParams` reuse those generated types in page exports.
|
|
213
181
|
|
|
214
182
|
`router.refresh()` clears the cached RSC response for the current path and fetches a fresh payload, so it is most useful for routes that render request-time data. `router.isNavigating` exposes pending client-side navigation state.
|
|
215
183
|
|
|
@@ -526,7 +494,7 @@ sitemap: {
|
|
|
526
494
|
}
|
|
527
495
|
```
|
|
528
496
|
|
|
529
|
-
The sitemap is written to the build output directory
|
|
497
|
+
The sitemap is written to the build output directory and served at `/sitemap.xml`. When enabled, Solas also writes a `robots.txt` (served at `/robots.txt`) that points at the sitemap, unless a `robots.txt` is provided in the public directory.
|
|
530
498
|
|
|
531
499
|
### `logger.level`
|
|
532
500
|
|
|
@@ -534,15 +502,7 @@ Use `logger.level` to control internal Solas logging.
|
|
|
534
502
|
|
|
535
503
|
Default: `info`
|
|
536
504
|
|
|
537
|
-
Valid values are `debug`, `info`, `warn`, `error`, and `fatal`.
|
|
538
|
-
|
|
539
|
-
- `debug`: show everything
|
|
540
|
-
- `info`: the default
|
|
541
|
-
- `warn`: only warnings and errors
|
|
542
|
-
- `error`: only errors
|
|
543
|
-
- `fatal`: only fatal errors
|
|
544
|
-
|
|
545
|
-
This is mainly useful when debugging framework behaviour such as routing and prerendering. It is for Solas internals, not your app's general-purpose logging, and it does not control top-level build and preview status output.
|
|
505
|
+
Valid values are `debug`, `info`, `warn`, `error`, and `fatal`. This controls Solas's internal logging only, not your app's logging or the build and preview status output.
|
|
546
506
|
|
|
547
507
|
```ts
|
|
548
508
|
export default defineConfig({
|
|
@@ -583,8 +543,6 @@ export default defineConfig({
|
|
|
583
543
|
})
|
|
584
544
|
```
|
|
585
545
|
|
|
586
|
-
The adapter is passed to `solas()` rather than added to the plugins array directly, so platform wiring stays in one place (the same shape as SvelteKit's `sveltekit({ adapter: adapter() })`).
|
|
587
|
-
|
|
588
546
|
Give the Worker a name and a compatibility date that enables `nodejs_compat`:
|
|
589
547
|
|
|
590
548
|
```ts
|
|
@@ -600,7 +558,7 @@ export default defineConfig({
|
|
|
600
558
|
})
|
|
601
559
|
```
|
|
602
560
|
|
|
603
|
-
> `cloudflare.config.ts` is an open beta and
|
|
561
|
+
> `cloudflare.config.ts` is an open beta and requires Node 22.18+. The Cloudflare build and deploy commands run under Node, not Bun. See the [`cf` documentation](https://developers.cloudflare.com/cf/).
|
|
604
562
|
|
|
605
563
|
#### Bindings
|
|
606
564
|
|
|
@@ -629,7 +587,7 @@ const value = await env.MY_KV.get('key')
|
|
|
629
587
|
|
|
630
588
|
The Cloudflare Vite plugin emulates bindings in `cf dev` and `vite preview`, so there is nothing extra to configure locally.
|
|
631
589
|
|
|
632
|
-
Bindings
|
|
590
|
+
Bindings are unavailable during the build-time prerender; reading one warns and returns `undefined`, so read it at request time or guard it with a fallback.
|
|
633
591
|
|
|
634
592
|
For types, the plugin writes `.cloudflare/types/index.d.ts` (bindings inferred from `cloudflare.config.ts`). Include it in `tsconfig.json` so `cloudflare:workers`'s `env` is typed:
|
|
635
593
|
|
|
@@ -639,15 +597,6 @@ For types, the plugin writes `.cloudflare/types/index.d.ts` (bindings inferred f
|
|
|
639
597
|
}
|
|
640
598
|
```
|
|
641
599
|
|
|
642
|
-
The adapter:
|
|
643
|
-
|
|
644
|
-
- configures `@cloudflare/vite-plugin` to build the `rsc` environment as the Worker, and injects the generated entrypoint, the `ASSETS` binding, and `htmlHandling: none` / `notFoundHandling: none` into the Worker config
|
|
645
|
-
- generates the Worker entry (a virtual module) that delegates to the typed `createWorker` helper
|
|
646
|
-
- points Solas's prerender step at the Cloudflare Worker bundle
|
|
647
|
-
- copies `dist/static` (full prerenders) and `dist/.solas` (runtime manifest + ppr artifacts) into the asset output under `/_solas-artifacts`
|
|
648
|
-
|
|
649
|
-
`htmlHandling: none` stops the asset layer serving a prerendered document at its route path. That matters because the asset layer ignores `Accept`, so an RSC navigation (`Accept: text/x-component`) to a fully-prerendered route would otherwise receive HTML and fail to parse. Routing documents through the Worker lets Solas serve the prerender for `text/html` and render RSC otherwise, while non-HTML assets (JS, CSS, fonts, images) are still served straight from the asset layer.
|
|
650
|
-
|
|
651
600
|
`cloudflare()` forwards its `cloudflare` option to `@cloudflare/vite-plugin`.
|
|
652
601
|
|
|
653
602
|
### Static
|
|
@@ -667,7 +616,7 @@ export default defineConfig({
|
|
|
667
616
|
})
|
|
668
617
|
```
|
|
669
618
|
|
|
670
|
-
`vite build` writes the site to `build/` (override with `outDir`): the client assets, each route as `<route>.html` (or `<route>/index.html` under `trailingSlash: 'always'`), its RSC payload as `<route>.rsc.txt`, plus `sitemap.xml` and `robots.txt`. Serve that directory with
|
|
619
|
+
`vite build` writes the site to `build/` (override with `outDir`): the client assets, each route as `<route>.html` (or `<route>/index.html` under `trailingSlash: 'always'`), its RSC payload as `<route>.rsc.txt`, plus `sitemap.xml` and `robots.txt`. Serve that directory with any static host.
|
|
671
620
|
|
|
672
621
|
Client-side navigation works with no server: the browser router fetches each route's `<route>.rsc.txt` payload instead of asking a server for `text/x-component`.
|
|
673
622
|
|
|
@@ -681,7 +630,7 @@ Runtime helpers run at request time. They live under `@jk2908/solas/runtime/*` a
|
|
|
681
630
|
|
|
682
631
|
### Node
|
|
683
632
|
|
|
684
|
-
`vite dev` and `vite preview` already run the built handler in Node
|
|
633
|
+
`vite dev` and `vite preview` already run the built handler in Node, so nothing extra is needed locally. For a production Node server, use `@jk2908/solas/runtime/node`:
|
|
685
634
|
|
|
686
635
|
```ts
|
|
687
636
|
// server.ts
|
|
@@ -699,9 +648,9 @@ await serve()
|
|
|
699
648
|
}
|
|
700
649
|
```
|
|
701
650
|
|
|
702
|
-
Set `NODE_ENV=production` before starting
|
|
651
|
+
Set `NODE_ENV=production` before starting, or the server renders development payloads.
|
|
703
652
|
|
|
704
|
-
`serve()` loads the built RSC entry (`dist/rsc/index.js` by default)
|
|
653
|
+
`serve()` loads the built RSC entry (`dist/rsc/index.js` by default). Options: `port` (defaults to `PORT` or `3000`), `host`, `outDir`, and `entry`. Run from the project root so the handler can resolve `dist`.
|
|
705
654
|
|
|
706
655
|
Set `precompress: true` in the Solas config to emit brotli variants of client and prerendered assets at build time. `serve()` negotiates them when the client sends `accept-encoding: br` and falls back to the original file otherwise. Dynamic HTML and RSC responses are streamed per request and are not precompressed, so compress those at a proxy or CDN if you need it.
|
|
707
656
|
|
|
@@ -734,9 +683,9 @@ http2
|
|
|
734
683
|
.listen(443)
|
|
735
684
|
```
|
|
736
685
|
|
|
737
|
-
`serve()`
|
|
686
|
+
`serve()` is HTTP/1.1; use these listeners when Node terminates TLS or HTTP/2 itself.
|
|
738
687
|
|
|
739
|
-
Bun and Deno
|
|
688
|
+
Bun and Deno can call the handler directly:
|
|
740
689
|
|
|
741
690
|
```ts
|
|
742
691
|
// Bun
|
|
@@ -752,13 +701,9 @@ import handler from './dist/rsc/index.js'
|
|
|
752
701
|
Deno.serve(request => handler.fetch(request))
|
|
753
702
|
```
|
|
754
703
|
|
|
755
|
-
The handler reads `dist` through `node:fs`, which Bun and Deno (2.x, via Node compat) provide. Run from the project root. Bun can also run `@jk2908/solas/runtime/node` as-is, since it implements `node:http`.
|
|
756
|
-
|
|
757
704
|
### Cloudflare runtime
|
|
758
705
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
To build a Worker manually, `createWorker` composes the entry behaviour, and `createCloudflareAssets` builds the store directly:
|
|
706
|
+
Workers have no filesystem, so they read Solas's output through the `ASSETS` binding. `createWorker` composes the Worker entry, and `createCloudflareAssets` builds the store:
|
|
762
707
|
|
|
763
708
|
```ts
|
|
764
709
|
import { createCloudflareAssets } from '@jk2908/solas/runtime/cloudflare'
|
|
@@ -766,10 +711,6 @@ import { createCloudflareAssets } from '@jk2908/solas/runtime/cloudflare'
|
|
|
766
711
|
const assets = createCloudflareAssets(env)
|
|
767
712
|
```
|
|
768
713
|
|
|
769
|
-
### Asset store
|
|
770
|
-
|
|
771
|
-
At request time Solas reads its output through an `Assets` interface (`exists`/`readText`/`readBuffer`/`mimeType`) injected into `createHandler`, defaulting to the Node standard library (which Bun runs natively). References are logical (`{ namespace, path }`), where the namespaces are `client` (Vite client output), `static` (full prerenders), and `artifact` (runtime manifest and ppr files). Platforms without a filesystem provide their own store; `@jk2908/solas/runtime/cloudflare` builds one around the `ASSETS` binding via `createWorker`. Build-time reads and writes use `node:fs` directly and are not abstracted.
|
|
772
|
-
|
|
773
714
|
## Scripts
|
|
774
715
|
|
|
775
716
|
Add scripts to your app:
|
package/package.json
CHANGED