DrSensor / DrSensor/nusa

Prerender (SSR/SSG)

Open
#38 0 comments 0 reactions 0 assignees View on GitHub
enhancement priority: low
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.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.