DrSensor / nusa

incremental runtime that bring both simplicity and power into webdev (buildless, cross-language, data-driven)
MIT License
4 stars 0 forks source link

Infer value #3

Open DrSensor opened 2 years ago

DrSensor commented 2 years ago

Warning: beware of #45

Infer from html

Infer the value only when:

/// script.ts -> script.js
export default class Global {
  // static count: number
  // count: number
  static accessor count: number
  accessor count = 0
  increment() {
    Global.count++
    this.count++
  }
}
<!-- page.html -->
<render-scope id="1">
  <button on:click="increment">

  <link href="script.js" media="(min-width: 640px)">
  <input value="1" :: value:="^count">
  <input value="1" :: infer:value="count">
</render-scope>

<render-scope id="2">
  <button on:click="increment">

  <link href="script.js" media="(min-width: )">
  <input :value="^count" value="4">
  <input :value="count" value="4">
</render-scope>
import { page } from "test:page.html"

const scope = page.$$('render-scope')
    , [ increment_button1
      , increment_button2 ] = scope.map(it => it.$("button"))
    , [ static_input1
      , input1 ] = scope[0].$$('input[type="button"]')
    , [ static_input2
      , input2 ] = scope[1].$$('input[type="button"]')

assert(static_input1.value === "1")
assert(input1.value === "1")

assert(static_input2.value === "4")
assert(input2.value === "4")

increment_button1.click()

assert(static_input1.value === "2")
assert(input1.value === "2")

assert(static_input2.value === "2")
assert(input2.value === "4") // no change

page.reload() ////////////////////////

assert(static_input1.value === "1")
assert(input1.value === "1")

assert(static_input2.value === "4")
assert(input2.value === "4")

increment_button2.click()

assert(static_input1.value === "5")
assert(input1.value === "1") // no change

assert(static_input2.value === "5")
assert(input2.value === "5")

attribute value from literal value

Parse bind attribute value as literal value if surrounded by backtick then set real attribute value from that literal value when module ready.

<render-scope>
  <link href=module.js>
  <button disabled :: disabled:="`false`">click</button>
</render-scope>

Infer on set

function infer<Container>(
  $1: Container,
  $2?: <assignment>(
    this?: Container,
    value: assignment,
  ) => typeof this extends undefined ? assignment : void,
);

🔀 Behaviour

  1. if second argument is defined then call that argument on every assignment
  2. if that argument is arrow function then either instantiate or call the Container while passing the return value of that arrow function to the Container arguments (use following last logic on how to treat the return value)

  3. if "constructor" in @infer(Container) then instantiate the Container on every assignment (assume it as a class)
  4. else just call the Container on every assignment (assume it as arrow function)

  5. if Symbol.toPrimitive in assignedValue then treat the return value of Symbol.toPrimitive() as assignedValue before being used as Container arguments (recursive check)
  6. else if Symbol.iterable in assignedValue then spread the assignedValue as Container arguments
export default class {
  @infer(Vector3) accessor position: Vector3 | [x: number, y: number, z: number]

  @infer(Date) accessor tick: Date | number

  move(e: MouseEvent) {
    this.tick = -performance.now()

    const [x, y, z] = raycast(e)
    this.position = [-x, -y, z] // or
    // this.position = new Vector3(-x, -y, z)
    this.position.normalize()

    this.tick += performance.now()
    console.debug(this.tick.getSeconds())
  }
}

Note @infer decorator can also be used to infer value from html as non-primitive value.

DrSensor commented 1 year ago

I just realize this can be used for resumable SSR! 😲

Rather than serializing the states into script tag (like Qwik does), you can just define your tag with attribute value, define the accessor without default value, and now the framework will infer the accessor value from the attribute value which generated by the server.

However, there's a catch. The accessor value must be undefined. To make it suitable for SSR, we need to introduce namespaced attr:<attribute name> attribute which override the accessor value from that attribute value.