Skip to content

Commit b0b61a7

Browse files
authored
Merge pull request #7610 from QwikDev/tweak-preloader
2 parents adf20ca + 0dae1e2 commit b0b61a7

File tree

20 files changed

+240
-122
lines changed

20 files changed

+240
-122
lines changed

packages/docs/src/repl/worker/repl-request-handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ const injectDevHtml = (clientId: string, html?: string) => {
133133
});
134134
135135
document.addEventListener('qsymbol', (ev) => {
136-
const symbolName = ev.detail;
136+
const symbolName = ev.detail?.symbol;
137137
sendToServerWindow({
138138
kind: 'symbol',
139139
scope: 'client',

packages/docs/src/root.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,7 @@ export default component$(() => {
7676

7777
export function collectSymbols() {
7878
(window as any).symbols = [];
79-
document.addEventListener('qsymbol', (e) => (window as any).symbols.push((e as any).detail));
79+
document.addEventListener('qsymbol', (e) =>
80+
(window as any).symbols.push((e as any).detail.symbol)
81+
);
8082
}

packages/docs/src/routes/api/qwik-server/api.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@
166166
}
167167
],
168168
"kind": "Interface",
169-
"content": "```typescript\nexport interface RenderOptions extends SerializeDocumentOptions \n```\n**Extends:** [SerializeDocumentOptions](#serializedocumentoptions)\n\n\n<table><thead><tr><th>\n\nProperty\n\n\n</th><th>\n\nModifiers\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\n[base?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nstring \\| ((options: [RenderOptions](#renderoptions)<!-- -->) =&gt; string)\n\n\n</td><td>\n\n_(Optional)_ Specifies the root of the JS files of the client build. Setting a base, will cause the render of the `q:base` attribute in the `q:container` element.\n\n\n</td></tr>\n<tr><td>\n\n[containerAttributes?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nRecord&lt;string, string&gt;\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[containerTagName?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nstring\n\n\n</td><td>\n\n_(Optional)_ When set, the app is serialized into a fragment. And the returned html is not a complete document. Defaults to `html`\n\n\n</td></tr>\n<tr><td>\n\n[locale?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nstring \\| ((options: [RenderOptions](#renderoptions)<!-- -->) =&gt; string)\n\n\n</td><td>\n\n_(Optional)_ Language to use when rendering the document.\n\n\n</td></tr>\n<tr><td>\n\n[prefetchStrategy?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n[PrefetchStrategy](#prefetchstrategy) \\| null\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[preloader?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n[PreloaderOptions](#preloaderoptions) \\| boolean\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[qwikLoader?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n[QwikLoaderOptions](#qwikloaderoptions)\n\n\n</td><td>\n\n_(Optional)_ Specifies if the Qwik Loader script is added to the document or not.\n\nDefaults to `{ include: true }`<!-- -->.\n\n\n</td></tr>\n<tr><td>\n\n[qwikPrefetchServiceWorker?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nQwikPrefetchServiceWorkerOptions\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[serverData?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nRecord&lt;string, any&gt;\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[snapshot?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nboolean\n\n\n</td><td>\n\n_(Optional)_ Defaults to `true`\n\n\n</td></tr>\n</tbody></table>",
169+
"content": "```typescript\nexport interface RenderOptions extends SerializeDocumentOptions \n```\n**Extends:** [SerializeDocumentOptions](#serializedocumentoptions)\n\n\n<table><thead><tr><th>\n\nProperty\n\n\n</th><th>\n\nModifiers\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\n[base?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nstring \\| ((options: [RenderOptions](#renderoptions)<!-- -->) =&gt; string)\n\n\n</td><td>\n\n_(Optional)_ Specifies the root of the JS files of the client build. Setting a base, will cause the render of the `q:base` attribute in the `q:container` element.\n\n\n</td></tr>\n<tr><td>\n\n[containerAttributes?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nRecord&lt;string, string&gt;\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[containerTagName?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nstring\n\n\n</td><td>\n\n_(Optional)_ When set, the app is serialized into a fragment. And the returned html is not a complete document. Defaults to `html`\n\n\n</td></tr>\n<tr><td>\n\n[locale?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nstring \\| ((options: [RenderOptions](#renderoptions)<!-- -->) =&gt; string)\n\n\n</td><td>\n\n_(Optional)_ Language to use when rendering the document.\n\n\n</td></tr>\n<tr><td>\n\n[prefetchStrategy?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n[PrefetchStrategy](#prefetchstrategy) \\| null\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[preloader?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n[PreloaderOptions](#preloaderoptions) \\| false\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[qwikLoader?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n[QwikLoaderOptions](#qwikloaderoptions)\n\n\n</td><td>\n\n_(Optional)_ Specifies if the Qwik Loader script is added to the document or not.\n\nDefaults to `{ include: true }`<!-- -->.\n\n\n</td></tr>\n<tr><td>\n\n[qwikPrefetchServiceWorker?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nQwikPrefetchServiceWorkerOptions\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[serverData?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nRecord&lt;string, any&gt;\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[snapshot?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nboolean\n\n\n</td><td>\n\n_(Optional)_ Defaults to `true`\n\n\n</td></tr>\n</tbody></table>",
170170
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/server/types.ts",
171171
"mdFile": "qwik.renderoptions.md"
172172
},

packages/docs/src/routes/api/qwik-server/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,7 @@ _(Optional)_
715715
716716
</td><td>
717717
718-
[PreloaderOptions](#preloaderoptions) \| boolean
718+
[PreloaderOptions](#preloaderoptions) \| false
719719
720720
</td><td>
721721

packages/docs/src/routes/api/qwik/api.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2194,7 +2194,7 @@
21942194
}
21952195
],
21962196
"kind": "TypeAlias",
2197-
"content": "Emitted by qwik-loader when a module was lazily loaded\n\n\n```typescript\nexport type QwikSymbolEvent = CustomEvent<{\n symbol: string;\n element: Element;\n reqTime: number;\n}>;\n```",
2197+
"content": "Emitted by qwik-loader when a module was lazily loaded\n\n\n```typescript\nexport type QwikSymbolEvent = CustomEvent<{\n symbol: string;\n element: Element;\n reqTime: number;\n qBase?: string;\n qManifest?: string;\n qVersion?: string;\n href?: string;\n}>;\n```",
21982198
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/render/jsx/types/jsx-qwik-events.ts",
21992199
"mdFile": "qwik.qwiksymbolevent.md"
22002200
},

packages/docs/src/routes/api/qwik/index.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4383,6 +4383,10 @@ export type QwikSymbolEvent = CustomEvent<{
43834383
symbol: string;
43844384
element: Element;
43854385
reqTime: number;
4386+
qBase?: string;
4387+
qManifest?: string;
4388+
qVersion?: string;
4389+
href?: string;
43864390
}>;
43874391
```
43884392

packages/docs/src/routes/docs/(qwikcity)/guides/bundle/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ To collect symbol usage from a running application:
139139
```html
140140
<script>
141141
window.symbols = [];
142-
document.addEventListener('qsymbol', (e) => window.symbols.push(e.detail));
142+
document.addEventListener('qsymbol', (e) => window.symbols.push(e.detail.symbol));
143143
</script>
144144
```
145145
2. Perform some set of operations mimicking user behavior.

packages/qwik/src/core/preloader/bundle-graph.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isBrowser } from '@builder.io/qwik/build';
2-
import { config, doc } from './constants';
2+
import { config, isJSRegex } from './constants';
33
import { adjustProbabilities, bundles, log, shouldResetFactor, trigger } from './queue';
44
import type { BundleGraph, BundleImport, ImportProbability } from './types';
55
import { BundleImportState_None, BundleImportState_Alias } from './types';
@@ -8,15 +8,9 @@ export let base: string | undefined;
88
export let graph: BundleGraph;
99

1010
const makeBundle = (name: string, deps?: ImportProbability[]) => {
11-
const url = name.endsWith('.js')
12-
? doc
13-
? new URL(`${base}${name}`, doc.baseURI).toString()
14-
: name
15-
: null;
1611
return {
1712
$name$: name,
18-
$url$: url,
19-
$state$: url ? BundleImportState_None : BundleImportState_Alias,
13+
$state$: isJSRegex.test(name) ? BundleImportState_None : BundleImportState_Alias,
2014
$deps$: shouldResetFactor ? deps?.map((d) => ({ ...d, $factor$: 1 })) : deps,
2115
$inverseProbability$: 1,
2216
$createdTs$: Date.now(),
@@ -37,7 +31,11 @@ export const parseBundleGraph = (serialized: (string | number)[]) => {
3731
if (idx < 0) {
3832
probability = -idx / 10;
3933
} else {
40-
deps.push({ $name$: serialized[idx] as string, $probability$: probability, $factor$: 1 });
34+
deps.push({
35+
$name$: serialized[idx] as string,
36+
$importProbability$: probability,
37+
$factor$: 1,
38+
});
4139
}
4240
i++;
4341
}

packages/qwik/src/core/preloader/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ export const rel =
1919

2020
// Global state
2121
export const loadStart = Date.now();
22+
23+
export const isJSRegex = /\.[mc]?js$/;

packages/qwik/src/core/preloader/preloader.unit.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,5 @@ test('preloader script', () => {
2121
* dereference objects etc, but that actually results in worse compression
2222
*/
2323
const compressed = compress(Buffer.from(preLoader), { mode: 1, quality: 11 });
24-
expect(compressed.length).toBe(1722);
25-
expect(preLoader.length).toBe(5107);
24+
expect([compressed.length, preLoader.length]).toEqual([1878, 5614]);
2625
});

packages/qwik/src/core/preloader/queue.ts

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
BundleImportState_Preload,
99
BundleImportState_Queued,
1010
} from './types';
11+
import type { QwikSymbolEvent } from '../render/jsx/types/jsx-qwik-events';
1112

1213
export const bundles: BundleImports = new Map();
1314
export let shouldResetFactor: boolean;
@@ -81,8 +82,8 @@ export const trigger = () => {
8182
Math.max(1, config.$maxIdlePreloads$ * probability)
8283
: // While the graph is not available, we limit to 2 preloads
8384
2;
84-
// When we're 100% sure, everything needs to be queued
85-
if (probability === 1 || preloadCount < allowedPreloads) {
85+
// When we're 99% sure, everything needs to be queued
86+
if (probability >= 0.99 || preloadCount < allowedPreloads) {
8687
queue.shift();
8788
preloadOne(bundle);
8889
} else {
@@ -120,7 +121,8 @@ const preloadOne = (bundle: BundleImport) => {
120121
);
121122

122123
const link = doc.createElement('link');
123-
link.href = bundle.$url$!;
124+
// Only bundles with state none are js bundles
125+
link.href = new URL(`${base}${bundle.$name$}`, doc.baseURI).toString();
124126
link.rel = rel;
125127
// Needed when rel is 'preload'
126128
link.as = 'script';
@@ -140,22 +142,34 @@ const preloadOne = (bundle: BundleImport) => {
140142
doc.head.appendChild(link);
141143
};
142144

145+
/**
146+
* Adjust the probability of a bundle based on the probability of its dependent bundles, and queue
147+
* it if it's likely enough to be preloaded.
148+
*
149+
* Note that if the probability is 100%, we treat the dynamic imports as 99% sure, and both will be
150+
* preloaded without limit.
151+
*
152+
* We also limit "organic" probability to 98% so they don't get unlimited preloads.
153+
*/
143154
export const adjustProbabilities = (
144155
bundle: BundleImport,
145-
adjustFactor: number,
156+
newInverseProbability: number,
146157
seen?: Set<BundleImport>
147158
) => {
148159
if (seen?.has(bundle)) {
149160
return;
150161
}
151162

152163
const previousInverseProbability = bundle.$inverseProbability$;
153-
bundle.$inverseProbability$ *= adjustFactor;
164+
bundle.$inverseProbability$ = newInverseProbability;
165+
// Don't propagate tiny changes
154166
if (previousInverseProbability - bundle.$inverseProbability$ < 0.01) {
155167
return;
156168
}
157169

158170
if (
171+
// don't queue until we have initialized the preloader
172+
base != null &&
159173
bundle.$state$ < BundleImportState_Preload &&
160174
bundle.$inverseProbability$ < config.$invPreloadProbability$
161175
) {
@@ -176,40 +190,57 @@ export const adjustProbabilities = (
176190
const probability = 1 - bundle.$inverseProbability$;
177191
for (const dep of bundle.$deps$) {
178192
const depBundle = getBundle(dep.$name$)!;
179-
const prevAdjust = dep.$factor$;
193+
if (depBundle.$inverseProbability$ === 0) {
194+
// it's already at max probability
195+
continue;
196+
}
180197
/**
181198
* The chance that a dep won't be loaded is 1-(the chance that the dep will be loaded)*(the
182199
* chance that the current bundle will be loaded).
183200
*
184201
* We can multiply this chance together with all other bundle adjustments to get the chance
185202
* that a dep will be loaded given all the chances of the other bundles.
186203
*
187-
* But when we're very likely to load the current bundle, make the dynamic imports more likely
204+
* But when we're very likely to load the current bundle, make the dynamic imports very likely
188205
* too.
189206
*/
190-
const newInverseProbability =
191-
dep.$probability$ !== 1 && adjustFactor < 0.1 ? 0.05 : 1 - dep.$probability$ * probability;
192-
193-
/** We need to undo the previous adjustment */
194-
const factor = newInverseProbability / prevAdjust;
195-
dep.$factor$ = factor;
207+
let newInverseProbability: number;
208+
if (
209+
dep.$importProbability$ > 0.5 &&
210+
(probability === 1 || (probability >= 0.99 && depsCount < 100))
211+
) {
212+
depsCount++;
213+
// we're loaded at max probability, so elevate dynamic imports to 99% sure
214+
newInverseProbability = Math.min(0.01, 1 - dep.$importProbability$);
215+
} else {
216+
const newInverseImportProbability = 1 - dep.$importProbability$ * probability;
217+
/** We need to undo the previous adjustment */
218+
const prevAdjust = dep.$factor$;
219+
const factor = newInverseImportProbability / prevAdjust;
220+
// limit organic probability to 98%
221+
newInverseProbability = Math.max(0.02, depBundle.$inverseProbability$ * factor);
222+
dep.$factor$ = factor;
223+
}
196224

197-
adjustProbabilities(depBundle, factor, seen);
225+
adjustProbabilities(depBundle, newInverseProbability, seen);
198226
}
199227
}
200228
};
201229

202230
export const handleBundle = (name: string, inverseProbability: number) => {
203231
const bundle = getBundle(name);
204232
if (bundle && bundle.$inverseProbability$ > inverseProbability) {
205-
adjustProbabilities(bundle, inverseProbability / bundle.$inverseProbability$);
233+
adjustProbabilities(bundle, inverseProbability);
206234
}
207235
};
208236

237+
let depsCount: number;
238+
209239
export const preload = (name: string | (number | string)[], probability?: number) => {
210-
if (base == null || !name.length) {
240+
if (!name?.length) {
211241
return;
212242
}
243+
depsCount = 0;
213244

214245
let inverseProbability = probability ? 1 - probability : 0.4;
215246
if (Array.isArray(name)) {
@@ -229,3 +260,15 @@ export const preload = (name: string | (number | string)[], probability?: number
229260
trigger();
230261
}
231262
};
263+
264+
if (isBrowser) {
265+
// Get early hints from qwikloader
266+
document.addEventListener('qsymbol', (ev) => {
267+
const { symbol, href } = (ev as QwikSymbolEvent).detail;
268+
// the qrl class doesn't emit href, we don't need to preload
269+
if (href) {
270+
const hash = symbol.slice(symbol.lastIndexOf('_') + 1);
271+
preload(hash, 1);
272+
}
273+
});
274+
}

packages/qwik/src/core/preloader/types.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export type BundleInfo = {
1212

1313
export type BundleImport = BundleInfo & {
1414
$name$: string;
15-
$url$: string | null;
1615
$state$: number;
1716
$createdTs$: number;
1817
$waitedMs$: number;
@@ -25,7 +24,7 @@ export type ImportProbability = {
2524
/** Bundle name */
2625
$name$: string;
2726
/** Probability */
28-
$probability$: number;
27+
$importProbability$: number;
2928
/** Probability adjust factor */
3029
$factor$: number;
3130
};

packages/qwik/src/core/qwik.core.api.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,10 @@ export type QwikSymbolEvent = CustomEvent<{
781781
symbol: string;
782782
element: Element;
783783
reqTime: number;
784+
qBase?: string;
785+
qManifest?: string;
786+
qVersion?: string;
787+
href?: string;
784788
}>;
785789

786790
// @public @deprecated (undocumented)

packages/qwik/src/core/render/jsx/types/jsx-qwik-events.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,28 @@ import type { AllEventKeys } from './jsx-qwik-attributes';
33
/** Emitted by qwik-loader when an element becomes visible. Used by `useVisibleTask$` @public */
44
export type QwikVisibleEvent = CustomEvent<IntersectionObserverEntry>;
55
/** Emitted by qwik-loader when a module was lazily loaded @public */
6-
export type QwikSymbolEvent = CustomEvent<{ symbol: string; element: Element; reqTime: number }>;
6+
export type QwikSymbolEvent = CustomEvent<{
7+
symbol: string;
8+
element: Element;
9+
reqTime: number;
10+
qBase?: string;
11+
qManifest?: string;
12+
qVersion?: string;
13+
href?: string;
14+
}>;
715
/** Emitted by qwik-loader on document when the document first becomes interactive @public */
816
export type QwikInitEvent = CustomEvent<{}>;
917
/** Emitted by qwik-loader on document when the document first becomes idle @public */
1018
export type QwikIdleEvent = CustomEvent<{}>;
1119
/** Emitted by qwik-core on document when the a view transition start @public */
1220
export type QwikViewTransitionEvent = CustomEvent<ViewTransition>;
21+
/** Emitted by qwik-loader on document when there was an error loading a module @public */
22+
export type QwikErrorEvent = CustomEvent<
23+
{
24+
importError?: 'sync' | 'async' | 'no-symbol';
25+
error: unknown;
26+
} & QwikSymbolEvent['detail']
27+
>;
1328

1429
// Utility types for supporting autocompletion in union types
1530

packages/qwik/src/qwikloader.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import type { QwikSymbolEvent, QwikVisibleEvent } from './core/render/jsx/types/jsx-qwik-events';
1+
import type {
2+
QwikErrorEvent,
3+
QwikSymbolEvent,
4+
QwikVisibleEvent,
5+
} from './core/render/jsx/types/jsx-qwik-events';
26
import type { QContainerElement } from './core/container/container';
37
import type { QContext } from './core/state/context';
48

@@ -117,7 +121,15 @@ type qWindow = Window & {
117121
let importError: undefined | 'sync' | 'async' | 'no-symbol';
118122
let error: undefined | Error;
119123
const isSync = qrl.startsWith('#');
120-
const eventData = { qBase, qManifest, qVersion, href, symbol, element, reqTime };
124+
const eventData: QwikSymbolEvent['detail'] = {
125+
qBase,
126+
qManifest,
127+
qVersion,
128+
href,
129+
symbol,
130+
element,
131+
reqTime,
132+
};
121133
if (isSync) {
122134
const hash = container.getAttribute('q:instance')!;
123135
handler = ((doc as any)['qFuncs_' + hash] || [])[Number.parseInt(symbol)];
@@ -126,6 +138,7 @@ type qWindow = Window & {
126138
error = new Error('sym:' + symbol);
127139
}
128140
} else {
141+
emitEvent<QwikSymbolEvent>('qsymbol', eventData);
129142
const uri = url.href.split('#')[0];
130143
try {
131144
const module = import(/* @vite-ignore */ uri);
@@ -141,7 +154,11 @@ type qWindow = Window & {
141154
}
142155
}
143156
if (!handler) {
144-
emitEvent('qerror', { importError, error, ...eventData });
157+
emitEvent<QwikErrorEvent>('qerror', {
158+
importError,
159+
error,
160+
...eventData,
161+
});
145162
console.error(error);
146163
// break out of the loop if handler is not found
147164
break;
@@ -150,14 +167,13 @@ type qWindow = Window & {
150167
if (element.isConnected) {
151168
try {
152169
doc.__q_context__ = [element, ev, url];
153-
isSync || emitEvent<QwikSymbolEvent>('qsymbol', { ...eventData });
154170
const results = handler(ev, element);
155171
// only await if there is a promise returned
156172
if (isPromise(results)) {
157173
await results;
158174
}
159175
} catch (error) {
160-
emitEvent('qerror', { error, ...eventData });
176+
emitEvent<QwikErrorEvent>('qerror', { error, ...eventData });
161177
} finally {
162178
doc.__q_context__ = previousCtx;
163179
}

0 commit comments

Comments
 (0)