Skip to content

Commit

Permalink
feat(v2): new signals implementation (#6443)
Browse files Browse the repository at this point in the history
feat(v2): new signals implementation (#6443)

---------

Co-authored-by: Wout Mertens <Wout.Mertens@gmail.com>
Co-authored-by: Varixo <admin@varixo.pl>
Co-authored-by: Jack Shelton <thejackshelton@users.noreply.github.com>
  • Loading branch information
4 people committed Sep 21, 2024
1 parent b6ac7d3 commit 6c4c5b9
Show file tree
Hide file tree
Showing 81 changed files with 3,411 additions and 1,360 deletions.
105 changes: 82 additions & 23 deletions packages/docs/src/routes/api/qwik/api.json

Large diffs are not rendered by default.

227 changes: 181 additions & 46 deletions packages/docs/src/routes/api/qwik/index.md

Large diffs are not rendered by default.

88 changes: 54 additions & 34 deletions packages/qwik/src/core/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export interface ClientContainer extends Container2 {
// (undocumented)
qManifestHash: string;
// (undocumented)
renderDone: Promise<void>;
renderDone: Promise<void> | null;
// (undocumented)
rootVNode: _ElementVNode;
}
Expand Down Expand Up @@ -166,6 +166,11 @@ export const componentQrl: <PROPS extends Record<any, any>>(componentQrl: QRL<On
// @public (undocumented)
export type ComputedFn<T> = () => T;

// @public (undocumented)
export interface ComputedSignal<T> extends ReadonlySignal<T> {
force(): void;
}

// @internal (undocumented)
export const _CONST_PROPS: unique symbol;

Expand Down Expand Up @@ -200,11 +205,20 @@ export interface CorrectedToggleEvent extends Event {
readonly prevState: 'open' | 'closed';
}

// @public (undocumented)
export const createComputed$: <T>(qrl: () => T) => ComputedSignal<T>;

// @public (undocumented)
export const createComputedQrl: <T>(qrl: QRL<() => T>) => ComputedSignal<T>;

// @public
export const createContextId: <STATE = unknown>(name: string) => ContextId<STATE>;

// @public @deprecated
export const createSignal: UseSignal;
// @public (undocumented)
export const createSignal: {
<T>(): Signal<T | undefined>;
<T>(value: T): Signal<T>;
};

// @public (undocumented)
export interface CSSProperties extends CSS_2.Properties<string | number>, CSS_2.PropertiesHyphen<string | number> {
Expand Down Expand Up @@ -247,14 +261,14 @@ export interface DialogHTMLAttributes<T extends Element> extends Attrs<'dialog',
//
// @public
export interface DOMAttributes<EL extends Element> extends DOMAttributesBase<EL>, QwikEvents<EL> {
// Warning: (ae-forgotten-export) The symbol "Signal_2" needs to be exported by the entry point index.d.ts
//
// (undocumented)
class?: ClassList | Signal<ClassList> | undefined;
class?: ClassList | Signal_2<ClassList> | 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)
Expand All @@ -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;
Expand Down Expand Up @@ -301,9 +315,7 @@ class DomContainer extends _SharedContainer implements ClientContainer, StoreTra
// (undocumented)
qManifestHash: string;
// (undocumented)
renderDone: Promise<void>;
// (undocumented)
rendering: boolean;
renderDone: Promise<void> | null;
// (undocumented)
resolveContext<T>(host: HostElement, contextId: ContextId<T>): T | undefined;
// (undocumented)
Expand All @@ -323,6 +335,13 @@ export { DomContainer as _DomContainer }
// @public (undocumented)
export type EagernessOptions = 'visible' | 'load' | 'idle';

// @internal (undocumented)
export class _EffectData<T extends Record<string, any> = Record<string, any>> {
constructor(data: T);
// (undocumented)
data: T;
}

// @internal (undocumented)
export type _ElementVNode = [
_VNodeFlags.Element,
Expand Down Expand Up @@ -372,10 +391,10 @@ export const eventQrl: <T>(qrl: QRL<T>) => QRL<T>;
export interface FieldsetHTMLAttributes<T extends Element> 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: <T extends (...args: any) => any>(fn: T, args: Parameters<T>, fnStr?: string) => SignalDerived<ReturnType<T>, Parameters<T>>;
export const _fnSignal: <T extends (...args: any) => any>(fn: T, args: Parameters<T>, fnStr?: string) => WrappedSignal<any>;

// @public (undocumented)
export interface FormHTMLAttributes<T extends Element> extends Attrs<'form', T> {
Expand Down Expand Up @@ -521,8 +540,8 @@ export type IntrinsicSVGElements = {
// @internal (undocumented)
export const _isJSXNode: <T>(n: unknown) => n is JSXNode<T>;

// @public
export const isSignal: <T = unknown>(obj: any) => obj is Signal<T>;
// @public (undocumented)
export const isSignal: (value: any) => value is Signal<unknown>;

// @internal (undocumented)
export function _isStringifiable(value: unknown): value is _Stringifiable;
Expand All @@ -541,7 +560,7 @@ export const _jsxBranch: <T>(input?: T) => T | undefined;
export const _jsxC: (type: any, mutable: any, _flags: any, key: any) => JSXNode<any>;

// @public (undocumented)
export type JSXChildren = string | number | boolean | null | undefined | Function | RegExp | JSXChildren[] | Promise<JSXChildren> | Signal<JSXChildren> | JSXNode;
export type JSXChildren = string | number | boolean | null | undefined | Function | RegExp | JSXChildren[] | Promise<JSXChildren> | Signal_2<JSXChildren> | JSXNode;

// Warning: (ae-forgotten-export) The symbol "JsxDevOpts" needs to be exported by the entry point index.d.ts
//
Expand Down Expand Up @@ -936,7 +955,10 @@ export type QwikVisibleEvent = CustomEvent<IntersectionObserverEntry>;
export type QwikWheelEvent<T = Element> = NativeWheelEvent;

// @public (undocumented)
export type ReadonlySignal<T = unknown> = Readonly<Signal<T>>;
export interface ReadonlySignal<T = unknown> {
// (undocumented)
readonly value: T;
}

// @internal (undocumented)
export const _regSymbol: (symbol: any, hash: string) => any;
Expand Down Expand Up @@ -1025,7 +1047,7 @@ export interface ResourceProps<T> {
// (undocumented)
onResolved: (value: T) => JSXOutput;
// (undocumented)
readonly value: ResourceReturn<T> | Signal<Promise<T> | T> | Promise<T>;
readonly value: ResourceReturn<T> | Signal_2<Promise<T> | T> | Promise<T>;
}

// @public (undocumented)
Expand Down Expand Up @@ -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<string, any>;
// 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<string, any>, locale: string);
Expand All @@ -1113,14 +1131,14 @@ export abstract class _SharedContainer implements Container2 {
abstract setContext<T>(host: HostElement, context: ContextId<T>, value: T): void;
// (undocumented)
abstract setHostProp<T>(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<T>(signal: Signal, sub: Subscriber): T;
trackSignalValue<T>(signal: Signal_2, subscriber: Effect, property: string, data: _EffectData): T;
}

// @public
export interface Signal<T = any> {
export interface Signal<T = any> extends ReadonlySignal<T> {
// (undocumented)
value: T;
}
Expand Down Expand Up @@ -1925,7 +1943,7 @@ export interface UseSignal {
<T>(value: T | (() => T)): Signal<T>;
}

// @public
// @public (undocumented)
export const useSignal: UseSignal;

// @public
Expand Down Expand Up @@ -2016,6 +2034,8 @@ export type _VNode = _ElementVNode | _TextVNode | _VirtualVNode;

// @internal
export const enum _VNodeFlags {
// (undocumented)
Deleted = 32,
// (undocumented)
Element = 1,
// (undocumented)
Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions packages/qwik/src/core/container/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion packages/qwik/src/core/container/pause.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -174,7 +175,7 @@ Task Symbol: ${task.$qrl$.$symbol$}
if (isResourceTask(task)) {
collector.$resources$.push(task.$state$!);
}
cleanupTask(task);
cleanupTask(task as Task);
}
}
}
Expand Down
7 changes: 2 additions & 5 deletions packages/qwik/src/core/container/render.unit.tsx
Original file line number Diff line number Diff line change
@@ -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$(() => {
Expand Down
94 changes: 94 additions & 0 deletions packages/qwik/src/core/debug.ts
Original file line number Diff line number Diff line change
@@ -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 += '</' + value.type + '>';
} else {
str += '/>';
}
}
return str;
} else {
return String(value);
}
};
2 changes: 1 addition & 1 deletion packages/qwik/src/core/examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
Loading

0 comments on commit 6c4c5b9

Please sign in to comment.