Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(qrl): add resolved #5118

Merged
merged 1 commit into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading