From f6a850aac75ae0505221a3a678de7d81a1826421 Mon Sep 17 00:00:00 2001 From: Wout Mertens Date: Tue, 21 Jan 2025 16:16:53 +0100 Subject: [PATCH 1/8] fix(serde): catch rejections to prevent console errors --- packages/qwik/src/core/shared/shared-serialization.ts | 2 ++ .../apps/e2e/src/components/lexical-scope/lexicalScope.tsx | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/qwik/src/core/shared/shared-serialization.ts b/packages/qwik/src/core/shared/shared-serialization.ts index 3d25c5b3c47..215b5ebd15f 100644 --- a/packages/qwik/src/core/shared/shared-serialization.ts +++ b/packages/qwik/src/core/shared/shared-serialization.ts @@ -504,6 +504,8 @@ const allocate = (container: DeserializeContainer, typeId: number, value: unknow reject = rej; }); resolvers.set(promise, [resolve, reject]); + // Don't leave unhandled promise rejections + promise.catch(() => {}); return promise; case TypeIds.Uint8Array: const encodedLength = (value as string).length; diff --git a/starters/apps/e2e/src/components/lexical-scope/lexicalScope.tsx b/starters/apps/e2e/src/components/lexical-scope/lexicalScope.tsx index 1cda850fcc4..9d08800b4d6 100644 --- a/starters/apps/e2e/src/components/lexical-scope/lexicalScope.tsx +++ b/starters/apps/e2e/src/components/lexical-scope/lexicalScope.tsx @@ -122,7 +122,7 @@ export const LexicalScopeChild = component$((props: LexicalScopeProps) => { // eslint-disable-next-line console.assert(isNaN(nan)); - rejected.catch((reason) => { + rejected.catch((reason) => promise.then((promiseValue) => { state.result = JSON.stringify([ a, @@ -159,8 +159,8 @@ export const LexicalScopeChild = component$((props: LexicalScopeProps) => { JSON.stringify([...map.entries()]), ]); state.count++; - }); - }); + }), + ); }); return ( From 8367034e0daeac7a7aacf5100a4c445e00e63bde Mon Sep 17 00:00:00 2001 From: Wout Mertens Date: Mon, 20 Jan 2025 10:33:49 +0100 Subject: [PATCH 2/8] fix(testing): provide proper ctx for trigger() --- packages/qwik/src/testing/element-fixture.ts | 30 +++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/qwik/src/testing/element-fixture.ts b/packages/qwik/src/testing/element-fixture.ts index eb001433201..a59a644255c 100644 --- a/packages/qwik/src/testing/element-fixture.ts +++ b/packages/qwik/src/testing/element-fixture.ts @@ -1,14 +1,15 @@ +import { getDomContainer } from '@qwik.dev/core'; import { vi } from 'vitest'; import { assertDefined } from '../core/shared/error/assert'; import type { QRLInternal } from '../core/shared/qrl/qrl-class'; -import { _getQContainerElement, getDomContainer } from '@qwik.dev/core'; -import { createWindow } from './document'; -import { getTestPlatform } from './platform'; -import type { MockDocument, MockWindow } from './types'; -import { delay } from '../core/shared/utils/promises'; import type { QElement, QwikLoaderEventScope } from '../core/shared/types'; import { fromCamelToKebabCase } from '../core/shared/utils/event-names'; import { QFuncsPrefix, QInstanceAttr } from '../core/shared/utils/markers'; +import { delay } from '../core/shared/utils/promises'; +import { invokeApply, newInvokeContextFromTuple } from '../core/use/use-core'; +import { createWindow } from './document'; +import { getTestPlatform } from './platform'; +import type { MockDocument, MockWindow } from './types'; /** * Creates a simple DOM structure for testing components. @@ -158,11 +159,20 @@ export const dispatch = async ( } else if (element.hasAttribute(attrName)) { const container = getDomContainer(element as HTMLElement); const qrl = element.getAttribute(attrName)!; - - qrl - .split('\n') - .map((qrl) => container.parseQRL(qrl.trim())) - .map((qrl) => qrl(event, element)); + const ctx = newInvokeContextFromTuple([element!, event]); + try { + await Promise.all( + qrl + .split('\n') + .map((qrl) => container.parseQRL(qrl.trim())) + .map((qrl) => { + return invokeApply(ctx, qrl, [event, element]); + }) + ); + } catch (error) { + console.error('!!! qrl error', qrl, error); + throw error; + } return; } element = element.parentElement; From 1cedb93c62616febbee336e5037607389f2d0fdb Mon Sep 17 00:00:00 2001 From: Wout Mertens Date: Sun, 19 Jan 2025 12:31:03 +0100 Subject: [PATCH 3/8] refactor(build): separate the qwik insights module this speeds up `pnpm build --qwik --dev` by a lot --- .github/workflows/ci.yml | 2 +- package.json | 14 +++++++------- scripts/build.ts | 7 +++++-- scripts/submodule-insights.ts | 2 +- scripts/util.ts | 1 + 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d1f8e23de22..ad4bff4bdf6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -213,7 +213,7 @@ jobs: pnpm install --frozen-lockfile - name: 'build: qwik' - run: pnpm build --qwik --set-dist-tag="${{ needs.changes.outputs.disttag }}" + run: pnpm build --qwik --insights --set-dist-tag="${{ needs.changes.outputs.disttag }}" - name: Print Qwik Dist Build continue-on-error: true diff --git a/package.json b/package.json index 31f9e06496d..1be517ab8e1 100644 --- a/package.json +++ b/package.json @@ -172,20 +172,20 @@ "build.clean": "rm -rf packages/qwik/dist/ && rm -rf packages/qwik-router/lib/ && rm -rf packages/docs/dist/ && rm -rf packages/insights/dist/", "build.cli": "tsx --require ./scripts/runBefore.ts scripts/index.ts --cli --dev", "build.cli.prod": "tsx --require ./scripts/runBefore.ts scripts/index.ts --cli", - "build.core": "tsx --require ./scripts/runBefore.ts scripts/index.ts --tsc --build --qwikrouter --api --platform-binding", + "build.core": "tsx --require ./scripts/runBefore.ts scripts/index.ts --tsc --qwik --insights --qwikrouter --api --platform-binding", "build.eslint": "tsx --require ./scripts/runBefore.ts scripts/index.ts --eslint", - "build.full": "tsx --require ./scripts/runBefore.ts scripts/index.ts --tsc --tsc-docs --qwik --supabaseauthhelpers --api --eslint --qwikrouter --qwikworker --qwikreact --cli --platform-binding --wasm", - "build.local": "tsx --require ./scripts/runBefore.ts scripts/index.ts --tsc --tsc-docs --qwik --supabaseauthhelpers --api --eslint --qwikrouter --qwikworker --qwikreact --cli --platform-binding-wasm-copy", - "build.only_javascript": "tsx --require ./scripts/runBefore.ts scripts/index.ts --tsc --build --api", + "build.full": "tsx --require ./scripts/runBefore.ts scripts/index.ts --tsc --tsc-docs --qwik --insights --supabaseauthhelpers --api --eslint --qwikrouter --qwikworker --qwikreact --cli --platform-binding --wasm", + "build.local": "tsx --require ./scripts/runBefore.ts scripts/index.ts --tsc --tsc-docs --qwik --insights --supabaseauthhelpers --api --eslint --qwikrouter --qwikworker --qwikreact --cli --platform-binding-wasm-copy", + "build.only_javascript": "tsx --require ./scripts/runBefore.ts scripts/index.ts --tsc --qwik --api", "build.packages.docs": "pnpm -C ./packages/docs/ run build", "build.packages.insights": "pnpm -C ./packages/insights/ run build", "build.platform": "tsx --require ./scripts/runBefore.ts scripts/index.ts --platform-binding", "build.platform.copy": "tsx --require ./scripts/runBefore.ts scripts/index.ts --platform-binding-wasm-copy", "build.qwik-router": "tsx --require ./scripts/runBefore.ts scripts/index.ts --tsc --qwikrouter", - "build.validate": "tsx --require ./scripts/runBefore.ts scripts/index.ts --tsc --build --api --eslint --qwikrouter --platform-binding --wasm --validate", - "build.vite": "tsx --require ./scripts/runBefore.ts scripts/index.ts --tsc --build --api --qwikrouter --eslint --platform-binding-wasm-copy", + "build.validate": "tsx --require ./scripts/runBefore.ts scripts/index.ts --tsc --qwik --api --eslint --qwikrouter --platform-binding --wasm --validate", + "build.vite": "tsx --require ./scripts/runBefore.ts scripts/index.ts --tsc --qwik --insights --api --qwikrouter --eslint --platform-binding-wasm-copy", "build.wasm": "tsx --require ./scripts/runBefore.ts scripts/index.ts --wasm", - "build.watch": "tsx --require ./scripts/runBefore.ts scripts/index.ts --build --qwikrouter --watch --dev --platform-binding", + "build.watch": "tsx --require ./scripts/runBefore.ts scripts/index.ts --qwik --qwikrouter --watch --dev --platform-binding", "change": "changeset", "cli": "pnpm build.cli && node packages/create-qwik/dist/create-qwik.cjs && tsx --require ./scripts/runBefore.ts scripts/validate-cli.ts --copy-local-qwik-dist", "cli.qwik": "pnpm build.cli && node packages/qwik/dist/qwik-cli.cjs", diff --git a/scripts/build.ts b/scripts/build.ts index 604b4612695..e3574fa60c9 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -75,9 +75,12 @@ export async function build(config: BuildConfig) { submoduleBuild(config), submoduleTesting(config), submoduleCli(config), - submoduleInsights(config), ]); - + } + if (config.insights) { + await submoduleInsights(config); + } + if (config.qwik) { // server bundling must happen after the results from the others // because it inlines the qwik loader and prefetch scripts await Promise.all([submoduleServer(config), submoduleOptimizer(config)]); diff --git a/scripts/submodule-insights.ts b/scripts/submodule-insights.ts index 292cd54c670..5ffce5b6fab 100644 --- a/scripts/submodule-insights.ts +++ b/scripts/submodule-insights.ts @@ -30,7 +30,7 @@ async function buildComponents(config: BuildConfig) { cwd: config.srcQwikDir, }); if (result.failed) { - panic(`tsc failed`); + panic(`insights build failed`); } } diff --git a/scripts/util.ts b/scripts/util.ts index 176e137c1c7..04a25dba772 100644 --- a/scripts/util.ts +++ b/scripts/util.ts @@ -47,6 +47,7 @@ const booleanOptions = [ 'dryRun', 'eslint', 'esmNode', + 'insights', 'platformBinding', 'platformBindingWasmCopy', 'prepareRelease', From 3c0f6c18a27bec14293052d3c80f34e28a021518 Mon Sep 17 00:00:00 2001 From: Wout Mertens Date: Fri, 17 Jan 2025 12:10:37 +0100 Subject: [PATCH 4/8] chore(optimizer): remove _hW export get it from a different qrl instead --- .../qwik/src/optimizer/core/src/code_move.rs | 7 +----- packages/qwik/src/optimizer/core/src/parse.rs | 13 ---------- .../snapshots/qwik_core__test__example_1.snap | 3 --- .../qwik_core__test__example_10.snap | 1 - .../qwik_core__test__example_11.snap | 1 - .../snapshots/qwik_core__test__example_2.snap | 1 - .../snapshots/qwik_core__test__example_3.snap | 1 - .../snapshots/qwik_core__test__example_4.snap | 1 - .../snapshots/qwik_core__test__example_5.snap | 1 - .../snapshots/qwik_core__test__example_6.snap | 1 - .../snapshots/qwik_core__test__example_7.snap | 1 - .../snapshots/qwik_core__test__example_8.snap | 1 - .../snapshots/qwik_core__test__example_9.snap | 1 - ...ore__test__example_capturing_fn_class.snap | 1 - ...est__example_custom_inlined_functions.snap | 1 - ...st__example_explicit_ext_no_transpile.snap | 1 - ..._test__example_explicit_ext_transpile.snap | 1 - .../qwik_core__test__example_exports.snap | 1 - ...le_functional_component_capture_props.snap | 1 - ...ore__test__example_immutable_analysis.snap | 1 - ...ore__test__example_invalid_references.snap | 1 - ...__test__example_invalid_segment_expr1.snap | 1 - .../qwik_core__test__example_jsx.snap | 1 - ...wik_core__test__example_jsx_listeners.snap | 2 -- ...wik_core__test__example_manual_chunks.snap | 2 -- ...wik_core__test__example_multi_capture.snap | 2 -- ...wik_core__test__example_qwik_conflict.snap | 2 -- .../qwik_core__test__example_qwik_react.snap | 1 - ...k_core__test__example_renamed_exports.snap | 1 - ...core__test__example_strip_server_code.snap | 3 --- ...core__test__example_use_client_effect.snap | 1 - ..._core__test__example_use_server_mount.snap | 2 -- ...qwik_core__test__example_with_tagname.snap | 1 - .../qwik_core__test__impure_template_fns.snap | 2 +- .../snapshots/qwik_core__test__issue_150.snap | 2 -- .../qwik_core__test__lib_mode_fn_signal.snap | 2 +- .../qwik_core__test__rename_builder_io.snap | 1 - ...uld_handle_dangerously_set_inner_html.snap | 2 +- ...ould_wrap_inner_inline_component_prop.snap | 2 +- ...uld_wrap_prop_from_destructured_array.snap | 1 - .../qwik_core__test__ternary_prop.snap | 3 +-- ...__test__transform_qrl_in_regular_prop.snap | 3 +-- .../qwik/src/optimizer/core/src/transform.rs | 25 ------------------- packages/qwik/src/optimizer/core/src/words.rs | 1 - 44 files changed, 7 insertions(+), 97 deletions(-) diff --git a/packages/qwik/src/optimizer/core/src/code_move.rs b/packages/qwik/src/optimizer/core/src/code_move.rs index f36fdf0c959..f22bcb13154 100644 --- a/packages/qwik/src/optimizer/core/src/code_move.rs +++ b/packages/qwik/src/optimizer/core/src/code_move.rs @@ -1,6 +1,6 @@ use crate::collector::{new_ident_from_id, GlobalCollect, Id, ImportKind}; use crate::parse::PathData; -use crate::transform::{add_handle_watch, create_synthetic_named_import}; +use crate::transform::create_synthetic_named_import; use crate::words::*; use anyhow::Error; @@ -24,7 +24,6 @@ pub struct NewModuleCtx<'a> { pub scoped_idents: &'a [Id], pub global: &'a GlobalCollect, pub core_module: &'a JsWord, - pub need_handle_watch: bool, pub need_transform: bool, pub explicit_extensions: bool, pub leading_comments: SingleThreadedCommentsMap, @@ -144,10 +143,6 @@ pub fn new_module(ctx: NewModuleCtx) -> Result<(ast::Module, SingleThreadedComme }; module.body.push(create_named_export(expr, ctx.name)); - if ctx.need_handle_watch { - // Inject qwik internal import - add_handle_watch(&mut module.body, ctx.core_module); - } Ok((module, comments)) } diff --git a/packages/qwik/src/optimizer/core/src/parse.rs b/packages/qwik/src/optimizer/core/src/parse.rs index 81d158390c1..ae9bbe3cd4f 100644 --- a/packages/qwik/src/optimizer/core/src/parse.rs +++ b/packages/qwik/src/optimizer/core/src/parse.rs @@ -399,8 +399,6 @@ pub fn transform_code(config: TransformCodeOptions) -> Result Result>(path: P) -> PathBuf { } normalized } - -pub fn might_need_handle_watch(ctx_kind: &SegmentKind, ctx_name: &str) -> bool { - if !matches!(ctx_kind, SegmentKind::Function) { - return false; - } - matches!( - ctx_name, - "useTask$" | "useVisibleTask$" | "useBrowserVisibleTask$" | "useClientEffect$" | "$" - ) -} diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_1.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_1.snap index b1febff4e48..7896e843431 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_1.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_1.snap @@ -25,7 +25,6 @@ import { qrl } from "@qwik.dev/core"; export const renderHeader_zBbHWn4e8Cg = ()=>{ return
import("./test.tsx_renderHeader_div_onClick_fV2uzAL99u4"), "renderHeader_div_onClick_fV2uzAL99u4")}/>; }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";wCAG8B;IAC7B,QACE,IAAI;AAEP\"}") @@ -55,7 +54,6 @@ export const renderHeader_component_U6Kkv07sbpQ = ()=>{ console.log("mount"); return render; }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"kDAQiC;IAChC,QAAQ,GAAG,CAAC;IACZ,OAAO;AACR\"}") @@ -91,7 +89,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma ============================= test.tsx_renderHeader_div_onClick_fV2uzAL99u4.tsx (ENTRY POINT)== export const renderHeader_div_onClick_fV2uzAL99u4 = (ctx)=>console.log(ctx); -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"oDAKkB,CAAC,MAAQ,QAAQ,GAAG,CAAC\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_10.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_10.snap index a0a25a82855..bed85c22ecb 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_10.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_10.snap @@ -49,7 +49,6 @@ export const Header_WlR3xnI6u38 = (decl1, { decl2 }, [decl3])=>{ } return
ident11 + ident12} required={false}/>; }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/project/test.tsx\"],\"names\":[],\"mappings\":\"kCAEiB,CAAC,OAAO,EAAC,KAAK,EAAC,EAAE,CAAC,MAAM;IAE3B,OAAO,EAAE;IACtB;IACU,QAAS;IACT,QAAS;IACnB,OAAO,QAAQ;QAAC;KAAO,EAAE;QAAC;IAAM,GAAG;QAAC,KAAK;IAAM;IAC/C,MAAM;QACL,OAAO,OAAO;QACd,SAAS;YACR,OAAO;QACR;IACD;IAEA,QACE,IAAI,SAAS,CAAC,UAAY,UAAU,SAAS,UAAU;AAE1D\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_11.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_11.snap index 77eb3aa30f3..23c4d4984a6 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_11.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_11.snap @@ -30,7 +30,6 @@ export const App = component$(() => { import dep3 from "dep3/something"; export const Header_component_Header_onClick_KjD9TCNkNxY = (ev)=>dep3(ev); -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/project/test.tsx\"],\"names\":[],\"mappings\":\";2DAQqB,CAAC,KAAO,KAAK\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_2.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_2.snap index e057fcc4f64..c875195589b 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_2.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_2.snap @@ -56,7 +56,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma ============================= test.tsx_Header_component_div_onClick_i7ekvWH3674.tsx (ENTRY POINT)== export const Header_component_div_onClick_i7ekvWH3674 = (ctx)=>console.log(ctx); -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"wDAKkB,CAAC,MAAQ,QAAQ,GAAG,CAAC\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_3.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_3.snap index be2bb416b49..279d2f746cb 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_3.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_3.snap @@ -51,7 +51,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma ============================= test.tsx_App_Header_component_div_onClick_aO7uI7Iw6oQ.tsx (ENTRY POINT)== export const App_Header_component_div_onClick_aO7uI7Iw6oQ = (ctx)=>console.log(ctx); -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"4DAMmB,CAAC,MAAQ,QAAQ,GAAG,CAAC\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_4.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_4.snap index 73677e62bba..99b4de336fa 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_4.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_4.snap @@ -51,7 +51,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma ============================= test.tsx_App_Header_component_div_onClick_aO7uI7Iw6oQ.tsx (ENTRY POINT)== export const App_Header_component_div_onClick_aO7uI7Iw6oQ = (ctx)=>console.log(ctx); -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"4DAMmB,CAAC,MAAQ,QAAQ,GAAG,CAAC\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_5.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_5.snap index 61494b5e1df..582ae8085e0 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_5.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_5.snap @@ -60,7 +60,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma ============================= test.tsx_Header_component_div_onClick_i7ekvWH3674.tsx (ENTRY POINT)== export const Header_component_div_onClick_i7ekvWH3674 = (ctx)=>console.log("2"); -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"wDAMmB,CAAC,MAAQ,QAAQ,GAAG,CAAC\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_6.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_6.snap index 71a61a6e732..05af724a3a0 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_6.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_6.snap @@ -13,7 +13,6 @@ export const sym1 = $((ctx) => console.log("1")); ============================= test.tsx_sym1_aXUrPXX5Lak.tsx (ENTRY POINT)== export const sym1_aXUrPXX5Lak = (ctx)=>console.log("1"); -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"gCAEsB,CAAC,MAAQ,QAAQ,GAAG,CAAC\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_7.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_7.snap index 2a7640f940a..5b4716ef171 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_7.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_7.snap @@ -93,7 +93,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma ============================= test.tsx_Header_component_div_onClick_i7ekvWH3674.tsx (ENTRY POINT)== export const Header_component_div_onClick_i7ekvWH3674 = (ctx)=>console.log(ctx); -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"wDAMkB,CAAC,MAAQ,QAAQ,GAAG,CAAC\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_8.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_8.snap index a5cef5dfdf5..7c23e22e92e 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_8.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_8.snap @@ -57,7 +57,6 @@ export const Header_component_1_2B8d0oH9ZWc = (hola)=>{ hola.nothere.stuff[global]; return
; }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";8CAIU,CAAC;IACT,MAAM,OAAO,IAAI;IAEH,KAAK,OAAO,CAAC,KAAK,CAAC,OAAO;IACxC,QACE;AAEH\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_9.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_9.snap index a27c6498ddf..466f1b3d990 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_9.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_9.snap @@ -26,7 +26,6 @@ export const Header_WjUaUQN7Oxg = (decl1, { decl2 }, [decl3])=>{ const { decl4, key: decl5 } = this; let [decl6, ...decl7] = stuff; }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"kCAEiB,CAAC,OAAO,EAAC,KAAK,EAAC,EAAE,CAAC,MAAM;IACxC,MAAM,EAAC,KAAK,EAAE,KAAK,KAAK,EAAC,GAAG,IAAI;IAChC,IAAI,CAAC,OAAO,GAAG,MAAM,GAAG;AAQzB\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_capturing_fn_class.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_capturing_fn_class.snap index 7e27e554b8c..1462a000f81 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_capturing_fn_class.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_capturing_fn_class.snap @@ -70,7 +70,6 @@ export const App_component_1_w0t0o3QMovU = ()=>{ new Thing(); return /*#__PURE__*/ _jsxSorted("div", null, null, null, 3, "u6_0"); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";2CAUU;IACR;IACA,IAAI;IACJ,qBACC,WAAC;AAEH\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_custom_inlined_functions.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_custom_inlined_functions.snap index 9146ccb28e3..971fbd3df68 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_custom_inlined_functions.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_custom_inlined_functions.snap @@ -153,7 +153,6 @@ export const App_component_1_w0t0o3QMovU = ()=>{ const [state] = useLexicalScope(); return /*#__PURE__*/ _jsxSorted("div", null, null, _wrapProp(state, "count"), 3, "u6_0"); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;2CAcU;;yBACR,WAAC,6BAAK\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_explicit_ext_no_transpile.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_explicit_ext_no_transpile.snap index 5f7f75fb8ce..9c205cda464 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_explicit_ext_no_transpile.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_explicit_ext_no_transpile.snap @@ -84,7 +84,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma ============================= test.tsx_App_component_1_w0t0o3QMovU.tsx == export const App_component_1_w0t0o3QMovU = ()=>
; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"2CAKU,KACP,MAAM\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_explicit_ext_transpile.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_explicit_ext_transpile.snap index ac0a574b05d..c5e5f9ee32e 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_explicit_ext_transpile.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_explicit_ext_transpile.snap @@ -85,7 +85,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma import { _jsxSorted } from "@qwik.dev/core"; export const App_component_1_w0t0o3QMovU = ()=>/*#__PURE__*/ _jsxSorted("div", null, null, null, 3, "u6_0"); -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";2CAKU,kBACR,WAAC\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_exports.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_exports.snap index d26cc7661eb..3e4db3eb75c 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_exports.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_exports.snap @@ -95,7 +95,6 @@ export const Header_component_1_uWM1kg0IGO0 = ()=>
{a}{b}{c}{d}{e}{f}{exp1}{internal}{foo}{bar}{DefaultFn}
{v1}{v2}{v3}{obj}
; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/project/test.tsx\"],\"names\":[],\"mappings\":\";;;;;;;;;;;;8CAeU,KACP,OAAO;GACP,CAAC,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,MAAM,UAAU,KAAK,KAAK,YAAY,IAAI;GAClE,CAAC,KAAK,IAAI,IAAI,IAAI,MAAM,IAAI;EAC7B,EAAE\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_functional_component_capture_props.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_functional_component_capture_props.snap index 977501706d2..b758cbd7602 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_functional_component_capture_props.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_functional_component_capture_props.snap @@ -144,7 +144,6 @@ export const App_component_1_w0t0o3QMovU = ()=>{ v3 ], 0, "u6_0"); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;2CAMU;;IACR,qBACC,WAAC;QAAI,QAAQ;;;;;QACX;QAAI;QAAI;QAAI;QAAI;QAAI;QAAI;QACxB;QAAI;QAAI;QAAI;QAAI;QAAI;QAAI;QACxB;QAAI;QAAI\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_immutable_analysis.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_immutable_analysis.snap index 3b7bfe45d19..b406d021537 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_immutable_analysis.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_immutable_analysis.snap @@ -249,7 +249,6 @@ export const App_component_remove_pU6yOC5P6sY = (id)=>{ const d = state.data; d.splice(d.findIndex((d)=>d.id === id), 1); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";gDAQkB,CAAC;;IACjB,MAAM,IAAI,MAAM,IAAI;IACpB,EAAE,MAAM,CACP,EAAE,SAAS,CAAC,CAAC,IAAM,EAAE,EAAE,KAAK,KAC5B\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_invalid_references.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_invalid_references.snap index acbee04ed4b..8a37457f174 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_invalid_references.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_invalid_references.snap @@ -94,7 +94,6 @@ import { _jsxSorted } from "@qwik.dev/core"; export const App_component_1_w0t0o3QMovU = ()=>{ return /*#__PURE__*/ _jsxSorted(I10, null, null, null, 3, "u6_0"); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;2CAWU;IACR,qBACC,WAAC;AAEH\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_invalid_segment_expr1.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_invalid_segment_expr1.snap index ca1a73136ca..9422e1e0c08 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_invalid_segment_expr1.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_invalid_segment_expr1.snap @@ -90,7 +90,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma ============================= test.tsx_App_component_1_w0t0o3QMovU.js (ENTRY POINT)== export const App_component_1_w0t0o3QMovU = render; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"2CAaU\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_jsx.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_jsx.snap index 8e282ed8968..683b3194d14 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_jsx.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_jsx.snap @@ -137,7 +137,6 @@ export const Foo_component_1_DvU6FitWglY = ()=>{ }, children, 1, null) ], 1, "u6_4"); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;;;2CAeU;;IACR,qBACC,WAAC;sBACA;0BACC,WAAC;gBAAI,OAAM;;0BACX,WAAC;gBAAI,OAAM;;0BACX,WAAC;gBAAI,OAAM;eAAQ;;sBAEpB,WAAC;YAAI,OAAM;yBACV,UAAC;YAAa,GAAG,KAAK;;sBAEvB,WAAC;YAAI,OAAM;;0BACV,WAAC;0BACD,WAAC;0BACD,WAAC;;sBAEF,WAAC;YAAI,OAAM;WACT\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_jsx_listeners.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_jsx_listeners.snap index 0c30dc16637..a4094de5e3b 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_jsx_listeners.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_jsx_listeners.snap @@ -152,7 +152,6 @@ export const Foo_component_1_DvU6FitWglY = ()=>{ custom$: /*#__PURE__*/ qrl(()=>import("./test.tsx_Foo_component_div_custom_pyHnxab17ms"), "Foo_component_div_custom_pyHnxab17ms") }, null, 3, "u6_0"); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;2CAKU;IACR,MAAM;IACN,qBACC,WAAC;QACA,QAAQ;QACR,iBAAiB;QACjB,iBAAiB;QAEjB,WAAS;QACT,oBAAkB;QAClB,oBAAkB;QAPnB,eAkBE;QAlBF,wBAkBE;QAlBF,wBAkBE;QALD,UAAU;QAbX,qBAcoB;QAdpB,mBAekB;QAEjB,OAAO;;AAGV\"}") @@ -361,7 +360,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma ============================= test.tsx_Foo_component_handler_H10xZtD0e7w.js (ENTRY POINT)== export const Foo_component_handler_H10xZtD0e7w = ()=>console.log('reused'); -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"iDAMoB,IAAM,QAAQ,GAAG,CAAC\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_manual_chunks.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_manual_chunks.snap index 2d80a32c4f4..828fd4290d0 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_manual_chunks.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_manual_chunks.snap @@ -56,7 +56,6 @@ export const Parent_component_useTask_gDH1EtUWqBU = async ()=>{ state.text = await mongo.users(); redis.set(state.text); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;oDAWU;;IACR,MAAM,IAAI,GAAG,MAAM,MAAM,KAAK;IAC9B,MAAM,GAAG,CAAC,MAAM,IAAI\"}") @@ -97,7 +96,6 @@ export const Child_component_useTask_Oh4n7ZeqJkU = async ()=>{ const [state] = useLexicalScope(); state.text = await mongo.users(); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;mDA6BU;;IACR,MAAM,IAAI,GAAG,MAAM,MAAM,KAAK\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_multi_capture.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_multi_capture.snap index 9419598dbd6..a90ca26c3c0 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_multi_capture.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_multi_capture.snap @@ -112,7 +112,6 @@ export const Foo_component_1_DvU6FitWglY = ()=>{ {props.foo}{fn()}{20}
; }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";2CAKU;;IACR,MAAM,KAAK,CAAC,EAAC,GAAG,EAAC,GAAK;IACtB,QACE,IAAI;IACJ,OAN4B,KAMtB,MALI,GAKO;GAClB,EAAE\"}") @@ -145,7 +144,6 @@ export const Bar_component_1_0xSyNSnVu3k = ()=>{ {props.bar}
; }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";2CAgBU;;IACR,QACE,IAAI;IACJ,OAJ4B,IAIvB;GACN,EAAE\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_qwik_conflict.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_qwik_conflict.snap index c6171b5d6cb..736d11db7d1 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_qwik_conflict.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_qwik_conflict.snap @@ -97,7 +97,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma ============================= test.tsx_Root_component_useStyles_u5DkUxGrGnU.js (ENTRY POINT)== export const Root_component_useStyles_u5DkUxGrGnU = 'thing'; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"oDAuBa\"}") @@ -158,7 +157,6 @@ import { _jsxSorted } from "@qwik.dev/core"; export const Root_component_1_cBpQNYDUHI4 = ()=>{ return /*#__PURE__*/ _jsxSorted("div", null, null, null, 3, "u6_1"); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";4CAwBU;IACR,qBACC,WAAC;AAEH\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_qwik_react.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_qwik_react.snap index 2be8741fbc2..d331791a37a 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_qwik_react.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_qwik_react.snap @@ -128,7 +128,6 @@ export const qwikifyQrl_component_useWatch_x04JC5xeP1U = async (track)=>{ } } }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/node_modules/@qwik.dev/react/index.qwik.mjs\"],\"names\":[],\"mappings\":\";;;;yDAawB,OAAO;IAC5B,MAAM,CAAC,aAAa,OAAO,aAAa,MAAM,GAAG;IACjD,MAAM;IACN,IAAI;QACH,IAAI,MAAM,IAAI,EAAE,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,YAAY;aACrF;YACJ,MAAM,CAAC,KAAK,OAAO,GAAG,MAAM,QAAQ,GAAG,CAAC;gBACvC,YAAY,OAAO;gBACnB,MAAM,CAAC;aACP;YACD,IAAI;YACJ,IAAI,YAAY,iBAAiB,GAAG,GAAG,OAAO,OAAO,WAAW,CAAC,aAAa,OAAO,IAAI,CAAC,KAAK,YAAY,QAAQ,MAAM,KAAK;iBACzH;gBACJ,OAAO,OAAO,UAAU,CAAC;gBACzB,KAAK,MAAM,CAAC,OAAO,IAAI,CAAC,KAAK,YAAY;YAC1C;YACA,MAAM,IAAI,GAAG,YAAY;gBACxB;gBACA,KAAK;gBACL;YACD;QACD;;AAEF\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_renamed_exports.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_renamed_exports.snap index ed41a8a7720..574b009055a 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_renamed_exports.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_renamed_exports.snap @@ -34,7 +34,6 @@ export const App_Component_1_A08tXHb9pEk = ()=>{ const [state] = useLexicalScope(); return /*#__PURE__*/ _jsxSorted("div", null, null, _wrapProp(state, "thing"), 3, "u6_0"); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;2CAMiB;;yBACf,WAAC,6BAAK\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_strip_server_code.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_strip_server_code.snap index e70aed85ab8..67e7c94a0c8 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_strip_server_code.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_strip_server_code.snap @@ -59,7 +59,6 @@ export const s_gDH1EtUWqBU = async ()=>{ state.text = await mongo.users(); redis.set(state.text); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;6BAaU;;IAER,MAAM,IAAI,GAAG,MAAM,MAAM,KAAK;IAC9B,MAAM,GAAG,CAAC,MAAM,IAAI\"}") @@ -96,7 +95,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma export const s_2ca3HLDC7yc = ()=>{ // from $(), should not be removed }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"6BAqBc;AACX,kCAAkC;AACnC\"}") @@ -227,7 +225,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma export const s_P8oRQhHsurk = ()=>{ // Code }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"6BAgCU;AACR,OAAO;AACR\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_use_client_effect.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_use_client_effect.snap index 4120fe1eb0d..1266d776288 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_use_client_effect.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_use_client_effect.snap @@ -44,7 +44,6 @@ export const Child_component_useBrowserVisibleTask_0IGFPOyJmQA = ()=>{ clearInterval(timer); }; }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";iEASwB;;IACtB,MAAM,QAAQ,YAAY;QAC1B,MAAM,KAAK;IACX,GAAG;IACH,OAAO;QACP,cAAc;IACd\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_use_server_mount.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_use_server_mount.snap index 46ffb25c3e0..a882a768869 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_use_server_mount.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_use_server_mount.snap @@ -56,7 +56,6 @@ export const Parent_component_useTask_gDH1EtUWqBU = async ()=>{ state.text = await mongo.users(); redis.set(state.text); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;oDAWU;;IACR,MAAM,IAAI,GAAG,MAAM,MAAM,KAAK;IAC9B,MAAM,GAAG,CAAC,MAAM,IAAI\"}") @@ -97,7 +96,6 @@ export const Child_component_useTask_Oh4n7ZeqJkU = async ()=>{ const [state] = useLexicalScope(); state.text = await mongo.users(); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;mDA6BU;;IACR,MAAM,IAAI,GAAG,MAAM,MAAM,KAAK\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_with_tagname.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_with_tagname.snap index c71bbf17067..edd9c0de4ad 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_with_tagname.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_with_tagname.snap @@ -55,7 +55,6 @@ export const Foo_component_1_DvU6FitWglY = ()=>{ return
; }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"2CAIU;IACR,QACE,IAAI;GACL,EAAE;AAEJ\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__impure_template_fns.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__impure_template_fns.snap index 27bc978084c..cbde8506a45 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__impure_template_fns.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__impure_template_fns.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 3788 +assertion_line: 3917 expression: output snapshot_kind: text --- diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__issue_150.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__issue_150.snap index fe9835ffb12..88ffe82bbbf 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__issue_150.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__issue_150.snap @@ -39,7 +39,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma ============================= test.tsx_d_wKNFJEIQVUA.js (ENTRY POINT)== export const d_wKNFJEIQVUA = ()=>console.log('thing'); -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"6BAmBY,IAAI,QAAQ,GAAG,CAAC\"}") @@ -78,7 +77,6 @@ export const Greeter_component_1_krCndSwhX4U = ()=>{ } }, null, null, 3, "u6_0"); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;+CAMU;;IACR,qBACC,WAAC;QACA,OAAO;YACN,OAAO;YACP,OAAO,MAAM,SAAS;YACtB,OAAO,OAAO,SAAS;QACxB\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__lib_mode_fn_signal.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__lib_mode_fn_signal.snap index 2c77ce1838f..c57a12ff5c0 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__lib_mode_fn_signal.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__lib_mode_fn_signal.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 3724 +assertion_line: 3853 expression: output snapshot_kind: text --- diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__rename_builder_io.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__rename_builder_io.snap index be3e1817eed..4f9f595096b 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__rename_builder_io.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__rename_builder_io.snap @@ -31,7 +31,6 @@ snapshot_kind: text ============================= test.tsx_Bar_GXXnVUtURSw.ts (ENTRY POINT)== export const Bar_GXXnVUtURSw = "a thing"; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"+BAUuB\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_handle_dangerously_set_inner_html.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_handle_dangerously_set_inner_html.snap index 9af744c52b5..db4168e4c0c 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_handle_dangerously_set_inner_html.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_handle_dangerously_set_inner_html.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 3651 +assertion_line: 3780 expression: output snapshot_kind: text --- diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_wrap_inner_inline_component_prop.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_wrap_inner_inline_component_prop.snap index 2a16721bb26..035f0bcea6d 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_wrap_inner_inline_component_prop.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_wrap_inner_inline_component_prop.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 3959 +assertion_line: 4088 expression: output snapshot_kind: text --- diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_wrap_prop_from_destructured_array.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_wrap_prop_from_destructured_array.snap index 44c5a7a60b2..3986c700bd5 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_wrap_prop_from_destructured_array.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_wrap_prop_from_destructured_array.snap @@ -192,7 +192,6 @@ export const Input_component_useTask_Sbgs9Wtfkt0 = ({ track })=>{ track(()=>props.error2); track(()=>props.error3); }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";mDAMa,CAAC,EAAE,KAAK,EAAE;;IAClB,MAAM,IAAM,MAAM,KAAK;IACvB,MAAM,IAAM,MAAM,MAAM;IACxB,MAAM,IAAM,MAAM,MAAM\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__ternary_prop.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__ternary_prop.snap index d3cb9b3d36e..f57c7b79f47 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__ternary_prop.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__ternary_prop.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 3748 +assertion_line: 3877 expression: output snapshot_kind: text --- @@ -29,7 +29,6 @@ export const Cmp_component_handleClick_WawHV3HwS1A = ()=>{ const [toggleSig] = useLexicalScope(); toggleSig.value = !toggleSig.value; }; -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";qDAK0B;;IACtB,UAAU,KAAK,GAAG,CAAC,UAAU,KAAK\"}") diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__transform_qrl_in_regular_prop.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__transform_qrl_in_regular_prop.snap index 70aaf17879b..c772a048109 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__transform_qrl_in_regular_prop.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__transform_qrl_in_regular_prop.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 3773 +assertion_line: 3902 expression: output snapshot_kind: text --- @@ -45,7 +45,6 @@ Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"ma ============================= test.tsx_Cmp_component_Cmp_foo_iPpdemxi0uE.ts (ENTRY POINT)== export const Cmp_component_Cmp_foo_iPpdemxi0uE = ()=>console.log('hi there'); -export { _hW } from "@qwik.dev/core"; Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\"iDAGe,IAAM,QAAQ,GAAG,CAAC\"}") diff --git a/packages/qwik/src/optimizer/core/src/transform.rs b/packages/qwik/src/optimizer/core/src/transform.rs index 6b95c453ab7..4f853475414 100644 --- a/packages/qwik/src/optimizer/core/src/transform.rs +++ b/packages/qwik/src/optimizer/core/src/transform.rs @@ -2160,31 +2160,6 @@ impl<'a> Fold for QwikTransform<'a> { } } -pub fn add_handle_watch(body: &mut Vec, core_module: &JsWord) { - body.push(ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportNamed( - ast::NamedExport { - src: Some(Box::new(ast::Str { - span: DUMMY_SP, - value: core_module.clone(), - raw: None, - })), - span: DUMMY_SP, - with: None, - type_only: false, - specifiers: vec![ast::ExportSpecifier::Named(ast::ExportNamedSpecifier { - orig: ast::ModuleExportName::Ident(ast::Ident::new( - HANDLE_WATCH.clone(), - DUMMY_SP, - Default::default(), - )), - exported: None, - is_type_only: false, - span: DUMMY_SP, - })], - }, - ))); -} - pub fn create_synthetic_named_export(local: &Id, exported: Option) -> ast::ModuleItem { ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportNamed(ast::NamedExport { span: DUMMY_SP, diff --git a/packages/qwik/src/optimizer/core/src/words.rs b/packages/qwik/src/optimizer/core/src/words.rs index 7180da14f13..28e2e502476 100644 --- a/packages/qwik/src/optimizer/core/src/words.rs +++ b/packages/qwik/src/optimizer/core/src/words.rs @@ -8,7 +8,6 @@ lazy_static! { pub static ref REF: JsWord = JsWord::from("ref"); pub static ref QSLOT: JsWord = JsWord::from("q:slot"); pub static ref CHILDREN: JsWord = JsWord::from("children"); - pub static ref HANDLE_WATCH: JsWord = JsWord::from("_hW"); pub static ref _QRL: JsWord = JsWord::from("qrl"); pub static ref _QRL_DEV: JsWord = JsWord::from("qrlDEV"); pub static ref _INLINED_QRL: JsWord = JsWord::from("inlinedQrl"); From 27020fafa3f3be0ca68d12e93a121006f095e59a Mon Sep 17 00:00:00 2001 From: Wout Mertens Date: Sun, 19 Jan 2025 12:30:00 +0100 Subject: [PATCH 5/8] refactor(core): schedule tasks via core qrl --- .changeset/olive-cameras-collect.md | 5 +++ packages/qwik/handlers.mjs | 9 +++++ packages/qwik/package.json | 2 + packages/qwik/src/core/api.md | 6 +-- packages/qwik/src/core/internal.ts | 2 +- .../qwik/src/core/shared/qrl/qrl-class.ts | 1 + packages/qwik/src/core/use/use-task.ts | 15 +++----- packages/qwik/src/optimizer/src/manifest.ts | 38 +++++++++++++------ .../qwik/src/optimizer/src/plugins/plugin.ts | 38 +++++++++++++++++++ .../optimizer/src/plugins/vite-dev-server.ts | 6 ++- packages/qwik/src/qwikloader.ts | 1 + 11 files changed, 97 insertions(+), 26 deletions(-) create mode 100644 .changeset/olive-cameras-collect.md create mode 100644 packages/qwik/handlers.mjs diff --git a/.changeset/olive-cameras-collect.md b/.changeset/olive-cameras-collect.md new file mode 100644 index 00000000000..4a5cdb5c658 --- /dev/null +++ b/.changeset/olive-cameras-collect.md @@ -0,0 +1,5 @@ +--- +'@qwik.dev/core': patch +--- + +CHORE: replace the `_hW` export in segments with a shared export `_task` in core. This opens up using QRLs from core. diff --git a/packages/qwik/handlers.mjs b/packages/qwik/handlers.mjs new file mode 100644 index 00000000000..de787e31b57 --- /dev/null +++ b/packages/qwik/handlers.mjs @@ -0,0 +1,9 @@ +/** + * This re-exports the QRL handlers so that they can be used as QRLs. + * + * In vite dev mode, this file is referenced directly. This ensures that the correct path to core is + * used so that there's only one instance of Qwik. + * + * Make sure that these handlers are listed in manifest.ts + */ +export { _task } from '@qwik.dev/core'; diff --git a/packages/qwik/package.json b/packages/qwik/package.json index 186a2945260..4889e800d24 100644 --- a/packages/qwik/package.json +++ b/packages/qwik/package.json @@ -41,6 +41,7 @@ "./cli": { "require": "./dist/cli.cjs" }, + "./handlers.mjs": "./handlers.mjs", "./internal": { "types": "./core-internal.d.ts", "import": { @@ -144,6 +145,7 @@ "bindings", "build.d.ts", "cli.d.ts", + "handlers.mjs", "jsx-dev-runtime.d.ts", "jsx-runtime.d.ts", "loader.d.ts", diff --git a/packages/qwik/src/core/api.md b/packages/qwik/src/core/api.md index 95487f0d77a..c8f59ea16c9 100644 --- a/packages/qwik/src/core/api.md +++ b/packages/qwik/src/core/api.md @@ -316,9 +316,6 @@ function h, PROPS extends {} = {} export { h as createElement } export { h } -// @internal -export const _hW: () => void; - // @internal @deprecated (undocumented) export const _IMMUTABLE: unique symbol; @@ -997,6 +994,9 @@ export interface SyncQRL extends QRL { resolved: TYPE; } +// @internal +export const _task: () => void; + // @public (undocumented) export interface TaskCtx { // (undocumented) diff --git a/packages/qwik/src/core/internal.ts b/packages/qwik/src/core/internal.ts index d173f70544b..e5947a25507 100644 --- a/packages/qwik/src/core/internal.ts +++ b/packages/qwik/src/core/internal.ts @@ -1,7 +1,7 @@ export { _noopQrl, _noopQrlDEV, _regSymbol } from './shared/qrl/qrl'; export { _walkJSX } from './ssr/ssr-render-jsx'; export { _SharedContainer } from './shared/shared-container'; -export { _hW } from './use/use-task'; +export { scheduleTask as _task } from './use/use-task'; export { _wrapSignal, _wrapProp } from './signal/signal-utils'; export { _restProps } from './shared/utils/prop'; export { _IMMUTABLE } from './shared/utils/constants'; diff --git a/packages/qwik/src/core/shared/qrl/qrl-class.ts b/packages/qwik/src/core/shared/qrl/qrl-class.ts index 3deda85ec9b..ac61c449e99 100644 --- a/packages/qwik/src/core/shared/qrl/qrl-class.ts +++ b/packages/qwik/src/core/shared/qrl/qrl-class.ts @@ -68,6 +68,7 @@ export type QRLInternalMethods = { export type QRLInternal = QRL & QRLInternalMethods; +// TODO remove refSymbol, it's not used export const createQRL = ( chunk: string | null, symbol: string, diff --git a/packages/qwik/src/core/use/use-task.ts b/packages/qwik/src/core/use/use-task.ts index b8a68a8e602..48a9b59eac3 100644 --- a/packages/qwik/src/core/use/use-task.ts +++ b/packages/qwik/src/core/use/use-task.ts @@ -275,20 +275,15 @@ export const useRunTask = ( }; const getTaskHandlerQrl = (task: Task): QRL<(ev: Event) => void> => { - const taskQrl = task.$qrl$; const taskHandler = createQRL<(ev: Event) => void>( - taskQrl.$chunk$, - '_hW', - _hW, + null, + '_task', + scheduleTask, null, null, [task], - taskQrl.$symbol$ + null ); - // Needed for chunk lookup in dev mode - if (taskQrl.dev) { - taskHandler.dev = taskQrl.dev; - } return taskHandler; }; @@ -318,7 +313,7 @@ export const isTask = (value: any): value is Task => { * * @internal */ -export const _hW = () => { +export const scheduleTask = () => { const [task] = useLexicalScope<[Task]>(); const container = getDomContainer(task.$el$ as VNode); const type = task.$flags$ & TaskFlags.VISIBLE_TASK ? ChoreType.VISIBLE : ChoreType.TASK; diff --git a/packages/qwik/src/optimizer/src/manifest.ts b/packages/qwik/src/optimizer/src/manifest.ts index 286397b1776..c8a29264988 100644 --- a/packages/qwik/src/optimizer/src/manifest.ts +++ b/packages/qwik/src/optimizer/src/manifest.ts @@ -2,6 +2,10 @@ import type { OutputBundle } from 'rollup'; import { type NormalizedQwikPluginOptions } from './plugins/plugin'; import type { GlobalInjections, SegmentAnalysis, Path, QwikBundle, QwikManifest } from './types'; +// The handlers that are exported by the core package +// See handlers.mjs +const extraSymbols = new Set(['_task']); + // This is just the initial prioritization of the symbols and entries // at build time so there's less work during each SSR. However, SSR should // still further optimize the priorities depending on the user/document. @@ -270,7 +274,13 @@ export function generateManifestFromBundles( return canonPath(bundle.fileName); }; // We need to find our QRL exports - const qrlNames = new Set([...segments.map((h) => h.name)]); + const qrlNames = new Set(segments.map((h) => h.name)); + for (const symbol of extraSymbols) { + qrlNames.add(symbol); + } + const taskNames = new Set( + segments.filter((h) => /use[a-zA-Z]*Task(_\d+)?$/.test(h.displayName)).map((h) => h.name) + ); for (const outputBundle of Object.values(outputBundles)) { if (outputBundle.type !== 'chunk') { continue; @@ -281,25 +291,18 @@ export function generateManifestFromBundles( size: outputBundle.code.length, }; - let hasSymbols = false; - let hasHW = false; for (const symbol of outputBundle.exports) { if (qrlNames.has(symbol)) { // When not minifying we see both the entry and the segment file // The segment file will only have 1 export, we want the entry if (!manifest.mapping[symbol] || outputBundle.exports.length !== 1) { - hasSymbols = true; + if (taskNames.has(symbol)) { + bundle.isTask = true; + } manifest.mapping[symbol] = bundleFileName; } } - if (symbol === '_hW') { - hasHW = true; - } - } - if (hasSymbols && hasHW) { - bundle.isTask = true; } - const bundleImports = outputBundle.imports // Tree shaking might remove imports .filter((i) => outputBundle.code.includes(path.basename(i))) @@ -349,6 +352,19 @@ export function generateManifestFromBundles( loc: segment.loc, }; } + for (const symbol of extraSymbols) { + manifest.symbols[symbol] = { + origin: 'Qwik core', + displayName: symbol, + canonicalFilename: '', + hash: symbol, + ctxKind: 'function', + ctxName: symbol, + captures: false, + parent: null, + loc: [0, 0], + }; + } // To inspect the bundles, uncomment the following lines // and temporarily add the writeFileSync import from fs // writeFileSync( diff --git a/packages/qwik/src/optimizer/src/plugins/plugin.ts b/packages/qwik/src/optimizer/src/plugins/plugin.ts index 964134b42c1..b0e7bf41a4d 100644 --- a/packages/qwik/src/optimizer/src/plugins/plugin.ts +++ b/packages/qwik/src/optimizer/src/plugins/plugin.ts @@ -373,9 +373,11 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { }; let optimizer: Optimizer; + let shouldAddHandlers = false; const buildStart = async (_ctx: Rollup.PluginContext) => { debug(`buildStart()`, opts.buildMode, opts.scope, opts.target, opts.rootDir, opts.srcDir); optimizer = getOptimizer(); + shouldAddHandlers = !devServer; if (optimizer.sys.env === 'node' && opts.target === 'ssr' && opts.lint) { try { linter = await createLinter(optimizer.sys, opts.rootDir, opts.tsconfigFileNames); @@ -484,7 +486,29 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { id: QWIK_CLIENT_MANIFEST_ID, moduleSideEffects: false, }; + } else if (pathId.endsWith(QWIK_HANDLERS_ID)) { + debug(`resolveId(${count})`, 'Resolved', QWIK_HANDLERS_ID); + result = { + id: QWIK_HANDLERS_ID, + moduleSideEffects: false, + }; } else { + // If qwik core is loaded, also add the handlers + if (!isServer && shouldAddHandlers && id.endsWith('@qwik.dev/core')) { + shouldAddHandlers = false; + const key = await ctx.resolve('@qwik.dev/core/handlers.mjs', importerId, { + skipSelf: true, + }); + if (!key) { + throw new Error('Failed to resolve @qwik.dev/core/handlers.mjs'); + } + ctx.emitFile({ + id: key.id, + type: 'chunk', + preserveSignature: 'allow-extension', + }); + } + const qrlMatch = /^(?.*\.[mc]?[jt]sx?)_(?[^/]+)\.js(?$|\?.*$)/.exec(id) ?.groups as { parent: string; name: string; query: string } | undefined; @@ -566,6 +590,18 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { code: await getQwikServerManifestModule(isServer), }; } + /** + * In dev mode, we need a path to core for qrls. However, we don't know what that is. By + * re-exporting the core symbols, we let Vite provide the correct path to core and we prevent + * duplicate Qwik instances. + */ + if (id === QWIK_HANDLERS_ID) { + debug(`load(${count})`, QWIK_HANDLERS_ID, opts.buildMode); + return { + moduleSideEffects: false, + code: `export * from '@qwik.dev/core';`, + }; + } // QRL segments const parsedId = parseId(id); @@ -978,6 +1014,8 @@ export const QWIK_CORE_SERVER = '@qwik.dev/core/server'; export const QWIK_CLIENT_MANIFEST_ID = '@qwik-client-manifest'; +export const QWIK_HANDLERS_ID = '@qwik-handlers'; + export const SRC_DIR_DEFAULT = 'src'; export const CLIENT_OUT_DIR = 'dist'; diff --git a/packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts b/packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts index 212b5978f7b..7d802d87f8b 100644 --- a/packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts +++ b/packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts @@ -10,7 +10,7 @@ import clickToComponent from './click-to-component.html?raw'; import errorHost from './error-host.html?raw'; import imageDevTools from './image-size-runtime.html?raw'; import perfWarning from './perf-warning.html?raw'; -import { type NormalizedQwikPluginOptions, parseId } from './plugin'; +import { type NormalizedQwikPluginOptions, parseId, QWIK_HANDLERS_ID } from './plugin'; import type { QwikViteDevResponse } from './vite'; import { VITE_ERROR_OVERLAY_STYLES } from './vite-error'; import { formatError } from './vite-utils'; @@ -37,6 +37,10 @@ function createSymbolMapper(base: string): SymbolMapperFn { return [symbolName, '']; } if (!parent) { + // Core symbols + if (symbolName.startsWith('_')) { + return [symbolName, `${base}${QWIK_HANDLERS_ID}`]; + } console.error( 'qwik vite-dev-server symbolMapper: unknown qrl requested without parent:', symbolName diff --git a/packages/qwik/src/qwikloader.ts b/packages/qwik/src/qwikloader.ts index 7bb3dd588b9..c96484f2dc6 100644 --- a/packages/qwik/src/qwikloader.ts +++ b/packages/qwik/src/qwikloader.ts @@ -25,6 +25,7 @@ export const qwikLoader = ( const roots = new Set([doc]); // Some shortenings for minification + // TODO use more, like hasAttribute const replace = 'replace'; const forEach = 'forEach'; const target = 'target'; From 770ddb280a452ea944572a1796dc608f9ddeeff1 Mon Sep 17 00:00:00 2001 From: Wout Mertens Date: Sun, 19 Jan 2025 14:29:08 +0100 Subject: [PATCH 6/8] chore(core): remove unused qrl refSymbol --- .../qwik/src/core/shared/qrl/qrl-class.ts | 11 ++--- .../qwik/src/core/shared/qrl/qrl.public.ts | 6 +-- packages/qwik/src/core/shared/qrl/qrl.ts | 6 +-- packages/qwik/src/core/shared/qrl/qrl.unit.ts | 46 ++++++------------- .../src/core/shared/shared-serialization.ts | 9 ++-- .../core/shared/shared-serialization.unit.ts | 5 +- packages/qwik/src/core/use/use-task.ts | 12 ++--- 7 files changed, 31 insertions(+), 64 deletions(-) diff --git a/packages/qwik/src/core/shared/qrl/qrl-class.ts b/packages/qwik/src/core/shared/qrl/qrl-class.ts index ac61c449e99..3efad3b1094 100644 --- a/packages/qwik/src/core/shared/qrl/qrl-class.ts +++ b/packages/qwik/src/core/shared/qrl/qrl-class.ts @@ -41,7 +41,6 @@ export const isSyncQrl = (value: any): value is SyncQRLInternal => { export type QRLInternalMethods = { readonly $chunk$: string | null; readonly $symbol$: string; - readonly $refSymbol$: string | null; readonly $hash$: string; $capture$: string[] | null; @@ -68,15 +67,13 @@ export type QRLInternalMethods = { export type QRLInternal = QRL & QRLInternalMethods; -// TODO remove refSymbol, it's not used export const createQRL = ( chunk: string | null, symbol: string, symbolRef: null | ValueOrPromise, symbolFn: null | (() => Promise>), capture: null | Readonly, - captureRef: Readonly | null, - refSymbol: string | null + captureRef: Readonly | null ): QRLInternal => { if (qDev && qSerialize) { if (captureRef) { @@ -202,11 +199,10 @@ export const createQRL = ( } }; - const resolvedSymbol = refSymbol ?? symbol; - const hash = getSymbolHash(resolvedSymbol); + const hash = getSymbolHash(symbol); Object.assign(qrl, { - getSymbol: () => resolvedSymbol, + getSymbol: () => symbol, getHash: () => hash, getCaptured: () => captureRef, resolve, @@ -214,7 +210,6 @@ export const createQRL = ( $setContainer$: setContainer, $chunk$: chunk, $symbol$: symbol, - $refSymbol$: refSymbol, $hash$: hash, getFn: bindFnToContext, diff --git a/packages/qwik/src/core/shared/qrl/qrl.public.ts b/packages/qwik/src/core/shared/qrl/qrl.public.ts index b1fd5187358..eaf97a3abea 100644 --- a/packages/qwik/src/core/shared/qrl/qrl.public.ts +++ b/packages/qwik/src/core/shared/qrl/qrl.public.ts @@ -253,7 +253,7 @@ export const $ = (expression: T): QRL => { ); } - return createQRL(null, 's' + runtimeSymbolId++, expression, null, null, null, null); + return createQRL(null, 's' + runtimeSymbolId++, expression, null, null, null); }; /** @private Use To avoid optimizer replacement */ export const dollar = $; @@ -302,7 +302,7 @@ export const sync$ = (fn: T): SyncQRL => { fn = new Function('return ' + fn.toString())() as any; } - return createQRL('', SYNC_QRL, fn, null, null, null, null) as any; + return createQRL('', SYNC_QRL, fn, null, null, null) as any; }; /** @@ -323,5 +323,5 @@ export const _qrlSync = function ( serializedFn = fn.toString(); } (fn as any).serialized = serializedFn; - return createQRL('', SYNC_QRL, fn, null, null, null, null) as any; + return createQRL('', SYNC_QRL, fn, null, null, null) as any; }; diff --git a/packages/qwik/src/core/shared/qrl/qrl.ts b/packages/qwik/src/core/shared/qrl/qrl.ts index 20a734262ad..27a3a314826 100644 --- a/packages/qwik/src/core/shared/qrl/qrl.ts +++ b/packages/qwik/src/core/shared/qrl/qrl.ts @@ -85,7 +85,7 @@ export const qrl = ( } // Unwrap subscribers - return createQRL(chunk, symbol, null, symbolFn, null, lexicalScopeCapture, null); + return createQRL(chunk, symbol, null, symbolFn, null, lexicalScopeCapture); }; /** @internal */ @@ -95,7 +95,7 @@ export const inlinedQrl = ( lexicalScopeCapture: any[] = EMPTY_ARRAY ): QRL => { // Unwrap subscribers - return createQRL(null, symbolName, symbol, null, null, lexicalScopeCapture, null); + return createQRL(null, symbolName, symbol, null, null, lexicalScopeCapture); }; /** @internal */ @@ -103,7 +103,7 @@ export const _noopQrl = ( symbolName: string, lexicalScopeCapture: any[] = EMPTY_ARRAY ): QRL => { - return createQRL(null, symbolName, null, null, null, lexicalScopeCapture, null); + return createQRL(null, symbolName, null, null, null, lexicalScopeCapture); }; /** @internal */ diff --git a/packages/qwik/src/core/shared/qrl/qrl.unit.ts b/packages/qwik/src/core/shared/qrl/qrl.unit.ts index 470afaafa3c..bb5278a2aa7 100644 --- a/packages/qwik/src/core/shared/qrl/qrl.unit.ts +++ b/packages/qwik/src/core/shared/qrl/qrl.unit.ts @@ -108,35 +108,29 @@ describe('serialization', () => { new WeakMap() ); assert.equal( - qrlToString(serializationContext, createQRL('./chunk', '', null, null, null, null, null)), + qrlToString(serializationContext, createQRL('./chunk', '', null, null, null, null)), 'chunk#' ); assert.equal( - qrlToString(serializationContext, createQRL('./c', 's1', null, null, null, null, null)), + qrlToString(serializationContext, createQRL('./c', 's1', null, null, null, null)), 'c#s1' ); assert.equal( - qrlToString(serializationContext, createQRL('./c', 's1', null, null, [], null, null)), + qrlToString(serializationContext, createQRL('./c', 's1', null, null, [], null)), 'c#s1' ); assert.equal( - qrlToString( - serializationContext, - createQRL('./c', 's1', null, null, [1, '2'] as any, null, null) - ), + qrlToString(serializationContext, createQRL('./c', 's1', null, null, [1, '2'] as any, null)), 'c#s1[1 2]' ); assert.equal( - qrlToString( - serializationContext, - createQRL('c', 's1', null, null, [1 as any, '2'], null, null) - ), + qrlToString(serializationContext, createQRL('c', 's1', null, null, [1 as any, '2'], null)), 'c#s1[1 2]' ); assert.equal( qrlToString( serializationContext, - createQRL('src/routes/[...index]/a+b/c?foo', 's1', null, null, [1 as any, '2'], null, null) + createQRL('src/routes/[...index]/a+b/c?foo', 's1', null, null, [1 as any, '2'], null) ), 'src/routes/[...index]/a+b/c?foo#s1[1 2]' ); @@ -174,7 +168,7 @@ describe('serialization', () => { describe('createQRL', () => { test('should create QRL', () => { - const q = createQRL('chunk', 'symbol', 'resolved', null, null, null, null); + const q = createQRL('chunk', 'symbol', 'resolved', null, null, null); matchProps(q, { $chunk$: 'chunk', $symbol$: 'symbol', @@ -182,11 +176,11 @@ describe('createQRL', () => { }); }); test('should have .resolved: given scalar', async () => { - const q = createQRL('chunk', 'symbol', 'resolved', null, null, null, null); + const q = createQRL('chunk', 'symbol', 'resolved', null, null, null); assert.equal(q.resolved, 'resolved'); }); test('should have .resolved: given promise for scalar', async () => { - const q = createQRL('chunk', 'symbol', Promise.resolve('resolved'), null, null, null, null); + const q = createQRL('chunk', 'symbol', Promise.resolve('resolved'), null, null, null); assert.equal(q.resolved, undefined); assert.equal(await q.resolve(), 'resolved'); assert.equal(q.resolved, 'resolved'); @@ -198,7 +192,6 @@ describe('createQRL', () => { null, () => Promise.resolve({ symbol: 'resolved' }), null, - null, null ); assert.equal(q.resolved, undefined); @@ -208,25 +201,17 @@ describe('createQRL', () => { const fn = () => 'hi'; test('should have .resolved: given function without captures', async () => { - const q = createQRL('chunk', 'symbol', fn, null, null, null, null); + const q = createQRL('chunk', 'symbol', fn, null, null, null); assert.equal(q.resolved, fn); }); test('should have .resolved: given promise for function without captures', async () => { - const q = createQRL('chunk', 'symbol', Promise.resolve(fn), null, null, null, null); + const q = createQRL('chunk', 'symbol', Promise.resolve(fn), null, null, null); assert.equal(q.resolved, undefined); assert.equal(await q.resolve(), fn); assert.equal(q.resolved, fn); }); test('should have .resolved: promise for function without captures', async () => { - const q = createQRL( - 'chunk', - 'symbol', - null, - () => Promise.resolve({ symbol: fn }), - null, - null, - null - ); + const q = createQRL('chunk', 'symbol', null, () => Promise.resolve({ symbol: fn }), null, null); assert.equal(q.resolved, undefined); assert.equal(await q.resolve(), fn); assert.equal(q.resolved, fn); @@ -234,13 +219,13 @@ describe('createQRL', () => { const capFn = () => useLexicalScope(); test('should have .resolved: given function with captures', async () => { - const q = createQRL('chunk', 'symbol', capFn, null, null, ['hi'], null); + const q = createQRL('chunk', 'symbol', capFn, null, null, ['hi']); assert.isDefined(q.resolved); assert.notEqual(q.resolved, capFn); assert.deepEqual(q.resolved!(), ['hi']); }); test('should have .resolved: given promise for function with captures', async () => { - const q = createQRL('chunk', 'symbol', Promise.resolve(capFn), null, null, ['hi'], null); + const q = createQRL('chunk', 'symbol', Promise.resolve(capFn), null, null, ['hi']); assert.equal(q.resolved, undefined); assert.deepEqual(await q(), ['hi']); assert.notEqual(q.resolved, capFn); @@ -253,8 +238,7 @@ describe('createQRL', () => { null, () => Promise.resolve({ symbol: capFn }), null, - ['hi'], - null + ['hi'] ); assert.equal(q.resolved, undefined); assert.deepEqual(await q(), ['hi']); diff --git a/packages/qwik/src/core/shared/shared-serialization.ts b/packages/qwik/src/core/shared/shared-serialization.ts index 215b5ebd15f..66523cfa3b0 100644 --- a/packages/qwik/src/core/shared/shared-serialization.ts +++ b/packages/qwik/src/core/shared/shared-serialization.ts @@ -568,7 +568,7 @@ export function parseQRL(qrl: string): QRLInternal { assertDefined(backChannel, 'Missing QRL_RUNTIME_CHUNK'); qrlRef = backChannel.get(symbol); } - return createQRL(chunk, symbol, qrlRef, null, captureIds, null, null); + return createQRL(chunk, symbol, qrlRef, null, captureIds, null); } export function inflateQRL(container: DeserializeContainer, qrl: QRLInternal) { @@ -1313,15 +1313,12 @@ export function qrlToString( let symbol = value.$symbol$; let chunk = value.$chunk$; - const refSymbol = value.$refSymbol$ ?? symbol; const platform = getPlatform(); if (platform) { - const result = platform.chunkForSymbol(refSymbol, chunk, value.dev?.file); + const result = platform.chunkForSymbol(symbol, chunk, value.dev?.file); if (result) { chunk = result[1]; - if (!value.$refSymbol$) { - symbol = result[0]; - } + symbol = result[0]; } } diff --git a/packages/qwik/src/core/shared/shared-serialization.unit.ts b/packages/qwik/src/core/shared/shared-serialization.unit.ts index d4bb7812df8..b72f5040c19 100644 --- a/packages/qwik/src/core/shared/shared-serialization.unit.ts +++ b/packages/qwik/src/core/shared/shared-serialization.unit.ts @@ -732,10 +732,7 @@ describe('shared-serialization', () => { `); }); it('should dedupe function sub-data', async () => { - const objs = await serialize( - [shared1], - createQRL(null, 'foo', 123, null, null, [shared1], null) - ); + const objs = await serialize([shared1], createQRL(null, 'foo', 123, null, null, [shared1])); expect(dumpState(objs)).toMatchInlineSnapshot(` " 0 Array [ diff --git a/packages/qwik/src/core/use/use-task.ts b/packages/qwik/src/core/use/use-task.ts index 48a9b59eac3..5b0fa518331 100644 --- a/packages/qwik/src/core/use/use-task.ts +++ b/packages/qwik/src/core/use/use-task.ts @@ -275,15 +275,9 @@ export const useRunTask = ( }; const getTaskHandlerQrl = (task: Task): QRL<(ev: Event) => void> => { - const taskHandler = createQRL<(ev: Event) => void>( - null, - '_task', - scheduleTask, - null, - null, - [task], - null - ); + const taskHandler = createQRL<(ev: Event) => void>(null, '_task', scheduleTask, null, null, [ + task, + ]); return taskHandler; }; From a052f8fcb4a5a690353a0e148d5c4b6b09cf613b Mon Sep 17 00:00:00 2001 From: Wout Mertens Date: Sun, 19 Jan 2025 17:57:04 +0100 Subject: [PATCH 7/8] fix(core): schedule qrls instead of direct call --- .changeset/heavy-radios-dream.md | 5 ++ packages/qwik/handlers.mjs | 2 +- packages/qwik/src/core/api.md | 3 + packages/qwik/src/core/client/queue-qrl.ts | 27 +++++++ packages/qwik/src/core/client/vnode-diff.ts | 7 +- packages/qwik/src/core/internal.ts | 1 + packages/qwik/src/core/shared/error/error.ts | 4 +- packages/qwik/src/core/shared/scheduler.ts | 73 +++++++++++++------ .../src/core/shared/shared-serialization.ts | 1 + packages/qwik/src/core/ssr/ssr-render-jsx.ts | 19 ++++- .../qwik/src/core/tests/render-api.spec.tsx | 6 +- packages/qwik/src/optimizer/src/manifest.ts | 2 +- .../qwik/src/testing/rendering.unit-util.tsx | 9 ++- 13 files changed, 124 insertions(+), 35 deletions(-) create mode 100644 .changeset/heavy-radios-dream.md create mode 100644 packages/qwik/src/core/client/queue-qrl.ts diff --git a/.changeset/heavy-radios-dream.md b/.changeset/heavy-radios-dream.md new file mode 100644 index 00000000000..8db7e56e562 --- /dev/null +++ b/.changeset/heavy-radios-dream.md @@ -0,0 +1,5 @@ +--- +'@qwik.dev/core': patch +--- + +FIX: QRLs are now scheduled instead of directly executed by qwik-loader, so that they are executed in the right order. diff --git a/packages/qwik/handlers.mjs b/packages/qwik/handlers.mjs index de787e31b57..71aea661713 100644 --- a/packages/qwik/handlers.mjs +++ b/packages/qwik/handlers.mjs @@ -6,4 +6,4 @@ * * Make sure that these handlers are listed in manifest.ts */ -export { _task } from '@qwik.dev/core'; +export { _run, _task } from '@qwik.dev/core'; diff --git a/packages/qwik/src/core/api.md b/packages/qwik/src/core/api.md index c8f59ea16c9..7bbda239ded 100644 --- a/packages/qwik/src/core/api.md +++ b/packages/qwik/src/core/api.md @@ -805,6 +805,9 @@ export type ResourceReturn = ResourcePending | ResourceResolved | Resou // @internal (undocumented) export const _restProps: (props: Record, omit: string[], target?: {}) => {}; +// @internal +export const _run: (...args: unknown[]) => ValueOrPromise; + // @internal export function _serialize(data: unknown[]): Promise; diff --git a/packages/qwik/src/core/client/queue-qrl.ts b/packages/qwik/src/core/client/queue-qrl.ts new file mode 100644 index 00000000000..bbcc149b45b --- /dev/null +++ b/packages/qwik/src/core/client/queue-qrl.ts @@ -0,0 +1,27 @@ +import { QError, qError } from '../shared/error/error'; +import type { QRLInternal } from '../shared/qrl/qrl-class'; +import { ChoreType } from '../shared/scheduler'; +import { getInvokeContext } from '../use/use-core'; +import { useLexicalScope } from '../use/use-lexical-scope.public'; +import { _getQContainerElement, getDomContainer } from './dom-container'; + +/** + * This is called by qwik-loader to schedule a QRL. It has to be synchronous. + * + * @internal + */ +export const queueQRL = (...args: unknown[]) => { + // This will already check container + const [runQrl] = useLexicalScope<[QRLInternal<(...args: unknown[]) => unknown>]>(); + const context = getInvokeContext(); + const el = context.$element$!; + const containerElement = _getQContainerElement(el) as HTMLElement; + const container = getDomContainer(containerElement); + + const scheduler = container.$scheduler$; + if (!scheduler) { + throw qError(QError.schedulerNotFound); + } + + return scheduler(ChoreType.RUN_QRL, null, runQrl, args); +}; diff --git a/packages/qwik/src/core/client/vnode-diff.ts b/packages/qwik/src/core/client/vnode-diff.ts index 7bb62f59edc..cab386f59dd 100644 --- a/packages/qwik/src/core/client/vnode-diff.ts +++ b/packages/qwik/src/core/client/vnode-diff.ts @@ -764,7 +764,12 @@ export const vnode_diff = ( let returnValue = false; qrls.flat(2).forEach((qrl) => { if (qrl) { - const value = qrl(event, element) as any; + const value = container.$scheduler$( + ChoreType.RUN_QRL, + null, + qrl as QRLInternal<(...args: unknown[]) => unknown>, + [event, element] + ) as unknown; returnValue = returnValue || value === true; } }); diff --git a/packages/qwik/src/core/internal.ts b/packages/qwik/src/core/internal.ts index e5947a25507..5aaf7cbb838 100644 --- a/packages/qwik/src/core/internal.ts +++ b/packages/qwik/src/core/internal.ts @@ -1,6 +1,7 @@ export { _noopQrl, _noopQrlDEV, _regSymbol } from './shared/qrl/qrl'; export { _walkJSX } from './ssr/ssr-render-jsx'; export { _SharedContainer } from './shared/shared-container'; +export { queueQRL as _run } from './client/queue-qrl'; export { scheduleTask as _task } from './use/use-task'; export { _wrapSignal, _wrapProp } from './signal/signal-utils'; export { _restProps } from './shared/utils/prop'; diff --git a/packages/qwik/src/core/shared/error/error.ts b/packages/qwik/src/core/shared/error/error.ts index 409a03042f4..116acfc959b 100644 --- a/packages/qwik/src/core/shared/error/error.ts +++ b/packages/qwik/src/core/shared/error/error.ts @@ -6,7 +6,7 @@ export const codeToText = (code: number, ...parts: any[]): string => { // Keep one error, one line to make it easier to search for the error message. const MAP = [ 'Error while serializing class or style attributes', // 0 - '', // 1 unused + 'Scheduler not found', // 1 '', // 2 unused 'Only primitive and object literals can be serialized. {{0}}', // 3 '', // 4 unused @@ -76,7 +76,7 @@ export const codeToText = (code: number, ...parts: any[]): string => { export const enum QError { stringifyClassOrStyle = 0, - UNUSED_1 = 1, + schedulerNotFound = 1, UNUSED_2 = 2, verifySerializable = 3, UNUSED_4 = 4, diff --git a/packages/qwik/src/core/shared/scheduler.ts b/packages/qwik/src/core/shared/scheduler.ts index 8cc3f594fc9..245b1225a71 100644 --- a/packages/qwik/src/core/shared/scheduler.ts +++ b/packages/qwik/src/core/shared/scheduler.ts @@ -122,18 +122,24 @@ export const enum ChoreType { /* order of elements (not encoded here) */ MICRO /* **************************** */ = 0b0000_1111, - /** Ensure tha the QRL promise is resolved before processing next chores in the queue */ + /** Ensure that the QRL promise is resolved before processing next chores in the queue */ QRL_RESOLVE /* ********************** */ = 0b0000_0001, - RESOURCE /* ************************* */ = 0b0000_0010, - TASK /* ***************************** */ = 0b0000_0011, - NODE_DIFF /* ************************ */ = 0b0000_0100, - NODE_PROP /* ************************ */ = 0b0000_0101, - COMPONENT_SSR /* ******************** */ = 0b0000_0110, - COMPONENT /* ************************ */ = 0b0000_0111, - RECOMPUTE_AND_SCHEDULE_EFFECTS /* *** */ = 0b0000_1000, + RUN_QRL, + RESOURCE, + TASK, + NODE_DIFF, + NODE_PROP, + COMPONENT_SSR, + COMPONENT, + RECOMPUTE_AND_SCHEDULE_EFFECTS, + + // Next macro level JOURNAL_FLUSH /* ******************** */ = 0b0001_0000, + // Next macro level VISIBLE /* ************************** */ = 0b0010_0000, + // Next macro level CLEANUP_VISIBLE /* ****************** */ = 0b0011_0000, + // Next macro level WAIT_FOR_ALL /* ********************* */ = 0b1111_1111, } @@ -202,6 +208,12 @@ export const createScheduler = ( type: ChoreType.TASK | ChoreType.VISIBLE | ChoreType.RESOURCE, task: Task ): ValueOrPromise; + function schedule( + type: ChoreType.RUN_QRL, + ignore: null, + target: QRLInternal<(...args: unknown[]) => unknown>, + args: unknown[] + ): ValueOrPromise; function schedule( type: ChoreType.COMPONENT, host: HostElement, @@ -234,7 +246,10 @@ export const createScheduler = ( targetOrQrl: ChoreTarget | string | null = null, payload: any = null ): ValueOrPromise { - const runLater: boolean = type !== ChoreType.WAIT_FOR_ALL && type !== ChoreType.COMPONENT_SSR; + const runLater: boolean = + type !== ChoreType.WAIT_FOR_ALL && + type !== ChoreType.COMPONENT_SSR && + type !== ChoreType.RUN_QRL; const isTask = type === ChoreType.TASK || type === ChoreType.VISIBLE || @@ -289,15 +304,19 @@ export const createScheduler = ( return runUptoChore.$promise$; } while (choreQueue.length) { - const nextChore = choreQueue.shift()!; + const nextChore = choreQueue[0]; const order = choreComparator(nextChore, runUptoChore, rootVNode); - if (order === null) { - continue; - } - if (order > 0) { + if (order !== null && order > 0) { // we have processed all of the chores up to and including the given chore. break; } + // We can take the chore out of the queue + choreQueue.shift(); + if (order === null) { + // There was an error with the chore. + DEBUG && debugTrace('skip chore', nextChore, currentChore, choreQueue); + continue; + } const isDeletedVNode = vNodeAlreadyDeleted(nextChore); if ( isDeletedVNode && @@ -364,6 +383,12 @@ export const createScheduler = ( const result = runResource(chore.$payload$ as ResourceDescriptor, container, host); returnValue = isDomContainer(container) ? null : result; break; + case ChoreType.RUN_QRL: + { + const fn = (chore.$target$ as QRLInternal<(...args: unknown[]) => unknown>).getFn(); + returnValue = fn(...(chore.$payload$ as unknown[])); + } + break; case ChoreType.TASK: returnValue = runTask(chore.$payload$ as Task, container, host); break; @@ -423,11 +448,11 @@ export const createScheduler = ( } } return maybeThenPassError(returnValue, (value) => { - DEBUG && debugTrace('execute.DONE', null, currentChore, choreQueue); if (currentChore) { currentChore.$executed$ = true; currentChore.$resolve$?.(value); } + DEBUG && debugTrace('execute.DONE', null, currentChore, choreQueue); currentChore = null; return (chore.$returnValue$ = value); }); @@ -476,7 +501,6 @@ function choreComparator(a: Chore, b: Chore, rootVNode: ElementVNode | null): nu const aHost = a.$host$; const bHost = b.$host$; - // QRL_RESOLVE does not have a host. if (aHost !== bHost && aHost !== null && bHost !== null) { if (vnode_isVNode(aHost) && vnode_isVNode(bHost)) { // we are running on the client. @@ -511,13 +535,13 @@ function choreComparator(a: Chore, b: Chore, rootVNode: ElementVNode | null): nu return idxDiff; } - // If the host is the same, we need to compare the target. + // If the host is the same (or missing), and the type is the same, we need to compare the target. if ( a.$target$ !== b.$target$ && - ((a.$type$ === ChoreType.QRL_RESOLVE && b.$type$ === ChoreType.QRL_RESOLVE) || - (a.$type$ === ChoreType.NODE_PROP && b.$type$ === ChoreType.NODE_PROP) || - (a.$type$ === ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS && - b.$type$ === ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS)) + (a.$type$ === ChoreType.QRL_RESOLVE || + a.$type$ === ChoreType.RUN_QRL || + a.$type$ === ChoreType.NODE_PROP || + a.$type$ === ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS) ) { // 1 means that we are going to process chores as FIFO return 1; @@ -571,6 +595,7 @@ function debugChoreToString(chore: Chore): string { ( { [ChoreType.QRL_RESOLVE]: 'QRL_RESOLVE', + [ChoreType.RUN_QRL]: 'RUN_QRL', [ChoreType.RESOURCE]: 'RESOURCE', [ChoreType.TASK]: 'TASK', [ChoreType.NODE_DIFF]: 'NODE_DIFF', @@ -586,7 +611,7 @@ function debugChoreToString(chore: Chore): string { )[chore.$type$] || 'UNKNOWN: ' + chore.$type$; const host = String(chore.$host$).replaceAll(/\n.*/gim, ''); const qrlTarget = (chore.$target$ as QRLInternal)?.$symbol$; - return `Chore(${type} ${chore.$type$ === ChoreType.QRL_RESOLVE ? qrlTarget : host} ${chore.$idx$})`; + return `Chore(${type} ${chore.$type$ === ChoreType.QRL_RESOLVE || chore.$type$ === ChoreType.RUN_QRL ? qrlTarget : host} ${chore.$idx$})`; } function debugTrace( @@ -603,7 +628,9 @@ function debugTrace( ); } if (currentChore) { - lines.push('running: ' + debugChoreToString(currentChore)); + lines.push( + `${currentChore.$executed$ ? ' done' : 'running'}: ` + debugChoreToString(currentChore) + ); } if (queue) { queue.forEach((chore, idx) => { diff --git a/packages/qwik/src/core/shared/shared-serialization.ts b/packages/qwik/src/core/shared/shared-serialization.ts index 66523cfa3b0..3c8f55b84c7 100644 --- a/packages/qwik/src/core/shared/shared-serialization.ts +++ b/packages/qwik/src/core/shared/shared-serialization.ts @@ -190,6 +190,7 @@ const inflate = ( switch (typeId) { case TypeIds.Object: // We use getters for making complex values lazy + // TODO scan the data for computeQRLs and schedule resolve chores for (let i = 0; i < (data as any[]).length; i += 4) { const key = deserializeData( container, diff --git a/packages/qwik/src/core/ssr/ssr-render-jsx.ts b/packages/qwik/src/core/ssr/ssr-render-jsx.ts index 354d9f0afcb..2d2351ae45a 100644 --- a/packages/qwik/src/core/ssr/ssr-render-jsx.ts +++ b/packages/qwik/src/core/ssr/ssr-render-jsx.ts @@ -1,6 +1,6 @@ import { isDev } from '@qwik.dev/core/build'; import { isQwikComponent } from '../shared/component.public'; -import { isQrl } from '../shared/qrl/qrl-class'; +import { createQRL, isQrl, type QRLInternal } from '../shared/qrl/qrl-class'; import type { QRL } from '../shared/qrl/qrl.public'; import { Fragment, directGetPropsProxyProp } from '../shared/jsx/jsx-runtime'; import { Slot } from '../shared/jsx/slot.public'; @@ -36,6 +36,7 @@ import { qInspector } from '../shared/utils/qdev'; import { serializeAttribute } from '../shared/utils/styles'; import { QError, qError } from '../shared/error/error'; import { getFileLocationFromJsx } from '../shared/utils/jsx-filename'; +import { queueQRL } from '../client/queue-qrl'; class ParentComponentData { constructor( @@ -484,12 +485,24 @@ function setEvent( const appendToValue = (valueToAppend: string) => { value = (value == null ? '' : value + '\n') + valueToAppend; }; + const getQrlString = (qrl: QRLInternal) => { + /** + * If there are captures we need to schedule so everything is executed in the right order + qrls + * are resolved. + * + * For internal qrls (starting with `_`) we assume that they do the right thing. + */ + if (!qrl.$symbol$.startsWith('_') && (qrl.$captureRef$ || qrl.$capture$)) { + qrl = createQRL(null, '_run', queueQRL, null, null, [qrl]); + } + return qrlToString(serializationCtx, qrl); + }; if (Array.isArray(qrls)) { for (let i = 0; i <= qrls.length; i++) { const qrl: unknown = qrls[i]; if (isQrl(qrl)) { - appendToValue(qrlToString(serializationCtx, qrl)); + appendToValue(getQrlString(qrl)); addQwikEventToSerializationContext(serializationCtx, key, qrl); } else if (qrl != null) { // nested arrays etc. @@ -500,7 +513,7 @@ function setEvent( } } } else if (isQrl(qrls)) { - value = qrlToString(serializationCtx, qrls); + value = getQrlString(qrls); addQwikEventToSerializationContext(serializationCtx, key, qrls); } diff --git a/packages/qwik/src/core/tests/render-api.spec.tsx b/packages/qwik/src/core/tests/render-api.spec.tsx index e787aadae2d..d3c081c2c0b 100644 --- a/packages/qwik/src/core/tests/render-api.spec.tsx +++ b/packages/qwik/src/core/tests/render-api.spec.tsx @@ -57,6 +57,9 @@ const mapping = { click: 'click.js', s_counter: 's_counter.js', s_click: 's_click.js', + // Our internal symbols + _run: 'core', + _task: 'core', }; const defaultManifest: QwikManifest = { @@ -1121,7 +1124,8 @@ describe('render api', () => { stream, streaming, }); - expect(stream.write).toHaveBeenCalledTimes(5); + // This can change when the size of the output changes + expect(stream.write).toHaveBeenCalledTimes(6); }); }); }); diff --git a/packages/qwik/src/optimizer/src/manifest.ts b/packages/qwik/src/optimizer/src/manifest.ts index c8a29264988..89cce7ebb48 100644 --- a/packages/qwik/src/optimizer/src/manifest.ts +++ b/packages/qwik/src/optimizer/src/manifest.ts @@ -4,7 +4,7 @@ import type { GlobalInjections, SegmentAnalysis, Path, QwikBundle, QwikManifest // The handlers that are exported by the core package // See handlers.mjs -const extraSymbols = new Set(['_task']); +const extraSymbols = new Set(['_run', '_task']); // This is just the initial prioritization of the symbols and entries // at build time so there's less work during each SSR. However, SSR should diff --git a/packages/qwik/src/testing/rendering.unit-util.tsx b/packages/qwik/src/testing/rendering.unit-util.tsx index f59e43d43a2..b85d7edff0c 100644 --- a/packages/qwik/src/testing/rendering.unit-util.tsx +++ b/packages/qwik/src/testing/rendering.unit-util.tsx @@ -271,14 +271,17 @@ function renderStyles(getStyles: () => Record) { }); } -export async function rerenderComponent(element: HTMLElement) { +export async function rerenderComponent(element: HTMLElement, flush?: boolean) { const container = _getDomContainer(element); const vElement = vnode_locate(container.rootVNode, element); const host = getHostVNode(vElement) as HostElement; const qrl = container.getHostProp>>(host, OnRenderProp)!; const props = container.getHostProp(host, ELEMENT_PROPS); - await container.$scheduler$(ChoreType.COMPONENT, host, qrl, props); - await getTestPlatform().flush(); + container.$scheduler$(ChoreType.COMPONENT, host, qrl, props); + if (flush) { + // Note that this can deadlock + await getTestPlatform().flush(); + } } function getHostVNode(vElement: _VNode | null) { From ec7e9b2af21f0b7ad4f1fec0bb3db11834a2ba84 Mon Sep 17 00:00:00 2001 From: Wout Mertens Date: Tue, 21 Jan 2025 18:49:06 +0100 Subject: [PATCH 8/8] fix(scheduler+ssr): correct order and draining --- packages/qwik/src/core/shared/error/error.ts | 18 +- packages/qwik/src/core/shared/scheduler.ts | 292 +++++++++--------- packages/qwik/src/core/signal/signal.ts | 8 +- packages/qwik/src/core/ssr/ssr-render-jsx.ts | 69 +---- packages/qwik/src/core/ssr/ssr-types.ts | 1 + .../qwik/src/core/tests/use-task.spec.tsx | 35 +-- packages/qwik/src/core/use/use-task.ts | 53 +--- .../qwik/src/core/use/use-visible-task.ts | 22 +- packages/qwik/src/server/ssr-container.ts | 10 +- .../e2e/src/components/computed/computed.tsx | 1 - 10 files changed, 214 insertions(+), 295 deletions(-) diff --git a/packages/qwik/src/core/shared/error/error.ts b/packages/qwik/src/core/shared/error/error.ts index 116acfc959b..92254f548c1 100644 --- a/packages/qwik/src/core/shared/error/error.ts +++ b/packages/qwik/src/core/shared/error/error.ts @@ -7,7 +7,7 @@ export const codeToText = (code: number, ...parts: any[]): string => { const MAP = [ 'Error while serializing class or style attributes', // 0 'Scheduler not found', // 1 - '', // 2 unused + 'track() received object, without prop to track', // 2 'Only primitive and object literals can be serialized. {{0}}', // 3 '', // 4 unused 'You can render over a existing q:container. Skipping render().', // 5 @@ -50,12 +50,11 @@ export const codeToText = (code: number, ...parts: any[]): string => { "Element must have 'q:container' attribute.", // 42 'Unknown vnode type {{0}}.', // 43 'Materialize error: missing element: {{0}} {{1}} {{2}}', // 44 - 'Cannot coerce a Signal, use `.value` instead', // 46 - 'useComputedSignal$ QRL {{0}} {{1}} returned a Promise', // 47 - 'ComputedSignal is read-only', // 48 - 'WrappedSignal is read-only', // 49 - 'SsrError: Promises not expected here.', // 50 - 'Attribute value is unsafe for SSR', // 51 + 'Cannot coerce a Signal, use `.value` instead', // 45 + 'useComputedSignal$ QRL {{0}} {{1}} returned a Promise', // 46 + 'ComputedSignal is read-only', // 47 + 'WrappedSignal is read-only', // 48 + 'Attribute value is unsafe for SSR', // 49 ]; let text = MAP[code] ?? ''; if (parts.length) { @@ -77,7 +76,7 @@ export const codeToText = (code: number, ...parts: any[]): string => { export const enum QError { stringifyClassOrStyle = 0, schedulerNotFound = 1, - UNUSED_2 = 2, + trackObjectWithoutProp = 2, verifySerializable = 3, UNUSED_4 = 4, cannotRenderOverExistingContainer = 5, @@ -124,8 +123,7 @@ export const enum QError { computedNotSync = 46, computedReadOnly = 47, wrappedReadOnly = 48, - promisesNotExpected = 49, - unsafeAttr = 50, + unsafeAttr = 49, } export const qError = (code: number, errorMessageArgs: any[] = []): Error => { diff --git a/packages/qwik/src/core/shared/scheduler.ts b/packages/qwik/src/core/shared/scheduler.ts index 245b1225a71..9537399b08b 100644 --- a/packages/qwik/src/core/shared/scheduler.ts +++ b/packages/qwik/src/core/shared/scheduler.ts @@ -80,15 +80,7 @@ * declaration order within component. */ -import { assertEqual } from './error/assert'; -import type { QRLInternal } from './qrl/qrl-class'; -import type { JSXOutput } from './jsx/types/jsx-node'; -import { Task, TaskFlags, cleanupTask, runTask, type TaskFn } from '../use/use-task'; -import { runResource, type ResourceDescriptor } from '../use/use-resource'; -import { logWarn } from './utils/log'; -import { isPromise, maybeThenPassError, retryOnPromise, safeCall } from './utils/promises'; -import type { ValueOrPromise } from './utils/types'; -import { isDomContainer } from '../client/dom-container'; +import { isDomContainer, type DomContainer } from '../client/dom-container'; import { ElementVNodeProps, VNodeFlags, @@ -97,21 +89,28 @@ import { type ElementVNode, type VirtualVNode, } from '../client/types'; -import { vnode_isVNode, vnode_setAttr, VNodeJournalOpCode } from '../client/vnode'; +import { VNodeJournalOpCode, vnode_isVNode, vnode_setAttr } from '../client/vnode'; import { vnode_diff } from '../client/vnode-diff'; -import { executeComponent } from './component-execution'; -import type { Container, HostElement } from './types'; +import { triggerEffects, type ComputedSignal, type WrappedSignal } from '../signal/signal'; import { isSignal, type Signal } from '../signal/signal.public'; -import { type DomContainer } from '../client/dom-container'; -import { serializeAttribute } from './utils/styles'; +import type { TargetType } from '../signal/store'; +import type { ISsrNode } from '../ssr/ssr-types'; +import { runResource, type ResourceDescriptor } from '../use/use-resource'; +import { Task, TaskFlags, cleanupTask, runTask, type TaskFn } from '../use/use-task'; +import { executeComponent } from './component-execution'; import type { OnRenderFn } from './component.public'; +import { assertEqual } from './error/assert'; import type { Props } from './jsx/jsx-runtime'; +import type { JSXOutput } from './jsx/types/jsx-node'; +import type { QRLInternal } from './qrl/qrl-class'; +import { ssrNodeDocumentPosition, vnode_documentPosition } from './scheduler-document-position'; +import type { Container, HostElement } from './types'; +import { logWarn } from './utils/log'; import { QScopedStyle } from './utils/markers'; +import { isPromise, retryOnPromise, safeCall } from './utils/promises'; import { addComponentStylePrefix } from './utils/scoped-styles'; -import { type WrappedSignal, type ComputedSignal, triggerEffects } from '../signal/signal'; -import type { TargetType } from '../signal/store'; -import { ssrNodeDocumentPosition, vnode_documentPosition } from './scheduler-document-position'; -import type { ISsrNode } from '../ssr/ssr-types'; +import { serializeAttribute } from './utils/styles'; +import type { ValueOrPromise } from './utils/types'; // Turn this on to get debug output of what the scheduler is doing. const DEBUG: boolean = false; @@ -150,7 +149,7 @@ export interface Chore { $target$: ChoreTarget | null; $payload$: unknown; $resolve$: (value: any) => void; - $promise$: Promise; + $promise$?: Promise; $returnValue$: any; $executed$: boolean; } @@ -168,6 +167,10 @@ export type Scheduler = ReturnType; type ChoreTarget = HostElement | QRLInternal<(...args: unknown[]) => unknown> | Signal | TargetType; +const getPromise = (chore: Chore) => { + return (chore.$promise$ ||= new Promise((resolve) => (chore.$resolve$ = resolve))); +}; + export const createScheduler = ( container: Container, scheduleDrain: () => void, @@ -215,13 +218,7 @@ export const createScheduler = ( args: unknown[] ): ValueOrPromise; function schedule( - type: ChoreType.COMPONENT, - host: HostElement, - qrl: QRLInternal>, - props: Props | null - ): ValueOrPromise; - function schedule( - type: ChoreType.COMPONENT_SSR, + type: ChoreType.COMPONENT | ChoreType.COMPONENT_SSR, host: HostElement, qrl: QRLInternal>, props: Props | null @@ -273,7 +270,6 @@ export const createScheduler = ( $returnValue$: null, $executed$: false, }; - chore.$promise$ = new Promise((resolve) => (chore.$resolve$ = resolve)); DEBUG && debugTrace('schedule', chore, currentChore, choreQueue); chore = sortedInsert(choreQueue, chore, (container as DomContainer).rootVNode || null); if (!journalFlushScheduled && runLater) { @@ -283,9 +279,9 @@ export const createScheduler = ( scheduleDrain(); } if (runLater) { - return chore.$promise$; + return getPromise(chore); } else { - return drainUpTo(chore, (container as DomContainer).rootVNode || null); + return drainUpTo(chore); } } @@ -294,29 +290,13 @@ export const createScheduler = ( * * @param runUptoChore */ - function drainUpTo(runUptoChore: Chore, rootVNode: ElementVNode | null): ValueOrPromise { - // If it already ran, it's not in the queue - if (runUptoChore.$executed$) { - return runUptoChore.$returnValue$; - } - if (currentChore) { - // Already running chore, which means we're waiting for async completion - return runUptoChore.$promise$; - } + function drainUpTo(runUptoChore: Chore): ValueOrPromise { while (choreQueue.length) { - const nextChore = choreQueue[0]; - const order = choreComparator(nextChore, runUptoChore, rootVNode); - if (order !== null && order > 0) { - // we have processed all of the chores up to and including the given chore. - break; - } - // We can take the chore out of the queue - choreQueue.shift(); - if (order === null) { - // There was an error with the chore. - DEBUG && debugTrace('skip chore', nextChore, currentChore, choreQueue); - continue; + if (currentChore) { + // Already running chore, which means we're waiting for async completion + return getPromise(currentChore).then(() => drainUpTo(runUptoChore)); } + const nextChore = choreQueue.shift()!; const isDeletedVNode = vNodeAlreadyDeleted(nextChore); if ( isDeletedVNode && @@ -326,13 +306,12 @@ export const createScheduler = ( DEBUG && debugTrace('skip chore', nextChore, currentChore, choreQueue); continue; } - const returnValue = executeChore(nextChore); - if (isPromise(returnValue)) { - const promise = returnValue.then(() => drainUpTo(runUptoChore, rootVNode)); - return promise; + executeChore(nextChore); + if (nextChore === runUptoChore) { + break; } } - return runUptoChore.$returnValue$; + return runUptoChore.$executed$ ? runUptoChore.$returnValue$ : getPromise(runUptoChore); } function executeChore(chore: Chore): ValueOrPromise { @@ -376,12 +355,18 @@ export const createScheduler = ( ); break; case ChoreType.RESOURCE: - // Don't await the return value of the resource, because async resources should not be awaited. - // The reason for this is that we should be able to update for example a node with loading - // text. If we await the resource, the loading text will not be displayed until the resource - // is loaded. - const result = runResource(chore.$payload$ as ResourceDescriptor, container, host); - returnValue = isDomContainer(container) ? null : result; + { + // Don't await the return value of the resource, because async resources should not be awaited. + // The reason for this is that we should be able to update for example a node with loading + // text. If we await the resource, the loading text will not be displayed until the resource + // is loaded. + const result = runResource( + chore.$payload$ as ResourceDescriptor, + container, + host + ); + returnValue = isDomContainer(container) ? null : result; + } break; case ChoreType.RUN_QRL: { @@ -390,72 +375,88 @@ export const createScheduler = ( } break; case ChoreType.TASK: - returnValue = runTask(chore.$payload$ as Task, container, host); - break; case ChoreType.VISIBLE: returnValue = runTask(chore.$payload$ as Task, container, host); break; case ChoreType.CLEANUP_VISIBLE: - const task = chore.$payload$ as Task; - cleanupTask(task); + { + const task = chore.$payload$ as Task; + cleanupTask(task); + } break; case ChoreType.NODE_DIFF: - const parentVirtualNode = chore.$target$ as VirtualVNode; - let jsx = chore.$payload$ as JSXOutput; - if (isSignal(jsx)) { - jsx = jsx.value as any; + { + const parentVirtualNode = chore.$target$ as VirtualVNode; + let jsx = chore.$payload$ as JSXOutput; + if (isSignal(jsx)) { + jsx = jsx.value as any; + } + returnValue = retryOnPromise(() => + vnode_diff(container as DomContainer, jsx, parentVirtualNode, null) + ); } - returnValue = retryOnPromise(() => - vnode_diff(container as DomContainer, jsx, parentVirtualNode, null) - ); break; case ChoreType.NODE_PROP: - const virtualNode = chore.$host$ as unknown as ElementVNode; - const payload = chore.$payload$ as NodePropPayload; - let value: Signal | string = payload.$value$; - if (isSignal(value)) { - value = value.value as any; - } - const isConst = payload.$isConst$; - const journal = (container as DomContainer).$journal$; - const property = chore.$idx$ as string; - const serializedValue = serializeAttribute(property, value, payload.$scopedStyleIdPrefix$); - if (isConst) { - const element = virtualNode[ElementVNodeProps.element] as Element; - journal.push(VNodeJournalOpCode.SetAttribute, element, property, serializedValue); - } else { - vnode_setAttr(journal, virtualNode, property, serializedValue); + { + const virtualNode = chore.$host$ as unknown as ElementVNode; + const payload = chore.$payload$ as NodePropPayload; + let value: Signal | string = payload.$value$; + if (isSignal(value)) { + value = value.value as any; + } + const isConst = payload.$isConst$; + const journal = (container as DomContainer).$journal$; + const property = chore.$idx$ as string; + const serializedValue = serializeAttribute( + property, + value, + payload.$scopedStyleIdPrefix$ + ); + if (isConst) { + const element = virtualNode[ElementVNodeProps.element] as Element; + journal.push(VNodeJournalOpCode.SetAttribute, element, property, serializedValue); + } else { + vnode_setAttr(journal, virtualNode, property, serializedValue); + } } break; case ChoreType.QRL_RESOLVE: { - const target = chore.$target$ as QRLInternal; - returnValue = !target.resolved ? target.resolve() : null; + { + const target = chore.$target$ as QRLInternal; + returnValue = !target.resolved ? target.resolve() : null; + } break; } case ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS: { - const target = chore.$target$ as ComputedSignal | WrappedSignal; - const forceRunEffects = target.$forceRunEffects$; - target.$forceRunEffects$ = false; - if (!target.$effects$?.length) { - break; - } - returnValue = retryOnPromise(() => { - if (target.$computeIfNeeded$() || forceRunEffects) { - triggerEffects(container, target, target.$effects$); + { + const target = chore.$target$ as ComputedSignal | WrappedSignal; + const forceRunEffects = target.$forceRunEffects$; + target.$forceRunEffects$ = false; + if (!target.$effects$?.length) { + break; } - }); + returnValue = retryOnPromise(() => { + if (target.$computeIfNeeded$() || forceRunEffects) { + triggerEffects(container, target, target.$effects$); + } + }); + } break; } } - return maybeThenPassError(returnValue, (value) => { - if (currentChore) { - currentChore.$executed$ = true; - currentChore.$resolve$?.(value); - } - DEBUG && debugTrace('execute.DONE', null, currentChore, choreQueue); + const after = (value: any) => { currentChore = null; - return (chore.$returnValue$ = value); - }); + chore.$executed$ = true; + chore.$returnValue$ = value; + DEBUG && debugTrace('execute.DONE', null, chore, choreQueue); + chore.$resolve$?.(value); + return value; + }; + if (isPromise(returnValue)) { + chore.$promise$ = returnValue; + return returnValue.then(after); + } + return after(returnValue); } }; @@ -496,58 +497,51 @@ function choreComparator(a: Chore, b: Chore, rootVNode: ElementVNode | null): nu return macroTypeDiff; } - // JOURNAL_FLUSH does not have a host or $idx$, so we can't compare it. - if (a.$type$ !== ChoreType.JOURNAL_FLUSH) { - const aHost = a.$host$; - const bHost = b.$host$; - - if (aHost !== bHost && aHost !== null && bHost !== null) { - if (vnode_isVNode(aHost) && vnode_isVNode(bHost)) { - // we are running on the client. - const hostDiff = vnode_documentPosition(aHost, bHost, rootVNode); - if (hostDiff !== 0) { - return hostDiff; - } - } else { - // we are running on the server. - // On server we can't schedule task for a different host! - // Server is SSR, and therefore scheduling for anything but the current host - // implies that things need to be re-run nad that is not supported because of streaming. - const errorMessage = `SERVER: during HTML streaming, re-running tasks on a different host is not allowed. + const aHost = a.$host$; + const bHost = b.$host$; + + if (aHost !== bHost && aHost !== null && bHost !== null) { + if (vnode_isVNode(aHost) && vnode_isVNode(bHost)) { + // we are running on the client. + const hostDiff = vnode_documentPosition(aHost, bHost, rootVNode); + if (hostDiff !== 0) { + return hostDiff; + } + } else { + // we are running on the server. + // On server we can't schedule task for a different host! + // Server is SSR, and therefore scheduling for anything but the current host + // implies that things need to be re-run nad that is not supported because of streaming. + const errorMessage = `SERVER: during HTML streaming, re-running tasks on a different host is not allowed. You are attempting to change a state that has already been streamed to the client. This can lead to inconsistencies between Server-Side Rendering (SSR) and Client-Side Rendering (CSR). Problematic Node: ${aHost.toString()}`; - logWarn(errorMessage); - const hostDiff = ssrNodeDocumentPosition(aHost as ISsrNode, bHost as ISsrNode); - if (hostDiff !== 0) { - return hostDiff; - } + logWarn(errorMessage); + const hostDiff = ssrNodeDocumentPosition(aHost as ISsrNode, bHost as ISsrNode); + if (hostDiff !== 0) { + return hostDiff; } } + } - const microTypeDiff = (a.$type$ & ChoreType.MICRO) - (b.$type$ & ChoreType.MICRO); - if (microTypeDiff !== 0) { - return microTypeDiff; - } + const microTypeDiff = (a.$type$ & ChoreType.MICRO) - (b.$type$ & ChoreType.MICRO); + if (microTypeDiff !== 0) { + return microTypeDiff; + } + // types are the same - const idxDiff = toNumber(a.$idx$) - toNumber(b.$idx$); - if (idxDiff !== 0) { - return idxDiff; - } + const idxDiff = toNumber(a.$idx$) - toNumber(b.$idx$); + if (idxDiff !== 0) { + return idxDiff; + } - // If the host is the same (or missing), and the type is the same, we need to compare the target. - if ( - a.$target$ !== b.$target$ && - (a.$type$ === ChoreType.QRL_RESOLVE || - a.$type$ === ChoreType.RUN_QRL || - a.$type$ === ChoreType.NODE_PROP || - a.$type$ === ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS) - ) { - // 1 means that we are going to process chores as FIFO - return 1; - } + // If the host is the same (or missing), and the type is the same, we need to compare the target. + if (a.$target$ !== b.$target$ || a.$payload$ !== b.$payload$) { + // 1 means that we are going to process chores as FIFO + return 1; } + // The chorse are the same and will run only once return 0; } diff --git a/packages/qwik/src/core/signal/signal.ts b/packages/qwik/src/core/signal/signal.ts index 9cfdf15d09d..f0d7793d2a0 100644 --- a/packages/qwik/src/core/signal/signal.ts +++ b/packages/qwik/src/core/signal/signal.ts @@ -32,6 +32,7 @@ import type { Props } from '../shared/jsx/jsx-runtime'; import type { OnRenderFn } from '../shared/component.public'; import { NEEDS_COMPUTATION } from './flags'; import { QError, qError } from '../shared/error/error'; +import { isDomContainer } from '../client/dom-container'; const DEBUG = false; @@ -350,7 +351,12 @@ export const triggerEffects = ( const qrl = container.getHostProp>>(host, OnRenderProp); assertDefined(qrl, 'Component must have QRL'); const props = container.getHostProp(host, ELEMENT_PROPS); - container.$scheduler$(ChoreType.COMPONENT, host, qrl, props); + container.$scheduler$( + isDomContainer(container) ? ChoreType.COMPONENT : ChoreType.COMPONENT_SSR, + host, + qrl, + props + ); } else if (property === EffectProperty.VNODE) { const host: HostElement = effect as any; const target = host; diff --git a/packages/qwik/src/core/ssr/ssr-render-jsx.ts b/packages/qwik/src/core/ssr/ssr-render-jsx.ts index 2d2351ae45a..6a99f3f3e88 100644 --- a/packages/qwik/src/core/ssr/ssr-render-jsx.ts +++ b/packages/qwik/src/core/ssr/ssr-render-jsx.ts @@ -34,9 +34,9 @@ import { applyInlineComponent, applyQwikComponentBody } from './ssr-render-compo import type { ISsrComponentFrame, ISsrNode, SSRContainer, SsrAttrs } from './ssr-types'; import { qInspector } from '../shared/utils/qdev'; import { serializeAttribute } from '../shared/utils/styles'; -import { QError, qError } from '../shared/error/error'; import { getFileLocationFromJsx } from '../shared/utils/jsx-filename'; import { queueQRL } from '../client/queue-qrl'; +import { ChoreType } from '../shared/scheduler'; class ParentComponentData { constructor( @@ -50,50 +50,17 @@ type StackValue = ValueOrPromise< >; /** @internal */ -export function _walkJSX( +export async function _walkJSX( ssr: SSRContainer, value: JSXOutput, options: { - allowPromises: true; currentStyleScoped: string | null; parentComponentFrame: ISsrComponentFrame | null; } -): ValueOrPromise; -/** @internal */ -export function _walkJSX( - ssr: SSRContainer, - value: JSXOutput, - options: { - allowPromises: false; - currentStyleScoped: string | null; - parentComponentFrame: ISsrComponentFrame | null; - } -): false; -/** @internal */ -export function _walkJSX( - ssr: SSRContainer, - value: JSXOutput, - options: { - allowPromises: boolean; - currentStyleScoped: string | null; - parentComponentFrame: ISsrComponentFrame | null; - } -): ValueOrPromise | false { +): Promise { const stack: StackValue[] = [value]; - let resolveDrain: () => void; - let rejectDrain: (reason: any) => void; - const drained = - options.allowPromises && - new Promise((res, rej) => { - resolveDrain = res; - rejectDrain = rej; - }); const enqueue = (value: StackValue) => stack.push(value); - const resolveValue = (value: JSXOutput) => { - stack.push(value); - drain(); - }; - const drain = (): void => { + const drain = async (): Promise => { while (stack.length) { const value = stack.pop(); if (value instanceof ParentComponentData) { @@ -102,33 +69,23 @@ export function _walkJSX( continue; } else if (typeof value === 'function') { if (value === Promise) { - if (!options.allowPromises) { - throw qError(QError.promisesNotExpected); - } - (stack.pop() as Promise).then(resolveValue, rejectDrain); - return; - } - const waitOn = (value as StackFn).apply(ssr); - if (waitOn) { - if (!options.allowPromises) { - throw qError(QError.promisesNotExpected); - } - waitOn.then(drain, rejectDrain); - return; + stack.push(await (stack.pop() as Promise)); + continue; } + await (value as StackFn).apply(ssr); continue; } processJSXNode(ssr, enqueue, value as JSXOutput, { styleScoped: options.currentStyleScoped, parentComponentFrame: options.parentComponentFrame, }); - } - if (stack.length === 0 && options.allowPromises) { - resolveDrain(); + if (ssr.$hasChores$) { + ssr.$hasChores$ = false; + await ssr.$scheduler$(ChoreType.WAIT_FOR_ALL); + } } }; - drain(); - return drained; + await drain(); } function processJSXNode( @@ -169,7 +126,6 @@ function processJSXNode( enqueue(async () => { for await (const chunk of value) { await _walkJSX(ssr, chunk as JSXOutput, { - allowPromises: true, currentStyleScoped: options.styleScoped, parentComponentFrame: options.parentComponentFrame, }); @@ -276,7 +232,6 @@ function processJSXNode( value = generator({ async write(chunk) { await _walkJSX(ssr, chunk as JSXOutput, { - allowPromises: true, currentStyleScoped: options.styleScoped, parentComponentFrame: options.parentComponentFrame, }); diff --git a/packages/qwik/src/core/ssr/ssr-types.ts b/packages/qwik/src/core/ssr/ssr-types.ts index 8bc7d51bbc9..c4b2c05552b 100644 --- a/packages/qwik/src/core/ssr/ssr-types.ts +++ b/packages/qwik/src/core/ssr/ssr-types.ts @@ -55,6 +55,7 @@ export interface ISsrComponentFrame { export type SymbolToChunkResolver = (symbol: string) => string; export interface SSRContainer extends Container { + $hasChores$: boolean; readonly tag: string; readonly writer: StreamWriter; readonly prefetchResources: PrefetchResource[]; diff --git a/packages/qwik/src/core/tests/use-task.spec.tsx b/packages/qwik/src/core/tests/use-task.spec.tsx index 891f06f8f93..a7b1cb62e43 100644 --- a/packages/qwik/src/core/tests/use-task.spec.tsx +++ b/packages/qwik/src/core/tests/use-task.spec.tsx @@ -55,7 +55,7 @@ describe.each([ }); const { vNode } = await render(, { debug }); - expect(log).toEqual(['Counter', 'task', 'resolved', 'Counter', 'render']); + expect(log).toEqual(['Counter', 'render', 'task', 'resolved']); expect(vNode).toMatchVDOM( @@ -98,17 +98,17 @@ describe.each([ return OK; }); - try { + if (render === ssrRenderToDom) { + await expect(() => render(, { debug })).rejects.toBe(error); + } else { await render( , { debug } ); - expect(ErrorProvider.error).toBe(render === domRender ? error : null); - } catch (e) { - expect(render).toBe(ssrRenderToDom); - expect(e).toBe(error); + // dom render does not throw errors + expect(ErrorProvider.error).toBe(error); } }); it('should not run next task until previous async task is finished', async () => { @@ -133,16 +133,7 @@ describe.each([ }); const { vNode } = await render(, { debug }); - expect(log).toEqual([ - 'Counter', - '1:task', - '1:resolved', - 'Counter', - '2:task', - '2:resolved', - 'Counter', - 'render', - ]); + expect(log).toEqual(['Counter', 'render', '1:task', '1:resolved', '2:task', '2:resolved']); expect(vNode).toMatchVDOM( @@ -312,6 +303,7 @@ describe.each([ (globalThis as any).log = [] as string[]; const Counter = component$(() => { const store = useStore({ count: 1, double: 0, quadruple: 0 }); + // Quadruple runs first, but will run again after double is updated useTask$(({ track }) => { (globalThis as any).log.push('quadruple'); const trackingValue = track(store, 'double') * 2; @@ -330,7 +322,7 @@ describe.each([ }); const { vNode, document } = await render(, { debug }); - expect((globalThis as any).log).toEqual(['quadruple', 'double', 'Counter', 'quadruple']); + expect((globalThis as any).log).toEqual(['Counter', 'quadruple', 'double', 'quadruple']); expect(vNode).toMatchVDOM(