Skip to content

Instantly share code, notes, and snippets.

@WebReflection
Last active October 6, 2024 12:35
Show Gist options
  • Save WebReflection/2d64f34cf58daa812ec876242c91a97c to your computer and use it in GitHub Desktop.
Save WebReflection/2d64f34cf58daa812ec876242c91a97c to your computer and use it in GitHub Desktop.

Revisions

  1. WebReflection revised this gist Dec 12, 2022. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions esx.md
    Original file line number Diff line number Diff line change
    @@ -65,6 +65,7 @@ interface ESXStatic {

    // always the same reference when this is used as outer template
    interface ESXNode extends ESXToken {
    id: object?
    children: (ESXStatic | ESXInterpolation | ESXNode)[];
    }

    @@ -76,13 +77,15 @@ class ESXFragment extends ESXNode {
    // <any-element />
    class ESXElement extends ESXNode {
    type = ESXToken.ELEMENT;
    name: string;
    value: string;
    attributes: (ESXAttribute | ESXInterpolation)[];
    }

    // <AnyComponent />
    class ESXComponent extends ESXNode {
    type = ESXToken.COMPONENT;
    name: string;
    value: function;
    attributes: (ESXAttribute | ESXInterpolation)[];
    get properties(): object?;
  2. WebReflection revised this gist Dec 5, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion esx.md
    Original file line number Diff line number Diff line change
    @@ -63,8 +63,8 @@ interface ESXStatic {
    value: string;
    }

    // always the same reference when this is used as outer template
    interface ESXNode extends ESXToken {
    id: object?;
    children: (ESXStatic | ESXInterpolation | ESXNode)[];
    }

  3. WebReflection revised this gist Dec 2, 2022. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions esx.md
    Original file line number Diff line number Diff line change
    @@ -10,6 +10,8 @@ Feel free to keep an eye on [udomsay](https://github.com/WebReflection/udomsay)

    This is a [TC39 proposal](https://es.discourse.group/t/proposal-esx-as-core-js-feature/1511) and [there is a Babel transformer](https://github.com/ungap/babel-plugin-transform-esx) for it too.

    There is also a *zero-tooling* solution based on *template litearl tags* [in this repository](https://github.com/ungap/esx#readme), which solves almost all pain-points around having *ESX* as syntax.

    - - -

    In a quest to explore improvements over common JSX transformers I have managed to [find great performance](https://twitter.com/WebReflection/status/1589741329378324480?s=20&t=DnTQ1z7G-77ufAMAIeXjrQ) able to compete with template literal based libraries.
  4. WebReflection revised this gist Nov 29, 2022. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions esx.md
    Original file line number Diff line number Diff line change
    @@ -86,3 +86,7 @@ class ESXComponent extends ESXNode {
    get properties(): object?;
    }
    ```

    ## Code samples

    To better understand what the transformer currently produce, please see [this innput](https://github.com/ungap/babel-plugin-transform-esx/blob/main/test/transform/input.js) and compare it with [this output](https://github.com/ungap/babel-plugin-transform-esx/blob/main/test/transform/output.js).
  5. WebReflection revised this gist Nov 29, 2022. 1 changed file with 39 additions and 295 deletions.
    334 changes: 39 additions & 295 deletions esx.md
    Original file line number Diff line number Diff line change
    @@ -30,315 +30,59 @@ This document describes all the moving parts of ESX in a way that:

    ## ESX

    ```js
    // base class: the only class needed in userland to:
    // * check if a child value is an instanceof ESXToken
    // * retrieve all XXX_TYPE values to properly parse
    // represented as {type: number, value}
    ```ts
    // base class for instanceof operations
    class ESXToken {
    // property/ies + interpolations + templates
    static STATIC_TYPE = 1 << 0; // 1
    static MIXED_TYPE = 1 << 1; // 2
    static RUNTIME_TYPE = 1 << 2; // 4
    static TEMPLATE_TYPE = 1 << 3; // 8

    // angle-brackets kind
    static ELEMENT_TYPE = 1 << 6; // 64
    static FRAGMENT_TYPE = 1 << 7; // 128
    static COMPONENT_TYPE = 1 << 8; // 256

    /**
    * @param {number} type any ESXToken.XXX_TYPE
    * @param {any} value the value carried by this token
    */
    constructor(type, value) {
    this.type = type;
    this.value = value;
    }
    }

    // any property
    // represented as {type: 1 | 2 | 4, name: string, value}
    class ESXProperty extends ESXToken {
    /**
    * @param {number} type ESXToken.STATIC_TYPE | ESXToken.MIXED_TYPE | ESXToken.RUNTIME_TYPE
    * @param {string} name the property name
    * @param {any} value the property value
    */
    constructor(type, name, value) {
    super(type, value);
    this.name = name;
    }
    static ATTRIBUTE: number;
    static INTERPOLATION: number;
    static STATIC: number;
    static FRAGMENT: number;
    static ELEMENT: number;
    static COMPONENT: number;
    }

    // <any /> <>angle</> <Brackets />
    class ESXChevrons extends ESXToken {
    /**
    *
    * @param {number} type ESXToken.ELEMENT_TYPE | ESXToken.FRAGMENT_TYPE | ESXToken.COMPONENT_TYPE
    * @param {string | null | Function} value
    * @param {ESXToken | null} properties
    * @param {ESXToken[]} children
    */
    constructor(type, value, properties, children) {
    super(type, value);
    this.properties = properties;
    this.children = children;
    }
    // possible `attributes` entry
    interface ESXAttribute {
    type = ESXToken.ATTRIBUTE;
    dynamic: boolean;
    name: string;
    value: unknown;
    }

    // any <tag-name />
    // represented as {type: 64, value: string, properties, children: []}
    class ESXElement extends ESXChevrons {
    /**
    *
    * @param {string} value the tag name
    * @param {ESXToken | null} properties the properties, if any
    * @param {ESXToken[]} children zero, one, or more element child
    */
    constructor(value, properties, ...children) {
    super(ESXToken.ELEMENT_TYPE, value, properties, children);
    }
    // possible `attributes` or `children` entry
    interface ESXInterpolation {
    type = ESXToken.INTERPOLATION;
    value: unknown;
    }

    // any <>fragment</>
    // represented as {type: 128, value: null, properties: null, children: []}
    class ESXFragment extends ESXChevrons {
    /**
    *
    * @param {null} value fragments have no representation
    * @param {ESXToken | null} properties the properties, if any
    * @param {ESXToken[]} children zero, one, or more element child
    */
    constructor(...children) {
    super(ESXToken.FRAGMENT_TYPE, null, null, children);
    }
    // possible `children` entry
    interface ESXStatic {
    type = ESXToken.STATIC;
    value: string;
    }

    // any <Component />
    // represented as {type: 256, value: Function, properties, children: []}
    class ESXComponent extends ESXChevrons {
    /**
    * @param {Function} value the component callback
    * @param {ESXToken | null} properties the properties, if any
    * @param {ESXToken[]} children zero, one, or more element child
    */
    constructor(value, properties, ...children) {
    super(ESXToken.COMPONENT_TYPE, value, properties, children);
    }
    interface ESXNode extends ESXToken {
    id: object?;
    children: (ESXStatic | ESXInterpolation | ESXNode)[];
    }

    // Outer JSX template elements
    // represented as {type: 8, id, value}
    class ESXTemplate extends ESXToken {
    /**
    * @param {object} id unique template reference
    * @param {ESXChevrons} value the entry value this template carries
    */
    constructor(id, value) {
    super(ESXToken.TEMPLATE_TYPE, value);
    this.id = id;
    }
    }
    ```

    #### Basic expectations
    ```jsx
    // ESX
    const div = <div />;
    ```
    ```js
    // internal representation
    const templateReference1 = {};
    const div = new ESXTemplate(
    templateReference1,
    new ESXElement('div', null)
    );
    ```
    ```js
    // concrete JS representation
    const templateReference1 = {};
    const div = {
    id: templateReference1,
    type: 8,
    value: {
    type: 64,
    value: 'div',
    properties: null,
    children: []
    }
    };
    ```

    #### Nested elements + props
    ```jsx
    // ESX
    const div = <div a="a" b={"b"}><p>c</p></div>;
    ```
    ```js
    // internal representation
    const templateReference2 = {};
    const div = new ESXTemplate(
    templateReference2,
    new ESXElement(
    'div',
    // properties token
    new ESXToken(
    // STATIC_TYPE:
    // all properties are static
    // MIXED_TYPE:
    // properties can also be static
    // RUNTIME_TYPE:
    // all properties are runtime, such as
    // <div even="though" {...spread} />
    // case that can overwrite also static props
    ESXToken.MIXED_TYPE,
    // properties value
    [
    new ESXProperty(ESXToken.STATIC_TYPE, 'a', 'a'),
    new ESXProperty(ESXToken.RUNTIME_TYPE, 'b', 'b')
    ]
    ),
    // a static child
    new ESXToken(
    ESXToken.STATIC_TYPE,
    new ESXElement(
    'p',
    null,
    new ESXToken(ESXToken.STATIC_TYPE, 'c')
    )
    )
    )
    );
    ```
    ```js
    // concrete JS representation
    const templateReference2 = {};
    const div = {
    id: templateReference2,
    type: 8,
    value: {
    type: 64,
    value: 'div',
    properties: {
    type: 2,
    value: [
    {type: 1, name: 'a', value: 'a'},
    {type: 4, name: 'b', value: 'b'}
    ]
    },
    children: [
    {
    type: 1,
    value: {
    type: 64,
    value: 'p',
    properties: null,
    children: [
    {type: 1, value: 'c'}
    ]
    }
    }
    ]
    }
    };
    ```

    #### All together
    ```jsx
    // ESX
    function MyComponent(...args) {
    return (
    <>
    {'A'},
    {'B'}
    </>
    );
    // <></>
    class ESXFragment extends ESXNode {
    type = ESXToken.FRAGMENT;
    }

    const props = {a: '', b: 'b'};
    const component = <MyComponent a="a" {...props} />;
    ```
    ```js
    // internal representation
    const templateReference3 = {};
    const templateReference4 = {};

    function MyComponent(...args) {
    return new ESXTemplate(
    templateReference3,
    new ESXFragment(
    // child as interpolation
    new ESXToken(ESXToken.RUNTIME_TYPE, 'A'),
    // the static comma interpolations separator
    new ESXToken(ESXToken.STATIC_TYPE, ', '),
    // last child as interpolation
    new ESXToken(ESXToken.RUNTIME_TYPE, 'B'),
    )
    );
    // <any-element />
    class ESXElement extends ESXNode {
    type = ESXToken.ELEMENT;
    value: string;
    attributes: (ESXAttribute | ESXInterpolation)[];
    }

    const props = {a: '', b: 'b'};
    const component = new ESXTemplate(
    templateReference4,
    new ESXComponent(
    MyComponent,
    new ESXToken(
    // there is a {...spread} involved which could
    // overwrite even known static props
    ESXToken.RUNTIME_TYPE,
    // properties value
    [
    new ESXProperty(ESXToken.STATIC_TYPE, 'a', 'a'),
    new ESXProperty(ESXToken.RUNTIME_TYPE, '', props)
    ]
    )
    )
    );
    ```
    ```js
    // concrete JS representation
    const templateReference3 = {};
    const templateReference4 = {};

    function MyComponent(...args) {
    return {
    id: templateReference3,
    type: 8,
    value: {
    type: 128,
    value: null,
    properties: null,
    children: [
    {type: 4, value: 'A'},
    {type: 1, value: ', '},
    {type: 4, value: 'B'}
    ]
    }
    };
    // <AnyComponent />
    class ESXComponent extends ESXNode {
    type = ESXToken.COMPONENT;
    value: function;
    attributes: (ESXAttribute | ESXInterpolation)[];
    get properties(): object?;
    }

    const props = {a: '', b: 'b'};
    const component = {
    id: templateReference4,
    type: 8,
    value: {
    type: 256,
    value: MyComponent,
    properties: {
    type: 4,
    value: [
    {type: 1, name: 'a', value: 'a'},
    {type: 4, name: '', value: props}
    ]
    },
    children: []
    }
    };
    ```

    ### Please help me out!

    If you have ideas around possible improvements, if you want to bring this to the TC39 attention as champion, if have any question related to this proposal, or if you'd like to know more about how *udomsay* became the fastest and smallest runtime using 90% of this proposal through a dedicated transformer, I will be more than happy if you could reach out, either here, in twitter, or in mastodon.

    Thank you very much for your patience reading through this gist 🙏
  6. WebReflection revised this gist Nov 24, 2022. 1 changed file with 8 additions and 0 deletions.
    8 changes: 8 additions & 0 deletions esx.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,13 @@
    ### About

    ## Highly Experimental

    If you are using this already, consider changes soon due [the discussion around current ESX proposal](https://es.discourse.group/t/proposal-esx-as-core-js-feature/1511/43).

    Feel free to keep an eye on [udomsay](https://github.com/WebReflection/udomsay) as that will be the implementation reference for consumers.

    - - -

    This is a [TC39 proposal](https://es.discourse.group/t/proposal-esx-as-core-js-feature/1511) and [there is a Babel transformer](https://github.com/ungap/babel-plugin-transform-esx) for it too.

    - - -
  7. WebReflection revised this gist Nov 21, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion esx.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    ### About

    This is a [TC39 proposal](https://es.discourse.group/t/proposal-esx-as-core-js-feature/1511)
    This is a [TC39 proposal](https://es.discourse.group/t/proposal-esx-as-core-js-feature/1511) and [there is a Babel transformer](https://github.com/ungap/babel-plugin-transform-esx) for it too.

    - - -

  8. WebReflection revised this gist Nov 21, 2022. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions esx.md
    Original file line number Diff line number Diff line change
    @@ -282,7 +282,7 @@ const component = new ESXTemplate(
    // properties value
    [
    new ESXProperty(ESXToken.STATIC_TYPE, 'a', 'a'),
    new ESXProperty(ESXToken.RUNTIME_TYPE, 'props', props)
    new ESXProperty(ESXToken.RUNTIME_TYPE, '', props)
    ]
    )
    )
    @@ -321,7 +321,7 @@ const component = {
    type: 4,
    value: [
    {type: 1, name: 'a', value: 'a'},
    {type: 4, name: 'props', value: props}
    {type: 4, name: '', value: props}
    ]
    },
    children: []
  9. WebReflection revised this gist Nov 16, 2022. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions esx.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,9 @@
    ### About

    This is a [TC39 proposal](https://es.discourse.group/t/proposal-esx-as-core-js-feature/1511)

    - - -

    In a quest to explore improvements over common JSX transformers I have managed to [find great performance](https://twitter.com/WebReflection/status/1589741329378324480?s=20&t=DnTQ1z7G-77ufAMAIeXjrQ) able to compete with template literal based libraries.

    In this document I would like to describe, via JS itself, and as PoC, how JSX could be both rebranded as ESX and improved.
  10. WebReflection revised this gist Nov 9, 2022. 1 changed file with 16 additions and 18 deletions.
    34 changes: 16 additions & 18 deletions esx.md
    Original file line number Diff line number Diff line change
    @@ -49,31 +49,16 @@ class ESXToken {
    // represented as {type: 1 | 2 | 4, name: string, value}
    class ESXProperty extends ESXToken {
    /**
    * @param {Object} property
    * @param {number} property.type ESXToken.STATIC_TYPE | ESXToken.MIXED_TYPE | ESXToken.RUNTIME_TYPE
    * @param {string} property.name the property name
    * @param {any} property.value the property value
    * @param {number} type ESXToken.STATIC_TYPE | ESXToken.MIXED_TYPE | ESXToken.RUNTIME_TYPE
    * @param {string} name the property name
    * @param {any} value the property value
    */
    constructor(type, name, value) {
    super(type, value);
    this.name = name;
    }
    }

    // Outer JSX template elements
    // represented as {type: 8, id, value}
    class ESXTemplate extends ESXToken {
    /**
    * @param {Object} template
    * @param {object} template.id unique template reference
    * @param {ESXEntry} template.value the entry value this template carries
    */
    constructor(id, value) {
    super(ESXToken.TEMPLATE_TYPE, value);
    this.id = id;
    }
    }

    // <any /> <>angle</> <Brackets />
    class ESXChevrons extends ESXToken {
    /**
    @@ -130,6 +115,19 @@ class ESXComponent extends ESXChevrons {
    super(ESXToken.COMPONENT_TYPE, value, properties, children);
    }
    }

    // Outer JSX template elements
    // represented as {type: 8, id, value}
    class ESXTemplate extends ESXToken {
    /**
    * @param {object} id unique template reference
    * @param {ESXChevrons} value the entry value this template carries
    */
    constructor(id, value) {
    super(ESXToken.TEMPLATE_TYPE, value);
    this.id = id;
    }
    }
    ```

    #### Basic expectations
  11. WebReflection revised this gist Nov 9, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion esx.md
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@ Differently from E4X, but also differently from JSX, this proposal leaves develo

    This document describes all the moving parts of ESX in a way that:

    * there is no DOM at all involved, only new primitives introduced by JSX
    * there is no DOM at all involved, only new primitives introduced by ESX
    * all relevant details around each part of the transformation are described through simple objects, here represented as classes instances, but these easily work just as object literals (easy polyfills via transformers)
    * no extra scope pollution is needed, hence no `jsxPragma` or `jsxFragment` around is required at all (no `React.createElement` or `React.Fragment` needed, nor `udomsay.interpolation`)
    * all classes can be used just as types to infer, as oppsite of being really classes ... no clashing in the logic can happen neither. The only global class needed out there is `ESXToken` which carries types and the prototypal inheritance for brand check.
  12. WebReflection revised this gist Nov 9, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion esx.md
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@ In this document I would like to describe, via JS itself, and as PoC, how JSX co

    ### Goal

    Differently from E4X, but also differently from JSX, this proposal leaves developers provide their own "*render*" implementation, freeing ESX usage from any previous attempt to confine JSX or E4X into the DOM world, where it's been proven, in the JSX case, it's not really where it belongs, as it can be used as neutral DSL.
    Differently from E4X, but also differently from JSX, this proposal leaves developers provide their own "*render*" implementation, freeing ESX usage from any previous attempt to confine JSX or E4X into the DOM world, where it's been proven, in the JSX case, it's not really where it belongs, as it can be used as neutral, general purpose, DSL.

    This document describes all the moving parts of ESX in a way that:

  13. WebReflection revised this gist Nov 9, 2022. 1 changed file with 6 additions and 0 deletions.
    6 changes: 6 additions & 0 deletions esx.md
    Original file line number Diff line number Diff line change
    @@ -22,6 +22,7 @@ This document describes all the moving parts of ESX in a way that:
    // base class: the only class needed in userland to:
    // * check if a child value is an instanceof ESXToken
    // * retrieve all XXX_TYPE values to properly parse
    // represented as {type: number, value}
    class ESXToken {
    // property/ies + interpolations + templates
    static STATIC_TYPE = 1 << 0; // 1
    @@ -45,6 +46,7 @@ class ESXToken {
    }

    // any property
    // represented as {type: 1 | 2 | 4, name: string, value}
    class ESXProperty extends ESXToken {
    /**
    * @param {Object} property
    @@ -59,6 +61,7 @@ class ESXProperty extends ESXToken {
    }

    // Outer JSX template elements
    // represented as {type: 8, id, value}
    class ESXTemplate extends ESXToken {
    /**
    * @param {Object} template
    @@ -88,6 +91,7 @@ class ESXChevrons extends ESXToken {
    }

    // any <tag-name />
    // represented as {type: 64, value: string, properties, children: []}
    class ESXElement extends ESXChevrons {
    /**
    *
    @@ -101,6 +105,7 @@ class ESXElement extends ESXChevrons {
    }

    // any <>fragment</>
    // represented as {type: 128, value: null, properties: null, children: []}
    class ESXFragment extends ESXChevrons {
    /**
    *
    @@ -114,6 +119,7 @@ class ESXFragment extends ESXChevrons {
    }

    // any <Component />
    // represented as {type: 256, value: Function, properties, children: []}
    class ESXComponent extends ESXChevrons {
    /**
    * @param {Function} value the component callback
  14. WebReflection revised this gist Nov 9, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion esx.md
    Original file line number Diff line number Diff line change
    @@ -13,7 +13,7 @@ This document describes all the moving parts of ESX in a way that:
    * there is no DOM at all involved, only new primitives introduced by JSX
    * all relevant details around each part of the transformation are described through simple objects, here represented as classes instances, but these easily work just as object literals (easy polyfills via transformers)
    * no extra scope pollution is needed, hence no `jsxPragma` or `jsxFragment` around is required at all (no `React.createElement` or `React.Fragment` needed, nor `udomsay.interpolation`)
    * all classes can be used just as types to infer, as oppsite of being really classes ... no clashing in the logic can happen neither
    * all classes can be used just as types to infer, as oppsite of being really classes ... no clashing in the logic can happen neither. The only global class needed out there is `ESXToken` which carries types and the prototypal inheritance for brand check.
    * hints to "*parse-once*" through templates and/or Components are all over the place, making *usignal* like alternative implementations possible, but also any *SSR* related project can benefit from these

    ## ESX
  15. WebReflection revised this gist Nov 9, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion esx.md
    Original file line number Diff line number Diff line change
    @@ -12,7 +12,7 @@ This document describes all the moving parts of ESX in a way that:

    * there is no DOM at all involved, only new primitives introduced by JSX
    * all relevant details around each part of the transformation are described through simple objects, here represented as classes instances, but these easily work just as object literals (easy polyfills via transformers)
    * no extra scope pollution is needed, hence no `jsxPragma` or `jsxFragment` around is required at all
    * no extra scope pollution is needed, hence no `jsxPragma` or `jsxFragment` around is required at all (no `React.createElement` or `React.Fragment` needed, nor `udomsay.interpolation`)
    * all classes can be used just as types to infer, as oppsite of being really classes ... no clashing in the logic can happen neither
    * hints to "*parse-once*" through templates and/or Components are all over the place, making *usignal* like alternative implementations possible, but also any *SSR* related project can benefit from these

  16. WebReflection revised this gist Nov 9, 2022. 1 changed file with 146 additions and 152 deletions.
    298 changes: 146 additions & 152 deletions esx.md
    Original file line number Diff line number Diff line change
    @@ -19,82 +19,109 @@ This document describes all the moving parts of ESX in a way that:
    ## ESX

    ```js
    // unique identifier for the fragment
    const ESXFragment = Symbol('ESX.Fragment');
    // base class: the only class needed in userland to:
    // * check if a child value is an instanceof ESXToken
    // * retrieve all XXX_TYPE values to properly parse
    class ESXToken {
    // property/ies + interpolations + templates
    static STATIC_TYPE = 1 << 0; // 1
    static MIXED_TYPE = 1 << 1; // 2
    static RUNTIME_TYPE = 1 << 2; // 4
    static TEMPLATE_TYPE = 1 << 3; // 8

    // angle-brackets kind
    static ELEMENT_TYPE = 1 << 6; // 64
    static FRAGMENT_TYPE = 1 << 7; // 128
    static COMPONENT_TYPE = 1 << 8; // 256

    // basic ESX value wrapper
    class ESXValue {
    static Static = 1 << 0;
    static Mixed = 1 << 1;
    static Runtime = 1 << 2;
    /**
    * @param {Object} details
    * @param {number} details.type ESXValue.Static | ESXValue.Mixed | ESXValue.Runtime
    * @param {any} details.value the reference carried through this instance
    * @param {number} type any ESXToken.XXX_TYPE
    * @param {any} value the value carried by this token
    */
    constructor({type, value}) {
    constructor(type, value) {
    this.type = type;
    this.value = value;
    }
    }

    // specialized ESX property value
    class ESXProperty extends ESXValue {
    // any property
    class ESXProperty extends ESXToken {
    /**
    * @param {Object} details
    * @param {number} details.type ESXValue.Static | ESXValue.Mixed | ESXValue.Runtime
    * @param {string} details.name the entry carried through this reference
    * @param {any} details.value the entry carried through this reference
    * @param {Object} property
    * @param {number} property.type ESXToken.STATIC_TYPE | ESXToken.MIXED_TYPE | ESXToken.RUNTIME_TYPE
    * @param {string} property.name the property name
    * @param {any} property.value the property value
    */
    constructor({type, name, value}) {
    super({type, value});
    constructor(type, name, value) {
    super(type, value);
    this.name = name;
    }
    }

    // Static, Mixed,or Runtime properties wrapper
    class ESXProperties {
    // Outer JSX template elements
    class ESXTemplate extends ESXToken {
    /**
    * @param {Object} details
    * @param {number} details.type ESXValue.Static | ESXValue.Mixed | ESXValue.Runtime
    * @param {ESXProperty[]} details.values
    * @param {Object} template
    * @param {object} template.id unique template reference
    * @param {ESXEntry} template.value the entry value this template carries
    */
    constructor({type, values}) {
    this.type = type;
    this.values = values;
    constructor(id, value) {
    super(ESXToken.TEMPLATE_TYPE, value);
    this.id = id;
    }
    }

    // Element, Fragment, or Component wrapper
    class ESXEntry {
    static Element = 1 << 0;
    static Fragment = 1 << 1;
    static Component = 1 << 2;
    // <any /> <>angle</> <Brackets />
    class ESXChevrons extends ESXToken {
    /**
    * @param {Object} entry
    * @param {numer} entry.type ESXEntry.Element | ESXEntry.Fragment | ESXEntry.Component
    * @param {string | symbol | Function} entry.value tag name | ESXFragment | Component
    * @param {ESXProperties | null} properties
    * @param {...ESXValue[]} children
    *
    * @param {number} type ESXToken.ELEMENT_TYPE | ESXToken.FRAGMENT_TYPE | ESXToken.COMPONENT_TYPE
    * @param {string | null | Function} value
    * @param {ESXToken | null} properties
    * @param {ESXToken[]} children
    */
    constructor({type, value}, properties, ...children) {
    this.type = type;
    this.value = value;
    constructor(type, value, properties, children) {
    super(type, value);
    this.properties = properties;
    this.children = children;
    }
    }

    // Outer JSX template elements
    class ESXTemplate {
    // any <tag-name />
    class ESXElement extends ESXChevrons {
    /**
    * @param {Object} template
    * @param {object} template.id unique template reference
    * @param {ESXEntry} template.value the entry value this template refer to
    *
    * @param {string} value the tag name
    * @param {ESXToken | null} properties the properties, if any
    * @param {ESXToken[]} children zero, one, or more element child
    */
    constructor({id, value}) {
    this.id = id;
    this.value = value;
    constructor(value, properties, ...children) {
    super(ESXToken.ELEMENT_TYPE, value, properties, children);
    }
    }

    // any <>fragment</>
    class ESXFragment extends ESXChevrons {
    /**
    *
    * @param {null} value fragments have no representation
    * @param {ESXToken | null} properties the properties, if any
    * @param {ESXToken[]} children zero, one, or more element child
    */
    constructor(...children) {
    super(ESXToken.FRAGMENT_TYPE, null, null, children);
    }
    }

    // any <Component />
    class ESXComponent extends ESXChevrons {
    /**
    * @param {Function} value the component callback
    * @param {ESXToken | null} properties the properties, if any
    * @param {ESXToken[]} children zero, one, or more element child
    */
    constructor(value, properties, ...children) {
    super(ESXToken.COMPONENT_TYPE, value, properties, children);
    }
    }
    ```
    @@ -107,26 +134,21 @@ const div = <div />;
    ```js
    // internal representation
    const templateReference1 = {};
    const div = new ESXTemplate({
    id: templateReference1,
    value: new ESXEntry(
    {
    type: ESXEntry.Element,
    value: 'div'
    },
    null
    )
    });
    const div = new ESXTemplate(
    templateReference1,
    new ESXElement('div', null)
    );
    ```
    ```js
    // concrete JS representation
    const templateReference1 = {};
    const div = {
    id: templateReference1,
    type: 8,
    value: {
    type: 1,
    type: 64,
    value: 'div',
    propertis: null,
    properties: null,
    children: []
    }
    };
    @@ -140,61 +162,51 @@ const div = <div a="a" b={"b"}><p>c</p></div>;
    ```js
    // internal representation
    const templateReference2 = {};
    const div = new ESXTemplate({
    id: templateReference2,
    value: new ESXEntry(
    {
    type: ESXEntry.Element,
    value: 'div'
    },
    new ESXProperties({
    // Static: all properties are static
    // Mixed: properties can also be Static
    // Runtime: all properties are runtime, such as
    const div = new ESXTemplate(
    templateReference2,
    new ESXElement(
    'div',
    // properties token
    new ESXToken(
    // STATIC_TYPE:
    // all properties are static
    // MIXED_TYPE:
    // properties can also be static
    // RUNTIME_TYPE:
    // all properties are runtime, such as
    // <div even="though" {...spread} />
    // case that can overwrite also static props
    type: ESXProperty.Mixed,
    values: [
    new ESXProperty({
    type: ESXProperty.Static,
    name: 'a',
    value: 'a'
    }),
    new ESXProperty({
    type: ESXProperty.Runtime,
    name: 'b',
    value: 'b'
    })
    ESXToken.MIXED_TYPE,
    // properties value
    [
    new ESXProperty(ESXToken.STATIC_TYPE, 'a', 'a'),
    new ESXProperty(ESXToken.RUNTIME_TYPE, 'b', 'b')
    ]
    }),
    new ESXValue({
    type: ESXValue.Static,
    value: new ESXEntry(
    {
    type: ESXEntry.Element,
    value: 'p'
    },
    ),
    // a static child
    new ESXToken(
    ESXToken.STATIC_TYPE,
    new ESXElement(
    'p',
    null,
    new ESXValue({
    type: ESXValue.Static,
    value: 'c'
    })
    new ESXToken(ESXToken.STATIC_TYPE, 'c')
    )
    })
    )
    )
    });
    );
    ```
    ```js
    // concrete JS representation
    const templateReference2 = {};
    const div = {
    id: templateReference2,
    type: 8,
    value: {
    type: 1,
    type: 64,
    value: 'div',
    properties: {
    type: 2,
    values: [
    value: [
    {type: 1, name: 'a', value: 'a'},
    {type: 4, name: 'b', value: 'b'}
    ]
    @@ -203,7 +215,7 @@ const div = {
    {
    type: 1,
    value: {
    type: 1,
    type: 64,
    value: 'p',
    properties: null,
    children: [
    @@ -236,70 +248,50 @@ const component = <MyComponent a="a" {...props} />;
    const templateReference3 = {};
    const templateReference4 = {};

    function MyComponent() {
    return new ESXTemplate({
    id: templateReference3,
    value: new ESXEntry(
    {
    type: ESXEntry.Fragment,
    value: ESXFragment
    },
    null,
    // children as interpolations
    new ESXValue({
    type: ESXValue.Runtime,
    value: 'A'
    }),
    //the static comma interpolations separator
    new ESXValue({
    type: ESXValue.Static,
    value: ', '
    }),
    new ESXValue({
    type: ESXValue.Runtime,
    value: 'B'
    })
    function MyComponent(...args) {
    return new ESXTemplate(
    templateReference3,
    new ESXFragment(
    // child as interpolation
    new ESXToken(ESXToken.RUNTIME_TYPE, 'A'),
    // the static comma interpolations separator
    new ESXToken(ESXToken.STATIC_TYPE, ', '),
    // last child as interpolation
    new ESXToken(ESXToken.RUNTIME_TYPE, 'B'),
    )
    });
    );
    }

    const props = {a: '', b: 'b'};
    const component = new ESXTemplate({
    id: templateReference4,
    value: new ESXEntry(
    {
    type: ESXEntry.Component,
    value: MyComponent
    },
    new ESXProperties({
    type: ESXProperty.Runtime,
    values: [
    new ESXProperty({
    type: ESXProperty.Static,
    name: 'a',
    value: 'a'
    }),
    new ESXProperty({
    type: ESXProperty.Runtime,
    name: 'props',
    value: props
    })
    const component = new ESXTemplate(
    templateReference4,
    new ESXComponent(
    MyComponent,
    new ESXToken(
    // there is a {...spread} involved which could
    // overwrite even known static props
    ESXToken.RUNTIME_TYPE,
    // properties value
    [
    new ESXProperty(ESXToken.STATIC_TYPE, 'a', 'a'),
    new ESXProperty(ESXToken.RUNTIME_TYPE, 'props', props)
    ]
    })
    )
    )
    });
    );
    ```
    ```js
    // concrete JS representation
    const templateReference3 = {};
    const templateReference4 = {};

    function MyComponent() {
    function MyComponent(...args) {
    return {
    id: templateReference3,
    type: 8,
    value: {
    type: 2,
    value: ESXFragment,
    type: 128,
    value: null,
    properties: null,
    children: [
    {type: 4, value: 'A'},
    @@ -313,16 +305,18 @@ function MyComponent() {
    const props = {a: '', b: 'b'};
    const component = {
    id: templateReference4,
    type: 8,
    value: {
    type: 4,
    type: 256,
    value: MyComponent,
    properties: {
    type: 4,
    values: [
    value: [
    {type: 1, name: 'a', value: 'a'},
    {type: 4, name: 'props', value: props}
    ]
    }
    },
    children: []
    }
    };
    ```
  17. WebReflection revised this gist Nov 8, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion esx.md
    Original file line number Diff line number Diff line change
    @@ -90,7 +90,7 @@ class ESXTemplate {
    /**
    * @param {Object} template
    * @param {object} template.id unique template reference
    * @param {ESXEntry} template.value unique template reference
    * @param {ESXEntry} template.value the entry value this template refer to
    */
    constructor({id, value}) {
    this.id = id;
  18. WebReflection revised this gist Nov 8, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion esx.md
    Original file line number Diff line number Diff line change
    @@ -8,7 +8,7 @@ In this document I would like to describe, via JS itself, and as PoC, how JSX co

    Differently from E4X, but also differently from JSX, this proposal leaves developers provide their own "*render*" implementation, freeing ESX usage from any previous attempt to confine JSX or E4X into the DOM world, where it's been proven, in the JSX case, it's not really where it belongs, as it can be used as neutral DSL.

    This document describes all the moving part of ESX in a way that:
    This document describes all the moving parts of ESX in a way that:

    * there is no DOM at all involved, only new primitives introduced by JSX
    * all relevant details around each part of the transformation are described through simple objects, here represented as classes instances, but these easily work just as object literals (easy polyfills via transformers)
  19. WebReflection revised this gist Nov 8, 2022. 1 changed file with 8 additions and 36 deletions.
    44 changes: 8 additions & 36 deletions esx.md
    Original file line number Diff line number Diff line change
    @@ -195,16 +195,8 @@ const div = {
    properties: {
    type: 2,
    values: [
    {
    type: 1,
    name: 'a',
    value: 'a'
    },
    {
    type: 4,
    name: 'b',
    value: 'b'
    }
    {type: 1, name: 'a', value: 'a'},
    {type: 4, name: 'b', value: 'b'}
    ]
    },
    children: [
    @@ -215,10 +207,7 @@ const div = {
    value: 'p',
    properties: null,
    children: [
    {
    type: 1,
    value: 'c'
    }
    {type: 1, value: 'c'}
    ]
    }
    }
    @@ -313,18 +302,9 @@ function MyComponent() {
    value: ESXFragment,
    properties: null,
    children: [
    {
    type: 4,
    value: 'A'
    },
    {
    type: 1,
    value: ', '
    },
    {
    type: 4,
    value: 'B'
    }
    {type: 4, value: 'A'},
    {type: 1, value: ', '},
    {type: 4, value: 'B'}
    ]
    }
    };
    @@ -339,16 +319,8 @@ const component = {
    properties: {
    type: 4,
    values: [
    {
    type: 1,
    name: 'a',
    value: 'a'
    },
    {
    type: 4,
    name: 'props',
    value: props
    }
    {type: 1, name: 'a', value: 'a'},
    {type: 4, name: 'props', value: props}
    ]
    }
    }
  20. WebReflection revised this gist Nov 8, 2022. 1 changed file with 109 additions and 0 deletions.
    109 changes: 109 additions & 0 deletions esx.md
    Original file line number Diff line number Diff line change
    @@ -118,6 +118,19 @@ const div = new ESXTemplate({
    )
    });
    ```
    ```js
    // concrete JS representation
    const templateReference1 = {};
    const div = {
    id: templateReference1,
    value: {
    type: 1,
    value: 'div',
    propertis: null,
    children: []
    }
    };
    ```

    #### Nested elements + props
    ```jsx
    @@ -171,6 +184,48 @@ const div = new ESXTemplate({
    )
    });
    ```
    ```js
    // concrete JS representation
    const templateReference2 = {};
    const div = {
    id: templateReference2,
    value: {
    type: 1,
    value: 'div',
    properties: {
    type: 2,
    values: [
    {
    type: 1,
    name: 'a',
    value: 'a'
    },
    {
    type: 4,
    name: 'b',
    value: 'b'
    }
    ]
    },
    children: [
    {
    type: 1,
    value: {
    type: 1,
    value: 'p',
    properties: null,
    children: [
    {
    type: 1,
    value: 'c'
    }
    ]
    }
    }
    ]
    }
    };
    ```

    #### All together
    ```jsx
    @@ -245,6 +300,60 @@ const component = new ESXTemplate({
    )
    });
    ```
    ```js
    // concrete JS representation
    const templateReference3 = {};
    const templateReference4 = {};

    function MyComponent() {
    return {
    id: templateReference3,
    value: {
    type: 2,
    value: ESXFragment,
    properties: null,
    children: [
    {
    type: 4,
    value: 'A'
    },
    {
    type: 1,
    value: ', '
    },
    {
    type: 4,
    value: 'B'
    }
    ]
    }
    };
    }

    const props = {a: '', b: 'b'};
    const component = {
    id: templateReference4,
    value: {
    type: 4,
    value: MyComponent,
    properties: {
    type: 4,
    values: [
    {
    type: 1,
    name: 'a',
    value: 'a'
    },
    {
    type: 4,
    name: 'props',
    value: props
    }
    ]
    }
    }
    };
    ```

    ### Please help me out!

  21. WebReflection created this gist Nov 8, 2022.
    253 changes: 253 additions & 0 deletions esx.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,253 @@
    ### About

    In a quest to explore improvements over common JSX transformers I have managed to [find great performance](https://twitter.com/WebReflection/status/1589741329378324480?s=20&t=DnTQ1z7G-77ufAMAIeXjrQ) able to compete with template literal based libraries.

    In this document I would like to describe, via JS itself, and as PoC, how JSX could be both rebranded as ESX and improved.

    ### Goal

    Differently from E4X, but also differently from JSX, this proposal leaves developers provide their own "*render*" implementation, freeing ESX usage from any previous attempt to confine JSX or E4X into the DOM world, where it's been proven, in the JSX case, it's not really where it belongs, as it can be used as neutral DSL.

    This document describes all the moving part of ESX in a way that:

    * there is no DOM at all involved, only new primitives introduced by JSX
    * all relevant details around each part of the transformation are described through simple objects, here represented as classes instances, but these easily work just as object literals (easy polyfills via transformers)
    * no extra scope pollution is needed, hence no `jsxPragma` or `jsxFragment` around is required at all
    * all classes can be used just as types to infer, as oppsite of being really classes ... no clashing in the logic can happen neither
    * hints to "*parse-once*" through templates and/or Components are all over the place, making *usignal* like alternative implementations possible, but also any *SSR* related project can benefit from these

    ## ESX

    ```js
    // unique identifier for the fragment
    const ESXFragment = Symbol('ESX.Fragment');

    // basic ESX value wrapper
    class ESXValue {
    static Static = 1 << 0;
    static Mixed = 1 << 1;
    static Runtime = 1 << 2;
    /**
    * @param {Object} details
    * @param {number} details.type ESXValue.Static | ESXValue.Mixed | ESXValue.Runtime
    * @param {any} details.value the reference carried through this instance
    */
    constructor({type, value}) {
    this.type = type;
    this.value = value;
    }
    }

    // specialized ESX property value
    class ESXProperty extends ESXValue {
    /**
    * @param {Object} details
    * @param {number} details.type ESXValue.Static | ESXValue.Mixed | ESXValue.Runtime
    * @param {string} details.name the entry carried through this reference
    * @param {any} details.value the entry carried through this reference
    */
    constructor({type, name, value}) {
    super({type, value});
    this.name = name;
    }
    }

    // Static, Mixed,or Runtime properties wrapper
    class ESXProperties {
    /**
    * @param {Object} details
    * @param {number} details.type ESXValue.Static | ESXValue.Mixed | ESXValue.Runtime
    * @param {ESXProperty[]} details.values
    */
    constructor({type, values}) {
    this.type = type;
    this.values = values;
    }
    }

    // Element, Fragment, or Component wrapper
    class ESXEntry {
    static Element = 1 << 0;
    static Fragment = 1 << 1;
    static Component = 1 << 2;
    /**
    * @param {Object} entry
    * @param {numer} entry.type ESXEntry.Element | ESXEntry.Fragment | ESXEntry.Component
    * @param {string | symbol | Function} entry.value tag name | ESXFragment | Component
    * @param {ESXProperties | null} properties
    * @param {...ESXValue[]} children
    */
    constructor({type, value}, properties, ...children) {
    this.type = type;
    this.value = value;
    this.properties = properties;
    this.children = children;
    }
    }

    // Outer JSX template elements
    class ESXTemplate {
    /**
    * @param {Object} template
    * @param {object} template.id unique template reference
    * @param {ESXEntry} template.value unique template reference
    */
    constructor({id, value}) {
    this.id = id;
    this.value = value;
    }
    }
    ```

    #### Basic expectations
    ```jsx
    // ESX
    const div = <div />;
    ```
    ```js
    // internal representation
    const templateReference1 = {};
    const div = new ESXTemplate({
    id: templateReference1,
    value: new ESXEntry(
    {
    type: ESXEntry.Element,
    value: 'div'
    },
    null
    )
    });
    ```

    #### Nested elements + props
    ```jsx
    // ESX
    const div = <div a="a" b={"b"}><p>c</p></div>;
    ```
    ```js
    // internal representation
    const templateReference2 = {};
    const div = new ESXTemplate({
    id: templateReference2,
    value: new ESXEntry(
    {
    type: ESXEntry.Element,
    value: 'div'
    },
    new ESXProperties({
    // Static: all properties are static
    // Mixed: properties can also be Static
    // Runtime: all properties are runtime, such as
    // <div even="though" {...spread} />
    // case that can overwrite also static props
    type: ESXProperty.Mixed,
    values: [
    new ESXProperty({
    type: ESXProperty.Static,
    name: 'a',
    value: 'a'
    }),
    new ESXProperty({
    type: ESXProperty.Runtime,
    name: 'b',
    value: 'b'
    })
    ]
    }),
    new ESXValue({
    type: ESXValue.Static,
    value: new ESXEntry(
    {
    type: ESXEntry.Element,
    value: 'p'
    },
    null,
    new ESXValue({
    type: ESXValue.Static,
    value: 'c'
    })
    )
    })
    )
    });
    ```

    #### All together
    ```jsx
    // ESX
    function MyComponent(...args) {
    return (
    <>
    {'A'},
    {'B'}
    </>
    );
    }

    const props = {a: '', b: 'b'};
    const component = <MyComponent a="a" {...props} />;
    ```
    ```js
    // internal representation
    const templateReference3 = {};
    const templateReference4 = {};

    function MyComponent() {
    return new ESXTemplate({
    id: templateReference3,
    value: new ESXEntry(
    {
    type: ESXEntry.Fragment,
    value: ESXFragment
    },
    null,
    // children as interpolations
    new ESXValue({
    type: ESXValue.Runtime,
    value: 'A'
    }),
    //the static comma interpolations separator
    new ESXValue({
    type: ESXValue.Static,
    value: ', '
    }),
    new ESXValue({
    type: ESXValue.Runtime,
    value: 'B'
    })
    )
    });
    }

    const props = {a: '', b: 'b'};
    const component = new ESXTemplate({
    id: templateReference4,
    value: new ESXEntry(
    {
    type: ESXEntry.Component,
    value: MyComponent
    },
    new ESXProperties({
    type: ESXProperty.Runtime,
    values: [
    new ESXProperty({
    type: ESXProperty.Static,
    name: 'a',
    value: 'a'
    }),
    new ESXProperty({
    type: ESXProperty.Runtime,
    name: 'props',
    value: props
    })
    ]
    })
    )
    });
    ```

    ### Please help me out!

    If you have ideas around possible improvements, if you want to bring this to the TC39 attention as champion, if have any question related to this proposal, or if you'd like to know more about how *udomsay* became the fastest and smallest runtime using 90% of this proposal through a dedicated transformer, I will be more than happy if you could reach out, either here, in twitter, or in mastodon.

    Thank you very much for your patience reading through this gist 🙏