diff --git a/packages/docs/src/routes/api/qwik/api.json b/packages/docs/src/routes/api/qwik/api.json index 58922e8b6fb..8fd61421054 100644 --- a/packages/docs/src/routes/api/qwik/api.json +++ b/packages/docs/src/routes/api/qwik/api.json @@ -520,6 +520,20 @@ "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task.ts", "mdFile": "qwik.computedfn.md" }, + { + "name": "ComputedSignal", + "id": "computedsignal", + "hierarchy": [ + { + "name": "ComputedSignal", + "id": "computedsignal" + } + ], + "kind": "Interface", + "content": "```typescript\nexport interface ComputedSignal extends ReadonlySignal \n```\n**Extends:** [ReadonlySignal](#readonlysignal)<T>\n\n\n\n\n
\n\nMethod\n\n\n\n\nDescription\n\n\n
\n\n[force()](#computedsignal-force)\n\n\n\n\nUse this to force recalculation and running subscribers, for example when the calculated value mutates but remains the same object. Useful for third-party libraries.\n\n\n
", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/v2/signal/v2-signal.public.ts", + "mdFile": "qwik.computedsignal.md" + }, { "name": "ContextId", "id": "contextid", @@ -562,6 +576,34 @@ "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/render/jsx/types/jsx-qwik-attributes.ts", "mdFile": "qwik.correctedtoggleevent.md" }, + { + "name": "createComputed$", + "id": "createcomputed_", + "hierarchy": [ + { + "name": "createComputed$", + "id": "createcomputed_" + } + ], + "kind": "Function", + "content": "```typescript\ncreateComputed$: (qrl: () => T) => ComputedSignal\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nqrl\n\n\n\n\n() => T\n\n\n\n\n\n
\n**Returns:**\n\n[ComputedSignal](#computedsignal)<T>", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/v2/signal/v2-signal.public.ts", + "mdFile": "qwik.createcomputed_.md" + }, + { + "name": "createComputedQrl", + "id": "createcomputedqrl", + "hierarchy": [ + { + "name": "createComputedQrl", + "id": "createcomputedqrl" + } + ], + "kind": "Function", + "content": "```typescript\ncreateComputedQrl: (qrl: QRL<() => T>) => ComputedSignal\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nqrl\n\n\n\n\n[QRL](#qrl)<() => T>\n\n\n\n\n\n
\n**Returns:**\n\n[ComputedSignal](#computedsignal)<T>", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/v2/signal/v2-signal.public.ts", + "mdFile": "qwik.createcomputedqrl.md" + }, { "name": "createContextId", "id": "createcontextid", @@ -586,8 +628,8 @@ } ], "kind": "Variable", - "content": "> Warning: This API is now obsolete.\n> \n> This is a technology preview\n> \n\nCreates a signal.\n\nIf the initial state is a function, the function is invoked to calculate the actual initial state.\n\n\n```typescript\ncreateSignal: UseSignal\n```", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-signal.ts", + "content": "```typescript\ncreateSignal: {\n (): Signal;\n (value: T): Signal;\n}\n```", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/v2/signal/v2-signal.public.ts", "mdFile": "qwik.createsignal.md" }, { @@ -684,7 +726,7 @@ } ], "kind": "Interface", - "content": "The Qwik-specific attributes that DOM elements accept\n\n\n```typescript\nexport interface DOMAttributes extends DOMAttributesBase, QwikEvents \n```\n**Extends:** DOMAttributesBase<EL>, QwikEvents<EL>\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[class?](#)\n\n\n\n\n\n\n\n[ClassList](#classlist) \\| [Signal](#signal)<[ClassList](#classlist)> \\| undefined\n\n\n\n\n_(Optional)_\n\n\n
", + "content": "The Qwik-specific attributes that DOM elements accept\n\n\n```typescript\nexport interface DOMAttributes extends DOMAttributesBase, QwikEvents \n```\n**Extends:** DOMAttributesBase<EL>, QwikEvents<EL>\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[class?](#)\n\n\n\n\n\n\n\n[ClassList](#classlist) \\| Signal<[ClassList](#classlist)> \\| undefined\n\n\n\n\n_(Optional)_\n\n\n
", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/render/jsx/types/jsx-qwik-attributes.ts", "mdFile": "qwik.domattributes.md" }, @@ -791,8 +833,8 @@ } ], "kind": "Function", - "content": "```typescript\nevent$: (qrl: T) => QRL\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nqrl\n\n\n\n\nT\n\n\n\n\n\n
\n**Returns:**\n\n[QRL](#qrl)<T>", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/qrl/qrl.public.ts", + "content": "```typescript\nevent$: (qrl: T) => import(\"./qrl.public\").QRL\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nqrl\n\n\n\n\nT\n\n\n\n\n\n
\n**Returns:**\n\nimport(\"./qrl.public\").[QRL](#qrl)<T>", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/qrl/qrl.public.dollar.ts", "mdFile": "qwik.event_.md" }, { @@ -837,6 +879,23 @@ "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/render/jsx/types/jsx-generated.ts", "mdFile": "qwik.fieldsethtmlattributes.md" }, + { + "name": "force", + "id": "computedsignal-force", + "hierarchy": [ + { + "name": "ComputedSignal", + "id": "computedsignal-force" + }, + { + "name": "force", + "id": "computedsignal-force" + } + ], + "kind": "MethodSignature", + "content": "Use this to force recalculation and running subscribers, for example when the calculated value mutates but remains the same object. Useful for third-party libraries.\n\n\n```typescript\nforce(): void;\n```\n**Returns:**\n\nvoid", + "mdFile": "qwik.computedsignal.force.md" + }, { "name": "FormHTMLAttributes", "id": "formhtmlattributes", @@ -1158,8 +1217,8 @@ } ], "kind": "Function", - "content": "Checks if a given object is a `Signal`.\n\n\n```typescript\nisSignal: (obj: any) => obj is Signal\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nobj\n\n\n\n\nany\n\n\n\n\nThe object to check if `Signal`.\n\n\n
\n**Returns:**\n\nobj is [Signal](#signal)<T>\n\nBoolean - True if the object is a `Signal`.", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/state/signal.ts", + "content": "```typescript\nisSignal: (value: any) => value is ISignal\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nvalue\n\n\n\n\nany\n\n\n\n\n\n
\n**Returns:**\n\nvalue is [ISignal](#signal)<unknown>", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/v2/signal/v2-signal.ts", "mdFile": "qwik.issignal.md" }, { @@ -1186,7 +1245,7 @@ } ], "kind": "TypeAlias", - "content": "```typescript\nexport type JSXChildren = string | number | boolean | null | undefined | Function | RegExp | JSXChildren[] | Promise | Signal | JSXNode;\n```\n**References:** [JSXChildren](#jsxchildren), [Signal](#signal), [JSXNode](#jsxnode)", + "content": "```typescript\nexport type JSXChildren = string | number | boolean | null | undefined | Function | RegExp | JSXChildren[] | Promise | Signal | JSXNode;\n```\n**References:** [JSXChildren](#jsxchildren), [JSXNode](#jsxnode)", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/render/jsx/types/jsx-qwik-attributes.ts", "mdFile": "qwik.jsxchildren.md" }, @@ -1718,7 +1777,7 @@ } ], "kind": "Function", - "content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\nLoad the prefetch graph for the container.\n\nEach Qwik container needs to include its own prefetch graph.\n\n\n```typescript\nPrefetchGraph: (opts?: {\n base?: string;\n manifestHash?: string;\n manifestURL?: string;\n nonce?: string;\n}) => JSXNode\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nopts\n\n\n\n\n{ base?: string; manifestHash?: string; manifestURL?: string; nonce?: string; }\n\n\n\n\n_(Optional)_ Options for the loading prefetch graph.\n\n- `base` - Base of the graph. For a default installation this will default to the q:base value `/build/`. But if more than one MFE is installed on the page, then each MFE needs to have its own base. - `manifestHash` - Hash of the manifest file to load. If not provided the hash will be extracted from the container attribute `q:manifest-hash` and assume the default build file `${base}/q-bundle-graph-${manifestHash}.json`. - `manifestURL` - URL of the manifest file to load if non-standard bundle graph location name.\n\n\n
\n**Returns:**\n\nJSXNode<string>", + "content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\nLoad the prefetch graph for the container.\n\nEach Qwik container needs to include its own prefetch graph.\n\n\n```typescript\nPrefetchGraph: (opts?: {\n base?: string;\n manifestHash?: string;\n manifestURL?: string;\n nonce?: string;\n}) => JSXNode\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nopts\n\n\n\n\n{ base?: string; manifestHash?: string; manifestURL?: string; nonce?: string; }\n\n\n\n\n_(Optional)_ Options for the loading prefetch graph.\n\n- `base` - Base of the graph. For a default installation this will default to the q:base value `/build/`. But if more than one MFE is installed on the page, then each MFE needs to have its own base. - `manifestHash` - Hash of the manifest file to load. If not provided the hash will be extracted from the container attribute `q:manifest-hash` and assume the default build file `${base}/q-bundle-graph-${manifestHash}.json`. - `manifestURL` - URL of the manifest file to load if non-standard bundle graph location name.\n\n\n
\n**Returns:**\n\n[JSXNode](#jsxnode)<string>", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts", "mdFile": "qwik.prefetchgraph.md" }, @@ -1732,7 +1791,7 @@ } ], "kind": "Function", - "content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\nInstall a service worker which will prefetch the bundles.\n\nThere can only be one service worker per page. Because there can be many separate Qwik Containers on the page each container needs to load its prefetch graph using `PrefetchGraph` component.\n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n scope?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n nonce?: string;\n}) => JSXNode<'script'>\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nopts\n\n\n\n\n{ base?: string; scope?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; nonce?: string; }\n\n\n\n\nOptions for the prefetch service worker.\n\n- `base` - Base URL for the service worker. Default is `import.meta.env.BASE_URL`, which is defined by Vite's `config.base` and defaults to `/`. - `scope` - Base URL for when the service-worker will activate. Default is `/` - `path` - Path to the service worker. Default is `qwik-prefetch-service-worker.js` unless you pass a path that starts with a `/` then the base is ignored. Default is `qwik-prefetch-service-worker.js` - `verbose` - Verbose logging for the service worker installation. Default is `false` - `nonce` - Optional nonce value for security purposes, defaults to `undefined`.\n\n\n
\n**Returns:**\n\nJSXNode<'script'>", + "content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\nInstall a service worker which will prefetch the bundles.\n\nThere can only be one service worker per page. Because there can be many separate Qwik Containers on the page each container needs to load its prefetch graph using `PrefetchGraph` component.\n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n scope?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n nonce?: string;\n}) => JSXNode<'script'>\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nopts\n\n\n\n\n{ base?: string; scope?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; nonce?: string; }\n\n\n\n\nOptions for the prefetch service worker.\n\n- `base` - Base URL for the service worker. Default is `import.meta.env.BASE_URL`, which is defined by Vite's `config.base` and defaults to `/`. - `scope` - Base URL for when the service-worker will activate. Default is `/` - `path` - Path to the service worker. Default is `qwik-prefetch-service-worker.js` unless you pass a path that starts with a `/` then the base is ignored. Default is `qwik-prefetch-service-worker.js` - `verbose` - Verbose logging for the service worker installation. Default is `false` - `nonce` - Optional nonce value for security purposes, defaults to `undefined`.\n\n\n
\n**Returns:**\n\n[JSXNode](#jsxnode)<'script'>", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts", "mdFile": "qwik.prefetchserviceworker.md" }, @@ -2235,9 +2294,9 @@ "id": "readonlysignal" } ], - "kind": "TypeAlias", - "content": "```typescript\nexport type ReadonlySignal = Readonly>;\n```\n**References:** [Signal](#signal)", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/state/signal.ts", + "kind": "Interface", + "content": "```typescript\nexport interface ReadonlySignal \n```\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[value](#)\n\n\n\n\n`readonly`\n\n\n\n\nT\n\n\n\n\n\n
", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/v2/signal/v2-signal.public.ts", "mdFile": "qwik.readonlysignal.md" }, { @@ -2390,7 +2449,7 @@ } ], "kind": "Interface", - "content": "```typescript\nexport interface ResourceProps \n```\n\n\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[onPending?](#)\n\n\n\n\n\n\n\n() => [JSXOutput](#jsxoutput)\n\n\n\n\n_(Optional)_\n\n\n
\n\n[onRejected?](#)\n\n\n\n\n\n\n\n(reason: Error) => [JSXOutput](#jsxoutput)\n\n\n\n\n_(Optional)_\n\n\n
\n\n[onResolved](#)\n\n\n\n\n\n\n\n(value: T) => [JSXOutput](#jsxoutput)\n\n\n\n\n\n
\n\n[value](#)\n\n\n\n\n`readonly`\n\n\n\n\n[ResourceReturn](#resourcereturn)<T> \\| [Signal](#signal)<Promise<T> \\| T> \\| Promise<T>\n\n\n\n\n\n
", + "content": "```typescript\nexport interface ResourceProps \n```\n\n\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[onPending?](#)\n\n\n\n\n\n\n\n() => [JSXOutput](#jsxoutput)\n\n\n\n\n_(Optional)_\n\n\n
\n\n[onRejected?](#)\n\n\n\n\n\n\n\n(reason: Error) => [JSXOutput](#jsxoutput)\n\n\n\n\n_(Optional)_\n\n\n
\n\n[onResolved](#)\n\n\n\n\n\n\n\n(value: T) => [JSXOutput](#jsxoutput)\n\n\n\n\n\n
\n\n[value](#)\n\n\n\n\n`readonly`\n\n\n\n\n[ResourceReturn](#resourcereturn)<T> \\| Signal<Promise<T> \\| T> \\| Promise<T>\n\n\n\n\n\n
", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-resource.ts", "mdFile": "qwik.resourceprops.md" }, @@ -2488,8 +2547,8 @@ } ], "kind": "Interface", - "content": "A signal is a reactive value which can be read and written. When the signal is written, all tasks which are tracking the signal will be re-run and all components that read the signal will be re-rendered.\n\nFurthermore, when a signal value is passed as a prop to a component, the optimizer will automatically forward the signal. This means that `return
hi
` will update the `title` attribute when the signal changes without having to re-render the component.\n\n\n```typescript\nexport interface Signal \n```\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[value](#)\n\n\n\n\n\n\n\nT\n\n\n\n\n\n
", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/state/signal.ts", + "content": "A signal is a reactive value which can be read and written. When the signal is written, all tasks which are tracking the signal will be re-run and all components that read the signal will be re-rendered.\n\nFurthermore, when a signal value is passed as a prop to a component, the optimizer will automatically forward the signal. This means that `return
hi
` will update the `title` attribute when the signal changes without having to re-render the component.\n\n\n```typescript\nexport interface Signal extends ReadonlySignal \n```\n**Extends:** [ReadonlySignal](#readonlysignal)<T>\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[value](#)\n\n\n\n\n\n\n\nT\n\n\n\n\n\n
", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/v2/signal/v2-signal.public.ts", "mdFile": "qwik.signal.md" }, { @@ -2979,7 +3038,7 @@ ], "kind": "Variable", "content": "```typescript\nuseComputed$: Computed\n```", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task.ts", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task-dollar.ts", "mdFile": "qwik.usecomputed_.md" }, { @@ -3160,7 +3219,7 @@ } ], "kind": "Variable", - "content": "Hook that creates a signal that is retained for the lifetime of the component.\n\n\n```typescript\nuseSignal: UseSignal\n```", + "content": "```typescript\nuseSignal: UseSignal\n```", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-signal.ts", "mdFile": "qwik.usesignal.md" }, @@ -3174,7 +3233,7 @@ } ], "kind": "Interface", - "content": "Hook that creates a signal that is retained for the lifetime of the component.\n\n\n```typescript\nuseSignal: UseSignal\n```", + "content": "```typescript\nuseSignal: UseSignal\n```", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-signal.ts", "mdFile": "qwik.usesignal.md" }, @@ -3286,8 +3345,8 @@ } ], "kind": "Function", - "content": "Reruns the `taskFn` when the observed inputs change.\n\nUse `useTask` to observe changes on a set of inputs, and then re-execute the `taskFn` when those inputs change.\n\nThe `taskFn` only executes if the observed inputs change. To observe the inputs, use the `obs` function to wrap property reads. This creates subscriptions that will trigger the `taskFn` to rerun.\n\n\n```typescript\nuseTask$: (qrl: TaskFn, opts?: UseTaskOptions | undefined) => void\n```\n\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nqrl\n\n\n\n\n[TaskFn](#taskfn)\n\n\n\n\n\n
\n\nopts\n\n\n\n\n[UseTaskOptions](#usetaskoptions) \\| undefined\n\n\n\n\n_(Optional)_\n\n\n
\n**Returns:**\n\nvoid", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task.ts", + "content": "Reruns the `taskFn` when the observed inputs change.\n\nUse `useTask` to observe changes on a set of inputs, and then re-execute the `taskFn` when those inputs change.\n\nThe `taskFn` only executes if the observed inputs change. To observe the inputs, use the `obs` function to wrap property reads. This creates subscriptions that will trigger the `taskFn` to rerun.\n\n\n```typescript\nuseTask$: (qrl: import(\"./use-task\").TaskFn, opts?: import(\"./use-task\").UseTaskOptions | undefined) => void\n```\n\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nqrl\n\n\n\n\nimport(\"./use-task\").[TaskFn](#taskfn)\n\n\n\n\n\n
\n\nopts\n\n\n\n\nimport(\"./use-task\").[UseTaskOptions](#usetaskoptions) \\| undefined\n\n\n\n\n_(Optional)_\n\n\n
\n**Returns:**\n\nvoid", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task-dollar.ts", "mdFile": "qwik.usetask_.md" }, { @@ -3328,8 +3387,8 @@ } ], "kind": "Function", - "content": "```tsx\nconst Timer = component$(() => {\n const store = useStore({\n count: 0,\n });\n\n useVisibleTask$(() => {\n // Only runs in the client\n const timer = setInterval(() => {\n store.count++;\n }, 500);\n return () => {\n clearInterval(timer);\n };\n });\n\n return
{store.count}
;\n});\n```\n\n\n```typescript\nuseVisibleTask$: (qrl: TaskFn, opts?: OnVisibleTaskOptions | undefined) => void\n```\n\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nqrl\n\n\n\n\n[TaskFn](#taskfn)\n\n\n\n\n\n
\n\nopts\n\n\n\n\n[OnVisibleTaskOptions](#onvisibletaskoptions) \\| undefined\n\n\n\n\n_(Optional)_\n\n\n
\n**Returns:**\n\nvoid", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task.ts", + "content": "```tsx\nconst Timer = component$(() => {\n const store = useStore({\n count: 0,\n });\n\n useVisibleTask$(() => {\n // Only runs in the client\n const timer = setInterval(() => {\n store.count++;\n }, 500);\n return () => {\n clearInterval(timer);\n };\n });\n\n return
{store.count}
;\n});\n```\n\n\n```typescript\nuseVisibleTask$: (qrl: import(\"./use-task\").TaskFn, opts?: import(\"./use-task\").OnVisibleTaskOptions | undefined) => void\n```\n\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nqrl\n\n\n\n\nimport(\"./use-task\").[TaskFn](#taskfn)\n\n\n\n\n\n
\n\nopts\n\n\n\n\nimport(\"./use-task\").[OnVisibleTaskOptions](#onvisibletaskoptions) \\| undefined\n\n\n\n\n_(Optional)_\n\n\n
\n**Returns:**\n\nvoid", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task-dollar.ts", "mdFile": "qwik.usevisibletask_.md" }, { diff --git a/packages/docs/src/routes/api/qwik/index.md b/packages/docs/src/routes/api/qwik/index.md index 7b983089592..79e114f6581 100644 --- a/packages/docs/src/routes/api/qwik/index.md +++ b/packages/docs/src/routes/api/qwik/index.md @@ -1416,6 +1416,36 @@ export type ComputedFn = () => T; [Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task.ts) +## ComputedSignal + +```typescript +export interface ComputedSignal extends ReadonlySignal +``` + +**Extends:** [ReadonlySignal](#readonlysignal)<T> + + + +
+ +Method + + + +Description + +
+ +[force()](#computedsignal-force) + + + +Use this to force recalculation and running subscribers, for example when the calculated value mutates but remains the same object. Useful for third-party libraries. + +
+ +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/v2/signal/v2-signal.public.ts) + ## ContextId ContextId is a typesafe ID for your context. @@ -1696,6 +1726,80 @@ Description [Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/render/jsx/types/jsx-qwik-attributes.ts) +## createComputed$ + +```typescript +createComputed$: (qrl: () => T) => ComputedSignal; +``` + + + +
+ +Parameter + + + +Type + + + +Description + +
+ +qrl + + + +() => T + + + +
+**Returns:** + +[ComputedSignal](#computedsignal)<T> + +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/v2/signal/v2-signal.public.ts) + +## createComputedQrl + +```typescript +createComputedQrl: (qrl: QRL<() => T>) => ComputedSignal; +``` + + + +
+ +Parameter + + + +Type + + + +Description + +
+ +qrl + + + +[QRL](#qrl)<() => T> + + + +
+**Returns:** + +[ComputedSignal](#computedsignal)<T> + +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/v2/signal/v2-signal.public.ts) + ## createContextId Create a context ID to be used in your application. The name should be written with no spaces. @@ -1779,19 +1883,14 @@ The name of the context. ## createSignal -> Warning: This API is now obsolete. -> -> This is a technology preview - -Creates a signal. - -If the initial state is a function, the function is invoked to calculate the actual initial state. - ```typescript -createSignal: UseSignal; +createSignal: { + (): Signal; + (value: T): Signal; +} ``` -[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-signal.ts) +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/v2/signal/v2-signal.public.ts) ## CSSProperties @@ -1959,7 +2058,7 @@ Description -[ClassList](#classlist) \| [Signal](#signal)<[ClassList](#classlist)> \| undefined +[ClassList](#classlist) \| Signal<[ClassList](#classlist)> \| undefined @@ -2085,7 +2184,7 @@ any \| undefined ## event$ ```typescript -event$: (qrl: T) => QRL; +event$: (qrl: T) => import("./qrl.public").QRL; ```
@@ -2115,9 +2214,9 @@ T
**Returns:** -[QRL](#qrl)<T> +import("./qrl.public").[QRL](#qrl)<T> -[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/qrl/qrl.public.ts) +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/qrl/qrl.public.dollar.ts) ## EventHandler @@ -2178,6 +2277,18 @@ export interface FieldsetHTMLAttributes extends Attrs<'fields [Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/render/jsx/types/jsx-generated.ts) +## force + +Use this to force recalculation and running subscribers, for example when the calculated value mutates but remains the same object. Useful for third-party libraries. + +```typescript +force(): void; +``` + +**Returns:** + +void + ## FormHTMLAttributes ```typescript @@ -2637,10 +2748,8 @@ export interface IntrinsicElements extends IntrinsicHTMLElements, IntrinsicSVGEl ## isSignal -Checks if a given object is a `Signal`. - ```typescript -isSignal: (obj: any) => obj is Signal +isSignal: (value: any) => value is ISignal ```
@@ -2658,7 +2767,7 @@ Description
-obj +value @@ -2666,17 +2775,13 @@ any -The object to check if `Signal`. -
**Returns:** -obj is [Signal](#signal)<T> - -Boolean - True if the object is a `Signal`. +value is [ISignal](#signal)<unknown> -[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/state/signal.ts) +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/v2/signal/v2-signal.ts) ## jsx @@ -2762,7 +2867,7 @@ export type JSXChildren = | JSXNode; ``` -**References:** [JSXChildren](#jsxchildren), [Signal](#signal), [JSXNode](#jsxnode) +**References:** [JSXChildren](#jsxchildren), [JSXNode](#jsxnode) [Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/render/jsx/types/jsx-qwik-attributes.ts) @@ -3515,7 +3620,7 @@ _(Optional)_ Options for the loading prefetch graph. **Returns:** -JSXNode<string> +[JSXNode](#jsxnode)<string> [Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts) @@ -3569,7 +3674,7 @@ Options for the prefetch service worker. **Returns:** -JSXNode<'script'> +[JSXNode](#jsxnode)<'script'> [Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts) @@ -4379,12 +4484,44 @@ export type QwikWheelEvent = NativeWheelEvent; ## ReadonlySignal ```typescript -export type ReadonlySignal = Readonly>; +export interface ReadonlySignal ``` -**References:** [Signal](#signal) + + +
-[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/state/signal.ts) +Property + + + +Modifiers + + + +Type + + + +Description + +
+ +[value](#) + + + +`readonly` + + + +T + + + +
+ +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/v2/signal/v2-signal.public.ts) ## render @@ -5026,7 +5163,7 @@ _(Optional)_ -[ResourceReturn](#resourcereturn)<T> \| [Signal](#signal)<Promise<T> \| T> \| Promise<T> +[ResourceReturn](#resourcereturn)<T> \| Signal<Promise<T> \| T> \| Promise<T> @@ -5230,9 +5367,11 @@ A signal is a reactive value which can be read and written. When the signal is w Furthermore, when a signal value is passed as a prop to a component, the optimizer will automatically forward the signal. This means that `return
hi
` will update the `title` attribute when the signal changes without having to re-render the component. ```typescript -export interface Signal +export interface Signal extends ReadonlySignal ``` +**Extends:** [ReadonlySignal](#readonlysignal)<T> +
Property @@ -5265,7 +5404,7 @@ T
-[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/state/signal.ts) +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/v2/signal/v2-signal.public.ts) ## Size @@ -10026,7 +10165,7 @@ T useComputed$: Computed; ``` -[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task.ts) +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task-dollar.ts) ## useComputedQrl @@ -10626,8 +10765,6 @@ T \| undefined ## useSignal -Hook that creates a signal that is retained for the lifetime of the component. - ```typescript useSignal: UseSignal; ``` @@ -10636,8 +10773,6 @@ useSignal: UseSignal; ## UseSignal -Hook that creates a signal that is retained for the lifetime of the component. - ```typescript useSignal: UseSignal; ``` @@ -11062,7 +11197,7 @@ Use `useTask` to observe changes on a set of inputs, and then re-execute the `ta The `taskFn` only executes if the observed inputs change. To observe the inputs, use the `obs` function to wrap property reads. This creates subscriptions that will trigger the `taskFn` to rerun. ```typescript -useTask$: (qrl: TaskFn, opts?: UseTaskOptions | undefined) => void +useTask$: (qrl: import("./use-task").TaskFn, opts?: import("./use-task").UseTaskOptions | undefined) => void ```
@@ -11084,7 +11219,7 @@ qrl -[TaskFn](#taskfn) +import("./use-task").[TaskFn](#taskfn) @@ -11095,7 +11230,7 @@ opts -[UseTaskOptions](#usetaskoptions) \| undefined +import("./use-task").[UseTaskOptions](#usetaskoptions) \| undefined @@ -11107,7 +11242,7 @@ _(Optional)_ void -[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task.ts) +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task-dollar.ts) ## UseTaskOptions @@ -11230,7 +11365,7 @@ const Timer = component$(() => { ``` ```typescript -useVisibleTask$: (qrl: TaskFn, opts?: OnVisibleTaskOptions | undefined) => void +useVisibleTask$: (qrl: import("./use-task").TaskFn, opts?: import("./use-task").OnVisibleTaskOptions | undefined) => void ```
@@ -11252,7 +11387,7 @@ qrl -[TaskFn](#taskfn) +import("./use-task").[TaskFn](#taskfn) @@ -11263,7 +11398,7 @@ opts -[OnVisibleTaskOptions](#onvisibletaskoptions) \| undefined +import("./use-task").[OnVisibleTaskOptions](#onvisibletaskoptions) \| undefined @@ -11275,7 +11410,7 @@ _(Optional)_ void -[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task.ts) +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task-dollar.ts) ## useVisibleTaskQrl diff --git a/packages/qwik/src/core/api.md b/packages/qwik/src/core/api.md index a651b576218..07212c136eb 100644 --- a/packages/qwik/src/core/api.md +++ b/packages/qwik/src/core/api.md @@ -133,7 +133,7 @@ export interface ClientContainer extends Container2 { // (undocumented) qManifestHash: string; // (undocumented) - renderDone: Promise; + renderDone: Promise | null; // (undocumented) rootVNode: _ElementVNode; } @@ -166,6 +166,11 @@ export const componentQrl: >(componentQrl: QRL = () => T; +// @public (undocumented) +export interface ComputedSignal extends ReadonlySignal { + force(): void; +} + // @internal (undocumented) export const _CONST_PROPS: unique symbol; @@ -200,11 +205,20 @@ export interface CorrectedToggleEvent extends Event { readonly prevState: 'open' | 'closed'; } +// @public (undocumented) +export const createComputed$: (qrl: () => T) => ComputedSignal; + +// @public (undocumented) +export const createComputedQrl: (qrl: QRL<() => T>) => ComputedSignal; + // @public export const createContextId: (name: string) => ContextId; -// @public @deprecated -export const createSignal: UseSignal; +// @public (undocumented) +export const createSignal: { + (): Signal; + (value: T): Signal; +}; // @public (undocumented) export interface CSSProperties extends CSS_2.Properties, CSS_2.PropertiesHyphen { @@ -247,14 +261,14 @@ export interface DialogHTMLAttributes extends Attrs<'dialog', // // @public export interface DOMAttributes extends DOMAttributesBase, QwikEvents { + // Warning: (ae-forgotten-export) The symbol "Signal_2" needs to be exported by the entry point index.d.ts + // // (undocumented) - class?: ClassList | Signal | undefined; + class?: ClassList | Signal_2 | undefined; } -// Warning: (ae-forgotten-export) The symbol "StoreTracker" needs to be exported by the entry point index.d.ts -// // @internal (undocumented) -class DomContainer extends _SharedContainer implements ClientContainer, StoreTracker { +class DomContainer extends _SharedContainer implements ClientContainer { // (undocumented) $appendStyle$(content: string, styleId: string, host: _VirtualVNode, scoped: boolean): void; // (undocumented) @@ -263,16 +277,16 @@ class DomContainer extends _SharedContainer implements ClientContainer, StoreTra $instanceHash$: string; // (undocumented) $journal$: VNodeJournal; - // Warning: (ae-forgotten-export) The symbol "ObjToProxyMap" needs to be exported by the entry point index.d.ts - // - // (undocumented) - $proxyMap$: ObjToProxyMap; // (undocumented) $qFuncs$: Array<(...args: unknown[]) => unknown>; // (undocumented) $rawStateData$: unknown[]; // (undocumented) $setRawState$(id: number, vParent: _ElementVNode | _VirtualVNode): void; + // Warning: (ae-forgotten-export) The symbol "ObjToProxyMap" needs to be exported by the entry point index.d.ts + // + // (undocumented) + $storeProxyMap$: ObjToProxyMap; constructor(element: _ContainerElement); // (undocumented) document: _QDocument; @@ -301,9 +315,7 @@ class DomContainer extends _SharedContainer implements ClientContainer, StoreTra // (undocumented) qManifestHash: string; // (undocumented) - renderDone: Promise; - // (undocumented) - rendering: boolean; + renderDone: Promise | null; // (undocumented) resolveContext(host: HostElement, contextId: ContextId): T | undefined; // (undocumented) @@ -323,6 +335,13 @@ export { DomContainer as _DomContainer } // @public (undocumented) export type EagernessOptions = 'visible' | 'load' | 'idle'; +// @internal (undocumented) +export class _EffectData = Record> { + constructor(data: T); + // (undocumented) + data: T; +} + // @internal (undocumented) export type _ElementVNode = [ _VNodeFlags.Element, @@ -372,10 +391,10 @@ export const eventQrl: (qrl: QRL) => QRL; export interface FieldsetHTMLAttributes extends Attrs<'fieldset', T> { } -// Warning: (ae-forgotten-export) The symbol "SignalDerived" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "WrappedSignal" needs to be exported by the entry point index.d.ts // // @internal (undocumented) -export const _fnSignal: any>(fn: T, args: Parameters, fnStr?: string) => SignalDerived, Parameters>; +export const _fnSignal: any>(fn: T, args: Parameters, fnStr?: string) => WrappedSignal; // @public (undocumented) export interface FormHTMLAttributes extends Attrs<'form', T> { @@ -521,8 +540,8 @@ export type IntrinsicSVGElements = { // @internal (undocumented) export const _isJSXNode: (n: unknown) => n is JSXNode; -// @public -export const isSignal: (obj: any) => obj is Signal; +// @public (undocumented) +export const isSignal: (value: any) => value is Signal; // @internal (undocumented) export function _isStringifiable(value: unknown): value is _Stringifiable; @@ -541,7 +560,7 @@ export const _jsxBranch: (input?: T) => T | undefined; export const _jsxC: (type: any, mutable: any, _flags: any, key: any) => JSXNode; // @public (undocumented) -export type JSXChildren = string | number | boolean | null | undefined | Function | RegExp | JSXChildren[] | Promise | Signal | JSXNode; +export type JSXChildren = string | number | boolean | null | undefined | Function | RegExp | JSXChildren[] | Promise | Signal_2 | JSXNode; // Warning: (ae-forgotten-export) The symbol "JsxDevOpts" needs to be exported by the entry point index.d.ts // @@ -936,7 +955,10 @@ export type QwikVisibleEvent = CustomEvent; export type QwikWheelEvent = NativeWheelEvent; // @public (undocumented) -export type ReadonlySignal = Readonly>; +export interface ReadonlySignal { + // (undocumented) + readonly value: T; +} // @internal (undocumented) export const _regSymbol: (symbol: any, hash: string) => any; @@ -1025,7 +1047,7 @@ export interface ResourceProps { // (undocumented) onResolved: (value: T) => JSXOutput; // (undocumented) - readonly value: ResourceReturn | Signal | T> | Promise; + readonly value: ResourceReturn | Signal_2 | T> | Promise; } // @public (undocumented) @@ -1076,18 +1098,14 @@ export abstract class _SharedContainer implements Container2 { $instanceHash$: string | null; // (undocumented) readonly $locale$: string; - // (undocumented) - readonly $proxyMap$: ObjToProxyMap; // Warning: (ae-forgotten-export) The symbol "Scheduler" needs to be exported by the entry point index.d.ts // // (undocumented) readonly $scheduler$: Scheduler; // (undocumented) $serverData$: Record; - // Warning: (ae-forgotten-export) The symbol "SubscriptionManager" needs to be exported by the entry point index.d.ts - // // (undocumented) - readonly $subsManager$: SubscriptionManager; + readonly $storeProxyMap$: ObjToProxyMap; // (undocumented) readonly $version$: string; constructor(scheduleDrain: () => void, journalFlush: () => void, serverData: Record, locale: string); @@ -1113,14 +1131,14 @@ export abstract class _SharedContainer implements Container2 { abstract setContext(host: HostElement, context: ContextId, value: T): void; // (undocumented) abstract setHostProp(host: HostElement, name: string, value: T): void; - // Warning: (ae-forgotten-export) The symbol "Subscriber" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "Effect" needs to be exported by the entry point index.d.ts // // (undocumented) - trackSignalValue(signal: Signal, sub: Subscriber): T; + trackSignalValue(signal: Signal_2, subscriber: Effect, property: string, data: _EffectData): T; } // @public -export interface Signal { +export interface Signal extends ReadonlySignal { // (undocumented) value: T; } @@ -1925,7 +1943,7 @@ export interface UseSignal { (value: T | (() => T)): Signal; } -// @public +// @public (undocumented) export const useSignal: UseSignal; // @public @@ -2016,6 +2034,8 @@ export type _VNode = _ElementVNode | _TextVNode | _VirtualVNode; // @internal export const enum _VNodeFlags { + // (undocumented) + Deleted = 32, // (undocumented) Element = 1, // (undocumented) @@ -2027,15 +2047,15 @@ export const enum _VNodeFlags { // (undocumented) INFLATED_TYPE_MASK = 15, // (undocumented) - NAMESPACE_MASK = 96, + NAMESPACE_MASK = 192, // (undocumented) - NEGATED_NAMESPACE_MASK = -97, + NEGATED_NAMESPACE_MASK = -193, // (undocumented) NS_html = 0, // (undocumented) - NS_math = 64, + NS_math = 128, // (undocumented) - NS_svg = 32, + NS_svg = 64, // (undocumented) Resolved = 16, // (undocumented) diff --git a/packages/qwik/src/core/container/container.ts b/packages/qwik/src/core/container/container.ts index c1e2437d639..e894e00e51f 100644 --- a/packages/qwik/src/core/container/container.ts +++ b/packages/qwik/src/core/container/container.ts @@ -7,7 +7,7 @@ import { fromKebabToCamelCase } from '../util/case'; import { QContainerAttr } from '../util/markers'; import { isElement } from '../util/element'; import { createSubscriptionManager, type SubscriberSignal } from '../state/common'; -import { isSignal, type Signal, type SignalImpl } from '../state/signal'; +import { isSignalV1, type Signal, type SignalImpl } from '../state/signal'; import { directGetAttribute } from '../render/fast-calls'; import type { QContext } from '../state/context'; import { isServerPlatform } from '../platform/platform'; @@ -156,7 +156,7 @@ export const removeContainerState = (containerEl: Element) => { export const setRef = (value: any, elm: Element) => { if (isFunction(value)) { return value(elm); - } else if (isSignal(value)) { + } else if (isSignalV1(value)) { if (isServerPlatform()) { // During SSR, assigning a ref should not cause reactivity because // the expectation is that the ref is filled in on the client diff --git a/packages/qwik/src/core/container/pause.ts b/packages/qwik/src/core/container/pause.ts index 26b21a31da0..952a64eeb52 100644 --- a/packages/qwik/src/core/container/pause.ts +++ b/packages/qwik/src/core/container/pause.ts @@ -49,6 +49,7 @@ import { cleanupTask, isResourceTask, type ResourceReturnInternal, + type Task, } from '../use/use-task'; import { isNotNullable, isPromise } from '../util/promises'; import { isArray, isObject, isSerializableObject } from '../util/types'; @@ -174,7 +175,7 @@ Task Symbol: ${task.$qrl$.$symbol$} if (isResourceTask(task)) { collector.$resources$.push(task.$state$!); } - cleanupTask(task); + cleanupTask(task as Task); } } } diff --git a/packages/qwik/src/core/container/render.unit.tsx b/packages/qwik/src/core/container/render.unit.tsx index 7c093446815..db6c112b136 100644 --- a/packages/qwik/src/core/container/render.unit.tsx +++ b/packages/qwik/src/core/container/render.unit.tsx @@ -1,10 +1,7 @@ import { assert, suite, test } from 'vitest'; -import { renderToString } from '../../server/render'; +import { renderToString } from '@builder.io/qwik/server'; import { createDocument, createDOM } from '@builder.io/qwik/testing'; -import { component$ } from '../component/component.public'; -import { _fnSignal } from '../internal'; -import { useSignal } from '../use/use-signal'; -import type { JSXOutput } from '../render/jsx/types/jsx-node'; +import { component$, useSignal, type JSXOutput, _fnSignal } from '@builder.io/qwik'; suite('jsx signals', () => { const RenderJSX = component$(() => { diff --git a/packages/qwik/src/core/debug.ts b/packages/qwik/src/core/debug.ts new file mode 100644 index 00000000000..83c3ca96b4a --- /dev/null +++ b/packages/qwik/src/core/debug.ts @@ -0,0 +1,94 @@ +import { isQrl } from '../server/prefetch-strategy'; +import { isJSXNode } from './render/jsx/jsx-runtime'; +import { isTask } from './use/use-task'; +import { vnode_isVNode, vnode_toString } from './v2/client/vnode'; +import { ComputedSignal, WrappedSignal, isSignal } from './v2/signal/v2-signal'; +import { isStore } from './v2/signal/v2-store'; + +const stringifyPath: any[] = []; +export function qwikDebugToString(value: any): any { + if (value === null) { + return 'null'; + } else if (value === undefined) { + return 'undefined'; + } else if (typeof value === 'string') { + return '"' + value + '"'; + } else if (typeof value === 'number' || typeof value === 'boolean') { + return String(value); + } else if (isTask(value)) { + return `Task(${qwikDebugToString(value.$qrl$)})`; + } else if (isQrl(value)) { + return `Qrl(${value.$symbol$})`; + } else if (typeof value === 'object' || typeof value === 'function') { + if (stringifyPath.includes(value)) { + return '*'; + } + if (stringifyPath.length > 10) { + // debugger; + } + try { + stringifyPath.push(value); + if (Array.isArray(value)) { + if (vnode_isVNode(value)) { + return vnode_toString.apply(value); + } else { + return value.map(qwikDebugToString); + } + } else if (isSignal(value)) { + if (value instanceof WrappedSignal) { + return 'WrappedSignal'; + } else if (value instanceof ComputedSignal) { + return 'ComputedSignal'; + } else { + return 'Signal'; + } + } else if (isStore(value)) { + return 'Store'; + } else if (isJSXNode(value)) { + return jsxToString(value); + } + } finally { + stringifyPath.pop(); + } + } + return value; +} + +export const pad = (text: string, prefix: string) => { + return String(text) + .split('\n') + .map((line, idx) => (idx ? prefix : '') + line) + .join('\n'); +}; + +export const jsxToString = (value: any): string => { + if (isJSXNode(value)) { + let type = value.type; + if (typeof type === 'function') { + type = type.name || 'Component'; + } + let str = '<' + value.type; + if (value.props) { + for (const [key, val] of Object.entries(value.props)) { + str += ' ' + key + '=' + qwikDebugToString(val); + } + const children = value.children; + if (children != null) { + str += '>'; + if (Array.isArray(children)) { + children.forEach((child) => { + str += jsxToString(child); + }); + } else { + str += jsxToString(children); + } + str += ''; + } else { + str += '/>'; + } + } + return str; + } else { + return String(value); + } +}; diff --git a/packages/qwik/src/core/examples.tsx b/packages/qwik/src/core/examples.tsx index 80f1fa67600..2dc4b5a2cc4 100644 --- a/packages/qwik/src/core/examples.tsx +++ b/packages/qwik/src/core/examples.tsx @@ -12,7 +12,7 @@ import { $, type QRL } from './qrl/qrl.public'; import { useOn, useOnDocument, useOnWindow } from './use/use-on'; import { useStore } from './use/use-store.public'; import { useStyles$, useStylesScoped$ } from './use/use-styles'; -import { useVisibleTask$, useTask$ } from './use/use-task'; +import { useVisibleTask$, useTask$ } from './use/use-task-dollar'; import { implicit$FirstArg } from './util/implicit_dollar'; import { isServer, isBrowser } from '../build'; diff --git a/packages/qwik/src/core/index.ts b/packages/qwik/src/core/index.ts index 11146aeea0c..a535b08018f 100644 --- a/packages/qwik/src/core/index.ts +++ b/packages/qwik/src/core/index.ts @@ -28,7 +28,8 @@ export type { // Internal Runtime ////////////////////////////////////////////////////////////////////////////////////////// export { $, sync$, _qrlSync, type SyncQRL } from './qrl/qrl.public'; -export { event$, eventQrl } from './qrl/qrl.public'; +export { eventQrl } from './qrl/qrl.public'; +export { event$ } from './qrl/qrl.public.dollar'; export { qrl, inlinedQrl, inlinedQrlDEV, qrlDEV } from './qrl/qrl'; export type { QRL, PropFunction, PropFnInterface } from './qrl/qrl.public'; @@ -96,7 +97,7 @@ export { useContext, useContextProvider, createContextId } from './use/use-conte export { useServerData } from './use/use-env-data'; export { useStylesQrl, useStyles$, useStylesScopedQrl, useStylesScoped$ } from './use/use-styles'; export { useOn, useOnDocument, useOnWindow } from './use/use-on'; -export { useSignal, useConstant, createSignal } from './use/use-signal'; +export { useSignal, useConstant } from './use/use-signal'; export { withLocale, getLocale } from './use/use-locale'; export type { UseStylesScoped } from './use/use-styles'; @@ -121,25 +122,29 @@ export type { } from './use/use-task'; export type { ResourceProps, ResourceOptions } from './use/use-resource'; export { useResource$, useResourceQrl, Resource } from './use/use-resource'; -export { useTask$, useTaskQrl } from './use/use-task'; -export { useVisibleTask$, useVisibleTaskQrl } from './use/use-task'; -export { - useComputed$, - useComputedQrl, - // TODO - // createComputed$, createComputedQrl -} from './use/use-task'; +export { useTaskQrl, useVisibleTaskQrl, useComputedQrl } from './use/use-task'; +export { useComputed$, useTask$, useVisibleTask$ } from './use/use-task-dollar'; export { useErrorBoundary } from './use/use-error-boundary'; export type { ErrorBoundaryStore } from './render/error-handling'; +export { + type ReadonlySignal, + type Signal, + type ComputedSignal, +} from './v2/signal/v2-signal.public'; +export { + isSignal, + createSignal, + createComputedQrl, + createComputed$, +} from './v2/signal/v2-signal.public'; +export { EffectData as _EffectData } from './v2/signal/v2-signal'; ////////////////////////////////////////////////////////////////////////////////////////// // Developer Low-Level API ////////////////////////////////////////////////////////////////////////////////////////// export type { ValueOrPromise } from './util/types'; -export type { Signal, ReadonlySignal } from './state/signal'; export { type NoSerialize, SubscriptionType } from './state/common'; export { noSerialize } from './state/common'; -export { isSignal } from './state/signal'; export { version } from './version'; ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/qwik/src/core/qrl/inlined-fn.ts b/packages/qwik/src/core/qrl/inlined-fn.ts index 9a742bca716..159385d7fdc 100644 --- a/packages/qwik/src/core/qrl/inlined-fn.ts +++ b/packages/qwik/src/core/qrl/inlined-fn.ts @@ -1,6 +1,7 @@ import { assertDefined } from '../error/assert'; import { SignalDerived } from '../state/signal'; import { qSerialize } from '../util/qdev'; +import { WrappedSignal } from '../v2/signal/v2-signal'; /** @internal */ export const _fnSignal = any>( @@ -8,7 +9,7 @@ export const _fnSignal = any>( args: Parameters, fnStr?: string ) => { - return new SignalDerived, Parameters>(fn, args, fnStr); + return new WrappedSignal(null, fn, args, fnStr || null); }; export const serializeDerivedSignalFunc = (signal: SignalDerived) => { diff --git a/packages/qwik/src/core/qrl/qrl-class.ts b/packages/qwik/src/core/qrl/qrl-class.ts index 13d2398182f..b074315dbb2 100644 --- a/packages/qwik/src/core/qrl/qrl-class.ts +++ b/packages/qwik/src/core/qrl/qrl-class.ts @@ -3,7 +3,7 @@ import { assertDefined } from '../error/assert'; import { qError, QError_qrlIsNotFunction } from '../error/error'; import { getPlatform, isServerPlatform } from '../platform/platform'; import { verifySerializable } from '../state/common'; -import { isSignal, type SignalInternal } from '../state/signal'; +import { type SignalInternal } from '../state/signal'; import { invoke, newInvokeContext, @@ -16,6 +16,7 @@ import { getQFuncs, QInstanceAttr } from '../util/markers'; import { maybeThen } from '../util/promises'; import { qDev, qSerialize, qTest, seal } from '../util/qdev'; import { isArray, isFunction, type ValueOrPromise } from '../util/types'; +import { isSignal } from '../v2/signal/v2-signal'; import type { QRLDev } from './qrl'; import type { QRL, QrlArgs, QrlReturn } from './qrl.public'; @@ -246,7 +247,7 @@ export function assertQrl(qrl: QRL): asserts qrl is QRLInternal { export function assertSignal(obj: unknown): asserts obj is SignalInternal { if (qDev) { - if (!isSignal(obj)) { + if (!isSignal(obj) && !isSignal(obj)) { throw new Error('Not a Signal'); } } diff --git a/packages/qwik/src/core/qrl/qrl.public.dollar.ts b/packages/qwik/src/core/qrl/qrl.public.dollar.ts new file mode 100644 index 00000000000..4d1d4aa9454 --- /dev/null +++ b/packages/qwik/src/core/qrl/qrl.public.dollar.ts @@ -0,0 +1,5 @@ +import { eventQrl } from './qrl.public'; +import { implicit$FirstArg } from '../util/implicit_dollar'; + +/** @public */ +export const event$ = implicit$FirstArg(eventQrl); diff --git a/packages/qwik/src/core/qrl/qrl.public.ts b/packages/qwik/src/core/qrl/qrl.public.ts index 22e7728c3ff..93bd20aac2e 100644 --- a/packages/qwik/src/core/qrl/qrl.public.ts +++ b/packages/qwik/src/core/qrl/qrl.public.ts @@ -1,4 +1,3 @@ -import { implicit$FirstArg } from '../util/implicit_dollar'; import { qDev, qRuntimeQrl } from '../util/qdev'; import type { QRLDev } from './qrl'; import { SYNC_QRL, createQRL } from './qrl-class'; @@ -272,9 +271,6 @@ export const eventQrl = (qrl: QRL): QRL => { return qrl; }; -/** @public */ -export const event$ = implicit$FirstArg(eventQrl); - /** @alpha */ export interface SyncQRL extends QRL { __brand__SyncQRL__: TYPE; diff --git a/packages/qwik/src/core/render/dom/render-dom.ts b/packages/qwik/src/core/render/dom/render-dom.ts index 4b3799a91ac..af8a32cf070 100644 --- a/packages/qwik/src/core/render/dom/render-dom.ts +++ b/packages/qwik/src/core/render/dom/render-dom.ts @@ -1,7 +1,7 @@ import { qError, QError_invalidJsxNodeType } from '../../error/error'; import { SubscriptionType } from '../../state/common'; import { HOST_FLAG_MOUNTED, type QContext } from '../../state/context'; -import { isSignal, type Signal } from '../../state/signal'; +import { isSignalV1, type Signal } from '../../state/signal'; import { invoke, newInvokeContext, type InvokeContext } from '../../use/use-core'; import { EMPTY_ARRAY, EMPTY_OBJ } from '../../util/flyweight'; import { logWarn } from '../../util/log'; @@ -167,7 +167,7 @@ export const processData = ( return newNode; } else if (isJSXNode(node)) { return processNode(node, invocationContext); - } else if (isSignal(node)) { + } else if (isSignalV1(node)) { const newNode = new ProcessedJSXNodeImpl('#signal', EMPTY_OBJ, null, EMPTY_ARRAY, 0, null); newNode.$signal$ = node; return newNode; diff --git a/packages/qwik/src/core/render/dom/render.unit.tsx b/packages/qwik/src/core/render/dom/render.unit.tsx index 484626c93f2..532650ad0c1 100644 --- a/packages/qwik/src/core/render/dom/render.unit.tsx +++ b/packages/qwik/src/core/render/dom/render.unit.tsx @@ -9,7 +9,7 @@ import { component$ } from '../../component/component.public'; import { inlinedQrl } from '../../qrl/qrl'; import { useLexicalScope } from '../../use/use-lexical-scope.public'; import { useStore } from '../../use/use-store.public'; -import { useVisibleTask$, useTask$ } from '../../use/use-task'; +import { useVisibleTask$, useTask$ } from '../../use/use-task-dollar'; import { useOn } from '../../use/use-on'; import { Slot } from '../jsx/slot.public'; import { render } from './render.public'; diff --git a/packages/qwik/src/core/render/dom/signals.ts b/packages/qwik/src/core/render/dom/signals.ts index 7fcaa1c9240..7bdd4292dc8 100644 --- a/packages/qwik/src/core/render/dom/signals.ts +++ b/packages/qwik/src/core/render/dom/signals.ts @@ -1,6 +1,6 @@ import { getLastSubscription, type SubscriberSignal } from '../../state/common'; import { getContext, tryGetContext } from '../../state/context'; -import { trackSignal } from '../../use/use-core'; +import { trackSignalV1 } from '../../use/use-core'; import { logError } from '../../util/log'; import { serializeClassWithHost, stringifyStyle } from '../execute-component'; import type { RenderContext } from '../types'; @@ -37,7 +37,7 @@ export const executeSignalOperation = (rCtx: RenderContext, operation: Subscribe const prop = operation[4]; const isSVG = elm.namespaceURI === SVG_NS; staticCtx.$containerState$.$subsManager$.$clearSignal$(operation); - let value = trackSignal(operation[2], operation.slice(0, -1) as any) as any; + let value = trackSignalV1(operation[2], operation.slice(0, -1) as any) as any; if (prop === 'class') { value = serializeClassWithHost(value, tryGetContext(hostElm)); } else if (prop === 'style') { @@ -59,7 +59,7 @@ export const executeSignalOperation = (rCtx: RenderContext, operation: Subscribe // MISKO: I believe no `invocationContext` is OK because the JSX in signal // has already been converted to JSX and there is nothing to execute there. const invocationContext = undefined; - let signalValue = trackSignal(operation[2], operation.slice(0, -1) as any); + let signalValue = trackSignalV1(operation[2], operation.slice(0, -1) as any); const subscription = getLastSubscription()!; if (Array.isArray(signalValue)) { diff --git a/packages/qwik/src/core/render/dom/visitor.ts b/packages/qwik/src/core/render/dom/visitor.ts index bfa324de06c..f9eb3c97997 100644 --- a/packages/qwik/src/core/render/dom/visitor.ts +++ b/packages/qwik/src/core/render/dom/visitor.ts @@ -65,9 +65,9 @@ import { tryGetContext, type QContext, } from '../../state/context'; -import { isSignal } from '../../state/signal'; +import { isSignalV1 } from '../../state/signal'; import { ReadWriteProxyHandler, createPropsState, createProxy } from '../../state/store'; -import { trackSignal } from '../../use/use-core'; +import { trackSignalV1 } from '../../use/use-core'; import { EMPTY_OBJ } from '../../util/flyweight'; import { appendChild, @@ -393,7 +393,7 @@ export const diffVnode = ( const signal = newVnode.$signal$; if (signal) { newVnode.$text$ = jsxToString( - trackSignal(signal, [ + trackSignalV1(signal, [ SubscriptionType.TEXT_MUTABLE, currentComponent.$element$, signal, @@ -443,8 +443,8 @@ export const diffVnode = ( continue; } - if (isSignal(newValue)) { - newValue = trackSignal(newValue, [ + if (isSignalV1(newValue)) { + newValue = trackSignalV1(newValue, [ SubscriptionType.PROP_IMMUTABLE, currentComponent.$element$, newValue, @@ -674,7 +674,7 @@ export const createElm = ( if (isJSXNode(signalValue)) { // convert signal value to ProcessedJSXNode const processedSignal = processData(signalValue); - if (isSignal(processedSignal)) { + if (isSignalV1(processedSignal)) { throw new Error('NOT IMPLEMENTED: Promise'); } else if (Array.isArray(processedSignal)) { throw new Error('NOT IMPLEMENTED: Array'); @@ -682,7 +682,7 @@ export const createElm = ( // crate elements const elm = createElm(rCtx, processedSignal as ProcessedJSXNode, flags, promises); // create subscription - trackSignal( + trackSignalV1( signal, flags & IS_IMMUTABLE ? ([SubscriptionType.TEXT_IMMUTABLE, elm, signal, elm] as TextSubscriber) @@ -701,7 +701,7 @@ export const createElm = ( const elm = doc.createTextNode(vnode.$text$); elm.data = vnode.$text$ = jsxToString(signalValue); // create subscription - trackSignal( + trackSignalV1( signal, flags & IS_IMMUTABLE ? ([SubscriptionType.TEXT_IMMUTABLE, elm, signal, elm] as TextSubscriber) @@ -812,7 +812,7 @@ export const createElm = ( for (const prop in expectProps) { if (prop !== 'children' && prop !== QSlot) { const immutableValue = immutableMeta[prop]; - if (isSignal(immutableValue)) { + if (isSignalV1(immutableValue)) { target['_IMMUTABLE_PREFIX' + prop] = immutableValue; } else { target[prop] = expectProps[prop]; @@ -1084,9 +1084,9 @@ export const setProperties = ( continue; } - if (isSignal(newValue)) { + if (isSignalV1(newValue)) { assertDefined(hostCtx, 'Signals can only be used in components'); - newValue = trackSignal( + newValue = trackSignalV1( newValue, immutable ? [SubscriptionType.PROP_IMMUTABLE, elm, newValue, hostCtx.$element$, prop, undefined] diff --git a/packages/qwik/src/core/render/execute-component.ts b/packages/qwik/src/core/render/execute-component.ts index e1b1f5745ac..624213e0abb 100644 --- a/packages/qwik/src/core/render/execute-component.ts +++ b/packages/qwik/src/core/render/execute-component.ts @@ -13,7 +13,7 @@ import { seal } from '../util/qdev'; import { SkipRender } from './jsx/utils.public'; import { handleError } from './error-handling'; import { HOST_FLAG_DIRTY, HOST_FLAG_MOUNTED, type QContext } from '../state/context'; -import { isSignal, SignalUnassignedException } from '../state/signal'; +import { isSignalV1, SignalUnassignedException } from '../state/signal'; import { isJSXNode } from './jsx/jsx-runtime'; import { isUnitlessNumber } from '../util/unitless_number'; import { isServerPlatform } from '../platform/platform'; @@ -269,7 +269,7 @@ export const setQId = (rCtx: RenderContext, elCtx: QContext) => { }; export const jsxToString = (data: any): string => { - if (isSignal(data)) { + if (isSignalV1(data)) { return jsxToString(data.value); } return data == null || typeof data === 'boolean' ? '' : String(data); diff --git a/packages/qwik/src/core/render/jsx/jsx-runtime.ts b/packages/qwik/src/core/render/jsx/jsx-runtime.ts index 16b6fef4298..0dfdca7cf78 100644 --- a/packages/qwik/src/core/render/jsx/jsx-runtime.ts +++ b/packages/qwik/src/core/render/jsx/jsx-runtime.ts @@ -1,23 +1,18 @@ -import { isBrowser } from '@builder.io/qwik/build'; -import type { JsxChild } from 'typescript'; -import { isQwikComponent, type OnRenderFn } from '../../component/component.public'; +import { type OnRenderFn } from '../../component/component.public'; import { _CONST_PROPS } from '../../internal'; -import { isQrl, type QRLInternal } from '../../qrl/qrl-class'; -import { verifySerializable } from '../../state/common'; +import { type QRLInternal } from '../../qrl/qrl-class'; import { _VAR_PROPS } from '../../state/constants'; -import { isSignal, SignalDerived } from '../../state/signal'; -import { invoke, untrack } from '../../use/use-core'; +import { untrack } from '../../use/use-core'; import { EMPTY_OBJ } from '../../util/flyweight'; -import { logError, logOnceWarn, logWarn } from '../../util/log'; +import { logOnceWarn, logWarn } from '../../util/log'; import { ELEMENT_ID, OnRenderProp, QScopedStyle, QSlot, QSlotS } from '../../util/markers'; -import { isPromise } from '../../util/promises'; -import { qDev, qRuntimeQrl, seal } from '../../util/qdev'; -import { isArray, isFunction, isObject, isString } from '../../util/types'; +import { qDev, seal } from '../../util/qdev'; +import { isArray, isObject, isString } from '../../util/types'; +import { WrappedSignal } from '../../v2/signal/v2-signal'; import { static_subtree } from '../execute-component'; import type { DevJSX, FunctionComponent, JSXNode } from './types/jsx-node'; import type { QwikJSX } from './types/jsx-qwik'; import type { JSXChildren } from './types/jsx-qwik-attributes'; -import { SkipRender } from './utils.public'; export type Props = Record; @@ -61,7 +56,6 @@ export const _jsxSorted = ( ...dev, }; } - validateJSXNode(node); seal(node); return node; }; @@ -261,131 +255,6 @@ export const RenderOnce: FunctionComponent<{ return new JSXNodeImpl(Virtual, EMPTY_OBJ, null, props.children, static_subtree, key); }; -const validateJSXNode = (node: JSXNode) => { - if (qDev) { - const { type, varProps, constProps, children } = node; - invoke(undefined, () => { - const isQwikC = isQwikComponent(type); - if (!isString(type) && !isFunction(type)) { - throw new Error( - `The of the JSX element must be either a string or a function. Instead, it's a "${typeof type}": ${String( - type - )}.` - ); - } - if (children) { - const flatChildren = isArray(children) ? children.flat() : [children]; - if (isString(type) || isQwikC) { - flatChildren.forEach((child: unknown) => { - if (!isValidJSXChild(child)) { - const typeObj = typeof child; - let explanation = ''; - if (typeObj === 'object') { - if (child?.constructor) { - explanation = `it's an instance of "${child?.constructor.name}".`; - } else { - explanation = `it's a object literal: ${printObjectLiteral(child as {})} `; - } - } else if (typeObj === 'function') { - explanation += `it's a function named "${(child as Function).name}".`; - } else { - explanation = `it's a "${typeObj}": ${String(child)}.`; - } - - throw new Error( - `One of the children of <${type}> is not an accepted value. JSX children must be either: string, boolean, number, , Array, undefined/null, or a Promise/Signal. Instead, ${explanation}\n` - ); - } - }); - } - if (isBrowser) { - if (isFunction(type) || constProps) { - const keys: Record = {}; - flatChildren.forEach((child: unknown) => { - if (isJSXNode(child) && child.key != null) { - const key = String(child.type) + ':' + child.key; - if (keys[key]) { - const err = createJSXError( - `Multiple JSX sibling nodes with the same key.\nThis is likely caused by missing a custom key in a for loop`, - child - ); - if (err) { - if (isString(child.type)) { - logOnceWarn(err); - } else { - logOnceWarn(err); - } - } - } else { - keys[key] = true; - } - } - }); - } - } - } - - const allProps = [ - ...(varProps ? Object.entries(varProps) : []), - ...(constProps ? Object.entries(constProps) : []), - ]; - if (!qRuntimeQrl) { - for (const [prop, value] of allProps) { - if (prop.endsWith('$') && value) { - if (!isQrl(value) && !Array.isArray(value)) { - throw new Error( - `The value passed in ${prop}={...}> must be a QRL, instead you passed a "${typeof value}". Make sure your ${typeof value} is wrapped with $(...), so it can be serialized. Like this:\n$(${String( - value - )})` - ); - } - } - if (prop !== 'children' && isQwikC && value) { - verifySerializable( - value, - `The value of the JSX attribute "${prop}" can not be serialized` - ); - } - } - } - if (isString(type)) { - const hasSetInnerHTML = allProps.some((a) => a[0] === 'dangerouslySetInnerHTML'); - if (hasSetInnerHTML && children && (Array.isArray(children) ? children.length > 0 : true)) { - const err = createJSXError( - `The JSX element <${type}> can not have both 'dangerouslySetInnerHTML' and children.`, - node - ); - logError(err); - } - // if (allProps.some((a) => a[0] === 'children')) { - // throw new Error(`The JSX element <${type}> can not have both 'children' as a property.`); - // } - if (type === 'style') { - if (children) { - logOnceWarn(`jsx: Using will escape the content, effectively breaking the CSS. -In order to disable content escaping use '