Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(v2): new signals implementation #6443

Merged
merged 91 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
c66de56
chore(v2): break circular dependency in imports
mhevery Jun 2, 2024
c8d1d03
WIP
mhevery Jun 2, 2024
e01a0fe
WIP: trying to get computed signals to work
mhevery Jun 2, 2024
3b22893
WIP: tests passing
mhevery Jun 2, 2024
bee49ed
WIP: hook up scheduler
mhevery Jun 2, 2024
e23b4bf
WIP: signals keep track of effects
mhevery Jun 12, 2024
defd5cf
WIP
mhevery Jun 14, 2024
50e4ba3
fixup
wmertens Jun 15, 2024
c7fb41b
wip
wmertens Jun 15, 2024
61220b4
wip fix tests some more
wmertens Jun 15, 2024
e90972f
WIP
mhevery Jun 29, 2024
e832da5
WIP: basic
mhevery Jul 4, 2024
ec285a2
WIP: progress
mhevery Jul 5, 2024
1f7e06a
WIP: another passing test
mhevery Jul 6, 2024
2bd7afe
WIP: use-signal.spec.tsx passing in CSR
mhevery Jul 9, 2024
dbbf276
WIP: serialization refactored, more work needed
mhevery Jul 9, 2024
70c32f6
WIP: most of the use-signal.spec tests pass
mhevery Jul 12, 2024
23a0a66
WIP: use-signal.spec.tsx passing
mhevery Jul 12, 2024
030b4fa
WIP: signals should work started work on stores
mhevery Jul 23, 2024
a4212d7
WIP: update signals
mhevery Jul 23, 2024
2d923a6
fix use-signal tests
Varixo Jul 28, 2024
289ab11
fix some visible task tests
Varixo Jul 28, 2024
4f6045f
wip: use-store.spec passes in CSR mode
mhevery Jul 31, 2024
6a6b609
WIP: basic serialization/deserialization of Store2 working
mhevery Aug 2, 2024
227d9dd
fix store impl
Varixo Aug 6, 2024
087872e
fix useComputed$
Varixo Aug 7, 2024
9e53aa9
fix some use-task and use-visible tests
Varixo Aug 8, 2024
1a53f55
fix ref
Varixo Aug 8, 2024
e91b6dd
fix use computed infinity loop
Varixo Aug 9, 2024
3ef8169
implement qrl resolve chore
Varixo Aug 9, 2024
f287219
remove computed chore
Varixo Aug 9, 2024
4289406
fix v2-signals tests
Varixo Aug 9, 2024
c6cee75
fix isPromise check
Varixo Aug 9, 2024
54aa838
Merge branch 'build/v2' into build/v2-signals
Varixo Aug 9, 2024
9f3c4ee
after merge changes
Varixo Aug 10, 2024
a4ae10a
fix csr use-resource tests
Varixo Aug 10, 2024
a14dc9f
serialization cleanup
Varixo Aug 10, 2024
66185ea
fix some projection tests
Varixo Aug 10, 2024
d6542fa
fix imports
Varixo Aug 10, 2024
ed77433
fix build
Varixo Aug 10, 2024
1b4f133
fix use-task infinity loop
Varixo Aug 10, 2024
39a256a
fix use-resource tests
Varixo Aug 12, 2024
a4dc963
fix get derived signal value
Varixo Aug 14, 2024
33857ae
implement signals cleanup
Varixo Aug 15, 2024
eb917f8
fix execute cleanup task on unmount for ssr
Varixo Aug 16, 2024
03fa2d1
fix store array
Varixo Aug 16, 2024
bdace09
fix build
Varixo Aug 16, 2024
598747e
fix add event to element returned by signal
Varixo Aug 16, 2024
09ab2da
fix no container error
Varixo Aug 17, 2024
4de71a5
fix unnecessary component rerendering
Varixo Aug 18, 2024
a8d830e
replace isSignal with isSignal2 for useresource
Varixo Aug 18, 2024
5c1424c
fix not execute signal when not used
Varixo Aug 18, 2024
6f31c77
fix scoped style id
Varixo Aug 18, 2024
82b7d17
fix projection tests
Varixo Aug 18, 2024
7295bec
fix build
Varixo Aug 18, 2024
80beaf6
fix lint
Varixo Aug 18, 2024
71e388a
change cleanup stale chore call
Varixo Aug 18, 2024
19be81b
Revert "change cleanup stale chore call"
Varixo Aug 19, 2024
3260bd7
fix store serialization and resource tests
Varixo Aug 19, 2024
3c8a4a8
add store toJSON
Varixo Aug 19, 2024
ea6ed77
fix set attributes
Varixo Aug 19, 2024
d10846b
fix qrl resolve chore
Varixo Aug 22, 2024
2417c15
add untrackedValue setter
Varixo Aug 22, 2024
ddfe999
fix attributes tests
Varixo Aug 23, 2024
4d9ac35
fix some e2e tests
Varixo Aug 27, 2024
13f8719
remove isRerunning option
Varixo Aug 27, 2024
f67de35
change cleanup vnode chores
Varixo Aug 27, 2024
9aab08e
skip chores on deleted vnodes
Varixo Aug 27, 2024
38afd9d
add failing tests
Varixo Aug 28, 2024
68c0ea7
Merge branch 'build/v2' into build/v2-signals
Varixo Aug 31, 2024
4fad01e
fix build
Varixo Aug 31, 2024
9b5e492
rename DerivedSignal2 to WrappedSignal
Varixo Aug 31, 2024
36d6fec
fix build
Varixo Aug 31, 2024
8eafb46
add failing test
Varixo Sep 6, 2024
d53bed9
serialize ready value instead of dirty marker and fix store destructu…
Varixo Sep 7, 2024
c6ea29d
fix use-computed lazy evaluation
Varixo Sep 7, 2024
b068ab3
replace ':' with Q_PROPS_SEPARATOR
Varixo Sep 7, 2024
ca8842d
rewrite scheduleRender function
Varixo Sep 12, 2024
fcd3e74
Allow assigning undefined as a new prop in store
Varixo Sep 12, 2024
7600594
change store target from empty class to a real object
Varixo Sep 17, 2024
98e9e08
fix delete store property
Varixo Sep 17, 2024
c5fff76
fix ssr store leak
Varixo Sep 18, 2024
d0b88c7
fix api change
Varixo Sep 19, 2024
31e3bc6
fix signal tracking
Varixo Sep 20, 2024
e706929
move visible task test
Varixo Sep 20, 2024
43729aa
implement subscription cleanup
Varixo Sep 21, 2024
1da95e2
fix unit tests
Varixo Sep 21, 2024
823cfd1
remove v2 from v2 signals names
Varixo Sep 21, 2024
5283258
fix types
Varixo Sep 21, 2024
5b15250
fix types and build
Varixo Sep 21, 2024
02ae97a
change effect node prop data impl
Varixo Sep 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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