Skip to content
This repository has been archived by the owner on Jan 30, 2025. It is now read-only.

Custom Tag Variables & Return Tag #115

Closed
LuLaValva opened this issue Dec 20, 2022 · 0 comments · Fixed by #116
Closed

Custom Tag Variables & Return Tag #115

LuLaValva opened this issue Dec 20, 2022 · 0 comments · Fixed by #116
Assignees

Comments

@LuLaValva
Copy link
Member

HTML Output

Sync

If we know the child returns a synchronous value, we can simply return a value from the child and assign it to a variable in the parent. This is already implemented, but at the time of implementation we had a <yield> tag instead of a <return> tag, so it needs to be renamed.

Async

I think this should probably be a separate task

Parent

If we don't know the value is sync, we need to use an async compilation - treat the returned value as maybe a promise. We'll wrap any reads of the tag variable in a fork call.

<ChildTag/tagVar/>
<const/value = someCall(tagVar)/>
const tagVar = ChildTag({ ... });
fork(tagVar, (result1) => {
    const value = someCall(tagVar)/>
});

Child

If a child returns an async value, it should return a promise.

Synchronously returning a Promise

We don't want to unwrap a promise that wasn't actually awaited, so we might want to have a custom promise wrapper and a custom fork that:

  1. checks if the passed value is this wrapper
  2. If it exists, unwrap the promise and pass it to the original fork
  3. If it doesn't exist, synchronously call the callback with the passed value

DOM Output

Parent

The parent will create a source to represent the tag variable and assign it to the child's scope in setup:

<Component/x/>
import { setTagVar } from "runtime";

const _x = _source(0, []);

const _setup = (_scope) => {
  setTagVar(_scope, 1, _x);

  _Component(_scope[1]);
};

setTagVar is a new runtime function that should look something like

function setTagVar(scope, childIndex, tagVarSignal) {
  scope[childIndex][AccessorChars.TAG_VARIABLE] = bindSignal(scope, tagVarSignal);
}

Child

The child will add a special tagVarSignal dependency to whatever signal is returned

<let/x = 0/>
<return=x/>
import { tagVarSignal } from "runtime";
const _x = _source(0, [tagVarSignal]);

This signal will look something like

export const tagVarSignal = wrapSignal(
  (methodName) => (scope, extraArg) =>
      scope[AccessorChars.TAG_VARIABLE][methodName](null, extraArg)
);

Resuming

  • The parent on the server needs to register and pass a "dummy" bound signal to the child
  • The parent in the browser needs to register the passed signal
  • The registration ids of the above need to be the same
  • The child on the server should serialize the bound signal at AccessorChars.TAG_VARIABLE in its scope

Additional consideration/optimization

We may not need to bind the signal that is passed to the child. When rendered client-side, the parent scope was being stored at scope._ until #114, so if we re-attached the parent (something that's probably going to be necessary for cleanup anyways), we could just pass that in tagVarSignal instead of null and relying on it being bound.

However, this means that we need to ensure that the parent is serialized, which is slightly more involved than the above plan for resuming, but it does mean no additional object would need to be created for tag variables:

  • The server could register statically instead of creating a new object for each render
  • The client could skip bindSignal which creates a new object and 3 closures.
@LuLaValva LuLaValva converted this from a draft issue Dec 20, 2022
Repository owner moved this from In Progress to Done in The Everything Project Dec 22, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

2 participants