Skip to content

Commit 4c26e1e

Browse files
committed
refactor(core): schedule tasks via core qrl
1 parent bb89ed5 commit 4c26e1e

File tree

11 files changed

+97
-26
lines changed

11 files changed

+97
-26
lines changed

Diff for: .changeset/olive-cameras-collect.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@qwik.dev/core': patch
3+
---
4+
5+
CHORE: replace the `_hW` export in segments with a shared export `_task` in core. This opens up using QRLs from core.

Diff for: packages/qwik/handlers.mjs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* This re-exports the QRL handlers so that they can be used as QRLs.
3+
*
4+
* In vite dev mode, this file is referenced directly. This ensures that the correct path to core is
5+
* used so that there's only one instance of Qwik.
6+
*
7+
* Make sure that these handlers are listed in manifest.ts
8+
*/
9+
export { _task } from '@qwik.dev/core';

Diff for: packages/qwik/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"./cli": {
4242
"require": "./dist/cli.cjs"
4343
},
44+
"./handlers.mjs": "./handlers.mjs",
4445
"./internal": {
4546
"types": "./core-internal.d.ts",
4647
"import": {
@@ -144,6 +145,7 @@
144145
"bindings",
145146
"build.d.ts",
146147
"cli.d.ts",
148+
"handlers.mjs",
147149
"jsx-dev-runtime.d.ts",
148150
"jsx-runtime.d.ts",
149151
"loader.d.ts",

Diff for: packages/qwik/src/core/api.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -316,9 +316,6 @@ function h<TYPE extends string | FunctionComponent<PROPS>, PROPS extends {} = {}
316316
export { h as createElement }
317317
export { h }
318318

319-
// @internal
320-
export const _hW: () => void;
321-
322319
// @internal @deprecated (undocumented)
323320
export const _IMMUTABLE: unique symbol;
324321

@@ -997,6 +994,9 @@ export interface SyncQRL<TYPE extends Function = any> extends QRL<TYPE> {
997994
resolved: TYPE;
998995
}
999996

997+
// @internal
998+
export const _task: () => void;
999+
10001000
// @public (undocumented)
10011001
export interface TaskCtx {
10021002
// (undocumented)

Diff for: packages/qwik/src/core/internal.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export { _noopQrl, _noopQrlDEV, _regSymbol } from './shared/qrl/qrl';
22
export { _walkJSX } from './ssr/ssr-render-jsx';
33
export { _SharedContainer } from './shared/shared-container';
4-
export { _hW } from './use/use-task';
4+
export { scheduleTask as _task } from './use/use-task';
55
export { _wrapSignal, _wrapProp } from './signal/signal-utils';
66
export { _restProps } from './shared/utils/prop';
77
export { _IMMUTABLE } from './shared/utils/constants';

Diff for: packages/qwik/src/core/shared/qrl/qrl-class.ts

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export type QRLInternalMethods<TYPE> = {
6868

6969
export type QRLInternal<TYPE = unknown> = QRL<TYPE> & QRLInternalMethods<TYPE>;
7070

71+
// TODO remove refSymbol, it's not used
7172
export const createQRL = <TYPE>(
7273
chunk: string | null,
7374
symbol: string,

Diff for: packages/qwik/src/core/use/use-task.ts

+5-10
Original file line numberDiff line numberDiff line change
@@ -275,20 +275,15 @@ export const useRunTask = (
275275
};
276276

277277
const getTaskHandlerQrl = (task: Task): QRL<(ev: Event) => void> => {
278-
const taskQrl = task.$qrl$;
279278
const taskHandler = createQRL<(ev: Event) => void>(
280-
taskQrl.$chunk$,
281-
'_hW',
282-
_hW,
279+
null,
280+
'_task',
281+
scheduleTask,
283282
null,
284283
null,
285284
[task],
286-
taskQrl.$symbol$
285+
null
287286
);
288-
// Needed for chunk lookup in dev mode
289-
if (taskQrl.dev) {
290-
taskHandler.dev = taskQrl.dev;
291-
}
292287
return taskHandler;
293288
};
294289

@@ -318,7 +313,7 @@ export const isTask = (value: any): value is Task => {
318313
*
319314
* @internal
320315
*/
321-
export const _hW = () => {
316+
export const scheduleTask = () => {
322317
const [task] = useLexicalScope<[Task]>();
323318
const container = getDomContainer(task.$el$ as VNode);
324319
const type = task.$flags$ & TaskFlags.VISIBLE_TASK ? ChoreType.VISIBLE : ChoreType.TASK;

Diff for: packages/qwik/src/optimizer/src/manifest.ts

+27-11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import type { OutputBundle } from 'rollup';
22
import { type NormalizedQwikPluginOptions } from './plugins/plugin';
33
import type { GlobalInjections, SegmentAnalysis, Path, QwikBundle, QwikManifest } from './types';
44

5+
// The handlers that are exported by the core package
6+
// See handlers.mjs
7+
const extraSymbols = new Set(['_task']);
8+
59
// This is just the initial prioritization of the symbols and entries
610
// at build time so there's less work during each SSR. However, SSR should
711
// still further optimize the priorities depending on the user/document.
@@ -270,7 +274,13 @@ export function generateManifestFromBundles(
270274
return canonPath(bundle.fileName);
271275
};
272276
// We need to find our QRL exports
273-
const qrlNames = new Set([...segments.map((h) => h.name)]);
277+
const qrlNames = new Set(segments.map((h) => h.name));
278+
for (const symbol of extraSymbols) {
279+
qrlNames.add(symbol);
280+
}
281+
const taskNames = new Set(
282+
segments.filter((h) => /use[a-zA-Z]*Task(_\d+)?$/.test(h.displayName)).map((h) => h.name)
283+
);
274284
for (const outputBundle of Object.values(outputBundles)) {
275285
if (outputBundle.type !== 'chunk') {
276286
continue;
@@ -281,25 +291,18 @@ export function generateManifestFromBundles(
281291
size: outputBundle.code.length,
282292
};
283293

284-
let hasSymbols = false;
285-
let hasHW = false;
286294
for (const symbol of outputBundle.exports) {
287295
if (qrlNames.has(symbol)) {
288296
// When not minifying we see both the entry and the segment file
289297
// The segment file will only have 1 export, we want the entry
290298
if (!manifest.mapping[symbol] || outputBundle.exports.length !== 1) {
291-
hasSymbols = true;
299+
if (taskNames.has(symbol)) {
300+
bundle.isTask = true;
301+
}
292302
manifest.mapping[symbol] = bundleFileName;
293303
}
294304
}
295-
if (symbol === '_hW') {
296-
hasHW = true;
297-
}
298-
}
299-
if (hasSymbols && hasHW) {
300-
bundle.isTask = true;
301305
}
302-
303306
const bundleImports = outputBundle.imports
304307
// Tree shaking might remove imports
305308
.filter((i) => outputBundle.code.includes(path.basename(i)))
@@ -349,6 +352,19 @@ export function generateManifestFromBundles(
349352
loc: segment.loc,
350353
};
351354
}
355+
for (const symbol of extraSymbols) {
356+
manifest.symbols[symbol] = {
357+
origin: 'Qwik core',
358+
displayName: symbol,
359+
canonicalFilename: '',
360+
hash: symbol,
361+
ctxKind: 'function',
362+
ctxName: symbol,
363+
captures: false,
364+
parent: null,
365+
loc: [0, 0],
366+
};
367+
}
352368
// To inspect the bundles, uncomment the following lines
353369
// and temporarily add the writeFileSync import from fs
354370
// writeFileSync(

Diff for: packages/qwik/src/optimizer/src/plugins/plugin.ts

+38
Original file line numberDiff line numberDiff line change
@@ -373,9 +373,11 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) {
373373
};
374374

375375
let optimizer: Optimizer;
376+
let shouldAddHandlers = false;
376377
const buildStart = async (_ctx: Rollup.PluginContext) => {
377378
debug(`buildStart()`, opts.buildMode, opts.scope, opts.target, opts.rootDir, opts.srcDir);
378379
optimizer = getOptimizer();
380+
shouldAddHandlers = !devServer;
379381
if (optimizer.sys.env === 'node' && opts.target === 'ssr' && opts.lint) {
380382
try {
381383
linter = await createLinter(optimizer.sys, opts.rootDir, opts.tsconfigFileNames);
@@ -484,7 +486,29 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) {
484486
id: QWIK_CLIENT_MANIFEST_ID,
485487
moduleSideEffects: false,
486488
};
489+
} else if (pathId.endsWith(QWIK_HANDLERS_ID)) {
490+
debug(`resolveId(${count})`, 'Resolved', QWIK_HANDLERS_ID);
491+
result = {
492+
id: QWIK_HANDLERS_ID,
493+
moduleSideEffects: false,
494+
};
487495
} else {
496+
// If qwik core is loaded, also add the handlers
497+
if (!isServer && shouldAddHandlers && id.endsWith('@qwik.dev/core')) {
498+
shouldAddHandlers = false;
499+
const key = await ctx.resolve('@qwik.dev/core/handlers.mjs', importerId, {
500+
skipSelf: true,
501+
});
502+
if (!key) {
503+
throw new Error('Failed to resolve @qwik.dev/core/handlers.mjs');
504+
}
505+
ctx.emitFile({
506+
id: key.id,
507+
type: 'chunk',
508+
preserveSignature: 'allow-extension',
509+
});
510+
}
511+
488512
const qrlMatch = /^(?<parent>.*\.[mc]?[jt]sx?)_(?<name>[^/]+)\.js(?<query>$|\?.*$)/.exec(id)
489513
?.groups as { parent: string; name: string; query: string } | undefined;
490514

@@ -566,6 +590,18 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) {
566590
code: await getQwikServerManifestModule(isServer),
567591
};
568592
}
593+
/**
594+
* In dev mode, we need a path to core for qrls. However, we don't know what that is. By
595+
* re-exporting the core symbols, we let Vite provide the correct path to core and we prevent
596+
* duplicate Qwik instances.
597+
*/
598+
if (id === QWIK_HANDLERS_ID) {
599+
debug(`load(${count})`, QWIK_HANDLERS_ID, opts.buildMode);
600+
return {
601+
moduleSideEffects: false,
602+
code: `export * from '@qwik.dev/core';`,
603+
};
604+
}
569605

570606
// QRL segments
571607
const parsedId = parseId(id);
@@ -978,6 +1014,8 @@ export const QWIK_CORE_SERVER = '@qwik.dev/core/server';
9781014

9791015
export const QWIK_CLIENT_MANIFEST_ID = '@qwik-client-manifest';
9801016

1017+
export const QWIK_HANDLERS_ID = '@qwik-handlers';
1018+
9811019
export const SRC_DIR_DEFAULT = 'src';
9821020

9831021
export const CLIENT_OUT_DIR = 'dist';

Diff for: packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import clickToComponent from './click-to-component.html?raw';
1010
import errorHost from './error-host.html?raw';
1111
import imageDevTools from './image-size-runtime.html?raw';
1212
import perfWarning from './perf-warning.html?raw';
13-
import { type NormalizedQwikPluginOptions, parseId } from './plugin';
13+
import { type NormalizedQwikPluginOptions, parseId, QWIK_HANDLERS_ID } from './plugin';
1414
import type { QwikViteDevResponse } from './vite';
1515
import { VITE_ERROR_OVERLAY_STYLES } from './vite-error';
1616
import { formatError } from './vite-utils';
@@ -37,6 +37,10 @@ function createSymbolMapper(base: string): SymbolMapperFn {
3737
return [symbolName, ''];
3838
}
3939
if (!parent) {
40+
// Core symbols
41+
if (symbolName.startsWith('_')) {
42+
return [symbolName, `${base}${QWIK_HANDLERS_ID}`];
43+
}
4044
console.error(
4145
'qwik vite-dev-server symbolMapper: unknown qrl requested without parent:',
4246
symbolName

Diff for: packages/qwik/src/qwikloader.ts

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export const qwikLoader = (
2525
const roots = new Set<EventTarget & ParentNode>([doc]);
2626

2727
// Some shortenings for minification
28+
// TODO use more, like hasAttribute
2829
const replace = 'replace';
2930
const forEach = 'forEach';
3031
const target = 'target';

0 commit comments

Comments
 (0)