kaleidawave / prism

(No longer in development). Experimental compiler for building isomorphic web applications with web components.
MIT License
110 stars 1 forks source link

Expression reversal #11

Open kaleidawave opened 4 years ago

kaleidawave commented 4 years ago

The idea behind Prism JIT hydration system is that as a compiler it knows where variables are interpolated and so can build ways to retrieve them and cache them into the components data / state.

The first step is the runtime knowing where in the DOM tree they are interpolated. Prism does this by adding identifiers to nodes and also what part they are interpolated at (attribute, text node etc). The compiler builds in these expressions in get-value.ts

And this is fine for simple templates:

<template>
    <div $data-x="varX"></div>
</template>

Compiler output:

getVarX() {
   return this.getElem(<prism generated id>).getAttribute("data-x")
}

The problem arises around more complex templates:

<template>
    <h1>
        {varX * 2}
    </h1>
</template>

Prism recognises that rendered output may not be 1:1 with rendered markup and there is a case for doing manipulation during templating. The problem is that unlike the previous scenario returning the text node data as a value for varX would incorrectly hydrate varX as being twice the value it is. A step is required to transform rendered output to original state through a reverse of what was done during interpolation.

Currently Prism has reversers for multiplication, division and template literals interpolation: reverse.ts

varX * 2 will divide the text content (relies on string casting to number) by 2. Division does the opposite. For simple template literals the following `Hello ${name}` will convert it into a slice expression.

This is an issue bigger than Prism:

For example attempting to produce a reverse expression for a + b (where a & b are numbers) is impossible. The rendered output may be 15 which could come from a = 3, b = 12 or a = 5, b = 10 etc.

There are other issues around text concatenation with multiple variables (Only for attributes, text node concatenation is fine as it is split up with comments). For example: `${x}a${y}`, if its rendered value is aaa then it is ambiguous whether the cases are x = "aa", y = "", x = "", y = "aa", or x = "a", y = "a".

There are some other very technical scenarios here around what if the x value is only hydrated to test if it begins with "b" or something where the ambiguity is okay. And there is a possibility that if you are ok with ambiguity there could be some regex here.

This is a common case around number and date formatting where the literal value is not there and it requires parsing to get the rendered version back to something looking like it was on the server.

Where this issue becomes very difficult is around non 1:1 mappings and expressions that involve more than one variable.

For now this can be solved by server rendering the data to $data-* attributes so the client has easy access to values. This will make payloads bigger unfortunately. (There is another issue here around not client side rendering these attributes or doing updates to them).

Types:

The other issue is that server nodes and attributes always return strings. This is a problem for properties than are numbers or Dates and at client runtime need to be of that type. This is one of the reasons for the generic data argument on Component. The type information parsed at compile time and will inject parseFloat or new Date() to properties that are of that type.