Skip to content

Commit

Permalink
feat(qrl): add resolved
Browse files Browse the repository at this point in the history
  • Loading branch information
wmertens committed Sep 7, 2023
1 parent 502e2ff commit f72d3e4
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 17 deletions.
1 change: 1 addition & 0 deletions packages/qwik/src/core/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ export interface QRL<TYPE = any> {
// (undocumented)
getSymbol(): string;
resolve(): Promise<TYPE>;
resolved: undefined | TYPE;
}

// @public
Expand Down
26 changes: 14 additions & 12 deletions packages/qwik/src/core/qrl/qrl-class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export interface QRLInternalMethods<TYPE> {
$captureRef$: any[] | null;
dev: QRLDev | null;

resolved: undefined | TYPE;

resolve(): Promise<TYPE>;
getSymbol(): string;
getHash(): string;
Expand Down Expand Up @@ -66,6 +68,12 @@ export const createQRL = <TYPE>(

let _containerEl: Element | undefined;

const qrl = async function (this: any, ...args: any) {
const fn = invokeFn.call(this, tryGetInvokeContext());
const result = await fn(...args);
return result;
} as unknown as QRLInternal<TYPE>;

const setContainer = (el: Element | undefined) => {
if (!_containerEl) {
_containerEl = el;
Expand All @@ -81,11 +89,11 @@ export const createQRL = <TYPE>(
return symbolRef;
}
if (symbolFn !== null) {
return (symbolRef = symbolFn().then((module) => (symbolRef = module[symbol])));
return (symbolRef = symbolFn().then((module) => (qrl.resolved = symbolRef = module[symbol])));
} else {
const symbol2 = getPlatform().importSymbol(_containerEl, chunk, symbol);
return (symbolRef = then(symbol2, (ref) => {
return (symbolRef = ref);
return (qrl.resolved = symbolRef = ref);
}));
}
};
Expand All @@ -110,7 +118,7 @@ export const createQRL = <TYPE>(
const baseContext = createOrReuseInvocationContext(currentCtx);
const context: InvokeContext = {
...baseContext,
$qrl$: QRL as QRLInternal<any>,
$qrl$: qrl as QRLInternal<any>,
};
if (context.$event$ === undefined) {
context.$event$ = this;
Expand All @@ -133,16 +141,10 @@ export const createQRL = <TYPE>(
}
};

const invokeQRL = async function (this: any, ...args: any) {
const fn = invokeFn.call(this, tryGetInvokeContext());
const result = await fn(...args);
return result;
};
const resolvedSymbol = refSymbol ?? symbol;
const hash = getSymbolHash(resolvedSymbol);

const QRL: QRLInternal<TYPE> = invokeQRL as any;
const methods: QRLInternalMethods<TYPE> = {
Object.assign(qrl, {
getSymbol: () => resolvedSymbol,
getHash: () => hash,
getCaptured: () => captureRef,
Expand All @@ -158,8 +160,8 @@ export const createQRL = <TYPE>(
$capture$: capture,
$captureRef$: captureRef,
dev: null,
};
const qrl = Object.assign(invokeQRL, methods);
resolved: undefined,
});
seal(qrl);
return qrl as any;
};
Expand Down
13 changes: 9 additions & 4 deletions packages/qwik/src/core/qrl/qrl.public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ import { createQRL } from './qrl-class';
* NOTE: `element` is needed because `QRL`s are relative and need a base location to resolve
* against. The base location is encoded in the HTML in the form of `<div q:base="/url">`.
*
* ## `QRL.resolved`
*
* Once `QRL.resolve()` returns, the value is stored under `QRL.resolved`. This allows the value
* to be used without having to await `QRL.resolve()` again.
*
* ## Question: Why not just use `import()`?
*
* At first glance, `QRL` serves the same purpose as `import()`. However, there are three subtle
Expand Down Expand Up @@ -132,7 +137,7 @@ export interface QRL<TYPE = any> {
__brand__QRL__: TYPE;

/**
* Resolve the QRL of closure and invoke it.
* Resolve the QRL of closure and invoke it. The signal is used to abort the invocation.
* @param signal - An AbortSignal object.
* @param args - Closure arguments.
* @returns A promise of the return value of the closure.
Expand All @@ -151,10 +156,10 @@ export interface QRL<TYPE = any> {
...args: TYPE extends (...args: infer ARGS) => any ? ARGS : never
): Promise<TYPE extends (...args: any[]) => infer RETURN ? Awaited<RETURN> : never>;

/**
* Resolve the QRL and return the actual value.
*/
/** Resolve the QRL and return the actual value. */
resolve(): Promise<TYPE>;
/** The resolved value, once `resolve()` returns. */
resolved: undefined | TYPE;
getCaptured(): any[] | null;
getSymbol(): string;
getHash(): string;
Expand Down
13 changes: 12 additions & 1 deletion packages/qwik/src/core/qrl/qrl.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import { equal } from 'uvu/assert';
const qrlSuite = suite('serialization');

qrlSuite('should parse', () => {
matchProps(parseQRL('./chunk#default'), { $chunk$: './chunk', $symbol$: 'default' });
matchProps(parseQRL('./chunk#default'), {
$chunk$: './chunk',
$symbol$: 'default',
resolved: undefined,
});
matchProps(parseQRL('./chunk#mySymbol'), {
$chunk$: './chunk',
$symbol$: 'mySymbol',
Expand Down Expand Up @@ -75,6 +79,13 @@ qrlSuite('should parse reference', () => {
});
qrlSuite('should parse self-reference', () => {});

qrlSuite('should store resolved value', async () => {
const q = qrl(() => Promise.resolve({ hi: 'hello' }), 'hi');
equal(q.resolved, undefined);
await q.resolve();
equal(q.resolved, 'hello');
});

function matchProps(obj: any, properties: Record<string, any>) {
for (const [key, value] of Object.entries(properties)) {
equal(obj[key], value, `${obj[key]} !== ${value}`);
Expand Down
4 changes: 4 additions & 0 deletions packages/qwik/src/core/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ At times it may be necessary to resolve a `QRL` reference to the actual value. T

NOTE: `element` is needed because `QRL`s are relative and need a base location to resolve against. The base location is encoded in the HTML in the form of `<div q:base="/url">`.

## `QRL.resolved`

Once `QRL.resolve()` returns, the value is stored under `QRL.resolved`. This allows the value to be used without having to await `QRL.resolve()` again.

## Question: Why not just use `import()`?

At first glance, `QRL` serves the same purpose as `import()`. However, there are three subtle differences that need to be taken into account.
Expand Down

0 comments on commit f72d3e4

Please sign in to comment.