Prerender (SSR/SSG)
- Dominant language
- JavaScript
- Stars
- 4
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
- [ ] JSX factory (because it can be converted into html tag string by certain libs)
- [ ] render JSX ```` as `` only if instance.prop changed else just render it as ``
- [ ] render JSX ``text {bind`prop`}`` as `text value` only if instance.prop changed else just render it as `text value`
- [ ] `@href(import.meta.url)` decorator
- [ ] `bind(...ESClasses)` tag string
- [ ] `[global]` access to static member
- [ ] auto wrap JSX `` into `` if it use ES class binding or CSS style scoping
```js
import { href } from "w/prerender"
@href(import.meta.url)
export default class Money {
static sum = 0
/** @type {string} */
#currency
get currency() { return this.#currency }
set currency(value) {
this.#currency = `$${this.value}`
}
accessor count = 0
increment() {
this.currency = ++this.count
Money.sum += sum
}
}
```
```js
import { href } from "w/prerender"
import USD from "./usd.js"
@href(import.meta.url)
export default class Money extends USD {
/** @type {string} */
set currency(value) {
super.currency = `¥${this.count}`
}
async toUSD() {
this.currency = await fetch(`https://trade.xyz/v1/convert/from=jpy&to=usd&price=${this.count}`)
}
}
```
```js
export { default as USD } from "../usd.js"
export { default as JPY } from "../jpy.js"
```
```js
import { bind, global } from "w/prerender"
import { USD, JPY } from "./modules/money.js"
// automatic
async function Item({ id }) {
const dollar =
const yen =
// const money = bind() // bind all module default classes as long as has been called inside this function. However, all followed by bind() must always be on top
const item = await fetch(`https://api.mystore.com/v1/inventory?item=${id}`)
const money = bind(dollar, yen) // doesn't need to be on top
dollar.count = yen.count = item.price;
dollar[global].sum = item.total * item.price;
// run script every time static sum has changed
console.log({money`^sum`})
return
price {money`count->toUSD currency`}
increment
}
```
## stream html on deadline
```js
class DeadlineController extends AbortController {
abort() { // cancel both streaming and ongoing fetch
super.abort()
this.cancel()
}
cancel() {/*cancel streaming*/}
}
export default new DeadlineController()
```
```jsx
import { render } from "w/prerender"
import deadline from "w/prerender/deadline"
const IPAddress = async () => {
const resp = await fetch("//ipfy.com", { signal: deadline.signal })
const addr = await resp.text()
return Your IP is {addr}
}
Page.href = "user/dashboard"
function Page() {/*use */}
// edge/serverless function
export default(request) {
if (no_AllowStreamingHeader_in(request))
deadline.cancel()
if (has_DontShowIpQuery_in(request))
deadline.abort()
return render(request, [], {
limit: { await: .1*s, response: 5*kb }, // each stream are buffered and has a limit
deadline: 200*ms, timeout: 1*s,
}) // if render not completed in 200ms (cause by async-await)
// then switch to stream mode for 1s
// and send the remaining html via SSE
}
```
### timing boundary
Sometimes you want to control what to render when static _deadline_ or stream _timeout_ expired.
#### \ boundary
```jsx
can't get IP address
probably service timeout
}>
```
which can be simplified as
```jsx
can't get IP address
probably service timeout
}
}/>
```
#### \ boundary
```jsx
server timeout
}>
```
which can be simplified as
```jsx
server timeout
}}/>
```
## Island
Nothing special here
```jsx
export const IPAddress = async (props) => {
const resp = await fetch("//ipfy.com", { signal: deadline.signal })
const addr = await resp.text()
return Your IP is {addr}
}
```
```js
import { href } from "w/prerender"
import { build } from "w/std"
import { IPAddress } from "./ipaddr.jsx"
@href(import.meta.url)
export default class {
@build(IPAddress) ip
}
```
```jsx
import { bind } from "w/prerender"
import Dashboard from "../dashboard.js"
function Page() {
return
Loading…
…
}
```
### auto Island
But it can be special
```diff
export const IPAddress = async (props) => {
const resp = await fetch("//ipfy.com", { signal: deadline.signal })
const addr = await resp.text()
return Your IP is {addr}
}
+ IPAddress.href = import.meta.url
```
```jsx
import { IPAddress } from "../ipaddr.jsx"
function Page() {
return
Loading…
…
}
```
The trick is simple: if `function.href.endsWith(".js")` then `import(href)` on the client, get the function name from `Comment` string, and finally render/build that function.
### \ boundary
Sometimes you want to keep it on the server and render it as static html
```diff
import { IPAddress } from "../ipaddr.jsx"
function Page() {
return
Loading…
+
+
…
}
```
You can use timing boundary to fallback into client rendering
```diff
import { IPAddress } from "../ipaddr.jsx"
function Page() {
return
Loading…
+
+
…
}
```
which can be simplified as
```jsx
```
or
```jsx
```
## `function` vs `=>`
- `function` will scope/isolate the DOM by wrapping the returned html in ``
- `=>` (arrow function) doesn't scope/isolate the DOM
EXCEPT function *generator
(doesn't matter if it async or not)
Generator functions are special because it's dynamic Component. Each yield and return doesn't wrap the value in ``. To scope/isolate the DOM, you must return a function.
```jsx
async function *Component() {
yield Loading… // not isolated
const data = await service()
yield function() { //
const c =
[c, c[global]].forEach(each => Object.assign(each, data))
return
Submit
}
}
```
## `use(Class)` vs ``
> **TL;DR**
> - `` - only for SSR/SSG and it have safeguard
> - `use()` - primarily for Island but can be used in SSR/SSG via \ boundary with 1 caveat: no safeguard
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.