# QRL brainstorm ## File extensions - `.event.js` - `qwikloader.js` always loads this. `@builder.io/core` will never runtime import `.event.js` (there are `import` statements which the tooling rewrites to QRLs.) - `.method.js` ???? - These are needed by the Entity. more brainstorming needed here. - `.template.js` - Needed by `decl:template="..."` attribute. These will be used by the renderer. - `.css.js` - These are derived by ther rendere from the `decl:template` to load CSS on as needed basis. ```htmlembedded= <style decl:style="QXK"> import(foo:MyItem.css) </style> <div decl:template="foo:MyItem" class="QXK-HOST"> <span class="QXK">...</span> </div> ``` - `MyItem.template.js` - `MyItem.css` - `MyItem.css` - `MyTime.css.sub-chunkA` - `MyTime.css.sub-chunkB` --- ```typescript= function MyComponent() { return ( <div class="MyClass"> <span></span> </div> ); } ``` ```htmlembedded= <div class="MyComponent-h MyClass"> <span class="MyComponent"></span> <span class="MyComponent"></span> </div> ``` --- ## Style ```typescript= import {PRIMARY, SECONDARY} from './material'; import PRIMARY from './material.css.primary'; // <style decl:style="XYZ"> class-name {...}</styl> // PRIMARY === class-name import from './a.css'; // PRIMARY => scoped-CSS-class. function AComponent() { return <md-button style={style(PRIMARY)}></md-button> } function BComponent() { return <md-button class={SECONDARY}></md-button> } ``` - `FooComponent` => `foo.component.ts` - `foo.template.ts` - `foo.css` - `foo.css.primary` / `foo.primary.css` ```typescript= (await someFunction(QRL<()=>string>`./adsf`))() => string someFunction(`./adsf`) ``` ```typescript= import { applyStyles } from '@builder.io/qwik/internal' ``` --- ```typescript= import {onButtonClick} from 'Foo_onClick'; <button on:click={onButtonClick}> <button on:click="????"> // Optimizer Converts above to bellow. <button on:click={QRL`./Foo_onClick#onButtonClick`}> ``` ---- ```typescript= // MyComponent.component.tsx export function MyComponent() {} ``` ```typescript= // index.ts export {MyComponent} from './MyComponent.component'; ``` ```typescript= // MyApp.tsx import {MyComponent} from './index'; function MyApp() { return <MyComponent/> } ``` ```typescript= function EvilComponent() { return exp ? <bar/> : <foo/> } ``` ```typescript= function CmpA() { return ( <CmpB style="..."/> ) } interface SomeProps extends HTMLAttributes {} function CmpB<SomeProps>(props: {}) { return ( <div style="..."></div> ) } ``` ```typescript= // MyComponent.component.tsx export function MyComponent(props: {name: string}) { return <div style="color: red; width: 20px">...</div> } ``` ```typescript= // MyApp.tsx import {MyComponent} from './MyComponent.component'; const MyComponent2 = defineComponent<{name: string}>(QRL`...`, div') function MyApp() { return <> <MyComponent style="width: 40px" name="bla"/> // <-- TYPE ERROR <MyComponent2 style="width: 40px" name="BLA"/> // WORKS </> } ``` [HostElements](https://www.typescriptlang.org/play?jsx=1&ssl=31&ssc=24&pln=31&pc=34#code/KYDwDg9gTgLgBAE2AYwDYEMrDgO3QW2AGcx1lsApAZQA04BvAKDjgEscZgoAzM7ASQ5R2RVsgCiqYIQ5EGzFolYA3AFzzFmuGnREiAfnVEYwnAHMANAq1xjATymHbJ9petaA2gHIIOVWjEAay8AXSdjUzdNAF8AbncSdD8NG20MPXCXcytU5wdgTMic1O9ff1Qg0MLXYpY4hWjGRsZuAFccZBhWXzgACQhjAAowKAgwInUkuwBKeSbGFvbO7pw4AEcAYQh8SBxgDgAeABUAPkGACwGYADkCYCMsyzhuZOHR8fUj2YBeE7gp6bqfrGSTSfYwY5-JgsLAwVpQVY4VqoVAAQnizUYMDsYGwwJgoJkENOcG+cEhcHcbzGEwYOgyDyKeUcjNc0TgADI4F9SX8pvEFowAPRCuAAcSwwE4UAAdDAiCBGKBILBtL5jOLJdLSestjtfODBl4ECovBZnksuj0JcApVwAEzUj4MPCEVnmaKA-44OwpWHw1YHRKrelEb4AIgAYhAIOHmcAI8gIKhoOosAhwydesAURAXXdogchcGTk1pgLldB4EmcBqbXaoPadZttrtDcbTea2h0rat69LHSMaep6K77s5Ip7Jj6-VKA+T8WldGGozG4-YpInk6m4OnM9nc-nCIWhfjS56BcLRQBBMBgOUKq9sfWq+j92D29ncUb4OBeGVCv2XBeBW4BVhaPYrHAACydi3mAjqzNCu5zgi5KljYBxAVAuB3BGADq0CoBmS56BGABCmDrtim7hkmKZQOoABGqCtMAcZlAEyCBBGMp8eGQoYVoWFag6uGEARREkaGFFUfGW4McxrHsXAnEVNxvH8YJChFueCxAA) --- ``` // Item - Item.component.tsx // template - Item.ts - Item_remove.handler.ts - Item_add.handler.ts - User.entity.ts - User_login.method.ts ``` ```typescript= import {Item} from './Item.component'; export function MyApp() { return <Item/> } ``` ```typescript= // Item_click.state.ts export default function () {} ``` ```typescript= import * as Item_click from './Item_click.handler'; export function Item() { return <button on:click={Item_click}>add</button> // <button on:click="./Item_click">add</button> // QRL 'Item_click' => 'Item_click.handler.js' } ``` ## No bundling magic breaking apart the imports: ```typescript= function qComponent(tag:string, fn: () => any); function qComponent(tag:string, props: any, fn: () => any); function qComponent(fn: () => any); function qComponent(...args[]: any[]) { return fn; } // Greeter.component.tsx const Greeter = qComponent(() => {...}); const Greeter = qComponent('div', () => {...}); export Greeter; // Main.component.tsx import Greeter from './Greeter.component'; function Main() { return ( <div> <Greeter/> </div> ); } ``` ## Bundling magic by breaking apart the imports: ```typescript= // Greeter.component.tsx const Greeter = qComponent(..); export Greeter; // Main.component.tsx import { jsxDeclareTemplate } from '@builder.io/qwik/internal'; // const Greeter = import('./Greeter.component'); const Greeter = jsxDeclareComponent('./some-path-123.js'); function Main() { return ( <div> <Greeter/> </div> ); } ``` ```typescript= const GreeterB = qComponent(async (props: GreeterProps, ref: any) => { const state = await getState<GreeterComponentState>(ref); const { name } = state; return ( <div> <div> Your name: <input value={name} on:keyup={QRL`./Greeter_input_onKeyup#?name=.target.value`} /> </div> <span>Hello {name}!</span> </div> ); }); ``` ```typescript= async function Greet(this: Ref, props: {name: string}) { const salutation = await inject(this, Salutation); const salutation = await useState(this, Salutation); return <span>{salutation} {name}!</span> } inject( Salutation, function Greet(salutation: any, props: {name: string}) { return <span>{salutation} {name}!</span> } ) async function Greet(this: Ref, props: {name: string}) { const salutationPromise = useState(this, Salutation); if (Build.isClient && isLoading) { return <span>loading...</span> } else { await salutationPromise; return <span>{salutation} {name}!</span> } } ``` ## Component import transformations Developer Code: ```typescript= import SomeEsmImport from './some.component'; ``` Internally, while bundling the module, its source is rewritten to: ```typescript= import {jsxDeclareComponent} from '@builder.io/qwik'; const SomeEsmImport = jsxDeclareComponent(import('./some.component')); ``` While generating the JavaScript output, it’s written as: ```typescript= import {jsxDeclareComponent} from '@builder.io/qwik'; const SomeEsmImport = jsxDeclareComponent('./abc123-hash.js', 'tag-name'); ``` ## Synthetic Entry Points Developer Code: ```typescript= // cmp.a.tsx export default function(){...} // cmp.b.tsx import CmpA from './cmp.a' export default function(){ return ( <div> <CmpA/> </div> ) } ``` Output Code: ```typescript= // @entry1.tsx (generated file) import { jsxDeclareComponent } from '@builder.io/qwik'; export { CmpA } = jsxDeclareComponent('./cmp.a', 'a-name'); export { CmpC } = jsxDeclareComponent('./cmp.c', 'c-name'); // cmp.a.tsx export default function(){...} // cmp.b.tsx import { CmpA } from '@entry1' export default function(){ return ( <div> <CmpA/> </div> ) } ``` ---- `my-counter.tsx` ```typescript= // First declare the component interface types. export interface MyCounterProps { step?: number; } export interface MyCounterState { count: number; } // Next declare the component's state factory. This will be used when new component is // being created to initialize the state. (It will not be used on rehydration.) export const MyCounterStateFactory = qStateFactory<MyCounterProps, MyCounterState>(() => ({ count: 0, })); export const MyCounterView = qView<typeof MyCounter>((props) => { const state = getState(props); return ( <div> <button on:click={MyCounterDecrement}>-</button> <span>{state.count}</span> <button on:click={MyCounterDecrement}>+</button> </div> ); }); // Next declare the component export const MyCounter = qComponent<MyCounterProps, MyCounterState>({ stateFactory: MyCounterStateFactory, template: MyCounterView, }); // Finally declare the component's behavior (event handlers) export const MyCounterIncrement = qHandler<typeof MyCounter>((props) => { const state = getState(props); state.count += props.step || 1; markDirty(props); }); export const MyCounterDecrement = qHandler<typeof MyCounter>((props) => { const state = getState(props); state.count -= props.step || 1; markDirty(props); }); ``` ```typescript= import {MyCounter} from './my-counter'; function MyApp() { return <MyCounter/> } ``` REWRITE ```typescript= import {MyCounter} from './@entry-group-123'; function MyApp() { return <MyCounter/> } ``` `@@entry-group-123` ```typescript= export MyCounter from './my-counter'; ``` `my-counter.js` ```typescript= // qComponent internally calls jsxDeclareComponent export const MyCounter = qComponent<MyCounterProps, MyCounterState>({ stateFactory: './@entry-group-123', template: './@entry-group-123', }); export const MyComponentView = (props) => { const state = getState(props); return ( <div> <button on:click="./@my-counter-123">-</button> <span>{state.count}</span> <button on:click="./@my-counter-123#MyCounterDecrement">+</button> </div> ); } ``` ------ ## 1) Developer's Source Code **my-counter.tsx** ```typescript= export const MyCounterView = qView<MyCounter>((props) => { const state = getState(props); return ( <div> <button on:click={MyCounterIncrement}>+</button> <span>{state.count}</span> </div> ); }); export const MyCounterIncrement = qHandler<MyCounter>((props) => { const state = getState(props); state.count += props.step || 1; markDirty(props); }); export const MyCounter = qComponent<MyCounter>({ view: MyCounterView, }); ``` **my-app.tsx** ```typescript= import { MyCounter } from './my-counter'; const MyAppView = qView<MyApp>(() => { return ( <section> <MyCounter/> </section> ); }); export const MyApp = qComponent<MyApp>({ view: MyAppView, }); ``` ## 2) Generate Entry Files rollup will use as "input" option to start with **entry-0** ```typescript= export { MyCounterIncrement } from './my-counter'; ``` **entry-1** ```typescript= export { MyCounterView } from './my-counter'; ``` **entry-2** ```typescript= export { MyCounter } from './my-counter'; ``` **entry-3** ```typescript= export { MyAppView } from './my-app'; ``` **entry-4** ```typescript= export { MyApp } from './my-app'; ``` ## 3) Rewrite imports, after typescript to js transpiling, but before final JavaScript output **my-counter.js** - Rewrite local vars to dynamic imports of the generated entry paths ```typescript= export const MyCounterView = qView((props) => { const state = getState(props); return ( h("div", null, h("button", { onClick: import("entry-0#MyCounterIncrement")}, "+"), h("span", null, state.count) ); ); }); export const MyCounterIncrement = qHandler((props) => { const state = getState(props); state.count += props.step || 1; markDirty(props); }); export const MyCounter = qComponent({ view: import('entry-1#MyCounterView'), }); ``` **my-app.js** - Change ESM import url to generated entry paths ```typescript= import { MyCounter } from 'entry-2'; const MyAppView = qView(() => { return h("section", null, h(MyCounter, null)); }); export const MyApp = qComponent({ view: import('entry-3#MyAppView'), }); ``` ## 4) Output JavaScript Files, rendering changes dynamic import()s to strings **chunk-abc.js** ```typescript= export const MyCounterIncrement = qHandler((props) => { const state = getState(props); state.count += props.step || 1; markDirty(props); }); ``` **chunk-cde.js** ```typescript= export const MyCounterView = qView((props) => { const state = getState(props); return ( <div> <button on:click="./chunk-abc#MyCounterIncrement">+</button> <span>{state.count}</span> </div> ); return ( h("div", null, h("button", { onClick: './chunk-abc#MyCounterIncrement'}, "+"), h("span", null, state.count) ); ); }); ``` **chunk-qrs.js** ```typescript= export const MyCounter = qComponent({ view: './chunk-cde.js#MyCounterView', }); ``` **chunk-xyz.js** ```typescript= export const MyApp = qComponent({ view: './chunk-qrs.js#MyCounterView' }); ``` --- ```typescript= export MyTestView = qView(() => (<button on:click={MyHandle}>...</button>)); ``` ```typescript= export MyTestView = qView(() => (<button on:click={QRLSSR`my-handle.js#MyHandle`}>...</button>)); ``` chunkMap = { 'my-handle.js#MyHandle': './chunk-abc#click' } ```htmlembedded= <button on:click="./chunk-abc#click">...</button> ``` ```typescript= const qrlMap = /* @PURE */ new Map(); function h() { const symbol = prop['on:click']; if (DEV_MODE && isSymbol(symbol)) { const uuid = generateUUID(); qrlMap.set(uuid, symbol); prop['on:click'] = uuid; } } export MyTestView = qView(() => (<button on:click={"UUID"}>...</button>)); ```